@dosu/cli 0.60.1 → 0.61.0

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 +4 -2
  2. package/bin/dosu.js +932 -670
  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.61.0", 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 = "41cac967-78f1-f75a-6475-6e2164756e21";
13804
13804
 
13805
13805
  // src/telemetry/telemetry.ts
13806
13806
  import { randomUUID as randomUUID2 } from "node:crypto";
@@ -60326,11 +60326,11 @@ dosu skill install | remove | update
60326
60326
  dosu telemetry status [--json]
60327
60327
  dosu telemetry enable | disable | reset
60328
60328
  dosu insights
60329
- dosu upgrade
60329
+ dosu upgrade [--auto on|off]
60330
60330
  dosu logs [--tail [n]] [--clear]
60331
60331
  \`\`\`
60332
60332
 
60333
- \`deployments\` selects the MCP deployment stored in local config; it is distinct from \`agents\`. \`insights\` opens an interactive visual report. \`logs --clear\` deletes the CLI log file.
60333
+ \`deployments\` selects the MCP deployment stored in local config; it is distinct from \`agents\`. \`insights\` opens an interactive visual report. \`logs --clear\` deletes the CLI log file. The CLI updates itself in the background by default; a \`[dosu:update] Installing ...\` notice on stderr needs no action. \`upgrade --auto off\` turns that off.
60334
60334
 
60335
60335
  ## Codebase audit
60336
60336
 
@@ -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;
@@ -62618,10 +62341,204 @@ var init_refresh = __esm(() => {
62618
62341
  init_providers();
62619
62342
  });
62620
62343
 
62621
- // src/version/update-check.ts
62622
- import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "node:fs";
62344
+ // src/version/auto-update.ts
62345
+ import { spawn as spawn3 } from "node:child_process";
62346
+ import {
62347
+ closeSync as closeSync6,
62348
+ existsSync as existsSync23,
62349
+ mkdirSync as mkdirSync13,
62350
+ openSync as openSync6,
62351
+ readFileSync as readFileSync22,
62352
+ rmSync as rmSync7,
62353
+ statSync as statSync8,
62354
+ writeFileSync as writeFileSync14,
62355
+ writeSync
62356
+ } from "node:fs";
62357
+ import { homedir as homedir11 } from "node:os";
62623
62358
  import { join as join38 } from "node:path";
62359
+ function statePath() {
62360
+ return join38(getConfigDir(), STATE_FILENAME2);
62361
+ }
62362
+ function lockPath2() {
62363
+ return join38(getConfigDir(), LOCK_FILENAME2);
62364
+ }
62624
62365
  function isValidVersion(value) {
62366
+ return typeof value === "string" && VERSION_PATTERN.test(value);
62367
+ }
62368
+ function readState() {
62369
+ try {
62370
+ const raw = JSON.parse(readFileSync22(statePath(), "utf-8"));
62371
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw))
62372
+ return {};
62373
+ const data = raw;
62374
+ const state = {};
62375
+ if (data.disabled === true)
62376
+ state.disabled = true;
62377
+ const last = data.lastAttempt;
62378
+ if (last && isValidVersion(last.version) && typeof last.ok === "boolean" && typeof last.finishedAt === "number") {
62379
+ state.lastAttempt = { version: last.version, ok: last.ok, finishedAt: last.finishedAt };
62380
+ }
62381
+ return state;
62382
+ } catch {
62383
+ return {};
62384
+ }
62385
+ }
62386
+ function writeState(state) {
62387
+ try {
62388
+ const dir = getConfigDir();
62389
+ if (!existsSync23(dir))
62390
+ mkdirSync13(dir, { recursive: true, mode: 448 });
62391
+ writeFileSync14(statePath(), `${JSON.stringify(state, null, 2)}
62392
+ `, { mode: 384 });
62393
+ return true;
62394
+ } catch {
62395
+ return false;
62396
+ }
62397
+ }
62398
+ function isEnabledFlag(value) {
62399
+ if (value === undefined)
62400
+ return false;
62401
+ const normalized = value.trim().toLowerCase();
62402
+ return normalized !== "" && !["0", "false", "no", "off"].includes(normalized);
62403
+ }
62404
+ function autoUpdateDisabledReason(env = process.env) {
62405
+ if (isEnabledFlag(env[AUTO_UPDATE_ENV]))
62406
+ return "env";
62407
+ if (readState().disabled)
62408
+ return "settings";
62409
+ return;
62410
+ }
62411
+ function setAutoUpdateEnabled(enabled) {
62412
+ const state = readState();
62413
+ if (enabled)
62414
+ delete state.disabled;
62415
+ else
62416
+ state.disabled = true;
62417
+ return writeState(state);
62418
+ }
62419
+ function canAutoUpdate(channel, env) {
62420
+ if (env.NODE_ENV === "test" || env.CI || env.DOSU_DEV === "true")
62421
+ return false;
62422
+ if (!SUPPORTED_CHANNELS.has(channel) || isNpxInvocation(channel, env))
62423
+ return false;
62424
+ return autoUpdateDisabledReason(env) === undefined;
62425
+ }
62426
+ function backgroundInvocation(channel, entrypoint = process.argv[1], execPath = process.execPath) {
62427
+ const args = ["upgrade", "--background"];
62428
+ if (channel === "npm") {
62429
+ return entrypoint ? { command: execPath, args: [entrypoint, ...args] } : null;
62430
+ }
62431
+ if (channel === "homebrew")
62432
+ return { command: execPath, args };
62433
+ return null;
62434
+ }
62435
+ function lockIsFresh(now) {
62436
+ try {
62437
+ return now - statSync8(lockPath2()).mtimeMs < LOCK_STALE_MS;
62438
+ } catch {
62439
+ return false;
62440
+ }
62441
+ }
62442
+ function claimLock(version, now) {
62443
+ try {
62444
+ mkdirSync13(getConfigDir(), { recursive: true, mode: 448 });
62445
+ if (!lockIsFresh(now))
62446
+ rmSync7(lockPath2(), { force: true });
62447
+ const fd = openSync6(lockPath2(), "wx", 384);
62448
+ try {
62449
+ writeSync(fd, version);
62450
+ } finally {
62451
+ closeSync6(fd);
62452
+ }
62453
+ return "claimed";
62454
+ } catch (err) {
62455
+ return err.code === "EEXIST" ? "held" : "failed";
62456
+ }
62457
+ }
62458
+ function releaseLock() {
62459
+ try {
62460
+ rmSync7(lockPath2(), { force: true });
62461
+ } catch {}
62462
+ }
62463
+ function readLockVersion() {
62464
+ try {
62465
+ const version = readFileSync22(lockPath2(), "utf-8").trim();
62466
+ return isValidVersion(version) ? version : undefined;
62467
+ } catch {
62468
+ return;
62469
+ }
62470
+ }
62471
+ function startAutoUpdate(version, options = {}) {
62472
+ const channel = options.channel ?? INSTALL_CHANNEL;
62473
+ const env = options.env ?? process.env;
62474
+ const now = options.now ?? Date.now();
62475
+ try {
62476
+ if (!isValidVersion(version) || !canAutoUpdate(channel, env))
62477
+ return "unavailable";
62478
+ if (lockIsFresh(now))
62479
+ return "in_progress";
62480
+ const last = readState().lastAttempt;
62481
+ if (last?.version === version && now - last.finishedAt < RETRY_INTERVAL_MS) {
62482
+ return "unavailable";
62483
+ }
62484
+ const invocation = backgroundInvocation(channel, options.entrypoint, options.execPath);
62485
+ if (!invocation)
62486
+ return "unavailable";
62487
+ const claim = claimLock(version, now);
62488
+ if (claim !== "claimed")
62489
+ return claim === "held" ? "in_progress" : "unavailable";
62490
+ try {
62491
+ const child = spawn3(invocation.command, invocation.args, {
62492
+ cwd: homedir11(),
62493
+ detached: true,
62494
+ stdio: "ignore",
62495
+ windowsHide: true,
62496
+ env: { ...env, DOSU_TELEMETRY_DISABLED: "1" }
62497
+ });
62498
+ child.on("error", releaseLock);
62499
+ child.unref();
62500
+ } catch (err) {
62501
+ releaseLock();
62502
+ throw err;
62503
+ }
62504
+ logger.info("auto-update", `Started background update to ${version}`);
62505
+ return "started";
62506
+ } catch (err) {
62507
+ logger.error("auto-update", `Could not start background update: ${err}`);
62508
+ return "unavailable";
62509
+ }
62510
+ }
62511
+ function runBackgroundUpgrade(upgrade, now = Date.now) {
62512
+ const version = readLockVersion();
62513
+ let status = 1;
62514
+ try {
62515
+ status = upgrade();
62516
+ } catch (err) {
62517
+ logger.error("auto-update", `Background update threw: ${err}`);
62518
+ }
62519
+ if (version) {
62520
+ const state = readState();
62521
+ state.lastAttempt = { version, ok: status === 0, finishedAt: now() };
62522
+ writeState(state);
62523
+ }
62524
+ logger.info("auto-update", `Background update ${status === 0 ? "finished" : "failed"}`);
62525
+ releaseLock();
62526
+ return status;
62527
+ }
62528
+ var STATE_FILENAME2 = "auto-update.json", LOCK_FILENAME2 = "auto-update.lock", LOCK_STALE_MS, RETRY_INTERVAL_MS, SUPPORTED_CHANNELS, VERSION_PATTERN, AUTO_UPDATE_ENV = "DOSU_DISABLE_AUTOUPDATE";
62529
+ var init_auto_update = __esm(() => {
62530
+ init_config();
62531
+ init_logger();
62532
+ LOCK_STALE_MS = 15 * 60 * 1000;
62533
+ RETRY_INTERVAL_MS = 6 * 60 * 60 * 1000;
62534
+ SUPPORTED_CHANNELS = new Set(["npm", "homebrew"]);
62535
+ VERSION_PATTERN = /^[0-9A-Za-z.+-]{1,64}$/;
62536
+ });
62537
+
62538
+ // src/version/update-check.ts
62539
+ import { existsSync as existsSync24, mkdirSync as mkdirSync14, readFileSync as readFileSync23, writeFileSync as writeFileSync15 } from "node:fs";
62540
+ import { join as join39 } from "node:path";
62541
+ function isValidVersion2(value) {
62625
62542
  return typeof value === "string" && SEMVER_PATTERN.test(value);
62626
62543
  }
62627
62544
  function stripPrerelease(version) {
@@ -62642,15 +62559,15 @@ function isNewerVersion(latest, current) {
62642
62559
  return false;
62643
62560
  }
62644
62561
  function getCachePath2() {
62645
- return join38(getConfigDir(), CACHE_FILENAME3);
62562
+ return join39(getConfigDir(), CACHE_FILENAME3);
62646
62563
  }
62647
62564
  function readCache() {
62648
62565
  try {
62649
62566
  const path = getCachePath2();
62650
- if (!existsSync23(path))
62567
+ if (!existsSync24(path))
62651
62568
  return null;
62652
- const data = JSON.parse(readFileSync22(path, "utf-8"));
62653
- if (typeof data.lastCheck === "number" && isValidVersion(data.latestVersion)) {
62569
+ const data = JSON.parse(readFileSync23(path, "utf-8"));
62570
+ if (typeof data.lastCheck === "number" && isValidVersion2(data.latestVersion)) {
62654
62571
  return data;
62655
62572
  }
62656
62573
  return null;
@@ -62661,10 +62578,10 @@ function readCache() {
62661
62578
  function writeCache(cache) {
62662
62579
  try {
62663
62580
  const dir = getConfigDir();
62664
- if (!existsSync23(dir)) {
62665
- mkdirSync13(dir, { recursive: true, mode: 448 });
62581
+ if (!existsSync24(dir)) {
62582
+ mkdirSync14(dir, { recursive: true, mode: 448 });
62666
62583
  }
62667
- writeFileSync14(getCachePath2(), JSON.stringify(cache), { mode: 384 });
62584
+ writeFileSync15(getCachePath2(), JSON.stringify(cache), { mode: 384 });
62668
62585
  } catch {}
62669
62586
  }
62670
62587
  async function fetchLatestVersion() {
@@ -62676,7 +62593,7 @@ async function fetchLatestVersion() {
62676
62593
  return null;
62677
62594
  const data = await resp.json();
62678
62595
  const latest = data.latest;
62679
- return isValidVersion(latest) ? latest : null;
62596
+ return isValidVersion2(latest) ? latest : null;
62680
62597
  } catch {
62681
62598
  return null;
62682
62599
  } finally {
@@ -62692,34 +62609,47 @@ function buildUpdateHint(channel, npx = false) {
62692
62609
  function styleHint(hint) {
62693
62610
  const match = hint.match(/^(.*)("[^"]+")(.*)$/);
62694
62611
  if (!match)
62695
- return import_picocolors31.default.dim(hint);
62612
+ return import_picocolors30.default.dim(hint);
62696
62613
  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("");
62614
+ return [before && import_picocolors30.default.dim(before), import_picocolors30.default.cyan(command), after && import_picocolors30.default.dim(after)].filter(Boolean).join("");
62615
+ }
62616
+ function framedNotice(content, width) {
62617
+ const innerWidth = Math.max(...content.map(visibleWidth2)) + BOX_PADDING * 2;
62618
+ const framed = (line) => {
62619
+ const pad = innerWidth - visibleWidth2(line);
62620
+ const left = Math.floor(pad / 2);
62621
+ return `${import_picocolors30.default.yellow("│")}${" ".repeat(left)}${line}${" ".repeat(pad - left)}${import_picocolors30.default.yellow("│")}`;
62622
+ };
62623
+ const box = [
62624
+ import_picocolors30.default.yellow(`╭${"─".repeat(innerWidth)}╮`),
62625
+ framed(""),
62626
+ ...content.map(framed),
62627
+ framed(""),
62628
+ import_picocolors30.default.yellow(`╰${"─".repeat(innerWidth)}╯`)
62629
+ ];
62630
+ return `
62631
+ ${centerBlock(box, width).join(`
62632
+ `)}
62633
+ `;
62634
+ }
62635
+ function versionArrow(current, latest) {
62636
+ return `${import_picocolors30.default.dim(current)} ${import_picocolors30.default.dim("→")} ${import_picocolors30.default.bold(brand(latest))}`;
62637
+ }
62638
+ function buildAutoUpdateNotice(current, latest, interactive, width = process.stderr.columns ?? 80) {
62639
+ if (interactive) {
62640
+ return framedNotice([
62641
+ `Updating Dosu: ${versionArrow(current, latest)}`,
62642
+ import_picocolors30.default.dim("Installing in the background; your next command uses it")
62643
+ ], width);
62644
+ }
62645
+ return `
62646
+ [dosu:update] Installing Dosu ${latest} in the background (running ${current}). ` + `The next Dosu command uses it; no action needed.
62647
+ `;
62698
62648
  }
62699
62649
  function buildUpdateNotice(current, latest, channel, interactive, npx = false, width = process.stderr.columns ?? 80) {
62700
62650
  const hint = buildUpdateHint(channel, npx);
62701
62651
  if (interactive) {
62702
- const content = [
62703
- `Update available: ${import_picocolors31.default.dim(current)} ${import_picocolors31.default.dim("→")} ${import_picocolors31.default.bold(brand(latest))}`,
62704
- styleHint(hint)
62705
- ];
62706
- const innerWidth = Math.max(...content.map(visibleWidth2)) + BOX_PADDING * 2;
62707
- const framed = (line) => {
62708
- const pad = innerWidth - visibleWidth2(line);
62709
- const left = Math.floor(pad / 2);
62710
- return `${import_picocolors31.default.yellow("│")}${" ".repeat(left)}${line}${" ".repeat(pad - left)}${import_picocolors31.default.yellow("│")}`;
62711
- };
62712
- const box = [
62713
- import_picocolors31.default.yellow(`╭${"─".repeat(innerWidth)}╮`),
62714
- framed(""),
62715
- ...content.map(framed),
62716
- framed(""),
62717
- import_picocolors31.default.yellow(`╰${"─".repeat(innerWidth)}╯`)
62718
- ];
62719
- return `
62720
- ${centerBlock(box, width).join(`
62721
- `)}
62722
- `;
62652
+ return framedNotice([`Update available: ${versionArrow(current, latest)}`, styleHint(hint)], width);
62723
62653
  }
62724
62654
  const agentAction = hint[0].toLowerCase() + hint.slice(1);
62725
62655
  const verifyCommand = npx ? "npx -y @dosu/cli@latest --version" : "dosu --version";
@@ -62732,8 +62662,12 @@ function getAvailableUpdate() {
62732
62662
  const cache = readCache();
62733
62663
  return cache && isNewerVersion(cache.latestVersion, VERSION) ? cache.latestVersion : null;
62734
62664
  }
62735
- function displayNotice(current, latest) {
62736
- console.error(buildUpdateNotice(current, latest, INSTALL_CHANNEL, process.stderr.isTTY === true, isNpxInvocation()));
62665
+ function handleNewerVersion(latest, notify) {
62666
+ const autoUpdate = startAutoUpdate(latest);
62667
+ if (!notify)
62668
+ return;
62669
+ const interactive = process.stderr.isTTY === true;
62670
+ console.error(autoUpdate === "unavailable" ? buildUpdateNotice(VERSION, latest, INSTALL_CHANNEL, interactive, isNpxInvocation()) : buildAutoUpdateNotice(VERSION, latest, interactive));
62737
62671
  }
62738
62672
  async function checkForUpdates(options = {}) {
62739
62673
  const notify = options.notify ?? true;
@@ -62741,8 +62675,8 @@ async function checkForUpdates(options = {}) {
62741
62675
  const cache = readCache();
62742
62676
  const isStale = !cache || Date.now() - cache.lastCheck > CHECK_INTERVAL_MS2;
62743
62677
  if (!isStale) {
62744
- if (notify && isNewerVersion(cache.latestVersion, VERSION)) {
62745
- displayNotice(VERSION, cache.latestVersion);
62678
+ if (isNewerVersion(cache.latestVersion, VERSION)) {
62679
+ handleNewerVersion(cache.latestVersion, notify);
62746
62680
  }
62747
62681
  return;
62748
62682
  }
@@ -62755,27 +62689,28 @@ async function checkForUpdates(options = {}) {
62755
62689
  if (latest) {
62756
62690
  logger.debug("update-check", `Cached latest version: ${latest}`);
62757
62691
  }
62758
- if (notify && isNewerVersion(latestKnownVersion, VERSION)) {
62759
- displayNotice(VERSION, latestKnownVersion);
62692
+ if (isNewerVersion(latestKnownVersion, VERSION)) {
62693
+ handleNewerVersion(latestKnownVersion, notify);
62760
62694
  }
62761
62695
  } catch (err) {
62762
62696
  logger.error("update-check", `Update check failed: ${err}`);
62763
62697
  }
62764
62698
  }
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;
62699
+ 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
62700
  var init_update_check = __esm(() => {
62767
62701
  init_config();
62768
62702
  init_logger();
62769
62703
  init_styles();
62770
62704
  init_layout();
62771
- import_picocolors31 = __toESM(require_picocolors(), 1);
62705
+ init_auto_update();
62706
+ import_picocolors30 = __toESM(require_picocolors(), 1);
62772
62707
  CHECK_INTERVAL_MS2 = 6 * 60 * 60 * 1000;
62773
62708
  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
62709
  });
62775
62710
 
62776
62711
  // src/version/mcp-refresh-check.ts
62777
- import { existsSync as existsSync24, mkdirSync as mkdirSync14, readFileSync as readFileSync23, writeFileSync as writeFileSync15 } from "node:fs";
62778
- import { join as join39 } from "node:path";
62712
+ import { existsSync as existsSync25, mkdirSync as mkdirSync15, readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "node:fs";
62713
+ import { join as join40 } from "node:path";
62779
62714
  function needsMcpRefresh(previous, current) {
62780
62715
  if (previous === null)
62781
62716
  return true;
@@ -62785,14 +62720,14 @@ function needsMcpRefresh(previous, current) {
62785
62720
  return MCP_FORMAT_CHANGES.some((change) => isNewerVersion(change, older) && !isNewerVersion(change, newer));
62786
62721
  }
62787
62722
  function getCachePath3() {
62788
- return join39(getConfigDir(), CACHE_FILENAME4);
62723
+ return join40(getConfigDir(), CACHE_FILENAME4);
62789
62724
  }
62790
62725
  function readMcpRefreshCache() {
62791
62726
  try {
62792
62727
  const path = getCachePath3();
62793
- if (!existsSync24(path))
62728
+ if (!existsSync25(path))
62794
62729
  return null;
62795
- const data = JSON.parse(readFileSync23(path, "utf-8"));
62730
+ const data = JSON.parse(readFileSync24(path, "utf-8"));
62796
62731
  if (typeof data?.version === "string")
62797
62732
  return { version: data.version };
62798
62733
  return null;
@@ -62803,10 +62738,10 @@ function readMcpRefreshCache() {
62803
62738
  function writeMcpRefreshCache(cache) {
62804
62739
  try {
62805
62740
  const dir = getConfigDir();
62806
- if (!existsSync24(dir)) {
62807
- mkdirSync14(dir, { recursive: true, mode: 448 });
62741
+ if (!existsSync25(dir)) {
62742
+ mkdirSync15(dir, { recursive: true, mode: 448 });
62808
62743
  }
62809
- writeFileSync15(getCachePath3(), JSON.stringify(cache), { mode: 384 });
62744
+ writeFileSync16(getCachePath3(), JSON.stringify(cache), { mode: 384 });
62810
62745
  } catch {}
62811
62746
  }
62812
62747
  function canRefreshMcp(cfg) {
@@ -62815,10 +62750,10 @@ function canRefreshMcp(cfg) {
62815
62750
  return false;
62816
62751
  return cfg.mode === MODE_OSS || Boolean(target.deployment_id);
62817
62752
  }
62818
- function displayNotice2(names) {
62753
+ function displayNotice(names) {
62819
62754
  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.`)}
