@dosu/cli 0.60.0 → 0.60.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 +2 -2
  2. package/bin/dosu.js +650 -621
  3. package/package.json +1 -1
package/bin/dosu.js CHANGED
@@ -1083,7 +1083,7 @@ function isNpxInvocation(channel = INSTALL_CHANNEL, env = process.env) {
1083
1083
  function getVersionString() {
1084
1084
  return `v${VERSION}`;
1085
1085
  }
1086
- var VERSION = "0.60.0", INSTALL_CHANNEL = "npm";
1086
+ var VERSION = "0.60.2", INSTALL_CHANNEL = "npm";
1087
1087
 
1088
1088
  // src/debug/logger.ts
1089
1089
  import {
@@ -13800,7 +13800,7 @@ var init_conflicts = __esm(() => {
13800
13800
  });
13801
13801
 
13802
13802
  // src/telemetry/debug-id.ts
13803
- var BUNDLE_DEBUG_ID_PLACEHOLDER = "bd089d5e-a4ab-0f80-6475-6e2164756e21";
13803
+ var BUNDLE_DEBUG_ID_PLACEHOLDER = "49fa5d82-a4a1-8b36-6475-6e2164756e21";
13804
13804
 
13805
13805
  // src/telemetry/telemetry.ts
13806
13806
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -14708,6 +14708,11 @@ var init_telemetry = __esm(() => {
14708
14708
  "quota_exceeded",
14709
14709
  "gateway_rejected",
14710
14710
  "claude_code_missing",
14711
+ "max_turns",
14712
+ "run_failed",
14713
+ "no_result",
14714
+ "timed_out",
14715
+ "sdk_error",
14711
14716
  "error"
14712
14717
  ]);
14713
14718
  GATEWAY_REASONS = new Set([
@@ -58603,13 +58608,16 @@ async function runLearner(options) {
58603
58608
  }
58604
58609
  if (message.subtype !== "success" || message.is_error) {
58605
58610
  logger.debug("learner", `run ${runID} failed: ${text}`);
58606
- return finish("error", "Study run failed; see debug log for details.");
58611
+ if (message.subtype === "error_max_turns") {
58612
+ return finish("max_turns", "Study run hit its turn limit before finishing.");
58613
+ }
58614
+ return finish("run_failed", "Study run failed; see debug log for details.");
58607
58615
  }
58608
58616
  logger.debug("learner", `run ${runID} completed: ${turns} turns, ${notesWritten} suggested pages`);
58609
58617
  return finish("completed", text);
58610
58618
  }
58611
58619
  }
58612
- return finish("error", "Study run ended without a result.");
58620
+ return finish("no_result", "Study run ended without a result.");
58613
58621
  } catch (error) {
58614
58622
  const text = error instanceof Error ? error.message : String(error);
58615
58623
  const gatewayError = classifyGatewayError(text) ?? classifyGatewayRejection(text);
@@ -58617,7 +58625,7 @@ async function runLearner(options) {
58617
58625
  return finish(gatewayError.outcome, gatewayError.message, gatewayError.reason);
58618
58626
  }
58619
58627
  logger.debug("learner", `run ${runID} threw: ${text}`);
58620
- return finish("error", abort.signal.aborted ? "Study run timed out and was aborted." : "Study run failed; see debug log for details.");
58628
+ return abort.signal.aborted ? finish("timed_out", "Study run timed out and was aborted.") : finish("sdk_error", "Study run failed; see debug log for details.");
58621
58629
  } finally {
58622
58630
  clearTimeout(timer);
58623
58631
  configDir.cleanup();
@@ -61633,283 +61641,6 @@ var init_topics = __esm(() => {
61633
61641
  import_picocolors29 = __toESM(require_picocolors(), 1);
61634
61642
  });
61635
61643
 
61636
- // src/commands/upgrade.ts
61637
- import { spawnSync } from "node:child_process";
61638
- import { realpathSync as realpathSync2 } from "node:fs";
61639
- import { homedir as homedir9 } from "node:os";
61640
- import { posix, win32 as win322 } from "node:path";
61641
- function isAbsoluteCommandProcessor(value) {
61642
- return Boolean(value && win322.isAbsolute(value) && win322.basename(value).toLowerCase() === "cmd.exe");
61643
- }
61644
- function windowsCommandProcessor(env = process.env) {
61645
- if (isAbsoluteCommandProcessor(env.ComSpec))
61646
- return env.ComSpec;
61647
- const systemRoot = env.SystemRoot && win322.isAbsolute(env.SystemRoot) ? env.SystemRoot : "C:\\Windows";
61648
- return win322.join(systemRoot, "System32", "cmd.exe");
61649
- }
61650
- function safePackageManagerEnv(env) {
61651
- return {
61652
- ...env,
61653
- COREPACK_ENABLE_NETWORK: "0",
61654
- COREPACK_ENABLE_PROJECT_SPEC: "0",
61655
- YARN_IGNORE_PATH: "1"
61656
- };
61657
- }
61658
- function buildPackageManagerInvocation(manager, action, platform = process.platform, comSpec, env = process.env) {
61659
- const args = action === "locate" ? PACKAGE_MANAGERS[manager].locateArgs : PACKAGE_MANAGERS[manager].installArgs;
61660
- const safeEnv = safePackageManagerEnv(env);
61661
- if (platform === "win32") {
61662
- return {
61663
- command: isAbsoluteCommandProcessor(comSpec) ? comSpec : windowsCommandProcessor(env),
61664
- args: ["/d", "/s", "/c", [manager, ...args].join(" ")],
61665
- env: { ...safeEnv, NoDefaultCurrentDirectoryInExePath: "1" }
61666
- };
61667
- }
61668
- return { command: manager, args: [...args], env: safeEnv };
61669
- }
61670
- function packageRoots(manager, platform, comSpec, env, cwd) {
61671
- const invocation = buildPackageManagerInvocation(manager, "locate", platform, comSpec, env);
61672
- const result = spawnSync(invocation.command, invocation.args, {
61673
- cwd,
61674
- encoding: "utf8",
61675
- env: invocation.env,
61676
- shell: false,
61677
- stdio: ["ignore", "pipe", "ignore"],
61678
- timeout: PROBE_TIMEOUT_MS
61679
- });
61680
- if (result.status !== 0 || typeof result.stdout !== "string")
61681
- return [];
61682
- const path = platform === "win32" ? win322 : posix;
61683
- const lines = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter((line) => path.isAbsolute(line));
61684
- if (manager === "pnpm") {
61685
- return lines.filter((line) => path.basename(line) === "cli" && path.basename(path.dirname(line)) === "@dosu" && path.basename(path.dirname(path.dirname(line))) === "node_modules");
61686
- }
61687
- if (lines.length !== 1)
61688
- return [];
61689
- return [
61690
- manager === "npm" ? path.join(lines[0], "@dosu", "cli") : path.join(lines[0], "node_modules", "@dosu", "cli")
61691
- ];
61692
- }
61693
- function owningPackageManager(entrypoint, platform, comSpec, env = process.env, cwd = homedir9()) {
61694
- const path = platform === "win32" ? win322 : posix;
61695
- if (!entrypoint || !path.isAbsolute(entrypoint))
61696
- return null;
61697
- let realEntrypoint;
61698
- try {
61699
- realEntrypoint = realpathSync2(entrypoint);
61700
- } catch {
61701
- return null;
61702
- }
61703
- const owners = Object.keys(PACKAGE_MANAGERS).filter((manager) => packageRoots(manager, platform, comSpec, env, cwd).some((root) => {
61704
- try {
61705
- const relative = path.relative(realpathSync2(root), realEntrypoint);
61706
- return relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
61707
- } catch {
61708
- return false;
61709
- }
61710
- }));
61711
- return owners.length === 1 ? owners[0] : null;
61712
- }
61713
- function upgradePlan(channel, manager, platform, comSpec, env = process.env) {
61714
- if (channel === "homebrew") {
61715
- return {
61716
- label: "Homebrew",
61717
- command: "brew",
61718
- args: ["upgrade", "dosu-ai/dosu/dosu"],
61719
- manualCommand: BREW_MANUAL_COMMAND
61720
- };
61721
- }
61722
- if (channel === "npm" && manager) {
61723
- const invocation = buildPackageManagerInvocation(manager, "install", platform, comSpec, env);
61724
- return {
61725
- label: PACKAGE_MANAGERS[manager].label,
61726
- ...invocation,
61727
- manualCommand: [manager, ...PACKAGE_MANAGERS[manager].installArgs].join(" ")
61728
- };
61729
- }
61730
- return null;
61731
- }
61732
- function printNonGlobalPackageGuidance() {
61733
- console.log("This Dosu copy is not a uniquely identified global package installation, so it was not changed.");
61734
- console.log(`
61735
- Run the latest version without installing:
61736
- ${NPX_COMMAND}`);
61737
- console.log(`
61738
- Or install Dosu globally with your package manager:`);
61739
- for (const manager of Object.keys(PACKAGE_MANAGERS)) {
61740
- console.log(` ${manager} ${PACKAGE_MANAGERS[manager].installArgs.join(" ")}`);
61741
- }
61742
- }
61743
- function runUpgrade(channel = INSTALL_CHANNEL, options = {}) {
61744
- const platform = options.platform ?? process.platform;
61745
- const env = options.env ?? process.env;
61746
- const cwd = options.cwd ?? homedir9();
61747
- let manager = null;
61748
- if (channel === "npm") {
61749
- if (isNpxInvocation(channel, env)) {
61750
- printNonGlobalPackageGuidance();
61751
- return 1;
61752
- }
61753
- manager = owningPackageManager(options.entrypoint ?? process.argv[1], platform, options.comSpec, env, cwd);
61754
- if (!manager) {
61755
- if (env.DOSU_DEV === "true") {
61756
- console.log("DOSU_DEV=true and this copy is not a global package: skipping the package install.");
61757
- return 0;
61758
- }
61759
- printNonGlobalPackageGuidance();
61760
- return 1;
61761
- }
61762
- }
61763
- const plan = upgradePlan(channel, manager, platform, options.comSpec, env);
61764
- if (!plan) {
61765
- console.log("Automatic upgrades are not available for this installation yet.");
61766
- console.log(`Download the latest release:
61767
- ${RELEASES_URL}`);
61768
- return 1;
61769
- }
61770
- console.log(`Updating Dosu with ${plan.label}...`);
61771
- console.log(` ${plan.manualCommand}
61772
- `);
61773
- const result = spawnSync(plan.command, plan.args, {
61774
- ...channel === "npm" ? { cwd } : {},
61775
- ...plan.env ? { env: plan.env } : {},
61776
- shell: false,
61777
- stdio: "inherit"
61778
- });
61779
- if (result.error || result.status !== 0) {
61780
- console.error(`
61781
- Could not update Dosu automatically.`);
61782
- console.error(`Run manually:
61783
- ${plan.manualCommand}`);
61784
- return result.status && result.status > 0 ? result.status : 1;
61785
- }
61786
- console.log(import_picocolors30.default.green(`
61787
- ✓ Update command completed.`));
61788
- console.log('Run "dosu --version" to verify.');
61789
- return 0;
61790
- }
61791
- function postUpgradeArgs(interactive) {
61792
- return interactive ? ["setup"] : ["mcp", "refresh"];
61793
- }
61794
- function newBinaryInvocation(channel, args, entrypoint = process.argv[1], execPath = process.execPath) {
61795
- if (channel === "npm") {
61796
- return entrypoint ? { command: execPath, args: [entrypoint, ...args] } : null;
61797
- }
61798
- if (channel === "homebrew") {
61799
- return { command: "dosu", args };
61800
- }
61801
- return null;
61802
- }
61803
- function isInteractiveTerminal() {
61804
- return process.stdin.isTTY === true && process.stdout.isTTY === true;
61805
- }
61806
- function setupAgentsWithNewBinary(channel, interactive, options) {
61807
- const invocation = newBinaryInvocation(channel, postUpgradeArgs(interactive), options.entrypoint ?? process.argv[1]);
61808
- if (!invocation) {
61809
- console.error(SETUP_FALLBACK);
61810
- return;
61811
- }
61812
- console.log(interactive ? `
61813
- Running setup with the new version to update your AI agents...
61814
- ` : `
61815
- Refreshing agent MCP configs with the new version...`);
61816
- const result = spawnSync(invocation.command, invocation.args, {
61817
- cwd: options.cwd ?? homedir9(),
61818
- shell: false,
61819
- stdio: "inherit"
61820
- });
61821
- if (result.error || result.status !== 0) {
61822
- console.error(SETUP_FALLBACK);
61823
- }
61824
- }
61825
- async function completeUpgrade(channel = INSTALL_CHANNEL, options = {}) {
61826
- const status = runUpgrade(channel, options);
61827
- if (status !== 0)
61828
- return status;
61829
- const interactive = options.interactive ?? isInteractiveTerminal();
61830
- if (!interactive) {
61831
- console.log("Updating Dosu skills...");
61832
- try {
61833
- const result = await installSkill();
61834
- if (result.success) {
61835
- console.log(import_picocolors30.default.green("✓ Skills updated."));
61836
- } else {
61837
- console.error('Skills could not be refreshed. Run "dosu skill update" to retry.');
61838
- }
61839
- } catch {
61840
- console.error('Skills could not be refreshed. Run "dosu skill update" to retry.');
61841
- }
61842
- }
61843
- setupAgentsWithNewBinary(channel, interactive, options);
61844
- return 0;
61845
- }
61846
- function upgradeCommand() {
61847
- return new Command("upgrade").description("Update Dosu to the latest version, then re-run setup to update your AI agents").action(async () => {
61848
- const status = await completeUpgrade();
61849
- if (status !== 0)
61850
- process.exitCode = status;
61851
- });
61852
- }
61853
- var import_picocolors30, 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.';
61854
- var init_upgrade = __esm(() => {
61855
- init_commander();
61856
- init_skill();
61857
- import_picocolors30 = __toESM(require_picocolors(), 1);
61858
- LATEST_PACKAGE = `${PACKAGE_NAME}@latest`;
61859
- PACKAGE_MANAGERS = {
61860
- npm: {
61861
- label: "npm",
61862
- locateArgs: ["root", "-g"],
61863
- installArgs: ["install", "-g", LATEST_PACKAGE]
61864
- },
61865
- pnpm: {
61866
- label: "pnpm",
61867
- locateArgs: ["list", "-g", "--depth=0", "--parseable", PACKAGE_NAME],
61868
- installArgs: ["add", "-g", LATEST_PACKAGE]
61869
- },
61870
- yarn: {
61871
- label: "Yarn Classic",
61872
- locateArgs: ["--silent", "global", "dir"],
61873
- installArgs: ["global", "add", LATEST_PACKAGE]
61874
- }
61875
- };
61876
- });
61877
-
61878
- // src/debug/follow.ts
61879
- import { closeSync as closeSync6, openSync as openSync6, readSync as readSync5, statSync as statSync8 } from "node:fs";
61880
- function createLogFollower(path, emit) {
61881
- const sizeOf = () => {
61882
- try {
61883
- return statSync8(path).size;
61884
- } catch {
61885
- return 0;
61886
- }
61887
- };
61888
- let offset = sizeOf();
61889
- return {
61890
- poll() {
61891
- const size = sizeOf();
61892
- if (size < offset)
61893
- offset = 0;
61894
- if (size === offset)
61895
- return;
61896
- try {
61897
- const fd = openSync6(path, "r");
61898
- try {
61899
- const buf = Buffer.alloc(size - offset);
61900
- const read = readSync5(fd, buf, 0, buf.length, offset);
61901
- offset += read;
61902
- if (read > 0)
61903
- emit(buf.toString("utf-8", 0, read));
61904
- } finally {
61905
- closeSync6(fd);
61906
- }
61907
- } catch {}
61908
- }
61909
- };
61910
- }
61911
- var init_follow = () => {};
61912
-
61913
61644
  // src/mcp/providers/base.ts
61914
61645
  function createJSONProvider(opts) {
61915
61646
  const buildServer = opts.buildServer ?? defaultBuildServer;
@@ -62441,15 +62172,15 @@ var init_vscode = __esm(() => {
62441
62172
  });
62442
62173
 
62443
62174
  // src/mcp/providers/windsurf.ts
62444
- import { homedir as homedir10 } from "node:os";
62175
+ import { homedir as homedir9 } from "node:os";
62445
62176
  import { join as join36 } from "node:path";
62446
62177
  var WindsurfProvider = () => createJSONProvider({
62447
62178
  providerName: "Windsurf",
62448
62179
  providerID: "windsurf",
62449
62180
  local: false,
62450
62181
  priorityValue: 9,
62451
- paths: [join36(homedir10(), ".codeium", "windsurf")],
62452
- globalPath: join36(homedir10(), ".codeium", "windsurf", "mcp_config.json"),
62182
+ paths: [join36(homedir9(), ".codeium", "windsurf")],
62183
+ globalPath: join36(homedir9(), ".codeium", "windsurf", "mcp_config.json"),
62453
62184
  topKey: "mcpServers"
62454
62185
  });
62455
62186
  var init_windsurf = __esm(() => {
@@ -62458,14 +62189,14 @@ var init_windsurf = __esm(() => {
62458
62189
 
62459
62190
  // src/mcp/providers/zed.ts
62460
62191
  import { rmSync as rmSync6 } from "node:fs";
62461
- import { homedir as homedir11, platform as platform3 } from "node:os";
62192
+ import { homedir as homedir10, platform as platform3 } from "node:os";
62462
62193
  import { join as join37 } from "node:path";
62463
62194
  function zedConfigDir() {
62464
62195
  const os = platform3();
62465
62196
  if (os === "win32")
62466
62197
  return join37(appSupportDir(), "Zed");
62467
62198
  if (os === "darwin")
62468
- return join37(homedir11(), ".config", "zed");
62199
+ return join37(homedir10(), ".config", "zed");
62469
62200
  return join37(appSupportDir(), "zed");
62470
62201
  }
62471
62202
  function zedDataDir() {
@@ -62474,7 +62205,7 @@ function zedDataDir() {
62474
62205
  return join37(appSupportDir(), "Zed");
62475
62206
  if (os === "win32")
62476
62207
  return join37(process.env.LOCALAPPDATA ?? "", "Zed");
62477
- return join37(process.env.XDG_DATA_HOME ?? join37(homedir11(), ".local", "share"), "zed");
62208
+ return join37(process.env.XDG_DATA_HOME ?? join37(homedir10(), ".local", "share"), "zed");
62478
62209
  }
62479
62210
  function legacyDarwinSettingsPath() {
62480
62211
  return platform3() === "darwin" ? join37(appSupportDir(), "Zed", "settings.json") : null;
@@ -62684,29 +62415,29 @@ function buildUpdateHint(channel, npx = false) {
62684
62415
  function styleHint(hint) {
62685
62416
  const match = hint.match(/^(.*)("[^"]+")(.*)$/);
62686
62417
  if (!match)
62687
- return import_picocolors31.default.dim(hint);
62418
+ return import_picocolors30.default.dim(hint);
62688
62419
  const [, before, command, after] = match;
62689
- return [before && import_picocolors31.default.dim(before), import_picocolors31.default.cyan(command), after && import_picocolors31.default.dim(after)].filter(Boolean).join("");
62420
+ return [before && import_picocolors30.default.dim(before), import_picocolors30.default.cyan(command), after && import_picocolors30.default.dim(after)].filter(Boolean).join("");
62690
62421
  }
62691
62422
  function buildUpdateNotice(current, latest, channel, interactive, npx = false, width = process.stderr.columns ?? 80) {
62692
62423
  const hint = buildUpdateHint(channel, npx);
62693
62424
  if (interactive) {
62694
62425
  const content = [
62695
- `Update available: ${import_picocolors31.default.dim(current)} ${import_picocolors31.default.dim("→")} ${import_picocolors31.default.bold(brand(latest))}`,
62426
+ `Update available: ${import_picocolors30.default.dim(current)} ${import_picocolors30.default.dim("→")} ${import_picocolors30.default.bold(brand(latest))}`,
62696
62427
  styleHint(hint)
62697
62428
  ];
62698
62429
  const innerWidth = Math.max(...content.map(visibleWidth2)) + BOX_PADDING * 2;
62699
62430
  const framed = (line) => {
62700
62431
  const pad = innerWidth - visibleWidth2(line);
62701
62432
  const left = Math.floor(pad / 2);
62702
- return `${import_picocolors31.default.yellow("│")}${" ".repeat(left)}${line}${" ".repeat(pad - left)}${import_picocolors31.default.yellow("│")}`;
62433
+ return `${import_picocolors30.default.yellow("│")}${" ".repeat(left)}${line}${" ".repeat(pad - left)}${import_picocolors30.default.yellow("│")}`;
62703
62434
  };
62704
62435
  const box = [
62705
- import_picocolors31.default.yellow(`╭${"─".repeat(innerWidth)}╮`),
62436
+ import_picocolors30.default.yellow(`╭${"─".repeat(innerWidth)}╮`),
62706
62437
  framed(""),
62707
62438
  ...content.map(framed),
62708
62439
  framed(""),
62709
- import_picocolors31.default.yellow(`╰${"─".repeat(innerWidth)}╯`)
62440
+ import_picocolors30.default.yellow(`╰${"─".repeat(innerWidth)}╯`)
62710
62441
  ];
62711
62442
  return `
62712
62443
  ${centerBlock(box, width).join(`
@@ -62754,13 +62485,13 @@ async function checkForUpdates(options = {}) {
62754
62485
  logger.error("update-check", `Update check failed: ${err}`);
62755
62486
  }
62756
62487
  }
62757
- var import_picocolors31, 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;
62488
+ var import_picocolors30, 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;
62758
62489
  var init_update_check = __esm(() => {
62759
62490
  init_config();
62760
62491
  init_logger();
62761
62492
  init_styles();
62762
62493
  init_layout();
62763
- import_picocolors31 = __toESM(require_picocolors(), 1);
62494
+ import_picocolors30 = __toESM(require_picocolors(), 1);
62764
62495
  CHECK_INTERVAL_MS2 = 6 * 60 * 60 * 1000;
62765
62496
  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-]+)*)?$/;
62766
62497
  });
@@ -62809,8 +62540,8 @@ function canRefreshMcp(cfg) {
62809
62540
  }
62810
62541
  function displayNotice2(names) {
62811
62542
  console.error(`
62812
- ${import_picocolors32.default.green(`✓ Dosu ${VERSION}: refreshed MCP config for ${names.join(", ")}`)}
62813
- ` + `${import_picocolors32.default.dim(` Run ${import_picocolors32.default.cyan('"dosu setup"')} to also update hooks and rules, then restart your AI agents.`)}
62543
+ ${import_picocolors31.default.green(`✓ Dosu ${VERSION}: refreshed MCP config for ${names.join(", ")}`)}
62544
+ ` + `${import_picocolors31.default.dim(` Run ${import_picocolors31.default.cyan('"dosu setup"')} to also update hooks and rules, then restart your AI agents.`)}
62814
62545
  `);
62815
62546
  }
62816
62547
  function checkForMcpRefresh(options = {}) {
@@ -62838,21 +62569,21 @@ function checkForMcpRefresh(options = {}) {
62838
62569
  logger.error("mcp-refresh", `MCP refresh check failed: ${err}`);
62839
62570
  }
62840
62571
  }
62841
- var import_picocolors32, CACHE_FILENAME4 = "mcp-refresh.json", MCP_FORMAT_CHANGES;
62572
+ var import_picocolors31, CACHE_FILENAME4 = "mcp-refresh.json", MCP_FORMAT_CHANGES;
62842
62573
  var init_mcp_refresh_check = __esm(() => {
62843
62574
  init_config();
62844
62575
  init_logger();
62845
62576
  init_refresh();
62846
62577
  init_update_check();
62847
- import_picocolors32 = __toESM(require_picocolors(), 1);
62578
+ import_picocolors31 = __toESM(require_picocolors(), 1);
62848
62579
  MCP_FORMAT_CHANGES = ["0.53.0"];
62849
62580
  });
62850
62581
 
62851
62582
  // src/version/skill-update-check.ts
62852
62583
  function displayNotice3() {
62853
62584
  console.error(`
62854
- ${import_picocolors33.default.green(`✓ Dosu ${VERSION}: refreshed the bundled agent skills`)}
62855
- ` + `${import_picocolors33.default.dim(" Restart your AI agents to pick up the new skill content.")}
62585
+ ${import_picocolors32.default.green(`✓ Dosu ${VERSION}: refreshed the bundled agent skills`)}
62586
+ ` + `${import_picocolors32.default.dim(" Restart your AI agents to pick up the new skill content.")}
62856
62587
  `);
62857
62588
  }
62858
62589
  function checkForSkillUpdates(options = {}) {
@@ -62873,289 +62604,11 @@ function checkForSkillUpdates(options = {}) {
62873
62604
  logger.error("skill-update-check", `Skill update check failed: ${err}`);
62874
62605
  }
62875
62606
  }
62876
- var import_picocolors33;
62607
+ var import_picocolors32;
62877
62608
  var init_skill_update_check = __esm(() => {
62878
62609
  init_skill();
62879
62610
  init_logger();
62880
- import_picocolors33 = __toESM(require_picocolors(), 1);
62881
- });
62882
-
62883
- // src/rules/installer.ts
62884
- import { existsSync as existsSync25, mkdirSync as mkdirSync15, readFileSync as readFileSync24, unlinkSync as unlinkSync5, writeFileSync as writeFileSync16 } from "node:fs";
62885
- import { homedir as homedir12 } from "node:os";
62886
- import { dirname as dirname12, join as join40 } from "node:path";
62887
- function claudeConfigDir4() {
62888
- return process.env.CLAUDE_CONFIG_DIR || join40(homedir12(), ".claude");
62889
- }
62890
- function codexHome4() {
62891
- return process.env.CODEX_HOME || join40(homedir12(), ".codex");
62892
- }
62893
- function isRuleAgent(agent) {
62894
- return Object.hasOwn(RULE_TARGETS, agent);
62895
- }
62896
- function rulePathForAgent(agent) {
62897
- return isRuleAgent(agent) ? RULE_TARGETS[agent].path() : null;
62898
- }
62899
- function normalizeRule(content) {
62900
- return `${content.trim()}
62901
- `;
62902
- }
62903
- async function fetchDosuRule(fetchImpl = globalThis.fetch) {
62904
- for (const url of DOSU_RULE_URLS) {
62905
- try {
62906
- const response = await fetchImpl(url);
62907
- if (!response.ok)
62908
- continue;
62909
- const content = await response.text();
62910
- if (content.trim())
62911
- return normalizeRule(content);
62912
- } catch {}
62913
- }
62914
- return normalizeRule(FALLBACK_DOSU_RULE);
62915
- }
62916
- function ensureParent(path) {
62917
- mkdirSync15(dirname12(path), { recursive: true });
62918
- }
62919
- function writeIfChanged(path, content) {
62920
- if (!existsSync25(path)) {
62921
- ensureParent(path);
62922
- writeFileSync16(path, content, "utf-8");
62923
- return "created";
62924
- }
62925
- if (readFileSync24(path, "utf-8") === content)
62926
- return "unchanged";
62927
- writeFileSync16(path, content, "utf-8");
62928
- return "updated";
62929
- }
62930
- function findSection(content) {
62931
- const startMatch = content.match(DOSU_RULE_SECTION_START_RE);
62932
- const start = startMatch?.index ?? -1;
62933
- const end = content.indexOf(DOSU_RULE_SECTION_END, Math.max(start, 0));
62934
- if (start === -1 && end === -1)
62935
- return null;
62936
- if (start === -1 || end < start) {
62937
- throw new Error("Dosu rule markers are incomplete; refusing to overwrite the instruction file");
62938
- }
62939
- return { start, end };
62940
- }
62941
- function lineEndingFor(content) {
62942
- return content.includes(`\r
62943
- `) ? `\r
62944
- ` : `
62945
- `;
62946
- }
62947
- function buildSection(content, lineEnding = `
62948
- `) {
62949
- const body = content.trimEnd().replace(/\r?\n/g, lineEnding);
62950
- return `${DOSU_RULE_SECTION_START}${lineEnding}${body}${lineEnding}${DOSU_RULE_SECTION_END}`;
62951
- }
62952
- function upsertSection(path, content) {
62953
- if (!existsSync25(path)) {
62954
- const section = buildSection(content);
62955
- ensureParent(path);
62956
- writeFileSync16(path, `${section}
62957
- `, "utf-8");
62958
- return "created";
62959
- }
62960
- const existing = readFileSync24(path, "utf-8");
62961
- const lineEnding = lineEndingFor(existing);
62962
- const section = buildSection(content, lineEnding);
62963
- const location = findSection(existing);
62964
- let next;
62965
- if (location) {
62966
- next = existing.slice(0, location.start) + section + existing.slice(location.end + DOSU_RULE_SECTION_END.length);
62967
- } else {
62968
- const separator = existing.length > 0 && !existing.endsWith(lineEnding) ? `${lineEnding}${lineEnding}` : existing.length > 0 ? lineEnding : "";
62969
- next = `${existing}${separator}${section}${lineEnding}`;
62970
- }
62971
- if (next === existing)
62972
- return "unchanged";
62973
- writeFileSync16(path, next, "utf-8");
62974
- return "updated";
62975
- }
62976
- function installRuleForAgent(agent, content) {
62977
- if (!isRuleAgent(agent))
62978
- return null;
62979
- const target = RULE_TARGETS[agent];
62980
- const path = target.path();
62981
- const normalized = normalizeRule(content);
62982
- const action = target.kind === "file" ? writeIfChanged(path, target.cursorFrontmatter ? `${CURSOR_FRONTMATTER}${normalized}` : normalized) : upsertSection(path, normalized);
62983
- return { agent, path, action };
62984
- }
62985
- function removeRuleForAgent(agent) {
62986
- if (!isRuleAgent(agent))
62987
- return null;
62988
- const target = RULE_TARGETS[agent];
62989
- const path = target.path();
62990
- if (!existsSync25(path))
62991
- return { agent, path, action: "not_found" };
62992
- if (target.kind === "file") {
62993
- unlinkSync5(path);
62994
- return { agent, path, action: "removed" };
62995
- }
62996
- const existing = readFileSync24(path, "utf-8");
62997
- const lineEnding = lineEndingFor(existing);
62998
- const location = findSection(existing);
62999
- if (!location)
63000
- return { agent, path, action: "not_found" };
63001
- const next = (existing.slice(0, location.start) + existing.slice(location.end + DOSU_RULE_SECTION_END.length)).replace(/(?:\r?\n){3,}/g, `${lineEnding}${lineEnding}`).replace(/^(?:\r?\n)+/, "").trimEnd();
63002
- if (!next) {
63003
- unlinkSync5(path);
63004
- } else {
63005
- writeFileSync16(path, `${next}${lineEnding}`, "utf-8");
63006
- }
63007
- return { agent, path, action: "removed" };
63008
- }
63009
- var DOSU_RULE_SECTION_VERSION = 1, DOSU_RULE_SECTION_START, DOSU_RULE_SECTION_END = "<!-- dosu:rules:end -->", DOSU_RULE_SECTION_START_RE, CURSOR_FRONTMATTER = `---
63010
- alwaysApply: true
63011
- ---
63012
-
63013
- `, DOSU_RULE_URLS, FALLBACK_DOSU_RULE = `The team you are assisting maintains shared knowledge in Dosu: consult it to build on prior work, and contribute durable knowledge so future teammates and agents do not have to rediscover it. Always use only tools currently listed by the server.
63014
-
63015
- When \`read_knowledge\` is listed, call it before non-trivial code or documentation work involving architecture, conventions, prior decisions, gotchas, incidents, ownership, or branch history. **If unsure whether relevant context exists, read first.** Pass \`repo\` and \`branch\` when available. Skip generic questions, trivial or self-contained edits, and context already injected by Dosu.
63016
-
63017
- When \`write_knowledge\` is listed, use it after the task for durable, non-obvious knowledge that future work would otherwise have to rediscover. Do not save task or PR summaries, progress, test results, obvious facts, speculation, duplicates, or sensitive data. **If nothing durable was learned, do not write.**
63018
-
63019
- Use \`review_knowledge\` only when the user asks to inspect or manage pending knowledge. Preview one item at a time and require explicit confirmation before making changes.
63020
-
63021
- When \`read_knowledge\` or \`write_knowledge\` returned a \`receipt_item_id\` this turn, call \`finalize_session_knowledge\` exactly once at the end of the turn — after completing the task, immediately before your final reply — passing all receipt_item_ids from this turn. Never call it when the current turn produced no receipt_item_id, and never call it more than once per turn.
63022
- `, RULE_TARGETS;
63023
- var init_installer = __esm(() => {
63024
- DOSU_RULE_SECTION_START = `<!-- dosu:rules:start v${DOSU_RULE_SECTION_VERSION} -->`;
63025
- DOSU_RULE_SECTION_START_RE = /<!-- dosu:rules:start(?: v\d+)? -->/;
63026
- DOSU_RULE_URLS = [
63027
- "https://raw.githubusercontent.com/dosu-ai/dosu-cli/main/rules/dosu.md",
63028
- "https://raw.githubusercontent.com/dosu-ai/dosu-cli/master/rules/dosu.md"
63029
- ];
63030
- RULE_TARGETS = {
63031
- claude: {
63032
- kind: "file",
63033
- path: () => join40(claudeConfigDir4(), "rules", "dosu.md")
63034
- },
63035
- cursor: {
63036
- kind: "file",
63037
- path: () => join40(homedir12(), ".cursor", "rules", "dosu.mdc"),
63038
- cursorFrontmatter: true
63039
- },
63040
- codex: {
63041
- kind: "section",
63042
- path: () => join40(codexHome4(), "AGENTS.md")
63043
- },
63044
- opencode: {
63045
- kind: "section",
63046
- path: () => join40(homedir12(), ".config", "opencode", "AGENTS.md")
63047
- },
63048
- gemini: {
63049
- kind: "section",
63050
- path: () => join40(homedir12(), ".gemini", "GEMINI.md")
63051
- },
63052
- antigravity: {
63053
- kind: "section",
63054
- path: () => join40(homedir12(), ".gemini", "GEMINI.md")
63055
- }
63056
- };
63057
- });
63058
-
63059
- // src/setup/agents-md-step.ts
63060
- import { execSync as execSync2 } from "node:child_process";
63061
- import { existsSync as existsSync26, readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "node:fs";
63062
- import { join as join41 } from "node:path";
63063
- function inGitWorkTree(cwd = process.cwd()) {
63064
- try {
63065
- const stdout = execSync2("git rev-parse --is-inside-work-tree", {
63066
- cwd,
63067
- stdio: ["ignore", "pipe", "ignore"]
63068
- });
63069
- return stdout.toString().trim() === "true";
63070
- } catch {
63071
- return false;
63072
- }
63073
- }
63074
- function dosuAgentsSectionState(cwd = process.cwd()) {
63075
- try {
63076
- const path = join41(cwd, "AGENTS.md");
63077
- if (!existsSync26(path))
63078
- return "missing";
63079
- const content = readFileSync25(path, "utf-8");
63080
- const startMatch = content.match(SECTION_START_RE);
63081
- if (!startMatch || !content.includes(DOSU_SECTION_END))
63082
- return "missing";
63083
- return Number(startMatch[1] ?? 0) >= DOSU_SECTION_VERSION ? "current" : "outdated";
63084
- } catch {
63085
- return "missing";
63086
- }
63087
- }
63088
- function lineEndingFor2(content) {
63089
- return content.includes(`\r
63090
- `) ? `\r
63091
- ` : `
63092
- `;
63093
- }
63094
- function renderDosuAgentsSection(content, lineEnding) {
63095
- const body = content.trim().replace(/\r?\n/g, lineEnding);
63096
- return `${DOSU_SECTION_START}${lineEnding}${body}${lineEnding}${DOSU_SECTION_END}`;
63097
- }
63098
- function buildDosuAgentsSection(content = FALLBACK_DOSU_RULE) {
63099
- return renderDosuAgentsSection(content, `
63100
- `);
63101
- }
63102
- function findSection2(content) {
63103
- const startMatch = content.match(SECTION_START_RE);
63104
- const start = startMatch?.index ?? -1;
63105
- const end = content.indexOf(DOSU_SECTION_END, Math.max(start, 0));
63106
- if (start === -1 && end === -1)
63107
- return null;
63108
- if (start === -1 || end < start) {
63109
- throw new Error("Dosu AGENTS.md markers are incomplete; refusing to overwrite the file");
63110
- }
63111
- return { start, end };
63112
- }
63113
- function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RULE) {
63114
- const path = join41(cwd, "AGENTS.md");
63115
- if (!existsSync26(path)) {
63116
- const section = buildDosuAgentsSection(content);
63117
- writeFileSync17(path, `${section}
63118
- `);
63119
- return { path, action: "created" };
63120
- }
63121
- const existing = readFileSync25(path, "utf-8");
63122
- const lineEnding = lineEndingFor2(existing);
63123
- const section = renderDosuAgentsSection(content, lineEnding);
63124
- const location = findSection2(existing);
63125
- let next;
63126
- if (location) {
63127
- next = existing.slice(0, location.start) + section + existing.slice(location.end + DOSU_SECTION_END.length);
63128
- } else {
63129
- next = `${existing.trimEnd()}${lineEnding}${lineEnding}${section}${lineEnding}`;
63130
- }
63131
- if (next === existing)
63132
- return { path, action: "unchanged" };
63133
- writeFileSync17(path, next);
63134
- return { path, action: "updated" };
63135
- }
63136
- async function stepUpdateAgentsMd(cwd = process.cwd(), content) {
63137
- logger.info("setup", "Step: update AGENTS.md");
63138
- try {
63139
- const result = upsertDosuAgentsSection(cwd, content ?? await fetchDosuRule());
63140
- logger.info("setup", `AGENTS.md ${result.action} at ${result.path}`);
63141
- const status = result.action === "unchanged" ? "already up to date" : result.action;
63142
- log2.success(formatSetupSummary("AGENTS.md", [{ path: result.path, status }]));
63143
- return true;
63144
- } catch (err) {
63145
- const msg = err instanceof Error ? err.message : String(err);
63146
- logger.error("setup", `AGENTS.md update failed: ${msg}`);
63147
- log2.error(`Could not update AGENTS.md: ${msg}`);
63148
- return false;
63149
- }
63150
- }
63151
- var DOSU_SECTION_VERSION = 2, DOSU_SECTION_START, DOSU_SECTION_END = "<!-- dosu:mcp:end -->", SECTION_START_RE;
63152
- var init_agents_md_step = __esm(() => {
63153
- init_logger();
63154
- init_installer();
63155
- init_prompts();
63156
- init_styles();
63157
- DOSU_SECTION_START = `<!-- dosu:mcp:start v${DOSU_SECTION_VERSION} -->`;
63158
- SECTION_START_RE = /<!-- dosu:mcp:start(?: v(\d+))? -->/;
62611
+ import_picocolors32 = __toESM(require_picocolors(), 1);
63159
62612
  });
63160
62613
 
63161
62614
  // src/auth/errors.ts
@@ -63185,16 +62638,51 @@ var init_errors3 = __esm(() => {
63185
62638
  // src/mcp/constants.ts
63186
62639
  var MCP_PROVIDER_SLUG2 = "dosu_mcp";
63187
62640
 
62641
+ // src/debug/follow.ts
62642
+ import { closeSync as closeSync6, openSync as openSync6, readSync as readSync5, statSync as statSync8 } from "node:fs";
62643
+ function createLogFollower(path, emit) {
62644
+ const sizeOf = () => {
62645
+ try {
62646
+ return statSync8(path).size;
62647
+ } catch {
62648
+ return 0;
62649
+ }
62650
+ };
62651
+ let offset = sizeOf();
62652
+ return {
62653
+ poll() {
62654
+ const size = sizeOf();
62655
+ if (size < offset)
62656
+ offset = 0;
62657
+ if (size === offset)
62658
+ return;
62659
+ try {
62660
+ const fd = openSync6(path, "r");
62661
+ try {
62662
+ const buf = Buffer.alloc(size - offset);
62663
+ const read = readSync5(fd, buf, 0, buf.length, offset);
62664
+ offset += read;
62665
+ if (read > 0)
62666
+ emit(buf.toString("utf-8", 0, read));
62667
+ } finally {
62668
+ closeSync6(fd);
62669
+ }
62670
+ } catch {}
62671
+ }
62672
+ };
62673
+ }
62674
+ var init_follow = () => {};
62675
+
63188
62676
  // src/sessions/session-title.ts
63189
- import { existsSync as existsSync27, mkdirSync as mkdirSync16, readFileSync as readFileSync26, renameSync as renameSync6, statSync as statSync9, writeFileSync as writeFileSync18 } from "node:fs";
63190
- import { homedir as homedir13 } from "node:os";
63191
- import { join as join42 } from "node:path";
62677
+ import { existsSync as existsSync25, mkdirSync as mkdirSync15, readFileSync as readFileSync24, renameSync as renameSync6, statSync as statSync9, writeFileSync as writeFileSync16 } from "node:fs";
62678
+ import { homedir as homedir11 } from "node:os";
62679
+ import { join as join40 } from "node:path";
63192
62680
  function cachePath2(configDir) {
63193
- return join42(configDir, CACHE_FILENAME5);
62681
+ return join40(configDir, CACHE_FILENAME5);
63194
62682
  }
63195
62683
  function loadCacheFile2(configDir) {
63196
62684
  try {
63197
- const raw = JSON.parse(readFileSync26(cachePath2(configDir), "utf-8"));
62685
+ const raw = JSON.parse(readFileSync24(cachePath2(configDir), "utf-8"));
63198
62686
  if (raw.schema_version !== CACHE_SCHEMA_VERSION2 || typeof raw.entries !== "object")
63199
62687
  return {};
63200
62688
  return raw.entries;
@@ -63204,12 +62692,12 @@ function loadCacheFile2(configDir) {
63204
62692
  }
63205
62693
  function saveCacheFile2(configDir, entries) {
63206
62694
  try {
63207
- if (!existsSync27(configDir))
63208
- mkdirSync16(configDir, { recursive: true, mode: 448 });
62695
+ if (!existsSync25(configDir))
62696
+ mkdirSync15(configDir, { recursive: true, mode: 448 });
63209
62697
  const path = cachePath2(configDir);
63210
62698
  const tmp = `${path}.${process.pid}.tmp`;
63211
62699
  const file = { schema_version: CACHE_SCHEMA_VERSION2, entries };
63212
- writeFileSync18(tmp, JSON.stringify(file), { mode: 384 });
62700
+ writeFileSync16(tmp, JSON.stringify(file), { mode: 384 });
63213
62701
  renameSync6(tmp, path);
63214
62702
  } catch {}
63215
62703
  }
@@ -63242,15 +62730,15 @@ function claudeSummaryFromHead(text) {
63242
62730
  }
63243
62731
  return null;
63244
62732
  }
63245
- function reconstructSession(harness, id, slug, home = homedir13(), exists = existsSync27) {
62733
+ function reconstructSession(harness, id, slug, home = homedir11(), exists = existsSync25) {
63246
62734
  if (!slug)
63247
62735
  return null;
63248
62736
  const candidates = [];
63249
62737
  if (harness === "claude") {
63250
- candidates.push(join42(home, ".claude", "projects", slug, `${id}.jsonl`));
62738
+ candidates.push(join40(home, ".claude", "projects", slug, `${id}.jsonl`));
63251
62739
  } else if (harness === "cursor") {
63252
- const project = join42(home, ".cursor", "projects", slug);
63253
- candidates.push(join42(project, "agent-transcripts", id, `${id}.jsonl`), join42(project, "agent-transcripts", `${id}.jsonl`));
62740
+ const project = join40(home, ".cursor", "projects", slug);
62741
+ candidates.push(join40(project, "agent-transcripts", id, `${id}.jsonl`), join40(project, "agent-transcripts", `${id}.jsonl`));
63254
62742
  } else {
63255
62743
  return null;
63256
62744
  }
@@ -63262,7 +62750,7 @@ function reconstructSession(harness, id, slug, home = homedir13(), exists = exis
63262
62750
  }
63263
62751
  function readHead2(path) {
63264
62752
  try {
63265
- return readFileSync26(path, "utf-8").slice(0, 128 * 1024);
62753
+ return readFileSync24(path, "utf-8").slice(0, 128 * 1024);
63266
62754
  } catch {
63267
62755
  return null;
63268
62756
  }
@@ -63342,7 +62830,7 @@ var init_alt_screen = __esm(() => {
63342
62830
  });
63343
62831
 
63344
62832
  // src/tui/activity-view.ts
63345
- import { existsSync as existsSync28, readFileSync as readFileSync27 } from "node:fs";
62833
+ import { existsSync as existsSync26, readFileSync as readFileSync25 } from "node:fs";
63346
62834
  import { basename as basename5 } from "node:path";
63347
62835
  function reduceActivityViewKey(key) {
63348
62836
  if (key === "q" || key === ESC7 || key === CTRL_C4)
@@ -63483,15 +62971,15 @@ function statusLine(status) {
63483
62971
  if (status.running) {
63484
62972
  const since = status.startedAt ? ` · since ${localTime(status.startedAt)}` : "";
63485
62973
  const pid = status.pid !== undefined ? ` (pid ${status.pid})` : "";
63486
- return `\uD83D\uDCDA ${import_picocolors34.default.bold(brand("Studying sessions..."))}${import_picocolors34.default.dim(`${pid}${since}`)}`;
62974
+ return `\uD83D\uDCDA ${import_picocolors33.default.bold(brand("Studying sessions..."))}${import_picocolors33.default.dim(`${pid}${since}`)}`;
63487
62975
  }
63488
62976
  if (status.state.paused) {
63489
- return `${import_picocolors34.default.yellow("○")} ${import_picocolors34.default.bold("Paused")} ${import_picocolors34.default.dim("· studying stays off until you resume")}`;
62977
+ return `${import_picocolors33.default.yellow("○")} ${import_picocolors33.default.bold("Paused")} ${import_picocolors33.default.dim("· studying stays off until you resume")}`;
63490
62978
  }
63491
62979
  if (status.staleLock) {
63492
- return `${import_picocolors34.default.yellow("○")} ${import_picocolors34.default.bold("Not running")} ${import_picocolors34.default.dim("· a previous run exited without cleaning up")}`;
62980
+ return `${import_picocolors33.default.yellow("○")} ${import_picocolors33.default.bold("Not running")} ${import_picocolors33.default.dim("· a previous run exited without cleaning up")}`;
63493
62981
  }
63494
- return `${import_picocolors34.default.dim("○")} ${import_picocolors34.default.bold("Idle")} ${import_picocolors34.default.dim("· hooks study new sessions automatically")}`;
62982
+ return `${import_picocolors33.default.dim("○")} ${import_picocolors33.default.bold("Idle")} ${import_picocolors33.default.dim("· hooks study new sessions automatically")}`;
63495
62983
  }
63496
62984
  function progressLine(done, ready, width, notes = 0) {
63497
62985
  const total = done + ready;
@@ -63500,7 +62988,7 @@ function progressLine(done, ready, width, notes = 0) {
63500
62988
  const cells = Math.max(10, Math.min(30, width - 44));
63501
62989
  const ratio = Math.max(0, Math.min(1, done / total));
63502
62990
  const filled = Math.min(cells, Math.round(ratio * cells));
63503
- const bar = brand("█".repeat(filled)) + import_picocolors34.default.dim("░".repeat(cells - filled));
62991
+ const bar = brand("█".repeat(filled)) + import_picocolors33.default.dim("░".repeat(cells - filled));
63504
62992
  const pct = Math.floor(ratio * 100);
63505
62993
  const suffix = notes > 0 ? ` · ${notes} suggested page${notes === 1 ? "" : "s"}` : "";
63506
62994
  return `${bar} ${done}/${total} studied · ${pct}%${suffix}`;
@@ -63539,17 +63027,17 @@ function confirmBox(queuedCount, backlog, width, mode = "start") {
63539
63027
  const verb = mode === "clear" ? "clear" : mode === "stop" ? "stop" : mode === "resume" ? "resume" : "start";
63540
63028
  const maxInner = Math.max(20, Math.min(width, contentWidth()) - 4);
63541
63029
  const rows = [
63542
- import_picocolors34.default.bold(brand(title)),
63543
- ...wrapLine(scope, maxInner, "").map((line) => import_picocolors34.default.dim(line)),
63030
+ import_picocolors33.default.bold(brand(title)),
63031
+ ...wrapLine(scope, maxInner, "").map((line) => import_picocolors33.default.dim(line)),
63544
63032
  "",
63545
- `${import_picocolors34.default.bold("enter")} ${verb} · ${import_picocolors34.default.bold("esc")} cancel`
63033
+ `${import_picocolors33.default.bold("enter")} ${verb} · ${import_picocolors33.default.bold("esc")} cancel`
63546
63034
  ];
63547
63035
  const inner = Math.min(maxInner, Math.max(...rows.map(visibleWidth2)));
63548
63036
  const pad = (row) => row + " ".repeat(Math.max(0, inner - visibleWidth2(row)));
63549
63037
  return [
63550
- import_picocolors34.default.dim(`╭${"─".repeat(inner + 2)}╮`),
63551
- ...rows.map((row) => `${import_picocolors34.default.dim("│")} ${pad(row)} ${import_picocolors34.default.dim("│")}`),
63552
- import_picocolors34.default.dim(`╰${"─".repeat(inner + 2)}╯`)
63038
+ import_picocolors33.default.dim(`╭${"─".repeat(inner + 2)}╮`),
63039
+ ...rows.map((row) => `${import_picocolors33.default.dim("│")} ${pad(row)} ${import_picocolors33.default.dim("│")}`),
63040
+ import_picocolors33.default.dim(`╰${"─".repeat(inner + 2)}╯`)
63553
63041
  ];
63554
63042
  }
63555
63043
  function buildActivityFrame(status, activity, width, backlog = null, pane = DEFAULT_PANE, queued = [], studiedBeforeRun = 0, open = [], runProgress = null, projectNames = {}, sessionNames = {}, frameLines) {
@@ -63565,7 +63053,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63565
63053
  progress = progressLine(studiedDelta + stepDone, Math.max(0, backlog.ready - stepDone), width, runProgress?.notes ?? 0);
63566
63054
  }
63567
63055
  const refusal = !status.running && status.state.last_refusal;
63568
- const refusalLines = refusal ? wrapLine(`! Studying paused: ${refusal.message} (${localTime(refusal.at)})`, width).map((line) => import_picocolors34.default.yellow(line)) : [];
63056
+ const refusalLines = refusal ? wrapLine(`! Studying paused: ${refusal.message} (${localTime(refusal.at)})`, width).map((line) => import_picocolors33.default.yellow(line)) : [];
63569
63057
  const fullRows = Boolean(pane.fullRows);
63570
63058
  const withName = (item, key) => projectNames[key] ? { ...item, project: projectNames[key] } : item;
63571
63059
  const latestPass = new Map;
@@ -63591,8 +63079,8 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63591
63079
  breadcrumb(["home", "activity"], width),
63592
63080
  "",
63593
63081
  statusLine(status),
63594
- import_picocolors34.default.dim(studied),
63595
- ...queueDetail.length > 0 ? wrapLine(queueDetail.join(" · "), width).map((line) => import_picocolors34.default.dim(line)) : [],
63082
+ import_picocolors33.default.dim(studied),
63083
+ ...queueDetail.length > 0 ? wrapLine(queueDetail.join(" · "), width).map((line) => import_picocolors33.default.dim(line)) : [],
63596
63084
  ...progress ? [progress] : [],
63597
63085
  ...refusalLines,
63598
63086
  "",
@@ -63608,7 +63096,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63608
63096
  status.running ? "s stop" : status.state.paused ? "s resume" : "s sync now",
63609
63097
  ...!status.running && status.state.watermark ? ["c clear"] : [],
63610
63098
  "esc back"
63611
- ].join(" · "), width, "").map((line) => import_picocolors34.default.dim(line))
63099
+ ].join(" · "), width, "").map((line) => import_picocolors33.default.dim(line))
63612
63100
  ]
63613
63101
  ];
63614
63102
  const defaultHeight = fullRows ? ACTIVITY_VIEW_FULL_LIST_ROWS : ACTIVITY_VIEW_LIST_LINES;
@@ -63621,7 +63109,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63621
63109
  ({ visible, above, below } = windowList(source, pane.scroll, height));
63622
63110
  display = visible.flatMap((row) => wrapRow(row, width));
63623
63111
  }
63624
- const listRows = display.length > 0 ? display.map((row) => import_picocolors34.default.dim(row)) : [import_picocolors34.default.dim(empty)];
63112
+ const listRows = display.length > 0 ? display.map((row) => import_picocolors33.default.dim(row)) : [import_picocolors33.default.dim(empty)];
63625
63113
  const scrollParts = [];
63626
63114
  if (above > 0)
63627
63115
  scrollParts.push(`↑ ${above} earlier`);
@@ -63630,7 +63118,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63630
63118
  const lines = [
63631
63119
  ...header,
63632
63120
  ...listRows,
63633
- ...scrollParts.length > 0 ? [import_picocolors34.default.dim(clip(scrollParts.join(" · "), width))] : [],
63121
+ ...scrollParts.length > 0 ? [import_picocolors33.default.dim(clip(scrollParts.join(" · "), width))] : [],
63634
63122
  ...footer
63635
63123
  ];
63636
63124
  return { lines, listHeight: height };
@@ -63663,7 +63151,7 @@ function createRowNamer() {
63663
63151
  names.titles[key] = t;
63664
63152
  };
63665
63153
  for (const s of [...backlog.queued, ...backlog.open]) {
63666
- if (!existsSync28(s.path))
63154
+ if (!existsSync26(s.path))
63667
63155
  continue;
63668
63156
  const key = `${s.harness}/${s.id}`;
63669
63157
  const dir = dirs.resolve(s);
@@ -63700,7 +63188,7 @@ function createRowNamer() {
63700
63188
  }
63701
63189
  function defaultReadLog2() {
63702
63190
  try {
63703
- return readFileSync27(logger.getLogPath(), "utf-8");
63191
+ return readFileSync25(logger.getLogPath(), "utf-8");
63704
63192
  } catch {
63705
63193
  return "";
63706
63194
  }
@@ -63885,7 +63373,7 @@ ${CLEAR_BELOW}`);
63885
63373
  input.on("data", onData);
63886
63374
  });
63887
63375
  }
63888
- 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;
63376
+ var import_picocolors33, ESC7, HIDE_CURSOR3, SHOW_CURSOR3, CTRL_C4, KEY_UP4, KEY_DOWN4, KEY_RIGHT3, KEY_LEFT3, CURSOR_HOME, CLEAR_BELOW, CLEAR_EOL, ACTIVITY_VIEW_POLL_MS = 500, ACTIVITY_VIEW_LIST_LINES = 10, ACTIVITY_VIEW_FULL_LIST_ROWS = 5, ACTIVITY_VIEW_BUFFER_LINES = 200, ACTIVITY_VIEW_TABS, DEFAULT_PANE, TITLE_READS_PER_DRAW = 25;
63889
63377
  var init_activity_view = __esm(() => {
63890
63378
  init_follow();
63891
63379
  init_logger();
@@ -63900,7 +63388,7 @@ var init_activity_view = __esm(() => {
63900
63388
  init_alt_screen();
63901
63389
  init_layout();
63902
63390
  init_menu();
63903
- import_picocolors34 = __toESM(require_picocolors(), 1);
63391
+ import_picocolors33 = __toESM(require_picocolors(), 1);
63904
63392
  ESC7 = String.fromCharCode(27);
63905
63393
  HIDE_CURSOR3 = `${ESC7}[?25l`;
63906
63394
  SHOW_CURSOR3 = `${ESC7}[?25h`;
@@ -63916,6 +63404,284 @@ var init_activity_view = __esm(() => {
63916
63404
  DEFAULT_PANE = { tab: "activity", scroll: 0 };
63917
63405
  });
63918
63406
 
63407
+ // src/rules/installer.ts
63408
+ import { existsSync as existsSync27, mkdirSync as mkdirSync16, readFileSync as readFileSync26, unlinkSync as unlinkSync5, writeFileSync as writeFileSync17 } from "node:fs";
63409
+ import { homedir as homedir12 } from "node:os";
63410
+ import { dirname as dirname12, join as join41 } from "node:path";
63411
+ function claudeConfigDir4() {
63412
+ return process.env.CLAUDE_CONFIG_DIR || join41(homedir12(), ".claude");
63413
+ }
63414
+ function codexHome4() {
63415
+ return process.env.CODEX_HOME || join41(homedir12(), ".codex");
63416
+ }
63417
+ function isRuleAgent(agent) {
63418
+ return Object.hasOwn(RULE_TARGETS, agent);
63419
+ }
63420
+ function rulePathForAgent(agent) {
63421
+ return isRuleAgent(agent) ? RULE_TARGETS[agent].path() : null;
63422
+ }
63423
+ function normalizeRule(content) {
63424
+ return `${content.trim()}
63425
+ `;
63426
+ }
63427
+ async function fetchDosuRule(fetchImpl = globalThis.fetch) {
63428
+ for (const url of DOSU_RULE_URLS) {
63429
+ try {
63430
+ const response = await fetchImpl(url);
63431
+ if (!response.ok)
63432
+ continue;
63433
+ const content = await response.text();
63434
+ if (content.trim())
63435
+ return normalizeRule(content);
63436
+ } catch {}
63437
+ }
63438
+ return normalizeRule(FALLBACK_DOSU_RULE);
63439
+ }
63440
+ function ensureParent(path) {
63441
+ mkdirSync16(dirname12(path), { recursive: true });
63442
+ }
63443
+ function writeIfChanged(path, content) {
63444
+ if (!existsSync27(path)) {
63445
+ ensureParent(path);
63446
+ writeFileSync17(path, content, "utf-8");
63447
+ return "created";
63448
+ }
63449
+ if (readFileSync26(path, "utf-8") === content)
63450
+ return "unchanged";
63451
+ writeFileSync17(path, content, "utf-8");
63452
+ return "updated";
63453
+ }
63454
+ function findSection(content) {
63455
+ const startMatch = content.match(DOSU_RULE_SECTION_START_RE);
63456
+ const start = startMatch?.index ?? -1;
63457
+ const end = content.indexOf(DOSU_RULE_SECTION_END, Math.max(start, 0));
63458
+ if (start === -1 && end === -1)
63459
+ return null;
63460
+ if (start === -1 || end < start) {
63461
+ throw new Error("Dosu rule markers are incomplete; refusing to overwrite the instruction file");
63462
+ }
63463
+ return { start, end };
63464
+ }
63465
+ function lineEndingFor(content) {
63466
+ return content.includes(`\r
63467
+ `) ? `\r
63468
+ ` : `
63469
+ `;
63470
+ }
63471
+ function buildSection(content, lineEnding = `
63472
+ `) {
63473
+ const body = content.trimEnd().replace(/\r?\n/g, lineEnding);
63474
+ return `${DOSU_RULE_SECTION_START}${lineEnding}${body}${lineEnding}${DOSU_RULE_SECTION_END}`;
63475
+ }
63476
+ function upsertSection(path, content) {
63477
+ if (!existsSync27(path)) {
63478
+ const section = buildSection(content);
63479
+ ensureParent(path);
63480
+ writeFileSync17(path, `${section}
63481
+ `, "utf-8");
63482
+ return "created";
63483
+ }
63484
+ const existing = readFileSync26(path, "utf-8");
63485
+ const lineEnding = lineEndingFor(existing);
63486
+ const section = buildSection(content, lineEnding);
63487
+ const location = findSection(existing);
63488
+ let next;
63489
+ if (location) {
63490
+ next = existing.slice(0, location.start) + section + existing.slice(location.end + DOSU_RULE_SECTION_END.length);
63491
+ } else {
63492
+ const separator = existing.length > 0 && !existing.endsWith(lineEnding) ? `${lineEnding}${lineEnding}` : existing.length > 0 ? lineEnding : "";
63493
+ next = `${existing}${separator}${section}${lineEnding}`;
63494
+ }
63495
+ if (next === existing)
63496
+ return "unchanged";
63497
+ writeFileSync17(path, next, "utf-8");
63498
+ return "updated";
63499
+ }
63500
+ function installRuleForAgent(agent, content) {
63501
+ if (!isRuleAgent(agent))
63502
+ return null;
63503
+ const target = RULE_TARGETS[agent];
63504
+ const path = target.path();
63505
+ const normalized = normalizeRule(content);
63506
+ const action = target.kind === "file" ? writeIfChanged(path, target.cursorFrontmatter ? `${CURSOR_FRONTMATTER}${normalized}` : normalized) : upsertSection(path, normalized);
63507
+ return { agent, path, action };
63508
+ }
63509
+ function removeRuleForAgent(agent) {
63510
+ if (!isRuleAgent(agent))
63511
+ return null;
63512
+ const target = RULE_TARGETS[agent];
63513
+ const path = target.path();
63514
+ if (!existsSync27(path))
63515
+ return { agent, path, action: "not_found" };
63516
+ if (target.kind === "file") {
63517
+ unlinkSync5(path);
63518
+ return { agent, path, action: "removed" };
63519
+ }
63520
+ const existing = readFileSync26(path, "utf-8");
63521
+ const lineEnding = lineEndingFor(existing);
63522
+ const location = findSection(existing);
63523
+ if (!location)
63524
+ return { agent, path, action: "not_found" };
63525
+ const next = (existing.slice(0, location.start) + existing.slice(location.end + DOSU_RULE_SECTION_END.length)).replace(/(?:\r?\n){3,}/g, `${lineEnding}${lineEnding}`).replace(/^(?:\r?\n)+/, "").trimEnd();
63526
+ if (!next) {
63527
+ unlinkSync5(path);
63528
+ } else {
63529
+ writeFileSync17(path, `${next}${lineEnding}`, "utf-8");
63530
+ }
63531
+ return { agent, path, action: "removed" };
63532
+ }
63533
+ var DOSU_RULE_SECTION_VERSION = 1, DOSU_RULE_SECTION_START, DOSU_RULE_SECTION_END = "<!-- dosu:rules:end -->", DOSU_RULE_SECTION_START_RE, CURSOR_FRONTMATTER = `---
63534
+ alwaysApply: true
63535
+ ---
63536
+
63537
+ `, DOSU_RULE_URLS, FALLBACK_DOSU_RULE = `The team you are assisting maintains shared knowledge in Dosu: consult it to build on prior work, and contribute durable knowledge so future teammates and agents do not have to rediscover it. Always use only tools currently listed by the server.
63538
+
63539
+ When \`read_knowledge\` is listed, call it before non-trivial code or documentation work involving architecture, conventions, prior decisions, gotchas, incidents, ownership, or branch history. **If unsure whether relevant context exists, read first.** Pass \`repo\` and \`branch\` when available. Skip generic questions, trivial or self-contained edits, and context already injected by Dosu.
63540
+
63541
+ When \`write_knowledge\` is listed, use it after the task for durable, non-obvious knowledge that future work would otherwise have to rediscover. Do not save task or PR summaries, progress, test results, obvious facts, speculation, duplicates, or sensitive data. **If nothing durable was learned, do not write.**
63542
+
63543
+ Use \`review_knowledge\` only when the user asks to inspect or manage pending knowledge. Preview one item at a time and require explicit confirmation before making changes.
63544
+
63545
+ When \`read_knowledge\` or \`write_knowledge\` returned a \`receipt_item_id\` this turn, call \`finalize_session_knowledge\` exactly once at the end of the turn — after completing the task, immediately before your final reply — passing all receipt_item_ids from this turn. Never call it when the current turn produced no receipt_item_id, and never call it more than once per turn.
63546
+ `, RULE_TARGETS;
63547
+ var init_installer = __esm(() => {
63548
+ DOSU_RULE_SECTION_START = `<!-- dosu:rules:start v${DOSU_RULE_SECTION_VERSION} -->`;
63549
+ DOSU_RULE_SECTION_START_RE = /<!-- dosu:rules:start(?: v\d+)? -->/;
63550
+ DOSU_RULE_URLS = [
63551
+ "https://raw.githubusercontent.com/dosu-ai/dosu-cli/main/rules/dosu.md",
63552
+ "https://raw.githubusercontent.com/dosu-ai/dosu-cli/master/rules/dosu.md"
63553
+ ];
63554
+ RULE_TARGETS = {
63555
+ claude: {
63556
+ kind: "file",
63557
+ path: () => join41(claudeConfigDir4(), "rules", "dosu.md")
63558
+ },
63559
+ cursor: {
63560
+ kind: "file",
63561
+ path: () => join41(homedir12(), ".cursor", "rules", "dosu.mdc"),
63562
+ cursorFrontmatter: true
63563
+ },
63564
+ codex: {
63565
+ kind: "section",
63566
+ path: () => join41(codexHome4(), "AGENTS.md")
63567
+ },
63568
+ opencode: {
63569
+ kind: "section",
63570
+ path: () => join41(homedir12(), ".config", "opencode", "AGENTS.md")
63571
+ },
63572
+ gemini: {
63573
+ kind: "section",
63574
+ path: () => join41(homedir12(), ".gemini", "GEMINI.md")
63575
+ },
63576
+ antigravity: {
63577
+ kind: "section",
63578
+ path: () => join41(homedir12(), ".gemini", "GEMINI.md")
63579
+ }
63580
+ };
63581
+ });
63582
+
63583
+ // src/setup/agents-md-step.ts
63584
+ import { execSync as execSync2 } from "node:child_process";
63585
+ import { existsSync as existsSync28, readFileSync as readFileSync27, writeFileSync as writeFileSync18 } from "node:fs";
63586
+ import { join as join42 } from "node:path";
63587
+ function inGitWorkTree(cwd = process.cwd()) {
63588
+ try {
63589
+ const stdout = execSync2("git rev-parse --is-inside-work-tree", {
63590
+ cwd,
63591
+ stdio: ["ignore", "pipe", "ignore"]
63592
+ });
63593
+ return stdout.toString().trim() === "true";
63594
+ } catch {
63595
+ return false;
63596
+ }
63597
+ }
63598
+ function dosuAgentsSectionState(cwd = process.cwd()) {
63599
+ try {
63600
+ const path = join42(cwd, "AGENTS.md");
63601
+ if (!existsSync28(path))
63602
+ return "missing";
63603
+ const content = readFileSync27(path, "utf-8");
63604
+ const startMatch = content.match(SECTION_START_RE);
63605
+ if (!startMatch || !content.includes(DOSU_SECTION_END))
63606
+ return "missing";
63607
+ return Number(startMatch[1] ?? 0) >= DOSU_SECTION_VERSION ? "current" : "outdated";
63608
+ } catch {
63609
+ return "missing";
63610
+ }
63611
+ }
63612
+ function lineEndingFor2(content) {
63613
+ return content.includes(`\r
63614
+ `) ? `\r
63615
+ ` : `
63616
+ `;
63617
+ }
63618
+ function renderDosuAgentsSection(content, lineEnding) {
63619
+ const body = content.trim().replace(/\r?\n/g, lineEnding);
63620
+ return `${DOSU_SECTION_START}${lineEnding}${body}${lineEnding}${DOSU_SECTION_END}`;
63621
+ }
63622
+ function buildDosuAgentsSection(content = FALLBACK_DOSU_RULE) {
63623
+ return renderDosuAgentsSection(content, `
63624
+ `);
63625
+ }
63626
+ function findSection2(content) {
63627
+ const startMatch = content.match(SECTION_START_RE);
63628
+ const start = startMatch?.index ?? -1;
63629
+ const end = content.indexOf(DOSU_SECTION_END, Math.max(start, 0));
63630
+ if (start === -1 && end === -1)
63631
+ return null;
63632
+ if (start === -1 || end < start) {
63633
+ throw new Error("Dosu AGENTS.md markers are incomplete; refusing to overwrite the file");
63634
+ }
63635
+ return { start, end };
63636
+ }
63637
+ function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RULE) {
63638
+ const path = join42(cwd, "AGENTS.md");
63639
+ if (!existsSync28(path)) {
63640
+ const section = buildDosuAgentsSection(content);
63641
+ writeFileSync18(path, `${section}
63642
+ `);
63643
+ return { path, action: "created" };
63644
+ }
63645
+ const existing = readFileSync27(path, "utf-8");
63646
+ const lineEnding = lineEndingFor2(existing);
63647
+ const section = renderDosuAgentsSection(content, lineEnding);
63648
+ const location = findSection2(existing);
63649
+ let next;
63650
+ if (location) {
63651
+ next = existing.slice(0, location.start) + section + existing.slice(location.end + DOSU_SECTION_END.length);
63652
+ } else {
63653
+ next = `${existing.trimEnd()}${lineEnding}${lineEnding}${section}${lineEnding}`;
63654
+ }
63655
+ if (next === existing)
63656
+ return { path, action: "unchanged" };
63657
+ writeFileSync18(path, next);
63658
+ return { path, action: "updated" };
63659
+ }
63660
+ async function stepUpdateAgentsMd(cwd = process.cwd(), content) {
63661
+ logger.info("setup", "Step: update AGENTS.md");
63662
+ try {
63663
+ const result = upsertDosuAgentsSection(cwd, content ?? await fetchDosuRule());
63664
+ logger.info("setup", `AGENTS.md ${result.action} at ${result.path}`);
63665
+ const status = result.action === "unchanged" ? "already up to date" : result.action;
63666
+ log2.success(formatSetupSummary("AGENTS.md", [{ path: result.path, status }]));
63667
+ return true;
63668
+ } catch (err) {
63669
+ const msg = err instanceof Error ? err.message : String(err);
63670
+ logger.error("setup", `AGENTS.md update failed: ${msg}`);
63671
+ log2.error(`Could not update AGENTS.md: ${msg}`);
63672
+ return false;
63673
+ }
63674
+ }
63675
+ var DOSU_SECTION_VERSION = 2, DOSU_SECTION_START, DOSU_SECTION_END = "<!-- dosu:mcp:end -->", SECTION_START_RE;
63676
+ var init_agents_md_step = __esm(() => {
63677
+ init_logger();
63678
+ init_installer();
63679
+ init_prompts();
63680
+ init_styles();
63681
+ DOSU_SECTION_START = `<!-- dosu:mcp:start v${DOSU_SECTION_VERSION} -->`;
63682
+ SECTION_START_RE = /<!-- dosu:mcp:start(?: v(\d+))? -->/;
63683
+ });
63684
+
63919
63685
  // src/setup/analytics.ts
63920
63686
  async function trackCliOnboardingEvent(cfg, onboardingRunID, event, properties = {}) {
63921
63687
  try {
@@ -65383,7 +65149,7 @@ async function stepSelectTools(detected) {
65383
65149
  initialValues: preselected,
65384
65150
  statusFor: (id, picked) => {
65385
65151
  if (configuredMap.get(id))
65386
- return picked ? dim("configured") : import_picocolors35.default.yellow("will remove");
65152
+ return picked ? dim("configured") : import_picocolors34.default.yellow("will remove");
65387
65153
  return picked ? brand("will configure") : undefined;
65388
65154
  },
65389
65155
  summary: (picked) => {
@@ -65558,7 +65324,7 @@ ${dim(wrapLog("Run it inside a session to keep that session out of studying. Rem
65558
65324
  log2.success("All agents already configured. No changes needed.");
65559
65325
  }
65560
65326
  }
65561
- var import_picocolors35, OAUTH_ANALYTICS_REASONS, SETUP_HANDSHAKE_TIMEOUT_MS = 1800000, LIBRARY_SELECT_CANCELLED;
65327
+ var import_picocolors34, OAUTH_ANALYTICS_REASONS, SETUP_HANDSHAKE_TIMEOUT_MS = 1800000, LIBRARY_SELECT_CANCELLED;
65562
65328
  var init_flow2 = __esm(() => {
65563
65329
  init_dist();
65564
65330
  init_errors3();
@@ -65583,7 +65349,7 @@ var init_flow2 = __esm(() => {
65583
65349
  init_github_step();
65584
65350
  init_rules_step();
65585
65351
  init_styles();
65586
- import_picocolors35 = __toESM(require_picocolors(), 1);
65352
+ import_picocolors34 = __toESM(require_picocolors(), 1);
65587
65353
  OAUTH_ANALYTICS_REASONS = new Set([
65588
65354
  "access_denied",
65589
65355
  "bad_oauth_state",
@@ -65597,6 +65363,269 @@ var init_flow2 = __esm(() => {
65597
65363
  LIBRARY_SELECT_CANCELLED = Symbol("library-select-cancelled");
65598
65364
  });
65599
65365
 
65366
+ // src/commands/upgrade.ts
65367
+ import { spawnSync } from "node:child_process";
65368
+ import { realpathSync as realpathSync2 } from "node:fs";
65369
+ import { homedir as homedir13 } from "node:os";
65370
+ import { posix, win32 as win322 } from "node:path";
65371
+ function isAbsoluteCommandProcessor(value) {
65372
+ return Boolean(value && win322.isAbsolute(value) && win322.basename(value).toLowerCase() === "cmd.exe");
65373
+ }
65374
+ function windowsCommandProcessor(env = process.env) {
65375
+ if (isAbsoluteCommandProcessor(env.ComSpec))
65376
+ return env.ComSpec;
65377
+ const systemRoot = env.SystemRoot && win322.isAbsolute(env.SystemRoot) ? env.SystemRoot : "C:\\Windows";
65378
+ return win322.join(systemRoot, "System32", "cmd.exe");
65379
+ }
65380
+ function safePackageManagerEnv(env) {
65381
+ return {
65382
+ ...env,
65383
+ COREPACK_ENABLE_NETWORK: "0",
65384
+ COREPACK_ENABLE_PROJECT_SPEC: "0",
65385
+ YARN_IGNORE_PATH: "1"
65386
+ };
65387
+ }
65388
+ function buildPackageManagerInvocation(manager, action, platform = process.platform, comSpec, env = process.env) {
65389
+ const args = action === "locate" ? PACKAGE_MANAGERS[manager].locateArgs : PACKAGE_MANAGERS[manager].installArgs;
65390
+ const safeEnv = safePackageManagerEnv(env);
65391
+ if (platform === "win32") {
65392
+ return {
65393
+ command: isAbsoluteCommandProcessor(comSpec) ? comSpec : windowsCommandProcessor(env),
65394
+ args: ["/d", "/s", "/c", [manager, ...args].join(" ")],
65395
+ env: { ...safeEnv, NoDefaultCurrentDirectoryInExePath: "1" }
65396
+ };
65397
+ }
65398
+ return { command: manager, args: [...args], env: safeEnv };
65399
+ }
65400
+ function packageRoots(manager, platform, comSpec, env, cwd) {
65401
+ const invocation = buildPackageManagerInvocation(manager, "locate", platform, comSpec, env);
65402
+ const result = spawnSync(invocation.command, invocation.args, {
65403
+ cwd,
65404
+ encoding: "utf8",
65405
+ env: invocation.env,
65406
+ shell: false,
65407
+ stdio: ["ignore", "pipe", "ignore"],
65408
+ timeout: PROBE_TIMEOUT_MS
65409
+ });
65410
+ if (result.status !== 0 || typeof result.stdout !== "string")
65411
+ return [];
65412
+ const path = platform === "win32" ? win322 : posix;
65413
+ const lines = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter((line) => path.isAbsolute(line));
65414
+ if (manager === "pnpm") {
65415
+ return lines.filter((line) => path.basename(line) === "cli" && path.basename(path.dirname(line)) === "@dosu" && path.basename(path.dirname(path.dirname(line))) === "node_modules");
65416
+ }
65417
+ if (lines.length !== 1)
65418
+ return [];
65419
+ return [
65420
+ manager === "npm" ? path.join(lines[0], "@dosu", "cli") : path.join(lines[0], "node_modules", "@dosu", "cli")
65421
+ ];
65422
+ }
65423
+ function owningPackageManager(entrypoint, platform, comSpec, env = process.env, cwd = homedir13()) {
65424
+ const path = platform === "win32" ? win322 : posix;
65425
+ if (!entrypoint || !path.isAbsolute(entrypoint))
65426
+ return null;
65427
+ let realEntrypoint;
65428
+ try {
65429
+ realEntrypoint = realpathSync2(entrypoint);
65430
+ } catch {
65431
+ return null;
65432
+ }
65433
+ const owners = Object.keys(PACKAGE_MANAGERS).filter((manager) => packageRoots(manager, platform, comSpec, env, cwd).some((root) => {
65434
+ try {
65435
+ const relative = path.relative(realpathSync2(root), realEntrypoint);
65436
+ return relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
65437
+ } catch {
65438
+ return false;
65439
+ }
65440
+ }));
65441
+ return owners.length === 1 ? owners[0] : null;
65442
+ }
65443
+ function upgradePlan(channel, manager, platform, comSpec, env = process.env) {
65444
+ if (channel === "homebrew") {
65445
+ return {
65446
+ label: "Homebrew",
65447
+ command: "brew",
65448
+ args: ["upgrade", "dosu-ai/dosu/dosu"],
65449
+ manualCommand: BREW_MANUAL_COMMAND
65450
+ };
65451
+ }
65452
+ if (channel === "npm" && manager) {
65453
+ const invocation = buildPackageManagerInvocation(manager, "install", platform, comSpec, env);
65454
+ return {
65455
+ label: PACKAGE_MANAGERS[manager].label,
65456
+ ...invocation,
65457
+ manualCommand: [manager, ...PACKAGE_MANAGERS[manager].installArgs].join(" ")
65458
+ };
65459
+ }
65460
+ return null;
65461
+ }
65462
+ function printNonGlobalPackageGuidance() {
65463
+ console.log("This Dosu copy is not a uniquely identified global package installation, so it was not changed.");
65464
+ console.log(`
65465
+ Run the latest version without installing:
65466
+ ${NPX_COMMAND}`);
65467
+ console.log(`
65468
+ Or install Dosu globally with your package manager:`);
65469
+ for (const manager of Object.keys(PACKAGE_MANAGERS)) {
65470
+ console.log(` ${manager} ${PACKAGE_MANAGERS[manager].installArgs.join(" ")}`);
65471
+ }
65472
+ }
65473
+ function runUpgrade(channel = INSTALL_CHANNEL, options = {}) {
65474
+ const platform = options.platform ?? process.platform;
65475
+ const env = options.env ?? process.env;
65476
+ const cwd = options.cwd ?? homedir13();
65477
+ let manager = null;
65478
+ if (channel === "npm") {
65479
+ if (isNpxInvocation(channel, env)) {
65480
+ printNonGlobalPackageGuidance();
65481
+ return 1;
65482
+ }
65483
+ manager = owningPackageManager(options.entrypoint ?? process.argv[1], platform, options.comSpec, env, cwd);
65484
+ if (!manager) {
65485
+ if (env.DOSU_DEV === "true") {
65486
+ console.log("DOSU_DEV=true and this copy is not a global package: skipping the package install.");
65487
+ return 0;
65488
+ }
65489
+ printNonGlobalPackageGuidance();
65490
+ return 1;
65491
+ }
65492
+ }
65493
+ const plan = upgradePlan(channel, manager, platform, options.comSpec, env);
65494
+ if (!plan) {
65495
+ console.log("Automatic upgrades are not available for this installation yet.");
65496
+ console.log(`Download the latest release:
65497
+ ${RELEASES_URL}`);
65498
+ return 1;
65499
+ }
65500
+ console.log(`Updating Dosu with ${plan.label}...`);
65501
+ console.log(` ${plan.manualCommand}
65502
+ `);
65503
+ const result = spawnSync(plan.command, plan.args, {
65504
+ ...channel === "npm" ? { cwd } : {},
65505
+ ...plan.env ? { env: plan.env } : {},
65506
+ shell: false,
65507
+ stdio: "inherit"
65508
+ });
65509
+ if (result.error || result.status !== 0) {
65510
+ console.error(`
65511
+ Could not update Dosu automatically.`);
65512
+ console.error(`Run manually:
65513
+ ${plan.manualCommand}`);
65514
+ return result.status && result.status > 0 ? result.status : 1;
65515
+ }
65516
+ console.log(import_picocolors35.default.green(`
65517
+ ✓ Update command completed.`));
65518
+ console.log('Run "dosu --version" to verify.');
65519
+ return 0;
65520
+ }
65521
+ function postUpgradeArgs(fromVersion = VERSION) {
65522
+ return ["upgrade", "--finish", fromVersion];
65523
+ }
65524
+ function newBinaryInvocation(channel, args, entrypoint = process.argv[1], execPath = process.execPath) {
65525
+ if (channel === "npm") {
65526
+ return entrypoint ? { command: execPath, args: [entrypoint, ...args] } : null;
65527
+ }
65528
+ if (channel === "homebrew") {
65529
+ return { command: "dosu", args };
65530
+ }
65531
+ return null;
65532
+ }
65533
+ function isInteractiveTerminal() {
65534
+ return process.stdin.isTTY === true && process.stdout.isTTY === true;
65535
+ }
65536
+ function finishWithNewBinary(channel, options) {
65537
+ const invocation = newBinaryInvocation(channel, postUpgradeArgs(), options.entrypoint ?? process.argv[1]);
65538
+ if (!invocation) {
65539
+ console.error(SETUP_FALLBACK);
65540
+ return;
65541
+ }
65542
+ const result = spawnSync(invocation.command, invocation.args, {
65543
+ cwd: options.cwd ?? homedir13(),
65544
+ shell: false,
65545
+ stdio: "inherit"
65546
+ });
65547
+ if (result.error || result.status !== 0) {
65548
+ console.error(SETUP_FALLBACK);
65549
+ }
65550
+ }
65551
+ async function completeUpgrade(channel = INSTALL_CHANNEL, options = {}) {
65552
+ const status = runUpgrade(channel, options);
65553
+ if (status !== 0)
65554
+ return status;
65555
+ finishWithNewBinary(channel, options);
65556
+ return 0;
65557
+ }
65558
+ async function finishUpgrade(from, options = {}) {
65559
+ const previous = SEMVER.test(from) ? from : null;
65560
+ if (!needsMcpRefresh(previous, VERSION)) {
65561
+ checkForSkillUpdates();
65562
+ writeMcpRefreshCache({ version: VERSION });
65563
+ console.log(import_picocolors35.default.green(`✓ Dosu ${VERSION} is ready. Your AI agents need no changes.`));
65564
+ return 0;
65565
+ }
65566
+ if (options.interactive ?? isInteractiveTerminal()) {
65567
+ console.log(`
65568
+ This update changes how agents connect to Dosu. Running setup...
65569
+ `);
65570
+ await Promise.resolve().then(() => init_flow2());
65571
+ await runSetup();
65572
+ return 0;
65573
+ }
65574
+ checkForSkillUpdates();
65575
+ const cfg = loadConfigNonBlocking();
65576
+ if (!cfg || !canRefreshMcp(cfg)) {
65577
+ console.error(SETUP_FALLBACK);
65578
+ return 0;
65579
+ }
65580
+ console.log(`
65581
+ Refreshing agent MCP configs with the new version...`);
65582
+ const result = refreshConfiguredProviders(cfg);
65583
+ writeMcpRefreshCache({ version: VERSION });
65584
+ for (const provider of result.updated)
65585
+ console.log(` ✓ ${provider.name()}`);
65586
+ for (const { provider, error } of result.failed) {
65587
+ console.log(` ✗ ${provider.name()}: ${error.message}`);
65588
+ }
65589
+ if (result.failed.length > 0)
65590
+ console.error(SETUP_FALLBACK);
65591
+ return 0;
65592
+ }
65593
+ function upgradeCommand() {
65594
+ return new Command("upgrade").description("Update Dosu to the latest version, re-running setup when the update changes agent config").addOption(new Option("--finish <from>").hideHelp()).action(async (opts) => {
65595
+ const status = opts.finish !== undefined ? await finishUpgrade(opts.finish) : await completeUpgrade();
65596
+ if (status !== 0)
65597
+ process.exitCode = status;
65598
+ });
65599
+ }
65600
+ var import_picocolors35, 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;
65601
+ var init_upgrade = __esm(() => {
65602
+ init_commander();
65603
+ init_config();
65604
+ init_refresh();
65605
+ init_mcp_refresh_check();
65606
+ init_skill_update_check();
65607
+ import_picocolors35 = __toESM(require_picocolors(), 1);
65608
+ LATEST_PACKAGE = `${PACKAGE_NAME}@latest`;
65609
+ PACKAGE_MANAGERS = {
65610
+ npm: {
65611
+ label: "npm",
65612
+ locateArgs: ["root", "-g"],
65613
+ installArgs: ["install", "-g", LATEST_PACKAGE]
65614
+ },
65615
+ pnpm: {
65616
+ label: "pnpm",
65617
+ locateArgs: ["list", "-g", "--depth=0", "--parseable", PACKAGE_NAME],
65618
+ installArgs: ["add", "-g", LATEST_PACKAGE]
65619
+ },
65620
+ yarn: {
65621
+ label: "Yarn Classic",
65622
+ locateArgs: ["--silent", "global", "dir"],
65623
+ installArgs: ["global", "add", LATEST_PACKAGE]
65624
+ }
65625
+ };
65626
+ SEMVER = /^\d+\.\d+\.\d+/;
65627
+ });
65628
+
65600
65629
  // src/tui/analytics-view.ts
65601
65630
  function analyticsListHeight(rows) {
65602
65631
  const room = frameMaxLines(rows) - ANALYTICS_VIEW_CHROME_LINES;
@@ -67843,4 +67872,4 @@ main().catch((err) => {
67843
67872
  process.exit(1);
67844
67873
  });
67845
67874
 
67846
- //# debugId=bd089d5e-a4ab-0f80-6475-6e2164756e21
67875
+ //# debugId=49fa5d82-a4a1-8b36-6475-6e2164756e21