@dosu/cli 0.60.1 → 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 +639 -618
  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.1", 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 = "9d93d312-f9af-7e47-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";
@@ -61641,283 +61641,6 @@ var init_topics = __esm(() => {
61641
61641
  import_picocolors29 = __toESM(require_picocolors(), 1);
61642
61642
  });
61643
61643
 
61644
- // src/commands/upgrade.ts
61645
- import { spawnSync } from "node:child_process";
61646
- import { realpathSync as realpathSync2 } from "node:fs";
61647
- import { homedir as homedir9 } from "node:os";
61648
- import { posix, win32 as win322 } from "node:path";
61649
- function isAbsoluteCommandProcessor(value) {
61650
- return Boolean(value && win322.isAbsolute(value) && win322.basename(value).toLowerCase() === "cmd.exe");
61651
- }
61652
- function windowsCommandProcessor(env = process.env) {
61653
- if (isAbsoluteCommandProcessor(env.ComSpec))
61654
- return env.ComSpec;
61655
- const systemRoot = env.SystemRoot && win322.isAbsolute(env.SystemRoot) ? env.SystemRoot : "C:\\Windows";
61656
- return win322.join(systemRoot, "System32", "cmd.exe");
61657
- }
61658
- function safePackageManagerEnv(env) {
61659
- return {
61660
- ...env,
61661
- COREPACK_ENABLE_NETWORK: "0",
61662
- COREPACK_ENABLE_PROJECT_SPEC: "0",
61663
- YARN_IGNORE_PATH: "1"
61664
- };
61665
- }
61666
- function buildPackageManagerInvocation(manager, action, platform = process.platform, comSpec, env = process.env) {
61667
- const args = action === "locate" ? PACKAGE_MANAGERS[manager].locateArgs : PACKAGE_MANAGERS[manager].installArgs;
61668
- const safeEnv = safePackageManagerEnv(env);
61669
- if (platform === "win32") {
61670
- return {
61671
- command: isAbsoluteCommandProcessor(comSpec) ? comSpec : windowsCommandProcessor(env),
61672
- args: ["/d", "/s", "/c", [manager, ...args].join(" ")],
61673
- env: { ...safeEnv, NoDefaultCurrentDirectoryInExePath: "1" }
61674
- };
61675
- }
61676
- return { command: manager, args: [...args], env: safeEnv };
61677
- }
61678
- function packageRoots(manager, platform, comSpec, env, cwd) {
61679
- const invocation = buildPackageManagerInvocation(manager, "locate", platform, comSpec, env);
61680
- const result = spawnSync(invocation.command, invocation.args, {
61681
- cwd,
61682
- encoding: "utf8",
61683
- env: invocation.env,
61684
- shell: false,
61685
- stdio: ["ignore", "pipe", "ignore"],
61686
- timeout: PROBE_TIMEOUT_MS
61687
- });
61688
- if (result.status !== 0 || typeof result.stdout !== "string")
61689
- return [];
61690
- const path = platform === "win32" ? win322 : posix;
61691
- const lines = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter((line) => path.isAbsolute(line));
61692
- if (manager === "pnpm") {
61693
- return lines.filter((line) => path.basename(line) === "cli" && path.basename(path.dirname(line)) === "@dosu" && path.basename(path.dirname(path.dirname(line))) === "node_modules");
61694
- }
61695
- if (lines.length !== 1)
61696
- return [];
61697
- return [
61698
- manager === "npm" ? path.join(lines[0], "@dosu", "cli") : path.join(lines[0], "node_modules", "@dosu", "cli")
61699
- ];
61700
- }
61701
- function owningPackageManager(entrypoint, platform, comSpec, env = process.env, cwd = homedir9()) {
61702
- const path = platform === "win32" ? win322 : posix;
61703
- if (!entrypoint || !path.isAbsolute(entrypoint))
61704
- return null;
61705
- let realEntrypoint;
61706
- try {
61707
- realEntrypoint = realpathSync2(entrypoint);
61708
- } catch {
61709
- return null;
61710
- }
61711
- const owners = Object.keys(PACKAGE_MANAGERS).filter((manager) => packageRoots(manager, platform, comSpec, env, cwd).some((root) => {
61712
- try {
61713
- const relative = path.relative(realpathSync2(root), realEntrypoint);
61714
- return relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
61715
- } catch {
61716
- return false;
61717
- }
61718
- }));
61719
- return owners.length === 1 ? owners[0] : null;
61720
- }
61721
- function upgradePlan(channel, manager, platform, comSpec, env = process.env) {
61722
- if (channel === "homebrew") {
61723
- return {
61724
- label: "Homebrew",
61725
- command: "brew",
61726
- args: ["upgrade", "dosu-ai/dosu/dosu"],
61727
- manualCommand: BREW_MANUAL_COMMAND
61728
- };
61729
- }
61730
- if (channel === "npm" && manager) {
61731
- const invocation = buildPackageManagerInvocation(manager, "install", platform, comSpec, env);
61732
- return {
61733
- label: PACKAGE_MANAGERS[manager].label,
61734
- ...invocation,
61735
- manualCommand: [manager, ...PACKAGE_MANAGERS[manager].installArgs].join(" ")
61736
- };
61737
- }
61738
- return null;
61739
- }
61740
- function printNonGlobalPackageGuidance() {
61741
- console.log("This Dosu copy is not a uniquely identified global package installation, so it was not changed.");
61742
- console.log(`
61743
- Run the latest version without installing:
61744
- ${NPX_COMMAND}`);
61745
- console.log(`
61746
- Or install Dosu globally with your package manager:`);
61747
- for (const manager of Object.keys(PACKAGE_MANAGERS)) {
61748
- console.log(` ${manager} ${PACKAGE_MANAGERS[manager].installArgs.join(" ")}`);
61749
- }
61750
- }
61751
- function runUpgrade(channel = INSTALL_CHANNEL, options = {}) {
61752
- const platform = options.platform ?? process.platform;
61753
- const env = options.env ?? process.env;
61754
- const cwd = options.cwd ?? homedir9();
61755
- let manager = null;
61756
- if (channel === "npm") {
61757
- if (isNpxInvocation(channel, env)) {
61758
- printNonGlobalPackageGuidance();
61759
- return 1;
61760
- }
61761
- manager = owningPackageManager(options.entrypoint ?? process.argv[1], platform, options.comSpec, env, cwd);
61762
- if (!manager) {
61763
- if (env.DOSU_DEV === "true") {
61764
- console.log("DOSU_DEV=true and this copy is not a global package: skipping the package install.");
61765
- return 0;
61766
- }
61767
- printNonGlobalPackageGuidance();
61768
- return 1;
61769
- }
61770
- }
61771
- const plan = upgradePlan(channel, manager, platform, options.comSpec, env);
61772
- if (!plan) {
61773
- console.log("Automatic upgrades are not available for this installation yet.");
61774
- console.log(`Download the latest release:
61775
- ${RELEASES_URL}`);
61776
- return 1;
61777
- }
61778
- console.log(`Updating Dosu with ${plan.label}...`);
61779
- console.log(` ${plan.manualCommand}
61780
- `);
61781
- const result = spawnSync(plan.command, plan.args, {
61782
- ...channel === "npm" ? { cwd } : {},
61783
- ...plan.env ? { env: plan.env } : {},
61784
- shell: false,
61785
- stdio: "inherit"
61786
- });
61787
- if (result.error || result.status !== 0) {
61788
- console.error(`
61789
- Could not update Dosu automatically.`);
61790
- console.error(`Run manually:
61791
- ${plan.manualCommand}`);
61792
- return result.status && result.status > 0 ? result.status : 1;
61793
- }
61794
- console.log(import_picocolors30.default.green(`
61795
- ✓ Update command completed.`));
61796
- console.log('Run "dosu --version" to verify.');
61797
- return 0;
61798
- }
61799
- function postUpgradeArgs(interactive) {
61800
- return interactive ? ["setup"] : ["mcp", "refresh"];
61801
- }
61802
- function newBinaryInvocation(channel, args, entrypoint = process.argv[1], execPath = process.execPath) {
61803
- if (channel === "npm") {
61804
- return entrypoint ? { command: execPath, args: [entrypoint, ...args] } : null;
61805
- }
61806
- if (channel === "homebrew") {
61807
- return { command: "dosu", args };
61808
- }
61809
- return null;
61810
- }
61811
- function isInteractiveTerminal() {
61812
- return process.stdin.isTTY === true && process.stdout.isTTY === true;
61813
- }
61814
- function setupAgentsWithNewBinary(channel, interactive, options) {
61815
- const invocation = newBinaryInvocation(channel, postUpgradeArgs(interactive), options.entrypoint ?? process.argv[1]);
61816
- if (!invocation) {
61817
- console.error(SETUP_FALLBACK);
61818
- return;
61819
- }
61820
- console.log(interactive ? `
61821
- Running setup with the new version to update your AI agents...
61822
- ` : `
61823
- Refreshing agent MCP configs with the new version...`);
61824
- const result = spawnSync(invocation.command, invocation.args, {
61825
- cwd: options.cwd ?? homedir9(),
61826
- shell: false,
61827
- stdio: "inherit"
61828
- });
61829
- if (result.error || result.status !== 0) {
61830
- console.error(SETUP_FALLBACK);
61831
- }
61832
- }
61833
- async function completeUpgrade(channel = INSTALL_CHANNEL, options = {}) {
61834
- const status = runUpgrade(channel, options);
61835
- if (status !== 0)
61836
- return status;
61837
- const interactive = options.interactive ?? isInteractiveTerminal();
61838
- if (!interactive) {
61839
- console.log("Updating Dosu skills...");
61840
- try {
61841
- const result = await installSkill();
61842
- if (result.success) {
61843
- console.log(import_picocolors30.default.green("✓ Skills updated."));
61844
- } else {
61845
- console.error('Skills could not be refreshed. Run "dosu skill update" to retry.');
61846
- }
61847
- } catch {
61848
- console.error('Skills could not be refreshed. Run "dosu skill update" to retry.');
61849
- }
61850
- }
61851
- setupAgentsWithNewBinary(channel, interactive, options);
61852
- return 0;
61853
- }
61854
- function upgradeCommand() {
61855
- return new Command("upgrade").description("Update Dosu to the latest version, then re-run setup to update your AI agents").action(async () => {
61856
- const status = await completeUpgrade();
61857
- if (status !== 0)
61858
- process.exitCode = status;
61859
- });
61860
- }
61861
- 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.';
61862
- var init_upgrade = __esm(() => {
61863
- init_commander();
61864
- init_skill();
61865
- import_picocolors30 = __toESM(require_picocolors(), 1);
61866
- LATEST_PACKAGE = `${PACKAGE_NAME}@latest`;
61867
- PACKAGE_MANAGERS = {
61868
- npm: {
61869
- label: "npm",
61870
- locateArgs: ["root", "-g"],
61871
- installArgs: ["install", "-g", LATEST_PACKAGE]
61872
- },
61873
- pnpm: {
61874
- label: "pnpm",
61875
- locateArgs: ["list", "-g", "--depth=0", "--parseable", PACKAGE_NAME],
61876
- installArgs: ["add", "-g", LATEST_PACKAGE]
61877
- },
61878
- yarn: {
61879
- label: "Yarn Classic",
61880
- locateArgs: ["--silent", "global", "dir"],
61881
- installArgs: ["global", "add", LATEST_PACKAGE]
61882
- }
61883
- };
61884
- });
61885
-
61886
- // src/debug/follow.ts
61887
- import { closeSync as closeSync6, openSync as openSync6, readSync as readSync5, statSync as statSync8 } from "node:fs";
61888
- function createLogFollower(path, emit) {
61889
- const sizeOf = () => {
61890
- try {
61891
- return statSync8(path).size;
61892
- } catch {
61893
- return 0;
61894
- }
61895
- };
61896
- let offset = sizeOf();
61897
- return {
61898
- poll() {
61899
- const size = sizeOf();
61900
- if (size < offset)
61901
- offset = 0;
61902
- if (size === offset)
61903
- return;
61904
- try {
61905
- const fd = openSync6(path, "r");
61906
- try {
61907
- const buf = Buffer.alloc(size - offset);
61908
- const read = readSync5(fd, buf, 0, buf.length, offset);
61909
- offset += read;
61910
- if (read > 0)
61911
- emit(buf.toString("utf-8", 0, read));
61912
- } finally {
61913
- closeSync6(fd);
61914
- }
61915
- } catch {}
61916
- }
61917
- };
61918
- }
61919
- var init_follow = () => {};
61920
-
61921
61644
  // src/mcp/providers/base.ts