62755
+ ${import_picocolors31.default.green(`✓ Dosu ${VERSION}: refreshed MCP config for ${names.join(", ")}`)}
62756
+ ` + `${import_picocolors31.default.dim(` Run ${import_picocolors31.default.cyan('"dosu setup"')} to also update hooks and rules, then restart your AI agents.`)}
62822
62757
  `);
62823
62758
  }
62824
62759
  function checkForMcpRefresh(options = {}) {
@@ -62840,27 +62775,27 @@ function checkForMcpRefresh(options = {}) {
62840
62775
  writeMcpRefreshCache({ version: VERSION });
62841
62776
  logger.info("mcp-refresh", `Version ${VERSION}: refreshed ${result.updated.length}, failed ${result.failed.length}`);
62842
62777
  if (notify && result.updated.length > 0) {
62843
- displayNotice2(result.updated.map((provider) => provider.name()));
62778
+ displayNotice(result.updated.map((provider) => provider.name()));
62844
62779
  }
62845
62780
  } catch (err) {
62846
62781
  logger.error("mcp-refresh", `MCP refresh check failed: ${err}`);
62847
62782
  }
62848
62783
  }
62849
- var import_picocolors32, CACHE_FILENAME4 = "mcp-refresh.json", MCP_FORMAT_CHANGES;
62784
+ var import_picocolors31, CACHE_FILENAME4 = "mcp-refresh.json", MCP_FORMAT_CHANGES;
62850
62785
  var init_mcp_refresh_check = __esm(() => {
62851
62786
  init_config();
62852
62787
  init_logger();
62853
62788
  init_refresh();
62854
62789
  init_update_check();
62855
- import_picocolors32 = __toESM(require_picocolors(), 1);
62790
+ import_picocolors31 = __toESM(require_picocolors(), 1);
62856
62791
  MCP_FORMAT_CHANGES = ["0.53.0"];
62857
62792
  });
62858
62793
 
62859
62794
  // src/version/skill-update-check.ts
62860
- function displayNotice3() {
62795
+ function displayNotice2() {
62861
62796
  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.")}
62797
+ ${import_picocolors32.default.green(`✓ Dosu ${VERSION}: refreshed the bundled agent skills`)}
62798
+ ` + `${import_picocolors32.default.dim(" Restart your AI agents to pick up the new skill content.")}
62864
62799
  `);
62865
62800
  }
62866
62801
  function checkForSkillUpdates(options = {}) {
@@ -62876,294 +62811,16 @@ function checkForSkillUpdates(options = {}) {
62876
62811
  }
62877
62812
  logger.info("skill-update-check", `Refreshed skills from ${state.version} to ${VERSION}`);
62878
62813
  if (notify && result.version)
62879
- displayNotice3();
62814
+ displayNotice2();
62880
62815
  } catch (err) {
62881
62816
  logger.error("skill-update-check", `Skill update check failed: ${err}`);
62882
62817
  }
62883
62818
  }
62884
- var import_picocolors33;
62819
+ var import_picocolors32;
62885
62820
  var init_skill_update_check = __esm(() => {
62886
62821
  init_skill();
62887
62822
  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+))? -->/;
62823
+ import_picocolors32 = __toESM(require_picocolors(), 1);
63167
62824
  });
63168
62825
 
63169
62826
  // src/auth/errors.ts
@@ -63193,16 +62850,51 @@ var init_errors3 = __esm(() => {
63193
62850
  // src/mcp/constants.ts
63194
62851
  var MCP_PROVIDER_SLUG2 = "dosu_mcp";
63195
62852
 
62853
+ // src/debug/follow.ts
62854
+ import { closeSync as closeSync7, openSync as openSync7, readSync as readSync5, statSync as statSync9 } from "node:fs";
62855
+ function createLogFollower(path, emit) {
62856
+ const sizeOf = () => {
62857
+ try {
62858
+ return statSync9(path).size;
62859
+ } catch {
62860
+ return 0;
62861
+ }
62862
+ };
62863
+ let offset = sizeOf();
62864
+ return {
62865
+ poll() {
62866
+ const size = sizeOf();
62867
+ if (size < offset)
62868
+ offset = 0;
62869
+ if (size === offset)
62870
+ return;
62871
+ try {
62872
+ const fd = openSync7(path, "r");
62873
+ try {
62874
+ const buf = Buffer.alloc(size - offset);
62875
+ const read = readSync5(fd, buf, 0, buf.length, offset);
62876
+ offset += read;
62877
+ if (read > 0)
62878
+ emit(buf.toString("utf-8", 0, read));
62879
+ } finally {
62880
+ closeSync7(fd);
62881
+ }
62882
+ } catch {}
62883
+ }
62884
+ };
62885
+ }
62886
+ var init_follow = () => {};
62887
+
63196
62888
  // 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";
62889
+ import { existsSync as existsSync26, mkdirSync as mkdirSync16, readFileSync as readFileSync25, renameSync as renameSync6, statSync as statSync10, writeFileSync as writeFileSync17 } from "node:fs";
62890
+ import { homedir as homedir12 } from "node:os";
62891
+ import { join as join41 } from "node:path";
63200
62892
  function cachePath2(configDir) {
63201
- return join42(configDir, CACHE_FILENAME5);
62893
+ return join41(configDir, CACHE_FILENAME5);
63202
62894
  }
63203
62895
  function loadCacheFile2(configDir) {
63204
62896
  try {
63205
- const raw = JSON.parse(readFileSync26(cachePath2(configDir), "utf-8"));
62897
+ const raw = JSON.parse(readFileSync25(cachePath2(configDir), "utf-8"));
63206
62898
  if (raw.schema_version !== CACHE_SCHEMA_VERSION2 || typeof raw.entries !== "object")
63207
62899
  return {};
63208
62900
  return raw.entries;
@@ -63212,12 +62904,12 @@ function loadCacheFile2(configDir) {
63212
62904
  }
63213
62905
  function saveCacheFile2(configDir, entries) {
63214
62906
  try {
63215
- if (!existsSync27(configDir))
62907
+ if (!existsSync26(configDir))
63216
62908
  mkdirSync16(configDir, { recursive: true, mode: 448 });
63217
62909
  const path = cachePath2(configDir);
63218
62910
  const tmp = `${path}.${process.pid}.tmp`;
63219
62911
  const file = { schema_version: CACHE_SCHEMA_VERSION2, entries };
63220
- writeFileSync18(tmp, JSON.stringify(file), { mode: 384 });
62912
+ writeFileSync17(tmp, JSON.stringify(file), { mode: 384 });
63221
62913
  renameSync6(tmp, path);
63222
62914
  } catch {}
63223
62915
  }
@@ -63250,15 +62942,15 @@ function claudeSummaryFromHead(text) {
63250
62942
  }
63251
62943
  return null;
63252
62944
  }
63253
- function reconstructSession(harness, id, slug, home = homedir13(), exists = existsSync27) {
62945
+ function reconstructSession(harness, id, slug, home = homedir12(), exists = existsSync26) {
63254
62946
  if (!slug)
63255
62947
  return null;
63256
62948
  const candidates = [];
63257
62949
  if (harness === "claude") {
63258
- candidates.push(join42(home, ".claude", "projects", slug, `${id}.jsonl`));
62950
+ candidates.push(join41(home, ".claude", "projects", slug, `${id}.jsonl`));
63259
62951
  } 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`));
62952
+ const project = join41(home, ".cursor", "projects", slug);
62953
+ candidates.push(join41(project, "agent-transcripts", id, `${id}.jsonl`), join41(project, "agent-transcripts", `${id}.jsonl`));
63262
62954
  } else {
63263
62955
  return null;
63264
62956
  }
@@ -63270,14 +62962,14 @@ function reconstructSession(harness, id, slug, home = homedir13(), exists = exis
63270
62962
  }
63271
62963
  function readHead2(path) {
63272
62964
  try {
63273
- return readFileSync26(path, "utf-8").slice(0, 128 * 1024);
62965
+ return readFileSync25(path, "utf-8").slice(0, 128 * 1024);
63274
62966
  } catch {
63275
62967
  return null;
63276
62968
  }
63277
62969
  }
63278
62970
  function fileMtime2(path) {
63279
62971
  try {
63280
- return statSync9(path).mtime.toISOString();
62972
+ return statSync10(path).mtime.toISOString();
63281
62973
  } catch {
63282
62974
  return "";
63283
62975
  }
@@ -63350,7 +63042,7 @@ var init_alt_screen = __esm(() => {
63350
63042
  });
63351
63043
 
63352
63044
  // src/tui/activity-view.ts
63353
- import { existsSync as existsSync28, readFileSync as readFileSync27 } from "node:fs";
63045
+ import { existsSync as existsSync27, readFileSync as readFileSync26 } from "node:fs";
63354
63046
  import { basename as basename5 } from "node:path";
63355
63047
  function reduceActivityViewKey(key) {
63356
63048
  if (key === "q" || key === ESC7 || key === CTRL_C4)
@@ -63491,15 +63183,15 @@ function statusLine(status) {
63491
63183
  if (status.running) {
63492
63184
  const since = status.startedAt ? ` · since ${localTime(status.startedAt)}` : "";
63493
63185
  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}`)}`;
63186
+ return `\uD83D\uDCDA ${import_picocolors33.default.bold(brand("Studying sessions..."))}${import_picocolors33.default.dim(`${pid}${since}`)}`;
63495
63187
  }
63496
63188
  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")}`;
63189
+ return `${import_picocolors33.default.yellow("○")} ${import_picocolors33.default.bold("Paused")} ${import_picocolors33.default.dim("· studying stays off until you resume")}`;
63498
63190
  }
63499
63191
  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")}`;
63192
+ return `${import_picocolors33.default.yellow("○")} ${import_picocolors33.default.bold("Not running")} ${import_picocolors33.default.dim("· a previous run exited without cleaning up")}`;
63501
63193
  }
63502
- return `${import_picocolors34.default.dim("○")} ${import_picocolors34.default.bold("Idle")} ${import_picocolors34.default.dim("· hooks study new sessions automatically")}`;
63194
+ return `${import_picocolors33.default.dim("○")} ${import_picocolors33.default.bold("Idle")} ${import_picocolors33.default.dim("· hooks study new sessions automatically")}`;
63503
63195
  }