61922
61645
  function createJSONProvider(opts) {
61923
61646
  const buildServer = opts.buildServer ?? defaultBuildServer;
@@ -62449,15 +62172,15 @@ var init_vscode = __esm(() => {
62449
62172
  });
62450
62173
 
62451
62174
  // src/mcp/providers/windsurf.ts
62452
- import { homedir as homedir10 } from "node:os";
62175
+ import { homedir as homedir9 } from "node:os";
62453
62176
  import { join as join36 } from "node:path";
62454
62177
  var WindsurfProvider = () => createJSONProvider({
62455
62178
  providerName: "Windsurf",
62456
62179
  providerID: "windsurf",
62457
62180
  local: false,
62458
62181
  priorityValue: 9,
62459
- paths: [join36(homedir10(), ".codeium", "windsurf")],
62460
- globalPath: join36(homedir10(), ".codeium", "windsurf", "mcp_config.json"),
62182
+ paths: [join36(homedir9(), ".codeium", "windsurf")],
62183
+ globalPath: join36(homedir9(), ".codeium", "windsurf", "mcp_config.json"),
62461
62184
  topKey: "mcpServers"
62462
62185
  });
62463
62186
  var init_windsurf = __esm(() => {
@@ -62466,14 +62189,14 @@ var init_windsurf = __esm(() => {
62466
62189
 
62467
62190
  // src/mcp/providers/zed.ts
62468
62191
  import { rmSync as rmSync6 } from "node:fs";
62469
- import { homedir as homedir11, platform as platform3 } from "node:os";
62192
+ import { homedir as homedir10, platform as platform3 } from "node:os";
62470
62193
  import { join as join37 } from "node:path";
62471
62194
  function zedConfigDir() {
62472
62195
  const os = platform3();
62473
62196
  if (os === "win32")
62474
62197
  return join37(appSupportDir(), "Zed");
62475
62198
  if (os === "darwin")
62476
- return join37(homedir11(), ".config", "zed");
62199
+ return join37(homedir10(), ".config", "zed");
62477
62200
  return join37(appSupportDir(), "zed");
62478
62201
  }
62479
62202
  function zedDataDir() {
@@ -62482,7 +62205,7 @@ function zedDataDir() {
62482
62205
  return join37(appSupportDir(), "Zed");
62483
62206
  if (os === "win32")
62484
62207
  return join37(process.env.LOCALAPPDATA ?? "", "Zed");
62485
- 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");
62486
62209
  }
62487
62210
  function legacyDarwinSettingsPath() {
62488
62211
  return platform3() === "darwin" ? join37(appSupportDir(), "Zed", "settings.json") : null;
@@ -62692,29 +62415,29 @@ function buildUpdateHint(channel, npx = false) {
62692
62415
  function styleHint(hint) {
62693
62416
  const match = hint.match(/^(.*)("[^"]+")(.*)$/);
62694
62417
  if (!match)
62695
- return import_picocolors31.default.dim(hint);
62418
+ return import_picocolors30.default.dim(hint);
62696
62419
  const [, before, command, after] = match;
62697
- 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("");
62698
62421
  }
62699
62422
  function buildUpdateNotice(current, latest, channel, interactive, npx = false, width = process.stderr.columns ?? 80) {
62700
62423
  const hint = buildUpdateHint(channel, npx);
62701
62424
  if (interactive) {
62702
62425
  const content = [
62703
- `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))}`,
62704
62427
  styleHint(hint)
62705
62428
  ];
62706
62429
  const innerWidth = Math.max(...content.map(visibleWidth2)) + BOX_PADDING * 2;
62707
62430
  const framed = (line) => {
62708
62431
  const pad = innerWidth - visibleWidth2(line);
62709
62432
  const left = Math.floor(pad / 2);
62710
- 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("│")}`;
62711
62434
  };
62712
62435
  const box = [
62713
- import_picocolors31.default.yellow(`╭${"─".repeat(innerWidth)}╮`),
62436
+ import_picocolors30.default.yellow(`╭${"─".repeat(innerWidth)}╮`),
62714
62437
  framed(""),
62715
62438
  ...content.map(framed),
62716
62439
  framed(""),
62717
- import_picocolors31.default.yellow(`╰${"─".repeat(innerWidth)}╯`)
62440
+ import_picocolors30.default.yellow(`╰${"─".repeat(innerWidth)}╯`)
62718
62441
  ];
62719
62442
  return `
62720
62443
  ${centerBlock(box, width).join(`
@@ -62762,13 +62485,13 @@ async function checkForUpdates(options = {}) {
62762
62485
  logger.error("update-check", `Update check failed: ${err}`);
62763
62486
  }
62764
62487
  }
62765
- 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;
62766
62489
  var init_update_check = __esm(() => {
62767
62490
  init_config();
62768
62491
  init_logger();
62769
62492
  init_styles();
62770
62493
  init_layout();
62771
- import_picocolors31 = __toESM(require_picocolors(), 1);
62494
+ import_picocolors30 = __toESM(require_picocolors(), 1);
62772
62495
  CHECK_INTERVAL_MS2 = 6 * 60 * 60 * 1000;
62773
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-]+)*)?$/;
62774
62497
  });
@@ -62817,8 +62540,8 @@ function canRefreshMcp(cfg) {
62817
62540
  }
62818
62541
  function displayNotice2(names) {
62819
62542
  console.error(`
62820
- ${import_picocolors32.default.green(`✓ Dosu ${VERSION}: refreshed MCP config for ${names.join(", ")}`)}
62821
- ` + `${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.`)}
62822
62545
  `);
62823
62546
  }
62824
62547
  function checkForMcpRefresh(options = {}) {
@@ -62846,21 +62569,21 @@ function checkForMcpRefresh(options = {}) {
62846
62569
  logger.error("mcp-refresh", `MCP refresh check failed: ${err}`);
62847
62570
  }
62848
62571
  }
62849
- var import_picocolors32, CACHE_FILENAME4 = "mcp-refresh.json", MCP_FORMAT_CHANGES;
62572
+ var import_picocolors31, CACHE_FILENAME4 = "mcp-refresh.json", MCP_FORMAT_CHANGES;
62850
62573
  var init_mcp_refresh_check = __esm(() => {
62851
62574
  init_config();
62852
62575
  init_logger();
62853
62576
  init_refresh();
62854
62577
  init_update_check();
62855
- import_picocolors32 = __toESM(require_picocolors(), 1);
62578
+ import_picocolors31 = __toESM(require_picocolors(), 1);
62856
62579
  MCP_FORMAT_CHANGES = ["0.53.0"];
62857
62580
  });
62858
62581
 
62859
62582
  // src/version/skill-update-check.ts
62860
62583
  function displayNotice3() {
62861
62584
  console.error(`
62862
- ${import_picocolors33.default.green(`✓ Dosu ${VERSION}: refreshed the bundled agent skills`)}
62863
- ` + `${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.")}
62864
62587
  `);
62865
62588
  }
62866
62589
  function checkForSkillUpdates(options = {}) {
@@ -62881,289 +62604,11 @@ function checkForSkillUpdates(options = {}) {
62881
62604
  logger.error("skill-update-check", `Skill update check failed: ${err}`);
62882
62605
  }
62883
62606
  }
62884
- var import_picocolors33;
62607
+ var import_picocolors32;
62885
62608
  var init_skill_update_check = __esm(() => {
62886
62609
  init_skill();
62887
62610
  init_logger();
62888
- import_picocolors33 = __toESM(require_picocolors(), 1);
62889
- });
62890
-
62891
- // src/rules/installer.ts
62892
- import { existsSync as existsSync25, mkdirSync as mkdirSync15, readFileSync as readFileSync24, unlinkSync as unlinkSync5, writeFileSync as writeFileSync16 } from "node:fs";
62893
- import { homedir as homedir12 } from "node:os";
62894
- import { dirname as dirname12, join as join40 } from "node:path";
62895
- function claudeConfigDir4() {
62896
- return process.env.CLAUDE_CONFIG_DIR || join40(homedir12(), ".claude");
62897
- }
62898
- function codexHome4() {
62899
- return process.env.CODEX_HOME || join40(homedir12(), ".codex");
62900
- }
62901
- function isRuleAgent(agent) {
62902
- return Object.hasOwn(RULE_TARGETS, agent);
62903
- }
62904
- function rulePathForAgent(agent) {
62905
- return isRuleAgent(agent) ? RULE_TARGETS[agent].path() : null;
62906
- }
62907
- function normalizeRule(content) {
62908
- return `${content.trim()}
62909
- `;
62910
- }
62911
- async function fetchDosuRule(fetchImpl = globalThis.fetch) {
62912
- for (const url of DOSU_RULE_URLS) {
62913
- try {
62914
- const response = await fetchImpl(url);
62915
- if (!response.ok)
62916
- continue;
62917
- const content = await response.text();
62918
- if (content.trim())
62919
- return normalizeRule(content);
62920
- } catch {}
62921
- }
62922
- return normalizeRule(FALLBACK_DOSU_RULE);
62923
- }
62924
- function ensureParent(path) {
62925
- mkdirSync15(dirname12(path), { recursive: true });
62926
- }
62927
- function writeIfChanged(path, content) {
62928
- if (!existsSync25(path)) {
62929
- ensureParent(path);
62930
- writeFileSync16(path, content, "utf-8");
62931
- return "created";
62932
- }
62933
- if (readFileSync24(path, "utf-8") === content)
62934
- return "unchanged";
62935
- writeFileSync16(path, content, "utf-8");
62936
- return "updated";
62937
- }
62938
- function findSection(content) {
62939
- const startMatch = content.match(DOSU_RULE_SECTION_START_RE);
62940
- const start = startMatch?.index ?? -1;
62941
- const end = content.indexOf(DOSU_RULE_SECTION_END, Math.max(start, 0));
62942
- if (start === -1 && end === -1)
62943
- return null;
62944
- if (start === -1 || end < start) {
62945
- throw new Error("Dosu rule markers are incomplete; refusing to overwrite the instruction file");
62946
- }
62947
- return { start, end };
62948
- }
62949
- function lineEndingFor(content) {
62950
- return content.includes(`\r
62951
- `) ? `\r
62952
- ` : `
62953
- `;
62954
- }
62955
- function buildSection(content, lineEnding = `
62956
- `) {
62957
- const body = content.trimEnd().replace(/\r?\n/g, lineEnding);
62958
- return `${DOSU_RULE_SECTION_START}${lineEnding}${body}${lineEnding}${DOSU_RULE_SECTION_END}`;
62959
- }
62960
- function upsertSection(path, content) {
62961
- if (!existsSync25(path)) {
62962
- const section = buildSection(content);
62963
- ensureParent(path);
62964
- writeFileSync16(path, `${section}
62965
- `, "utf-8");
62966
- return "created";
62967
- }
62968
- const existing = readFileSync24(path, "utf-8");
62969
- const lineEnding = lineEndingFor(existing);
62970
- const section = buildSection(content, lineEnding);
62971
- const location = findSection(existing);
62972
- let next;
62973
- if (location) {
62974
- next = existing.slice(0, location.start) + section + existing.slice(location.end + DOSU_RULE_SECTION_END.length);
62975
- } else {
62976
- const separator = existing.length > 0 && !existing.endsWith(lineEnding) ? `${lineEnding}${lineEnding}` : existing.length > 0 ? lineEnding : "";
62977
- next = `${existing}${separator}${section}${lineEnding}`;
62978
- }
62979
- if (next === existing)
62980
- return "unchanged";
62981
- writeFileSync16(path, next, "utf-8");
62982
- return "updated";
62983
- }
62984
- function installRuleForAgent(agent, content) {
62985
- if (!isRuleAgent(agent))
62986
- return null;
62987
- const target = RULE_TARGETS[agent];
62988
- const path = target.path();
62989
- const normalized = normalizeRule(content);
62990
- const action = target.kind === "file" ? writeIfChanged(path, target.cursorFrontmatter ? `${CURSOR_FRONTMATTER}${normalized}` : normalized) : upsertSection(path, normalized);
62991
- return { agent, path, action };
62992
- }
62993
- function removeRuleForAgent(agent) {
62994
- if (!isRuleAgent(agent))
62995
- return null;
62996
- const target = RULE_TARGETS[agent];
62997
- const path = target.path();
62998
- if (!existsSync25(path))
62999
- return { agent, path, action: "not_found" };
63000
- if (target.kind === "file") {
63001
- unlinkSync5(path);
63002
- return { agent, path, action: "removed" };
63003
- }
63004
- const existing = readFileSync24(path, "utf-8");
63005
- const lineEnding = lineEndingFor(existing);
63006
- const location = findSection(existing);
63007
- if (!location)
63008
- return { agent, path, action: "not_found" };
63009
- 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();
63010
- if (!next) {
63011
- unlinkSync5(path);
63012
- } else {
63013
- writeFileSync16(path, `${next}${lineEnding}`, "utf-8");
63014
- }
63015
- return { agent, path, action: "removed" };
63016
- }
63017
- var DOSU_RULE_SECTION_VERSION = 1, DOSU_RULE_SECTION_START, DOSU_RULE_SECTION_END = "<!-- dosu:rules:end -->", DOSU_RULE_SECTION_START_RE, CURSOR_FRONTMATTER = `---
63018
- alwaysApply: true
63019
- ---
63020
-
63021
- `, 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.
63022
-
63023
- 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.
63024
-
63025
- 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.**
63026
-
63027
- 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.
63028
-
63029
- 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.
63030
- `, RULE_TARGETS;
63031
- var init_installer = __esm(() => {
63032
- DOSU_RULE_SECTION_START = `<!-- dosu:rules:start v${DOSU_RULE_SECTION_VERSION} -->`;
63033
- DOSU_RULE_SECTION_START_RE = /<!-- dosu:rules:start(?: v\d+)? -->/;
63034
- DOSU_RULE_URLS = [
63035
- "https://raw.githubusercontent.com/dosu-ai/dosu-cli/main/rules/dosu.md",
63036
- "https://raw.githubusercontent.com/dosu-ai/dosu-cli/master/rules/dosu.md"
63037
- ];
63038
- RULE_TARGETS = {
63039
- claude: {
63040
- kind: "file",
63041
- path: () => join40(claudeConfigDir4(), "rules", "dosu.md")
63042
- },
63043
- cursor: {
63044
- kind: "file",
63045
- path: () => join40(homedir12(), ".cursor", "rules", "dosu.mdc"),
63046
- cursorFrontmatter: true
63047
- },
63048
- codex: {
63049
- kind: "section",
63050
- path: () => join40(codexHome4(), "AGENTS.md")
63051
- },
63052
- opencode: {
63053
- kind: "section",
63054
- path: () => join40(homedir12(), ".config", "opencode", "AGENTS.md")
63055
- },
63056
- gemini: {
63057
- kind: "section",
63058
- path: () => join40(homedir12(), ".gemini", "GEMINI.md")
63059
- },
63060
- antigravity: {
63061
- kind: "section",
63062
- path: () => join40(homedir12(), ".gemini", "GEMINI.md")
63063
- }
63064
- };
63065
- });
63066
-
63067
- // src/setup/agents-md-step.ts
63068
- import { execSync as execSync2 } from "node:child_process";
63069
- import { existsSync as existsSync26, readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "node:fs";
63070
- import { join as join41 } from "node:path";
63071
- function inGitWorkTree(cwd = process.cwd()) {
63072
- try {
63073
- const stdout = execSync2("git rev-parse --is-inside-work-tree", {
63074
- cwd,
63075
- stdio: ["ignore", "pipe", "ignore"]
63076
- });
63077
- return stdout.toString().trim() === "true";
63078
- } catch {
63079
- return false;
63080
- }
63081
- }
63082
- function dosuAgentsSectionState(cwd = process.cwd()) {
63083
- try {
63084
- const path = join41(cwd, "AGENTS.md");
63085
- if (!existsSync26(path))
63086
- return "missing";
63087
- const content = readFileSync25(path, "utf-8");
63088
- const startMatch = content.match(SECTION_START_RE);
63089
- if (!startMatch || !content.includes(DOSU_SECTION_END))
63090
- return "missing";
63091
- return Number(startMatch[1] ?? 0) >= DOSU_SECTION_VERSION ? "current" : "outdated";
63092
- } catch {
63093
- return "missing";
63094
- }
63095
- }
63096
- function lineEndingFor2(content) {
63097
- return content.includes(`\r
63098
- `) ? `\r
63099
- ` : `
63100
- `;
63101
- }
63102
- function renderDosuAgentsSection(content, lineEnding) {
63103
- const body = content.trim().replace(/\r?\n/g, lineEnding);
63104
- return `${DOSU_SECTION_START}${lineEnding}${body}${lineEnding}${DOSU_SECTION_END}`;
63105
- }
63106
- function buildDosuAgentsSection(content = FALLBACK_DOSU_RULE) {
63107
- return renderDosuAgentsSection(content, `
63108
- `);
63109
- }
63110
- function findSection2(content) {
63111
- const startMatch = content.match(SECTION_START_RE);
63112
- const start = startMatch?.index ?? -1;
63113
- const end = content.indexOf(DOSU_SECTION_END, Math.max(start, 0));
63114
- if (start === -1 && end === -1)
63115
- return null;
63116
- if (start === -1 || end < start) {
63117
- throw new Error("Dosu AGENTS.md markers are incomplete; refusing to overwrite the file");
63118
- }
63119
- return { start, end };
63120
- }
63121
- function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RULE) {
63122
- const path = join41(cwd, "AGENTS.md");
63123
- if (!existsSync26(path)) {
63124
- const section = buildDosuAgentsSection(content);
63125
- writeFileSync17(path, `${section}
63126
- `);
63127
- return { path, action: "created" };
63128
- }
63129
- const existing = readFileSync25(path, "utf-8");
63130
- const lineEnding = lineEndingFor2(existing);
63131
- const section = renderDosuAgentsSection(content, lineEnding);
63132
- const location = findSection2(existing);
63133
- let next;
63134
- if (location) {
63135
- next = existing.slice(0, location.start) + section + existing.slice(location.end + DOSU_SECTION_END.length);
63136
- } else {
63137
- next = `${existing.trimEnd()}${lineEnding}${lineEnding}${section}${lineEnding}`;
63138
- }
63139
- if (next === existing)
63140
- return { path, action: "unchanged" };
63141
- writeFileSync17(path, next);
63142
- return { path, action: "updated" };
63143
- }
63144
- async function stepUpdateAgentsMd(cwd = process.cwd(), content) {
63145
- logger.info("setup", "Step: update AGENTS.md");
63146
- try {
63147
- const result = upsertDosuAgentsSection(cwd, content ?? await fetchDosuRule());
63148
- logger.info("setup", `AGENTS.md ${result.action} at ${result.path}`);
63149
- const status = result.action === "unchanged" ? "already up to date" : result.action;
63150
- log2.success(formatSetupSummary("AGENTS.md", [{ path: result.path, status }]));
63151
- return true;
63152
- } catch (err) {
63153
- const msg = err instanceof Error ? err.message : String(err);
63154
- logger.error("setup", `AGENTS.md update failed: ${msg}`);
63155
- log2.error(`Could not update AGENTS.md: ${msg}`);
63156
- return false;
63157
- }
63158
- }
63159
- var DOSU_SECTION_VERSION = 2, DOSU_SECTION_START, DOSU_SECTION_END = "<!-- dosu:mcp:end -->", SECTION_START_RE;
63160
- var init_agents_md_step = __esm(() => {
63161
- init_logger();
63162
- init_installer();
63163
- init_prompts();
63164
- init_styles();
63165
- DOSU_SECTION_START = `<!-- dosu:mcp:start v${DOSU_SECTION_VERSION} -->`;
63166
- SECTION_START_RE = /<!-- dosu:mcp:start(?: v(\d+))? -->/;
62611
+ import_picocolors32 = __toESM(require_picocolors(), 1);
63167
62612
  });
63168
62613
 
63169
62614
  // src/auth/errors.ts
@@ -63193,16 +62638,51 @@ var init_errors3 = __esm(() => {
63193
62638
  // src/mcp/constants.ts
63194
62639
  var MCP_PROVIDER_SLUG2 = "dosu_mcp";
63195
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
+
63196
62676
  // src/sessions/session-title.ts
63197
- import { existsSync as existsSync27, mkdirSync as mkdirSync16, readFileSync as readFileSync26, renameSync as renameSync6, statSync as statSync9, writeFileSync as writeFileSync18 } from "node:fs";
63198
- import { homedir as homedir13 } from "node:os";
63199
- 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";
63200
62680
  function cachePath2(configDir) {
63201
- return join42(configDir, CACHE_FILENAME5);
62681
+ return join40(configDir, CACHE_FILENAME5);
63202
62682
  }
63203
62683
  function loadCacheFile2(configDir) {
63204
62684
  try {
63205
- const raw = JSON.parse(readFileSync26(cachePath2(configDir), "utf-8"));
62685
+ const raw = JSON.parse(readFileSync24(cachePath2(configDir), "utf-8"));
63206
62686
  if (raw.schema_version !== CACHE_SCHEMA_VERSION2 || typeof raw.entries !== "object")
63207
62687
  return {};
63208
62688
  return raw.entries;
@@ -63212,12 +62692,12 @@ function loadCacheFile2(configDir) {
63212
62692
  }
63213
62693
  function saveCacheFile2(configDir, entries) {
63214
62694
  try {
63215
- if (!existsSync27(configDir))
63216
- mkdirSync16(configDir, { recursive: true, mode: 448 });
62695
+ if (!existsSync25(configDir))
62696
+ mkdirSync15(configDir, { recursive: true, mode: 448 });
63217
62697
  const path = cachePath2(configDir);
63218
62698
  const tmp = `${path}.${process.pid}.tmp`;
63219
62699
  const file = { schema_version: CACHE_SCHEMA_VERSION2, entries };
63220
- writeFileSync18(tmp, JSON.stringify(file), { mode: 384 });
62700
+ writeFileSync16(tmp, JSON.stringify(file), { mode: 384 });
63221
62701
  renameSync6(tmp, path);
63222
62702
  } catch {}
63223
62703
  }
@@ -63250,15 +62730,15 @@ function claudeSummaryFromHead(text) {
63250
62730
  }
63251
62731
  return null;
63252
62732
  }
63253
- function reconstructSession(harness, id, slug, home = homedir13(), exists = existsSync27) {
62733
+ function reconstructSession(harness, id, slug, home = homedir11(), exists = existsSync25) {
63254
62734
  if (!slug)
63255
62735
  return null;
63256
62736
  const candidates = [];
63257
62737
  if (harness === "claude") {
63258
- candidates.push(join42(home, ".claude", "projects", slug, `${id}.jsonl`));
62738
+ candidates.push(join40(home, ".claude", "projects", slug, `${id}.jsonl`));
63259
62739
  } else if (harness === "cursor") {
63260
- const project = join42(home, ".cursor", "projects", slug);
63261
- 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`));
63262
62742
  } else {
63263
62743
  return null;
63264
62744
  }
@@ -63270,7 +62750,7 @@ function reconstructSession(harness, id, slug, home = homedir13(), exists = exis
63270
62750
  }
63271
62751
  function readHead2(path) {
63272
62752
  try {
63273
- return readFileSync26(path, "utf-8").slice(0, 128 * 1024);
62753
+ return readFileSync24(path, "utf-8").slice(0, 128 * 1024);
63274
62754
  } catch {
63275
62755
  return null;
63276
62756
  }
@@ -63350,7 +62830,7 @@ var init_alt_screen = __esm(() => {
63350
62830
  });
63351
62831
 
63352
62832
  // src/tui/activity-view.ts
63353
- import { existsSync as existsSync28, readFileSync as readFileSync27 } from "node:fs";
62833
+ import { existsSync as existsSync26, readFileSync as readFileSync25 } from "node:fs";
63354
62834
  import { basename as basename5 } from "node:path";
63355
62835
  function reduceActivityViewKey(key) {
63356
62836
  if (key === "q" || key === ESC7 || key === CTRL_C4)
@@ -63491,15 +62971,15 @@ function statusLine(status) {
63491
62971
  if (status.running) {
63492
62972
  const since = status.startedAt ? ` · since ${localTime(status.startedAt)}` : "";
63493
62973
  const pid = status.pid !== undefined ? ` (pid ${status.pid})` : "";
63494
- 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}`)}`;
63495
62975
  }
63496
62976
  if (status.state.paused) {
63497
- 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")}`;
63498
62978
  }
63499
62979
  if (status.staleLock) {
63500
- 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")}`;
63501
62981
  }
63502
- 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")}`;
63503
62983
  }
63504
62984
  function progressLine(done, ready, width, notes = 0) {
63505
62985
  const total = done + ready;
@@ -63508,7 +62988,7 @@ function progressLine(done, ready, width, notes = 0) {
63508
62988
  const cells = Math.max(10, Math.min(30, width - 44));
63509
62989
  const ratio = Math.max(0, Math.min(1, done / total));
63510
62990
  const filled = Math.min(cells, Math.round(ratio * cells));
63511
- 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));
63512
62992
  const pct = Math.floor(ratio * 100);
63513
62993
  const suffix = notes > 0 ? ` · ${notes} suggested page${notes === 1 ? "" : "s"}` : "";
63514
62994
  return `${bar} ${done}/${total} studied · ${pct}%${suffix}`;
@@ -63547,17 +63027,17 @@ function confirmBox(queuedCount, backlog, width, mode = "start") {
63547
63027
  const verb = mode === "clear" ? "clear" : mode === "stop" ? "stop" : mode === "resume" ? "resume" : "start";
63548
63028
  const maxInner = Math.max(20, Math.min(width, contentWidth()) - 4);
63549
63029
  const rows = [
63550
- import_picocolors34.default.bold(brand(title)),
63551
- ...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)),
63552
63032
  "",
63553
- `${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`
63554
63034
  ];
63555
63035
  const inner = Math.min(maxInner, Math.max(...rows.map(visibleWidth2)));
63556
63036
  const pad = (row) => row + " ".repeat(Math.max(0, inner - visibleWidth2(row)));
63557
63037
  return [
63558
- import_picocolors34.default.dim(`╭${"─".repeat(inner + 2)}╮`),
63559
- ...rows.map((row) => `${import_picocolors34.default.dim("│")} ${pad(row)} ${import_picocolors34.default.dim("│")}`),
63560
- 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)}╯`)
63561
63041
  ];
63562
63042
  }
63563
63043
  function buildActivityFrame(status, activity, width, backlog = null, pane = DEFAULT_PANE, queued = [], studiedBeforeRun = 0, open = [], runProgress = null, projectNames = {}, sessionNames = {}, frameLines) {
@@ -63573,7 +63053,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63573
63053
  progress = progressLine(studiedDelta + stepDone, Math.max(0, backlog.ready - stepDone), width, runProgress?.notes ?? 0);
63574
63054
  }
63575
63055
  const refusal = !status.running && status.state.last_refusal;
63576
- 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)) : [];
63577
63057
  const fullRows = Boolean(pane.fullRows);
63578
63058
  const withName = (item, key) => projectNames[key] ? { ...item, project: projectNames[key] } : item;
63579
63059
  const latestPass = new Map;
@@ -63599,8 +63079,8 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63599
63079
  breadcrumb(["home", "activity"], width),
63600
63080
  "",
63601
63081
  statusLine(status),
63602
- import_picocolors34.default.dim(studied),
63603
- ...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)) : [],
63604
63084
  ...progress ? [progress] : [],
63605
63085
  ...refusalLines,
63606
63086
  "",
@@ -63616,7 +63096,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63616
63096
  status.running ? "s stop" : status.state.paused ? "s resume" : "s sync now",
63617
63097
  ...!status.running && status.state.watermark ? ["c clear"] : [],
63618
63098
  "esc back"
63619
- ].join(" · "), width, "").map((line) => import_picocolors34.default.dim(line))
63099
+ ].join(" · "), width, "").map((line) => import_picocolors33.default.dim(line))
63620
63100
  ]
63621
63101
  ];
63622
63102
  const defaultHeight = fullRows ? ACTIVITY_VIEW_FULL_LIST_ROWS : ACTIVITY_VIEW_LIST_LINES;
@@ -63629,7 +63109,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63629
63109
  ({ visible, above, below } = windowList(source, pane.scroll, height));
63630
63110
  display = visible.flatMap((row) => wrapRow(row, width));
63631
63111
  }
63632
- 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)];
63633
63113
  const scrollParts = [];
63634
63114
  if (above > 0)
63635
63115
  scrollParts.push(`↑ ${above} earlier`);
@@ -63638,7 +63118,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63638
63118
  const lines = [
63639
63119
  ...header,
63640
63120
  ...listRows,
63641
- ...scrollParts.length > 0 ? [import_picocolors34.default.dim(clip(scrollParts.join(" · "), width))] : [],
63121
+ ...scrollParts.length > 0 ? [import_picocolors33.default.dim(clip(scrollParts.join(" · "), width))] : [],
63642
63122
  ...footer
63643
63123
  ];
63644
63124
  return { lines, listHeight: height };
@@ -63671,7 +63151,7 @@ function createRowNamer() {
63671
63151
  names.titles[key] = t;
63672
63152
  };
63673
63153
  for (const s of [...backlog.queued, ...backlog.open]) {
63674
- if (!existsSync28(s.path))
63154
+ if (!existsSync26(s.path))
63675
63155
  continue;
63676
63156
  const key = `${s.harness}/${s.id}`;
63677
63157
  const dir = dirs.resolve(s);
@@ -63708,7 +63188,7 @@ function createRowNamer() {
63708
63188
  }
63709
63189
  function defaultReadLog2() {
63710
63190
  try {
63711
- return readFileSync27(logger.getLogPath(), "utf-8");
63191
+ return readFileSync25(logger.getLogPath(), "utf-8");
63712
63192
  } catch {
63713
63193
  return "";
63714
63194
  }
@@ -63893,7 +63373,7 @@ ${CLEAR_BELOW}`);
63893
63373
  input.on("data", onData);
63894
63374
  });
63895
63375
  }
63896
- 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;
63897
63377
  var init_activity_view = __esm(() => {
63898
63378
  init_follow();
63899
63379
  init_logger();
@@ -63908,7 +63388,7 @@ var init_activity_view = __esm(() => {
63908
63388
  init_alt_screen();
63909
63389
  init_layout();
63910
63390
  init_menu();
63911
- import_picocolors34 = __toESM(require_picocolors(), 1);
63391
+ import_picocolors33 = __toESM(require_picocolors(), 1);
63912
63392
  ESC7 = String.fromCharCode(27);
63913
63393
  HIDE_CURSOR3 = `${ESC7}[?25l`;
63914
63394
  SHOW_CURSOR3 = `${ESC7}[?25h`;
@@ -63924,6 +63404,284 @@ var init_activity_view = __esm(() => {
63924
63404
  DEFAULT_PANE = { tab: "activity", scroll: 0 };
63925
63405
  });
63926
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
+
63927
63685
  // src/setup/analytics.ts
63928
63686
  async function trackCliOnboardingEvent(cfg, onboardingRunID, event, properties = {}) {
63929
63687
  try {
@@ -65391,7 +65149,7 @@ async function stepSelectTools(detected) {
65391
65149
  initialValues: preselected,
65392
65150
  statusFor: (id, picked) => {
65393
65151
  if (configuredMap.get(id))
65394
- return picked ? dim("configured") : import_picocolors35.default.yellow("will remove");
65152
+ return picked ? dim("configured") : import_picocolors34.default.yellow("will remove");
65395
65153
  return picked ? brand("will configure") : undefined;
65396
65154
  },
65397
65155
  summary: (picked) => {
@@ -65566,7 +65324,7 @@ ${dim(wrapLog("Run it inside a session to keep that session out of studying. Rem
65566
65324
  log2.success("All agents already configured. No changes needed.");
65567
65325
  }
65568
65326
  }
65569
- 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;
65570
65328
  var init_flow2 = __esm(() => {
65571
65329
  init_dist();
65572
65330
  init_errors3();
@@ -65591,7 +65349,7 @@ var init_flow2 = __esm(() => {
65591
65349
  init_github_step();
65592
65350
  init_rules_step();
65593
65351
  init_styles();
65594
- import_picocolors35 = __toESM(require_picocolors(), 1);
65352
+ import_picocolors34 = __toESM(require_picocolors(), 1);
65595
65353
  OAUTH_ANALYTICS_REASONS = new Set([
65596
65354
  "access_denied",
65597
65355
  "bad_oauth_state",
@@ -65605,6 +65363,269 @@ var init_flow2 = __esm(() => {
65605
65363
  LIBRARY_SELECT_CANCELLED = Symbol("library-select-cancelled");
65606
65364
  });
65607
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
+
65608
65629
  // src/tui/analytics-view.ts
65609
65630
  function analyticsListHeight(rows) {
65610
65631
  const room = frameMaxLines(rows) - ANALYTICS_VIEW_CHROME_LINES;
@@ -67851,4 +67872,4 @@ main().catch((err) => {
67851
67872
  process.exit(1);
67852
67873
  });
67853
67874
 
67854
- //# debugId=9d93d312-f9af-7e47-6475-6e2164756e21
67875
+ //# debugId=49fa5d82-a4a1-8b36-6475-6e2164756e21