63504
63196
  function progressLine(done, ready, width, notes = 0) {
63505
63197
  const total = done + ready;
@@ -63508,7 +63200,7 @@ function progressLine(done, ready, width, notes = 0) {
63508
63200
  const cells = Math.max(10, Math.min(30, width - 44));
63509
63201
  const ratio = Math.max(0, Math.min(1, done / total));
63510
63202
  const filled = Math.min(cells, Math.round(ratio * cells));
63511
- const bar = brand("█".repeat(filled)) + import_picocolors34.default.dim("░".repeat(cells - filled));
63203
+ const bar = brand("█".repeat(filled)) + import_picocolors33.default.dim("░".repeat(cells - filled));
63512
63204
  const pct = Math.floor(ratio * 100);
63513
63205
  const suffix = notes > 0 ? ` · ${notes} suggested page${notes === 1 ? "" : "s"}` : "";
63514
63206
  return `${bar} ${done}/${total} studied · ${pct}%${suffix}`;
@@ -63547,17 +63239,17 @@ function confirmBox(queuedCount, backlog, width, mode = "start") {
63547
63239
  const verb = mode === "clear" ? "clear" : mode === "stop" ? "stop" : mode === "resume" ? "resume" : "start";
63548
63240
  const maxInner = Math.max(20, Math.min(width, contentWidth()) - 4);
63549
63241
  const rows = [
63550
- import_picocolors34.default.bold(brand(title)),
63551
- ...wrapLine(scope, maxInner, "").map((line) => import_picocolors34.default.dim(line)),
63242
+ import_picocolors33.default.bold(brand(title)),
63243
+ ...wrapLine(scope, maxInner, "").map((line) => import_picocolors33.default.dim(line)),
63552
63244
  "",
63553
- `${import_picocolors34.default.bold("enter")} ${verb} · ${import_picocolors34.default.bold("esc")} cancel`
63245
+ `${import_picocolors33.default.bold("enter")} ${verb} · ${import_picocolors33.default.bold("esc")} cancel`
63554
63246
  ];
63555
63247
  const inner = Math.min(maxInner, Math.max(...rows.map(visibleWidth2)));
63556
63248
  const pad = (row) => row + " ".repeat(Math.max(0, inner - visibleWidth2(row)));
63557
63249
  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)}╯`)
63250
+ import_picocolors33.default.dim(`╭${"─".repeat(inner + 2)}╮`),
63251
+ ...rows.map((row) => `${import_picocolors33.default.dim("│")} ${pad(row)} ${import_picocolors33.default.dim("│")}`),
63252
+ import_picocolors33.default.dim(`╰${"─".repeat(inner + 2)}╯`)
63561
63253
  ];
63562
63254
  }
63563
63255
  function buildActivityFrame(status, activity, width, backlog = null, pane = DEFAULT_PANE, queued = [], studiedBeforeRun = 0, open = [], runProgress = null, projectNames = {}, sessionNames = {}, frameLines) {
@@ -63573,7 +63265,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63573
63265
  progress = progressLine(studiedDelta + stepDone, Math.max(0, backlog.ready - stepDone), width, runProgress?.notes ?? 0);
63574
63266
  }
63575
63267
  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)) : [];
63268
+ const refusalLines = refusal ? wrapLine(`! Studying paused: ${refusal.message} (${localTime(refusal.at)})`, width).map((line) => import_picocolors33.default.yellow(line)) : [];
63577
63269
  const fullRows = Boolean(pane.fullRows);
63578
63270
  const withName = (item, key) => projectNames[key] ? { ...item, project: projectNames[key] } : item;
63579
63271
  const latestPass = new Map;
@@ -63599,8 +63291,8 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63599
63291
  breadcrumb(["home", "activity"], width),
63600
63292
  "",
63601
63293
  statusLine(status),
63602
- import_picocolors34.default.dim(studied),
63603
- ...queueDetail.length > 0 ? wrapLine(queueDetail.join(" · "), width).map((line) => import_picocolors34.default.dim(line)) : [],
63294
+ import_picocolors33.default.dim(studied),
63295
+ ...queueDetail.length > 0 ? wrapLine(queueDetail.join(" · "), width).map((line) => import_picocolors33.default.dim(line)) : [],
63604
63296
  ...progress ? [progress] : [],
63605
63297
  ...refusalLines,
63606
63298
  "",
@@ -63616,7 +63308,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63616
63308
  status.running ? "s stop" : status.state.paused ? "s resume" : "s sync now",
63617
63309
  ...!status.running && status.state.watermark ? ["c clear"] : [],
63618
63310
  "esc back"
63619
- ].join(" · "), width, "").map((line) => import_picocolors34.default.dim(line))
63311
+ ].join(" · "), width, "").map((line) => import_picocolors33.default.dim(line))
63620
63312
  ]
63621
63313
  ];
63622
63314
  const defaultHeight = fullRows ? ACTIVITY_VIEW_FULL_LIST_ROWS : ACTIVITY_VIEW_LIST_LINES;
@@ -63629,7 +63321,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63629
63321
  ({ visible, above, below } = windowList(source, pane.scroll, height));
63630
63322
  display = visible.flatMap((row) => wrapRow(row, width));
63631
63323
  }
63632
- const listRows = display.length > 0 ? display.map((row) => import_picocolors34.default.dim(row)) : [import_picocolors34.default.dim(empty)];
63324
+ const listRows = display.length > 0 ? display.map((row) => import_picocolors33.default.dim(row)) : [import_picocolors33.default.dim(empty)];
63633
63325
  const scrollParts = [];
63634
63326
  if (above > 0)
63635
63327
  scrollParts.push(`↑ ${above} earlier`);
@@ -63638,7 +63330,7 @@ function buildActivityFrame(status, activity, width, backlog = null, pane = DEFA
63638
63330
  const lines = [
63639
63331
  ...header,
63640
63332
  ...listRows,
63641
- ...scrollParts.length > 0 ? [import_picocolors34.default.dim(clip(scrollParts.join(" · "), width))] : [],
63333
+ ...scrollParts.length > 0 ? [import_picocolors33.default.dim(clip(scrollParts.join(" · "), width))] : [],
63642
63334
  ...footer
63643
63335
  ];
63644
63336
  return { lines, listHeight: height };
@@ -63671,7 +63363,7 @@ function createRowNamer() {
63671
63363
  names.titles[key] = t;
63672
63364
  };
63673
63365
  for (const s of [...backlog.queued, ...backlog.open]) {
63674
- if (!existsSync28(s.path))
63366
+ if (!existsSync27(s.path))
63675
63367
  continue;
63676
63368
  const key = `${s.harness}/${s.id}`;
63677
63369
  const dir = dirs.resolve(s);
@@ -63708,7 +63400,7 @@ function createRowNamer() {
63708
63400
  }
63709
63401
  function defaultReadLog2() {
63710
63402
  try {
63711
- return readFileSync27(logger.getLogPath(), "utf-8");
63403
+ return readFileSync26(logger.getLogPath(), "utf-8");
63712
63404
  } catch {
63713
63405
  return "";
63714
63406
  }
@@ -63893,7 +63585,7 @@ ${CLEAR_BELOW}`);
63893
63585
  input.on("data", onData);
63894
63586
  });
63895
63587
  }
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;
63588
+ 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
63589
  var init_activity_view = __esm(() => {
63898
63590
  init_follow();
63899
63591
  init_logger();
@@ -63908,7 +63600,7 @@ var init_activity_view = __esm(() => {
63908
63600
  init_alt_screen();
63909
63601
  init_layout();
63910
63602
  init_menu();
63911
- import_picocolors34 = __toESM(require_picocolors(), 1);
63603
+ import_picocolors33 = __toESM(require_picocolors(), 1);
63912
63604
  ESC7 = String.fromCharCode(27);
63913
63605
  HIDE_CURSOR3 = `${ESC7}[?25l`;
63914
63606
  SHOW_CURSOR3 = `${ESC7}[?25h`;
@@ -63924,6 +63616,284 @@ var init_activity_view = __esm(() => {
63924
63616
  DEFAULT_PANE = { tab: "activity", scroll: 0 };
63925
63617
  });
63926
63618
 
63619
+ // src/rules/installer.ts
63620
+ import { existsSync as existsSync28, mkdirSync as mkdirSync17, readFileSync as readFileSync27, unlinkSync as unlinkSync5, writeFileSync as writeFileSync18 } from "node:fs";
63621
+ import { homedir as homedir13 } from "node:os";
63622
+ import { dirname as dirname12, join as join42 } from "node:path";
63623
+ function claudeConfigDir4() {
63624
+ return process.env.CLAUDE_CONFIG_DIR || join42(homedir13(), ".claude");
63625
+ }
63626
+ function codexHome4() {
63627
+ return process.env.CODEX_HOME || join42(homedir13(), ".codex");
63628
+ }
63629
+ function isRuleAgent(agent) {
63630
+ return Object.hasOwn(RULE_TARGETS, agent);
63631
+ }
63632
+ function rulePathForAgent(agent) {
63633
+ return isRuleAgent(agent) ? RULE_TARGETS[agent].path() : null;
63634
+ }
63635
+ function normalizeRule(content) {
63636
+ return `${content.trim()}
63637
+ `;
63638
+ }
63639
+ async function fetchDosuRule(fetchImpl = globalThis.fetch) {
63640
+ for (const url of DOSU_RULE_URLS) {
63641
+ try {
63642
+ const response = await fetchImpl(url);
63643
+ if (!response.ok)
63644
+ continue;
63645
+ const content = await response.text();
63646
+ if (content.trim())
63647
+ return normalizeRule(content);
63648
+ } catch {}
63649
+ }
63650
+ return normalizeRule(FALLBACK_DOSU_RULE);
63651
+ }
63652
+ function ensureParent(path) {
63653
+ mkdirSync17(dirname12(path), { recursive: true });
63654
+ }
63655
+ function writeIfChanged(path, content) {
63656
+ if (!existsSync28(path)) {
63657
+ ensureParent(path);
63658
+ writeFileSync18(path, content, "utf-8");
63659
+ return "created";
63660
+ }
63661
+ if (readFileSync27(path, "utf-8") === content)
63662
+ return "unchanged";
63663
+ writeFileSync18(path, content, "utf-8");
63664
+ return "updated";
63665
+ }
63666
+ function findSection(content) {
63667
+ const startMatch = content.match(DOSU_RULE_SECTION_START_RE);
63668
+ const start = startMatch?.index ?? -1;
63669
+ const end = content.indexOf(DOSU_RULE_SECTION_END, Math.max(start, 0));
63670
+ if (start === -1 && end === -1)
63671
+ return null;
63672
+ if (start === -1 || end < start) {
63673
+ throw new Error("Dosu rule markers are incomplete; refusing to overwrite the instruction file");
63674
+ }
63675
+ return { start, end };
63676
+ }
63677
+ function lineEndingFor(content) {
63678
+ return content.includes(`\r
63679
+ `) ? `\r
63680
+ ` : `
63681
+ `;
63682
+ }
63683
+ function buildSection(content, lineEnding = `
63684
+ `) {
63685
+ const body = content.trimEnd().replace(/\r?\n/g, lineEnding);
63686
+ return `${DOSU_RULE_SECTION_START}${lineEnding}${body}${lineEnding}${DOSU_RULE_SECTION_END}`;
63687
+ }
63688
+ function upsertSection(path, content) {
63689
+ if (!existsSync28(path)) {
63690
+ const section = buildSection(content);
63691
+ ensureParent(path);
63692
+ writeFileSync18(path, `${section}
63693
+ `, "utf-8");
63694
+ return "created";
63695
+ }
63696
+ const existing = readFileSync27(path, "utf-8");
63697
+ const lineEnding = lineEndingFor(existing);
63698
+ const section = buildSection(content, lineEnding);
63699
+ const location = findSection(existing);
63700
+ let next;
63701
+ if (location) {
63702
+ next = existing.slice(0, location.start) + section + existing.slice(location.end + DOSU_RULE_SECTION_END.length);
63703
+ } else {
63704
+ const separator = existing.length > 0 && !existing.endsWith(lineEnding) ? `${lineEnding}${lineEnding}` : existing.length > 0 ? lineEnding : "";
63705
+ next = `${existing}${separator}${section}${lineEnding}`;
63706
+ }
63707
+ if (next === existing)
63708
+ return "unchanged";
63709
+ writeFileSync18(path, next, "utf-8");
63710
+ return "updated";
63711
+ }
63712
+ function installRuleForAgent(agent, content) {
63713
+ if (!isRuleAgent(agent))
63714
+ return null;
63715
+ const target = RULE_TARGETS[agent];
63716
+ const path = target.path();
63717
+ const normalized = normalizeRule(content);
63718
+ const action = target.kind === "file" ? writeIfChanged(path, target.cursorFrontmatter ? `${CURSOR_FRONTMATTER}${normalized}` : normalized) : upsertSection(path, normalized);
63719
+ return { agent, path, action };
63720
+ }
63721
+ function removeRuleForAgent(agent) {
63722
+ if (!isRuleAgent(agent))
63723
+ return null;
63724
+ const target = RULE_TARGETS[agent];
63725
+ const path = target.path();
63726
+ if (!existsSync28(path))
63727
+ return { agent, path, action: "not_found" };
63728
+ if (target.kind === "file") {
63729
+ unlinkSync5(path);
63730
+ return { agent, path, action: "removed" };
63731
+ }
63732
+ const existing = readFileSync27(path, "utf-8");
63733
+ const lineEnding = lineEndingFor(existing);
63734
+ const location = findSection(existing);
63735
+ if (!location)
63736
+ return { agent, path, action: "not_found" };
63737
+ 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();
63738
+ if (!next) {
63739
+ unlinkSync5(path);
63740
+ } else {
63741
+ writeFileSync18(path, `${next}${lineEnding}`, "utf-8");
63742
+ }
63743
+ return { agent, path, action: "removed" };
63744
+ }
63745
+ var DOSU_RULE_SECTION_VERSION = 1, DOSU_RULE_SECTION_START, DOSU_RULE_SECTION_END = "<!-- dosu:rules:end -->", DOSU_RULE_SECTION_START_RE, CURSOR_FRONTMATTER = `---
63746
+ alwaysApply: true
63747
+ ---
63748
+
63749
+ `, 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.
63750
+
63751
+ 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.
63752
+
63753
+ 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.**
63754
+
63755
+ 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.
63756
+
63757
+ 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.
63758
+ `, RULE_TARGETS;
63759
+ var init_installer = __esm(() => {
63760
+ DOSU_RULE_SECTION_START = `<!-- dosu:rules:start v${DOSU_RULE_SECTION_VERSION} -->`;
63761
+ DOSU_RULE_SECTION_START_RE = /<!-- dosu:rules:start(?: v\d+)? -->/;
63762
+ DOSU_RULE_URLS = [
63763
+ "https://raw.githubusercontent.com/dosu-ai/dosu-cli/main/rules/dosu.md",
63764
+ "https://raw.githubusercontent.com/dosu-ai/dosu-cli/master/rules/dosu.md"
63765
+ ];
63766
+ RULE_TARGETS = {
63767
+ claude: {
63768
+ kind: "file",
63769
+ path: () => join42(claudeConfigDir4(), "rules", "dosu.md")
63770
+ },
63771
+ cursor: {
63772
+ kind: "file",
63773
+ path: () => join42(homedir13(), ".cursor", "rules", "dosu.mdc"),
63774
+ cursorFrontmatter: true
63775
+ },
63776
+ codex: {
63777
+ kind: "section",
63778
+ path: () => join42(codexHome4(), "AGENTS.md")
63779
+ },
63780
+ opencode: {
63781
+ kind: "section",
63782
+ path: () => join42(homedir13(), ".config", "opencode", "AGENTS.md")
63783
+ },
63784
+ gemini: {
63785
+ kind: "section",
63786
+ path: () => join42(homedir13(), ".gemini", "GEMINI.md")
63787
+ },
63788
+ antigravity: {
63789
+ kind: "section",
63790
+ path: () => join42(homedir13(), ".gemini", "GEMINI.md")
63791
+ }
63792
+ };
63793
+ });
63794
+
63795
+ // src/setup/agents-md-step.ts
63796
+ import { execSync as execSync2 } from "node:child_process";
63797
+ import { existsSync as existsSync29, readFileSync as readFileSync28, writeFileSync as writeFileSync19 } from "node:fs";
63798
+ import { join as join43 } from "node:path";
63799
+ function inGitWorkTree(cwd = process.cwd()) {
63800
+ try {
63801
+ const stdout = execSync2("git rev-parse --is-inside-work-tree", {
63802
+ cwd,
63803
+ stdio: ["ignore", "pipe", "ignore"]
63804
+ });
63805
+ return stdout.toString().trim() === "true";
63806
+ } catch {
63807
+ return false;
63808
+ }
63809
+ }
63810
+ function dosuAgentsSectionState(cwd = process.cwd()) {
63811
+ try {
63812
+ const path = join43(cwd, "AGENTS.md");
63813
+ if (!existsSync29(path))
63814
+ return "missing";
63815
+ const content = readFileSync28(path, "utf-8");
63816
+ const startMatch = content.match(SECTION_START_RE);
63817
+ if (!startMatch || !content.includes(DOSU_SECTION_END))
63818
+ return "missing";
63819
+ return Number(startMatch[1] ?? 0) >= DOSU_SECTION_VERSION ? "current" : "outdated";
63820
+ } catch {
63821
+ return "missing";
63822
+ }
63823
+ }
63824
+ function lineEndingFor2(content) {
63825
+ return content.includes(`\r
63826
+ `) ? `\r
63827
+ ` : `
63828
+ `;
63829
+ }
63830
+ function renderDosuAgentsSection(content, lineEnding) {
63831
+ const body = content.trim().replace(/\r?\n/g, lineEnding);
63832
+ return `${DOSU_SECTION_START}${lineEnding}${body}${lineEnding}${DOSU_SECTION_END}`;
63833
+ }
63834
+ function buildDosuAgentsSection(content = FALLBACK_DOSU_RULE) {
63835
+ return renderDosuAgentsSection(content, `
63836
+ `);
63837
+ }
63838
+ function findSection2(content) {
63839
+ const startMatch = content.match(SECTION_START_RE);
63840
+ const start = startMatch?.index ?? -1;
63841
+ const end = content.indexOf(DOSU_SECTION_END, Math.max(start, 0));
63842
+ if (start === -1 && end === -1)
63843
+ return null;
63844
+ if (start === -1 || end < start) {
63845
+ throw new Error("Dosu AGENTS.md markers are incomplete; refusing to overwrite the file");
63846
+ }
63847
+ return { start, end };
63848
+ }
63849
+ function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RULE) {
63850
+ const path = join43(cwd, "AGENTS.md");
63851
+ if (!existsSync29(path)) {
63852
+ const section = buildDosuAgentsSection(content);
63853
+ writeFileSync19(path, `${section}
63854
+ `);
63855
+ return { path, action: "created" };
63856
+ }
63857
+ const existing = readFileSync28(path, "utf-8");
63858
+ const lineEnding = lineEndingFor2(existing);
63859
+ const section = renderDosuAgentsSection(content, lineEnding);
63860
+ const location = findSection2(existing);
63861
+ let next;
63862
+ if (location) {
63863
+ next = existing.slice(0, location.start) + section + existing.slice(location.end + DOSU_SECTION_END.length);
63864
+ } else {
63865
+ next = `${existing.trimEnd()}${lineEnding}${lineEnding}${section}${lineEnding}`;
63866
+ }
63867
+ if (next === existing)
63868
+ return { path, action: "unchanged" };
63869
+ writeFileSync19(path, next);
63870
+ return { path, action: "updated" };
63871
+ }
63872
+ async function stepUpdateAgentsMd(cwd = process.cwd(), content) {
63873
+ logger.info("setup", "Step: update AGENTS.md");
63874
+ try {
63875
+ const result = upsertDosuAgentsSection(cwd, content ?? await fetchDosuRule());
63876
+ logger.info("setup", `AGENTS.md ${result.action} at ${result.path}`);
63877
+ const status = result.action === "unchanged" ? "already up to date" : result.action;
63878
+ log2.success(formatSetupSummary("AGENTS.md", [{ path: result.path, status }]));
63879
+ return true;
63880
+ } catch (err) {
63881
+ const msg = err instanceof Error ? err.message : String(err);
63882
+ logger.error("setup", `AGENTS.md update failed: ${msg}`);
63883
+ log2.error(`Could not update AGENTS.md: ${msg}`);
63884
+ return false;
63885
+ }
63886
+ }
63887
+ var DOSU_SECTION_VERSION = 2, DOSU_SECTION_START, DOSU_SECTION_END = "<!-- dosu:mcp:end -->", SECTION_START_RE;
63888
+ var init_agents_md_step = __esm(() => {
63889
+ init_logger();
63890
+ init_installer();
63891
+ init_prompts();
63892
+ init_styles();
63893
+ DOSU_SECTION_START = `<!-- dosu:mcp:start v${DOSU_SECTION_VERSION} -->`;
63894
+ SECTION_START_RE = /<!-- dosu:mcp:start(?: v(\d+))? -->/;
63895
+ });
63896
+
63927
63897
  // src/setup/analytics.ts
63928
63898
  async function trackCliOnboardingEvent(cfg, onboardingRunID, event, properties = {}) {
63929
63899
  try {
@@ -65391,7 +65361,7 @@ async function stepSelectTools(detected) {
65391
65361
  initialValues: preselected,
65392
65362
  statusFor: (id, picked) => {
65393
65363
  if (configuredMap.get(id))
65394
- return picked ? dim("configured") : import_picocolors35.default.yellow("will remove");
65364
+ return picked ? dim("configured") : import_picocolors34.default.yellow("will remove");
65395
65365
  return picked ? brand("will configure") : undefined;
65396
65366
  },
65397
65367
  summary: (picked) => {
@@ -65566,7 +65536,7 @@ ${dim(wrapLog("Run it inside a session to keep that session out of studying. Rem
65566
65536
  log2.success("All agents already configured. No changes needed.");
65567
65537
  }
65568
65538
  }
65569
- var import_picocolors35, OAUTH_ANALYTICS_REASONS, SETUP_HANDSHAKE_TIMEOUT_MS = 1800000, LIBRARY_SELECT_CANCELLED;
65539
+ var import_picocolors34, OAUTH_ANALYTICS_REASONS, SETUP_HANDSHAKE_TIMEOUT_MS = 1800000, LIBRARY_SELECT_CANCELLED;
65570
65540
  var init_flow2 = __esm(() => {
65571
65541
  init_dist();
65572
65542
  init_errors3();
@@ -65591,7 +65561,7 @@ var init_flow2 = __esm(() => {
65591
65561
  init_github_step();
65592
65562
  init_rules_step();
65593
65563
  init_styles();
65594
- import_picocolors35 = __toESM(require_picocolors(), 1);
65564
+ import_picocolors34 = __toESM(require_picocolors(), 1);
65595
65565
  OAUTH_ANALYTICS_REASONS = new Set([
65596
65566
  "access_denied",
65597
65567
  "bad_oauth_state",
@@ -65605,6 +65575,298 @@ var init_flow2 = __esm(() => {
65605
65575
  LIBRARY_SELECT_CANCELLED = Symbol("library-select-cancelled");
65606
65576
  });
65607
65577
 
65578
+ // src/commands/upgrade.ts
65579
+ import { spawnSync } from "node:child_process";
65580
+ import { realpathSync as realpathSync2 } from "node:fs";
65581
+ import { homedir as homedir14 } from "node:os";
65582
+ import { posix, win32 as win322 } from "node:path";
65583
+ function isAbsoluteCommandProcessor(value) {
65584
+ return Boolean(value && win322.isAbsolute(value) && win322.basename(value).toLowerCase() === "cmd.exe");
65585
+ }
65586
+ function windowsCommandProcessor(env = process.env) {
65587
+ if (isAbsoluteCommandProcessor(env.ComSpec))
65588
+ return env.ComSpec;
65589
+ const systemRoot = env.SystemRoot && win322.isAbsolute(env.SystemRoot) ? env.SystemRoot : "C:\\Windows";
65590
+ return win322.join(systemRoot, "System32", "cmd.exe");
65591
+ }
65592
+ function safePackageManagerEnv(env) {
65593
+ return {
65594
+ ...env,
65595
+ COREPACK_ENABLE_NETWORK: "0",
65596
+ COREPACK_ENABLE_PROJECT_SPEC: "0",
65597
+ YARN_IGNORE_PATH: "1"
65598
+ };
65599
+ }
65600
+ function buildPackageManagerInvocation(manager, action, platform = process.platform, comSpec, env = process.env) {
65601
+ const args = action === "locate" ? PACKAGE_MANAGERS[manager].locateArgs : PACKAGE_MANAGERS[manager].installArgs;
65602
+ const safeEnv = safePackageManagerEnv(env);
65603
+ if (platform === "win32") {
65604
+ return {
65605
+ command: isAbsoluteCommandProcessor(comSpec) ? comSpec : windowsCommandProcessor(env),
65606
+ args: ["/d", "/s", "/c", [manager, ...args].join(" ")],
65607
+ env: { ...safeEnv, NoDefaultCurrentDirectoryInExePath: "1" }
65608
+ };
65609
+ }
65610
+ return { command: manager, args: [...args], env: safeEnv };
65611
+ }
65612
+ function packageRoots(manager, platform, comSpec, env, cwd) {
65613
+ const invocation = buildPackageManagerInvocation(manager, "locate", platform, comSpec, env);
65614
+ const result = spawnSync(invocation.command, invocation.args, {
65615
+ cwd,
65616
+ encoding: "utf8",
65617
+ env: invocation.env,
65618
+ shell: false,
65619
+ stdio: ["ignore", "pipe", "ignore"],
65620
+ timeout: PROBE_TIMEOUT_MS
65621
+ });
65622
+ if (result.status !== 0 || typeof result.stdout !== "string")
65623
+ return [];
65624
+ const path = platform === "win32" ? win322 : posix;
65625
+ const lines = result.stdout.split(/\r?\n/).map((line) => line.trim()).filter((line) => path.isAbsolute(line));
65626
+ if (manager === "pnpm") {
65627
+ return lines.filter((line) => path.basename(line) === "cli" && path.basename(path.dirname(line)) === "@dosu" && path.basename(path.dirname(path.dirname(line))) === "node_modules");
65628
+ }
65629
+ if (lines.length !== 1)
65630
+ return [];
65631
+ return [
65632
+ manager === "npm" ? path.join(lines[0], "@dosu", "cli") : path.join(lines[0], "node_modules", "@dosu", "cli")
65633
+ ];
65634
+ }
65635
+ function owningPackageManager(entrypoint, platform, comSpec, env = process.env, cwd = homedir14()) {
65636
+ const path = platform === "win32" ? win322 : posix;
65637
+ if (!entrypoint || !path.isAbsolute(entrypoint))
65638
+ return null;
65639
+ let realEntrypoint;
65640
+ try {
65641
+ realEntrypoint = realpathSync2(entrypoint);
65642
+ } catch {
65643
+ return null;
65644
+ }
65645
+ const owners = Object.keys(PACKAGE_MANAGERS).filter((manager) => packageRoots(manager, platform, comSpec, env, cwd).some((root) => {
65646
+ try {
65647
+ const relative = path.relative(realpathSync2(root), realEntrypoint);
65648
+ return relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
65649
+ } catch {
65650
+ return false;
65651
+ }
65652
+ }));
65653
+ return owners.length === 1 ? owners[0] : null;
65654
+ }
65655
+ function upgradePlan(channel, manager, platform, comSpec, env = process.env) {
65656
+ if (channel === "homebrew") {
65657
+ return {
65658
+ label: "Homebrew",
65659
+ command: "brew",
65660
+ args: ["upgrade", "dosu-ai/dosu/dosu"],
65661
+ manualCommand: BREW_MANUAL_COMMAND
65662
+ };
65663
+ }
65664
+ if (channel === "npm" && manager) {
65665
+ const invocation = buildPackageManagerInvocation(manager, "install", platform, comSpec, env);
65666
+ return {
65667
+ label: PACKAGE_MANAGERS[manager].label,
65668
+ ...invocation,
65669
+ manualCommand: [manager, ...PACKAGE_MANAGERS[manager].installArgs].join(" ")
65670
+ };
65671
+ }
65672
+ return null;
65673
+ }
65674
+ function printNonGlobalPackageGuidance() {
65675
+ console.log("This Dosu copy is not a uniquely identified global package installation, so it was not changed.");
65676
+ console.log(`
65677
+ Run the latest version without installing:
65678
+ ${NPX_COMMAND}`);
65679
+ console.log(`
65680
+ Or install Dosu globally with your package manager:`);
65681
+ for (const manager of Object.keys(PACKAGE_MANAGERS)) {
65682
+ console.log(` ${manager} ${PACKAGE_MANAGERS[manager].installArgs.join(" ")}`);
65683
+ }
65684
+ }
65685
+ function runUpgrade(channel = INSTALL_CHANNEL, options = {}) {
65686
+ const platform = options.platform ?? process.platform;
65687
+ const env = options.env ?? process.env;
65688
+ const cwd = options.cwd ?? homedir14();
65689
+ let manager = null;
65690
+ if (channel === "npm") {
65691
+ if (isNpxInvocation(channel, env)) {
65692
+ printNonGlobalPackageGuidance();
65693
+ return 1;
65694
+ }
65695
+ manager = owningPackageManager(options.entrypoint ?? process.argv[1], platform, options.comSpec, env, cwd);
65696
+ if (!manager) {
65697
+ if (env.DOSU_DEV === "true") {
65698
+ console.log("DOSU_DEV=true and this copy is not a global package: skipping the package install.");
65699
+ return 0;
65700
+ }
65701
+ printNonGlobalPackageGuidance();
65702
+ return 1;
65703
+ }
65704
+ }
65705
+ const plan = upgradePlan(channel, manager, platform, options.comSpec, env);
65706
+ if (!plan) {
65707
+ console.log("Automatic upgrades are not available for this installation yet.");
65708
+ console.log(`Download the latest release:
65709
+ ${RELEASES_URL}`);
65710
+ return 1;
65711
+ }
65712
+ console.log(`Updating Dosu with ${plan.label}...`);
65713
+ console.log(` ${plan.manualCommand}
65714
+ `);
65715
+ const result = spawnSync(plan.command, plan.args, {
65716
+ ...channel === "npm" ? { cwd } : {},
65717
+ ...plan.env ? { env: plan.env } : {},
65718
+ shell: false,
65719
+ stdio: "inherit"
65720
+ });
65721
+ if (result.error || result.status !== 0) {
65722
+ console.error(`
65723
+ Could not update Dosu automatically.`);
65724
+ console.error(`Run manually:
65725
+ ${plan.manualCommand}`);
65726
+ return result.status && result.status > 0 ? result.status : 1;
65727
+ }
65728
+ console.log(import_picocolors35.default.green(`
65729
+ ✓ Update command completed.`));
65730
+ console.log('Run "dosu --version" to verify.');
65731
+ return 0;
65732
+ }
65733
+ function postUpgradeArgs(fromVersion = VERSION) {
65734
+ return ["upgrade", "--finish", fromVersion];
65735
+ }
65736
+ function newBinaryInvocation(channel, args, entrypoint = process.argv[1], execPath = process.execPath) {
65737
+ if (channel === "npm") {
65738
+ return entrypoint ? { command: execPath, args: [entrypoint, ...args] } : null;
65739
+ }
65740
+ if (channel === "homebrew") {
65741
+ return { command: "dosu", args };
65742
+ }
65743
+ return null;
65744
+ }
65745
+ function isInteractiveTerminal() {
65746
+ return process.stdin.isTTY === true && process.stdout.isTTY === true;
65747
+ }
65748
+ function finishWithNewBinary(channel, options) {
65749
+ const invocation = newBinaryInvocation(channel, postUpgradeArgs(), options.entrypoint ?? process.argv[1]);
65750
+ if (!invocation) {
65751
+ console.error(SETUP_FALLBACK);
65752
+ return;
65753
+ }
65754
+ const result = spawnSync(invocation.command, invocation.args, {
65755
+ cwd: options.cwd ?? homedir14(),
65756
+ shell: false,
65757
+ stdio: "inherit"
65758
+ });
65759
+ if (result.error || result.status !== 0) {
65760
+ console.error(SETUP_FALLBACK);
65761
+ }
65762
+ }
65763
+ async function completeUpgrade(channel = INSTALL_CHANNEL, options = {}) {
65764
+ const status = runUpgrade(channel, options);
65765
+ if (status !== 0)
65766
+ return status;
65767
+ finishWithNewBinary(channel, options);
65768
+ return 0;
65769
+ }
65770
+ async function finishUpgrade(from, options = {}) {
65771
+ const previous = SEMVER.test(from) ? from : null;
65772
+ if (!needsMcpRefresh(previous, VERSION)) {
65773
+ checkForSkillUpdates();
65774
+ writeMcpRefreshCache({ version: VERSION });
65775
+ console.log(import_picocolors35.default.green(`✓ Dosu ${VERSION} is ready. Your AI agents need no changes.`));
65776
+ return 0;
65777
+ }
65778
+ if (options.interactive ?? isInteractiveTerminal()) {
65779
+ console.log(`
65780
+ This update changes how agents connect to Dosu. Running setup...
65781
+ `);
65782
+ await Promise.resolve().then(() => init_flow2());
65783
+ await runSetup();
65784
+ return 0;
65785
+ }
65786
+ checkForSkillUpdates();
65787
+ const cfg = loadConfigNonBlocking();
65788
+ if (!cfg || !canRefreshMcp(cfg)) {
65789
+ console.error(SETUP_FALLBACK);
65790
+ return 0;
65791
+ }
65792
+ console.log(`
65793
+ Refreshing agent MCP configs with the new version...`);
65794
+ const result = refreshConfiguredProviders(cfg);
65795
+ writeMcpRefreshCache({ version: VERSION });
65796
+ for (const provider of result.updated)
65797
+ console.log(` ✓ ${provider.name()}`);
65798
+ for (const { provider, error } of result.failed) {
65799
+ console.log(` ✗ ${provider.name()}: ${error.message}`);
65800
+ }
65801
+ if (result.failed.length > 0)
65802
+ console.error(SETUP_FALLBACK);
65803
+ return 0;
65804
+ }
65805
+ function setAutoUpdate(value) {
65806
+ const normalized = value.trim().toLowerCase();
65807
+ if (normalized !== "on" && normalized !== "off") {
65808
+ console.error(`Invalid --auto value '${value}' (expected 'on' or 'off').`);
65809
+ return 2;
65810
+ }
65811
+ if (!setAutoUpdateEnabled(normalized === "on")) {
65812
+ console.error("Could not save the auto-update setting.");
65813
+ return 1;
65814
+ }
65815
+ if (normalized === "off") {
65816
+ console.log('Automatic updates are off. Run "dosu upgrade" to update by hand.');
65817
+ } else if (autoUpdateDisabledReason() === "env") {
65818
+ console.log(`Automatic updates are on, but ${AUTO_UPDATE_ENV} still disables them here.`);
65819
+ } else {
65820
+ console.log("Automatic updates are on.");
65821
+ }
65822
+ return 0;
65823
+ }
65824
+ function upgradeCommand() {
65825
+ return new Command("upgrade").description("Update Dosu to the latest version, re-running setup when the update changes agent config").option("--auto <on|off>", "Turn automatic background updates on or off").addOption(new Option("--background").hideHelp()).addOption(new Option("--finish <from>").hideHelp()).action(async (opts) => {
65826
+ let status;
65827
+ if (opts.auto !== undefined) {
65828
+ status = setAutoUpdate(opts.auto);
65829
+ } else if (opts.background) {
65830
+ status = runBackgroundUpgrade(() => runUpgrade());
65831
+ } else if (opts.finish !== undefined) {
65832
+ status = await finishUpgrade(opts.finish);
65833
+ } else {
65834
+ status = await completeUpgrade();
65835
+ }
65836
+ if (status !== 0)
65837
+ process.exitCode = status;
65838
+ });
65839
+ }
65840
+ 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;
65841
+ var init_upgrade = __esm(() => {
65842
+ init_commander();
65843
+ init_config();
65844
+ init_refresh();
65845
+ init_auto_update();
65846
+ init_mcp_refresh_check();
65847
+ init_skill_update_check();
65848
+ import_picocolors35 = __toESM(require_picocolors(), 1);
65849
+ LATEST_PACKAGE = `${PACKAGE_NAME}@latest`;
65850
+ PACKAGE_MANAGERS = {
65851
+ npm: {
65852
+ label: "npm",
65853
+ locateArgs: ["root", "-g"],
65854
+ installArgs: ["install", "-g", LATEST_PACKAGE]
65855
+ },
65856
+ pnpm: {
65857
+ label: "pnpm",
65858
+ locateArgs: ["list", "-g", "--depth=0", "--parseable", PACKAGE_NAME],
65859
+ installArgs: ["add", "-g", LATEST_PACKAGE]
65860
+ },
65861
+ yarn: {
65862
+ label: "Yarn Classic",
65863
+ locateArgs: ["--silent", "global", "dir"],
65864
+ installArgs: ["global", "add", LATEST_PACKAGE]
65865
+ }
65866
+ };
65867
+ SEMVER = /^\d+\.\d+\.\d+/;
65868
+ });
65869
+
65608
65870
  // src/tui/analytics-view.ts
65609
65871
  function analyticsListHeight(rows) {
65610
65872
  const room = frameMaxLines(rows) - ANALYTICS_VIEW_CHROME_LINES;
@@ -66288,7 +66550,7 @@ var init_pages_view = __esm(() => {
66288
66550
  });
66289
66551
 
66290
66552
  // src/tui/tui.ts
66291
- import { homedir as homedir14 } from "node:os";
66553
+ import { homedir as homedir15 } from "node:os";
66292
66554
  import { basename as basename6 } from "node:path";
66293
66555
  async function probeSession(cfg) {
66294
66556
  if (!isAuthenticated(cfg) || !isTokenExpired(cfg))
@@ -66553,7 +66815,7 @@ async function runSettings(cfg) {
66553
66815
  }
66554
66816
  }
66555
66817
  function displayDir(dir) {
66556
- const home = homedir14();
66818
+ const home = homedir15();
66557
66819
  return dir === home ? "~" : dir.startsWith(`${home}/`) ? `~${dir.slice(home.length)}` : dir;
66558
66820
  }
66559
66821
  function discoverProjectDirs() {
@@ -67263,7 +67525,7 @@ var init_flow3 = __esm(() => {
67263
67525
  });
67264
67526
 
67265
67527
  // src/cli/cli.ts
67266
- import { readFileSync as readFileSync28, unlinkSync as unlinkSync6 } from "node:fs";
67528
+ import { readFileSync as readFileSync29, unlinkSync as unlinkSync6 } from "node:fs";
67267
67529
  function shouldRunBackgroundChecks(actionName) {
67268
67530
  return actionName !== "upgrade";
67269
67531
  }
@@ -67709,7 +67971,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
67709
67971
  }
67710
67972
  if (opts.follow) {
67711
67973
  try {
67712
- const lines = readFileSync28(logPath, "utf-8").split(`
67974
+ const lines = readFileSync29(logPath, "utf-8").split(`
67713
67975
  `);
67714
67976
  if (lines.at(-1) === "")
67715
67977
  lines.pop();
@@ -67725,7 +67987,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
67725
67987
  if (opts.tail !== undefined) {
67726
67988
  const n = typeof opts.tail === "string" ? parseInt(opts.tail, 10) || 50 : 50;
67727
67989
  try {
67728
- const content = readFileSync28(logPath, "utf-8");
67990
+ const content = readFileSync29(logPath, "utf-8");
67729
67991
  const lines = content.split(`
67730
67992
  `);
67731
67993
  console.log(lines.slice(-n).join(`
@@ -67851,4 +68113,4 @@ main().catch((err) => {
67851
68113
  process.exit(1);
67852
68114
  });
67853
68115
 
67854
- //# debugId=9d93d312-f9af-7e47-6475-6e2164756e21
68116
+ //# debugId=41cac967-78f1-f75a-6475-6e2164756e21