@dosu/cli 0.32.1 → 0.33.1

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 (2) hide show
  1. package/bin/dosu.js +247 -115
  2. package/package.json +1 -1
package/bin/dosu.js CHANGED
@@ -2100,6 +2100,16 @@ var init_commander = __esm(() => {
2100
2100
  // src/config/config.ts
2101
2101
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2102
2102
  import { join } from "node:path";
2103
+ function replaceLoginSession(cfg, session) {
2104
+ cfg.access_token = session.access_token;
2105
+ cfg.refresh_token = session.refresh_token;
2106
+ cfg.expires_at = session.expires_at;
2107
+ cfg.deployment_id = undefined;
2108
+ cfg.deployment_name = undefined;
2109
+ cfg.api_key = undefined;
2110
+ cfg.org_id = undefined;
2111
+ cfg.space_id = undefined;
2112
+ }
2103
2113
  function getConfigDir() {
2104
2114
  const dirName = process.env.DOSU_DEV === "true" ? "dosu-cli-dev" : "dosu-cli";
2105
2115
  const xdgConfig = process.env.XDG_CONFIG_HOME;
@@ -4577,7 +4587,7 @@ var init_dist4 = __esm(() => {
4577
4587
  function getVersionString() {
4578
4588
  return `v${VERSION}`;
4579
4589
  }
4580
- var VERSION = "0.32.1", INSTALL_CHANNEL = "npm";
4590
+ var VERSION = "0.33.1", INSTALL_CHANNEL = "npm";
4581
4591
 
4582
4592
  // src/debug/logger.ts
4583
4593
  import {
@@ -10981,6 +10991,160 @@ var init_providers = __esm(() => {
10981
10991
  init_zed();
10982
10992
  });
10983
10993
 
10994
+ // src/setup/audit-handoff.ts
10995
+ import { spawnSync as spawnSync2 } from "node:child_process";
10996
+ function buildAuditHandoffPrompt(dosuCmd = dosuInvocation()) {
10997
+ return [
10998
+ "Audit this repository to see what docs Dosu can generate for it.",
10999
+ "1. Run the codebase audit from the Dosu skill. It writes findings to .dosu/audit.json.",
11000
+ "2. Show me the findings and which docs you recommend Dosu generate.",
11001
+ `3. Once I confirm, run \`${dosuCmd} audit --tasks <comma-separated task ids> --json\` to kick off generation. Dosu opens a PR and the CLI notifies me when it's ready.`
11002
+ ].join(`
11003
+ `);
11004
+ }
11005
+ function binOnPath(bin) {
11006
+ const cmd = process.platform === "win32" ? "where" : "which";
11007
+ try {
11008
+ return spawnSync2(cmd, [bin], { stdio: "ignore" }).status === 0;
11009
+ } catch {
11010
+ return false;
11011
+ }
11012
+ }
11013
+ function claudeCliAvailable() {
11014
+ return binOnPath("claude");
11015
+ }
11016
+ function dosuInvocation() {
11017
+ return binOnPath("dosu") ? "dosu" : "npx -y @dosu/cli";
11018
+ }
11019
+ function printManualAuditNudge() {
11020
+ log.message(`See what docs Dosu can generate for this repo:
11021
+
11022
+ ${info("Ask Dosu to audit this repo and show what docs it can generate.")}
11023
+
11024
+ Or run ${info(`${dosuInvocation()} audit`)} after your agent writes the audit.`);
11025
+ }
11026
+ async function offerAuditHandoff() {
11027
+ if (!detectGitRepo())
11028
+ return false;
11029
+ if (!claudeCliAvailable()) {
11030
+ printManualAuditNudge();
11031
+ return false;
11032
+ }
11033
+ const go = await confirm({
11034
+ message: "Kick off the codebase audit in Claude Code now?",
11035
+ initialValue: true
11036
+ });
11037
+ if (isCancel(go) || !go) {
11038
+ printManualAuditNudge();
11039
+ return false;
11040
+ }
11041
+ return true;
11042
+ }
11043
+ function launchAuditAgent() {
11044
+ logger.info("setup", "Handing off to Claude Code for the codebase audit");
11045
+ const result = spawnSync2("claude", ["--model", AUDIT_AGENT_MODEL, buildAuditHandoffPrompt()], {
11046
+ stdio: "inherit",
11047
+ shell: process.platform === "win32"
11048
+ });
11049
+ if (result.error) {
11050
+ logger.warn("setup", `Claude Code launch failed: ${result.error.message}`);
11051
+ printManualAuditNudge();
11052
+ }
11053
+ }
11054
+ var AUDIT_AGENT_MODEL = "haiku";
11055
+ var init_audit_handoff = __esm(() => {
11056
+ init_dist8();
11057
+ init_logger();
11058
+ init_github_step();
11059
+ init_styles();
11060
+ });
11061
+
11062
+ // src/setup/agents-md-step.ts
11063
+ import { execSync as execSync3 } from "node:child_process";
11064
+ import { existsSync as existsSync16, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "node:fs";
11065
+ import { join as join26 } from "node:path";
11066
+ function inGitWorkTree(cwd = process.cwd()) {
11067
+ try {
11068
+ const stdout2 = execSync3("git rev-parse --is-inside-work-tree", {
11069
+ cwd,
11070
+ stdio: ["ignore", "pipe", "ignore"]
11071
+ });
11072
+ return stdout2.toString().trim() === "true";
11073
+ } catch {
11074
+ return false;
11075
+ }
11076
+ }
11077
+ function buildDosuAgentsSection(dosuCmd = dosuInvocation()) {
11078
+ return [
11079
+ DOSU_SECTION_START,
11080
+ "## Dosu",
11081
+ "",
11082
+ "Shared team knowledge lives in [Dosu](https://dosu.dev), via the Dosu MCP server.",
11083
+ "",
11084
+ "- Before a task, and for any codebase or docs questions: pull context with `read_knowledge` before digging through source.",
11085
+ "- After a task: save durable learnings with `write_knowledge`.",
11086
+ "",
11087
+ `Missing these tools? Run \`${dosuCmd} setup --help\` — it covers agent-assisted setup.`,
11088
+ DOSU_SECTION_END
11089
+ ].join(`
11090
+ `);
11091
+ }
11092
+ function upsertDosuAgentsSection(cwd = process.cwd(), dosuCmd = dosuInvocation()) {
11093
+ const path3 = join26(cwd, "AGENTS.md");
11094
+ const section = buildDosuAgentsSection(dosuCmd);
11095
+ if (!existsSync16(path3)) {
11096
+ writeFileSync7(path3, `${section}
11097
+ `);
11098
+ return { path: path3, action: "created" };
11099
+ }
11100
+ const existing = readFileSync14(path3, "utf-8");
11101
+ const startMatch = existing.match(SECTION_START_RE);
11102
+ const start = startMatch?.index ?? -1;
11103
+ const end = existing.indexOf(DOSU_SECTION_END);
11104
+ let next;
11105
+ if (start !== -1 && end !== -1 && end >= start) {
11106
+ next = existing.slice(0, start) + section + existing.slice(end + DOSU_SECTION_END.length);
11107
+ } else {
11108
+ next = `${existing.trimEnd()}
11109
+
11110
+ ${section}
11111
+ `;
11112
+ }
11113
+ if (next === existing)
11114
+ return { path: path3, action: "unchanged" };
11115
+ writeFileSync7(path3, next);
11116
+ return { path: path3, action: "updated" };
11117
+ }
11118
+ function stepUpdateAgentsMd(cwd = process.cwd()) {
11119
+ logger.info("setup", "Step: update AGENTS.md");
11120
+ try {
11121
+ const result = upsertDosuAgentsSection(cwd);
11122
+ logger.info("setup", `AGENTS.md ${result.action} at ${result.path}`);
11123
+ if (result.action === "unchanged") {
11124
+ log.success(`AGENTS.md
11125
+ ${dim("Dosu section already up to date")}`);
11126
+ } else {
11127
+ log.success(`AGENTS.md
11128
+ ${dim(`Dosu section ${result.action} — ${result.path}`)}`);
11129
+ }
11130
+ return true;
11131
+ } catch (err) {
11132
+ const msg = err instanceof Error ? err.message : String(err);
11133
+ logger.error("setup", `AGENTS.md update failed: ${msg}`);
11134
+ log.error(`Could not update AGENTS.md: ${msg}`);
11135
+ return false;
11136
+ }
11137
+ }
11138
+ var DOSU_SECTION_VERSION = 1, DOSU_SECTION_START, DOSU_SECTION_END = "<!-- dosu:mcp:end -->", SECTION_START_RE;
11139
+ var init_agents_md_step = __esm(() => {
11140
+ init_dist8();
11141
+ init_logger();
11142
+ init_audit_handoff();
11143
+ init_styles();
11144
+ DOSU_SECTION_START = `<!-- dosu:mcp:start v${DOSU_SECTION_VERSION} -->`;
11145
+ SECTION_START_RE = /<!-- dosu:mcp:start(?: v(\d+))? -->/;
11146
+ });
11147
+
10984
11148
  // src/setup/analytics.ts
10985
11149
  async function trackCliOnboardingEvent(cfg, onboardingRunID, event, properties = {}) {
10986
11150
  if (!cfg.access_token)
@@ -11066,74 +11230,6 @@ var init_analytics = __esm(() => {
11066
11230
  init_logger();
11067
11231
  });
11068
11232
 
11069
- // src/setup/audit-handoff.ts
11070
- import { spawnSync as spawnSync2 } from "node:child_process";
11071
- function buildAuditHandoffPrompt(dosuCmd = dosuInvocation()) {
11072
- return [
11073
- "Audit this repository to see what docs Dosu can generate for it.",
11074
- "1. Run the codebase audit from the Dosu skill. It writes findings to .dosu/audit.json.",
11075
- "2. Show me the findings and which docs you recommend Dosu generate.",
11076
- `3. Once I confirm, run \`${dosuCmd} audit --tasks <comma-separated task ids> --json\` to kick off generation. Dosu opens a PR and the CLI notifies me when it's ready.`
11077
- ].join(`
11078
- `);
11079
- }
11080
- function binOnPath(bin) {
11081
- const cmd = process.platform === "win32" ? "where" : "which";
11082
- try {
11083
- return spawnSync2(cmd, [bin], { stdio: "ignore" }).status === 0;
11084
- } catch {
11085
- return false;
11086
- }
11087
- }
11088
- function claudeCliAvailable() {
11089
- return binOnPath("claude");
11090
- }
11091
- function dosuInvocation() {
11092
- return binOnPath("dosu") ? "dosu" : "npx -y @dosu/cli";
11093
- }
11094
- function printManualAuditNudge() {
11095
- log.message(`See what docs Dosu can generate for this repo:
11096
-
11097
- ${info("Ask Dosu to audit this repo and show what docs it can generate.")}
11098
-
11099
- Or run ${info(`${dosuInvocation()} audit`)} after your agent writes the audit.`);
11100
- }
11101
- async function offerAuditHandoff() {
11102
- if (!detectGitRepo())
11103
- return false;
11104
- if (!claudeCliAvailable()) {
11105
- printManualAuditNudge();
11106
- return false;
11107
- }
11108
- const go = await confirm({
11109
- message: "Kick off the codebase audit in Claude Code now?",
11110
- initialValue: true
11111
- });
11112
- if (isCancel(go) || !go) {
11113
- printManualAuditNudge();
11114
- return false;
11115
- }
11116
- return true;
11117
- }
11118
- function launchAuditAgent() {
11119
- logger.info("setup", "Handing off to Claude Code for the codebase audit");
11120
- const result = spawnSync2("claude", ["--model", AUDIT_AGENT_MODEL, buildAuditHandoffPrompt()], {
11121
- stdio: "inherit",
11122
- shell: process.platform === "win32"
11123
- });
11124
- if (result.error) {
11125
- logger.warn("setup", `Claude Code launch failed: ${result.error.message}`);
11126
- printManualAuditNudge();
11127
- }
11128
- }
11129
- var AUDIT_AGENT_MODEL = "haiku";
11130
- var init_audit_handoff = __esm(() => {
11131
- init_dist8();
11132
- init_logger();
11133
- init_github_step();
11134
- init_styles();
11135
- });
11136
-
11137
11233
  // src/auth/errors.ts
11138
11234
  var exports_errors = {};
11139
11235
  __export(exports_errors, {
@@ -11662,7 +11758,7 @@ async function runSetup(opts = {}) {
11662
11758
  authTab?.close();
11663
11759
  };
11664
11760
  await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_auth_completed");
11665
- const apiClient = new Client(cfg);
11761
+ let apiClient = new Client(cfg);
11666
11762
  let cloudSetupContext = null;
11667
11763
  if (cfg.mode !== MODE_OSS) {
11668
11764
  const s = spinner();
@@ -11693,7 +11789,9 @@ async function runSetup(opts = {}) {
11693
11789
  });
11694
11790
  return;
11695
11791
  }
11696
- const ok = await bindOnboardingDeployment(apiClient, cfg, cloudSetupContext.targetOrg ?? null);
11792
+ apiClient = new Client(cfg);
11793
+ const targetOrg = await resolveCurrentOnboardingTargetOrg(cfg);
11794
+ const ok = await bindOnboardingDeployment(apiClient, cfg, targetOrg);
11697
11795
  if (!ok) {
11698
11796
  await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_failed", {
11699
11797
  reason: "onboarding_deployment_failed"
@@ -11718,7 +11816,7 @@ async function runSetup(opts = {}) {
11718
11816
  }
11719
11817
  cfg.api_key = apiKey;
11720
11818
  saveConfig(cfg);
11721
- const choices = await stepOneShotConfirm();
11819
+ const choices = await stepOneShotConfirm(inGitWorkTree());
11722
11820
  if (!choices) {
11723
11821
  await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_cancelled", {
11724
11822
  reason: "options_cancelled"
@@ -11727,7 +11825,8 @@ async function runSetup(opts = {}) {
11727
11825
  }
11728
11826
  await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_options_selected", {
11729
11827
  configure_mcp: choices.configureMcp,
11730
- install_skill: choices.installSkill
11828
+ install_skill: choices.installSkill,
11829
+ update_agents_md: choices.updateAgentsMd
11731
11830
  });
11732
11831
  let mcpConfiguredThisRun = false;
11733
11832
  let mcpCompleted = false;
@@ -11756,14 +11855,19 @@ async function runSetup(opts = {}) {
11756
11855
  await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_skill_installed");
11757
11856
  }
11758
11857
  }
11858
+ let agentsMdCompleted = false;
11859
+ if (choices.updateAgentsMd) {
11860
+ agentsMdCompleted = stepUpdateAgentsMd();
11861
+ }
11759
11862
  let handoffToAudit = false;
11760
11863
  if (mcpConfiguredThisRun && cfg.mode !== MODE_OSS) {
11761
11864
  handoffToAudit = await offerAuditHandoff();
11762
11865
  }
11763
- if (mcpCompleted || skillCompleted) {
11866
+ if (mcpCompleted || skillCompleted || agentsMdCompleted) {
11764
11867
  await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_completed", {
11765
11868
  completed_mcp: mcpCompleted,
11766
- completed_skill: skillCompleted
11869
+ completed_skill: skillCompleted,
11870
+ completed_agents_md: agentsMdCompleted
11767
11871
  });
11768
11872
  }
11769
11873
  if (cfg.mode === MODE_OSS) {
@@ -11792,11 +11896,14 @@ function applyModeOverride(cfg, opts) {
11792
11896
  }
11793
11897
  saveConfig(cfg);
11794
11898
  }
11795
- async function stepOneShotConfirm() {
11899
+ async function stepOneShotConfirm(offerAgentsMd) {
11796
11900
  const items = [
11797
11901
  { value: "configureMcp", label: "Install Dosu MCP" },
11798
11902
  { value: "installSkill", label: "Install Dosu skill" }
11799
11903
  ];
11904
+ if (offerAgentsMd) {
11905
+ items.push({ value: "updateAgentsMd", label: "Add Dosu instructions to AGENTS.md" });
11906
+ }
11800
11907
  const selected = await multiselect({
11801
11908
  message: "Dosu will set these up — press Enter to accept, space to toggle",
11802
11909
  options: items.map((it) => ({ value: it.value, label: it.label })),
@@ -11810,7 +11917,8 @@ async function stepOneShotConfirm() {
11810
11917
  const chosen = new Set(selected);
11811
11918
  return {
11812
11919
  configureMcp: chosen.has("configureMcp"),
11813
- installSkill: chosen.has("installSkill")
11920
+ installSkill: chosen.has("installSkill"),
11921
+ updateAgentsMd: chosen.has("updateAgentsMd")
11814
11922
  };
11815
11923
  }
11816
11924
  async function stepConfigureMcpTools(cfg) {
@@ -11899,9 +12007,11 @@ async function openBrowserForSetup(cfg, onboardingRunID) {
11899
12007
  const token = result.token;
11900
12008
  s.stop("Authenticated");
11901
12009
  logger.info("setup", "Browser auth completed");
11902
- cfg.access_token = token.access_token;
11903
- cfg.refresh_token = token.refresh_token;
11904
- cfg.expires_at = Math.floor(Date.now() / 1000) + token.expires_in;
12010
+ replaceLoginSession(cfg, {
12011
+ access_token: token.access_token,
12012
+ refresh_token: token.refresh_token,
12013
+ expires_at: Math.floor(Date.now() / 1000) + token.expires_in
12014
+ });
11905
12015
  saveConfig(cfg);
11906
12016
  return { cfg, authTab: result.server };
11907
12017
  } catch (err) {
@@ -11946,9 +12056,11 @@ async function stepWebOnboarding(cfg, onboardingRunID, authTab) {
11946
12056
  s.stop("Could not open a browser");
11947
12057
  return false;
11948
12058
  }
11949
- cfg.access_token = result.token.access_token;
11950
- cfg.refresh_token = result.token.refresh_token;
11951
- cfg.expires_at = Math.floor(Date.now() / 1000) + result.token.expires_in;
12059
+ replaceLoginSession(cfg, {
12060
+ access_token: result.token.access_token,
12061
+ refresh_token: result.token.refresh_token,
12062
+ expires_at: Math.floor(Date.now() / 1000) + result.token.expires_in
12063
+ });
11952
12064
  saveConfig(cfg);
11953
12065
  s.stop("Onboarding finished in the browser");
11954
12066
  logger.info("setup", "Web onboarding handoff completed");
@@ -12004,6 +12116,17 @@ async function resolveOnboardingTargetOrg(trpc) {
12004
12116
  }
12005
12117
  return accessibleOrgs[0] ?? null;
12006
12118
  }
12119
+ async function resolveCurrentOnboardingTargetOrg(cfg) {
12120
+ try {
12121
+ const { createTypedClient: createTypedClient2 } = await Promise.resolve().then(() => (init_trpc(), exports_trpc));
12122
+ return await resolveOnboardingTargetOrg(createTypedClient2(cfg));
12123
+ } catch (err) {
12124
+ const msg = err instanceof Error ? err.message : String(err);
12125
+ logger.error("setup", `Failed to resolve post-onboarding organization: ${msg}`);
12126
+ log.error(`Could not determine your onboarding organization: ${msg}`);
12127
+ return null;
12128
+ }
12129
+ }
12007
12130
  async function bindOnboardingDeployment(apiClient, cfg, targetOrg) {
12008
12131
  if (!targetOrg) {
12009
12132
  log.error("Could not determine your onboarding organization.");
@@ -12263,14 +12386,15 @@ var init_flow2 = __esm(() => {
12263
12386
  init_config();
12264
12387
  init_logger();
12265
12388
  init_providers();
12389
+ init_agents_md_step();
12266
12390
  init_analytics();
12267
12391
  init_audit_handoff();
12268
12392
  init_styles();
12269
12393
  });
12270
12394
 
12271
12395
  // src/commands/insights.ts
12272
- import { existsSync as existsSync16, mkdirSync as mkdirSync8, readdirSync, unlinkSync, writeFileSync as writeFileSync7 } from "node:fs";
12273
- import { dirname as dirname5, join as join26 } from "node:path";
12396
+ import { existsSync as existsSync17, mkdirSync as mkdirSync8, readdirSync, unlinkSync, writeFileSync as writeFileSync8 } from "node:fs";
12397
+ import { dirname as dirname5, join as join27 } from "node:path";
12274
12398
  function shuffled(arr) {
12275
12399
  const copy2 = [...arr];
12276
12400
  for (let i2 = copy2.length - 1;i2 > 0; i2--) {
@@ -12374,17 +12498,17 @@ function makeAskFn(cfg) {
12374
12498
  };
12375
12499
  }
12376
12500
  function insightsDir() {
12377
- return join26(getConfigDir(), "insights");
12501
+ return join27(getConfigDir(), "insights");
12378
12502
  }
12379
12503
  function reportPath(timestamp) {
12380
12504
  const dir = insightsDir();
12381
12505
  if (!timestamp)
12382
- return join26(dir, "latest.html");
12506
+ return join27(dir, "latest.html");
12383
12507
  const iso = timestamp.toISOString().slice(0, 19).replace(/:/g, "-");
12384
- return join26(dir, `report-${iso}Z.html`);
12508
+ return join27(dir, `report-${iso}Z.html`);
12385
12509
  }
12386
12510
  function pruneOldReports(dir, keepN) {
12387
- if (!existsSync16(dir))
12511
+ if (!existsSync17(dir))
12388
12512
  return;
12389
12513
  let entries;
12390
12514
  try {
@@ -12396,7 +12520,7 @@ function pruneOldReports(dir, keepN) {
12396
12520
  const reports = entries.filter((f2) => REPORT_FILE_PATTERN.test(f2)).sort().reverse();
12397
12521
  for (const f2 of reports.slice(keepN)) {
12398
12522
  try {
12399
- unlinkSync(join26(dir, f2));
12523
+ unlinkSync(join27(dir, f2));
12400
12524
  } catch (err) {
12401
12525
  logger.debug("insights", `failed to prune ${f2}: ${err instanceof Error ? err.message : err}`);
12402
12526
  }
@@ -12469,9 +12593,9 @@ function defaultRunner(cfg) {
12469
12593
  render: renderHTML,
12470
12594
  writeFile: (path3, content) => {
12471
12595
  const dir = dirname5(path3);
12472
- if (!existsSync16(dir))
12596
+ if (!existsSync17(dir))
12473
12597
  mkdirSync8(dir, { recursive: true, mode: 448 });
12474
- writeFileSync7(path3, content, { mode: 384 });
12598
+ writeFileSync8(path3, content, { mode: 384 });
12475
12599
  },
12476
12600
  prune: pruneOldReports,
12477
12601
  openInBrowser: async (path3) => {
@@ -12626,9 +12750,11 @@ async function handleAuthenticate(cfg) {
12626
12750
  }
12627
12751
  const token = result.token;
12628
12752
  s.stop("Authenticated");
12629
- cfg.access_token = token.access_token;
12630
- cfg.refresh_token = token.refresh_token;
12631
- cfg.expires_at = Math.floor(Date.now() / 1000) + token.expires_in;
12753
+ replaceLoginSession(cfg, {
12754
+ access_token: token.access_token,
12755
+ refresh_token: token.refresh_token,
12756
+ expires_at: Math.floor(Date.now() / 1000) + token.expires_in
12757
+ });
12632
12758
  saveConfig(cfg);
12633
12759
  } catch (err) {
12634
12760
  const { OAuthCallbackError: OAuthCallbackError2 } = await Promise.resolve().then(() => (init_errors(), exports_errors));
@@ -12795,9 +12921,11 @@ async function runLoginCheck(opts) {
12795
12921
  }
12796
12922
  if (result.status === "authenticated") {
12797
12923
  const cfg = loadConfig();
12798
- cfg.access_token = result.access_token ?? "";
12799
- cfg.refresh_token = result.refresh_token ?? "";
12800
- cfg.expires_at = Math.floor(Date.now() / 1000) + (result.expires_in ?? 3600);
12924
+ replaceLoginSession(cfg, {
12925
+ access_token: result.access_token ?? "",
12926
+ refresh_token: result.refresh_token ?? "",
12927
+ expires_at: Math.floor(Date.now() / 1000) + (result.expires_in ?? 3600)
12928
+ });
12801
12929
  saveConfig(cfg);
12802
12930
  logger.info("auth.ticket", "Ticket redeemed via dosu login --check");
12803
12931
  if (opts.json) {
@@ -12988,9 +13116,11 @@ async function redeemTicket(ticket, cfg) {
12988
13116
  });
12989
13117
  return { code: 0, cfg, exit: true };
12990
13118
  }
12991
- cfg.access_token = result.access_token ?? "";
12992
- cfg.refresh_token = result.refresh_token ?? "";
12993
- cfg.expires_at = Math.floor(Date.now() / 1000) + (result.expires_in ?? 3600);
13119
+ replaceLoginSession(cfg, {
13120
+ access_token: result.access_token ?? "",
13121
+ refresh_token: result.refresh_token ?? "",
13122
+ expires_at: Math.floor(Date.now() / 1000) + (result.expires_in ?? 3600)
13123
+ });
12994
13124
  saveConfig(cfg);
12995
13125
  logger.info("agent.flow", "Ticket redeemed; session saved");
12996
13126
  emitStep({ step: "auth", email: result.email });
@@ -13198,7 +13328,7 @@ var init_flow3 = __esm(() => {
13198
13328
  // src/cli/cli.ts
13199
13329
  init_commander();
13200
13330
  init_client();
13201
- import { readFileSync as readFileSync16, unlinkSync as unlinkSync2 } from "node:fs";
13331
+ import { readFileSync as readFileSync17, unlinkSync as unlinkSync2 } from "node:fs";
13202
13332
 
13203
13333
  // src/commands/analytics.ts
13204
13334
  init_commander();
@@ -15260,7 +15390,7 @@ init_dist();
15260
15390
  init_commander();
15261
15391
  init_trpc();
15262
15392
  var import_picocolors19 = __toESM(require_picocolors(), 1);
15263
- import { readFileSync as readFileSync14 } from "node:fs";
15393
+ import { readFileSync as readFileSync15 } from "node:fs";
15264
15394
  function requireConfig10() {
15265
15395
  return requireLoginConfig();
15266
15396
  }
@@ -15436,7 +15566,7 @@ function reviewCommand() {
15436
15566
  let body = opts.body;
15437
15567
  if (opts.bodyFile) {
15438
15568
  try {
15439
- body = readFileSync14(opts.bodyFile, "utf-8");
15569
+ body = readFileSync15(opts.bodyFile, "utf-8");
15440
15570
  } catch (err) {
15441
15571
  console.error(import_picocolors19.default.red(`Failed to read --body-file: ${err.message}`));
15442
15572
  process.exit(1);
@@ -15956,8 +16086,8 @@ init_skill_update_check();
15956
16086
  init_config();
15957
16087
  init_logger();
15958
16088
  var import_picocolors24 = __toESM(require_picocolors(), 1);
15959
- import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
15960
- import { join as join27 } from "node:path";
16089
+ import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "node:fs";
16090
+ import { join as join28 } from "node:path";
15961
16091
  var CACHE_FILENAME3 = "update-check.json";
15962
16092
  var CHECK_INTERVAL_MS3 = 24 * 60 * 60 * 1000;
15963
16093
  var FETCH_TIMEOUT_MS3 = 5000;
@@ -15980,14 +16110,14 @@ function isNewerVersion(latest, current) {
15980
16110
  return false;
15981
16111
  }
15982
16112
  function getCachePath3() {
15983
- return join27(getConfigDir(), CACHE_FILENAME3);
16113
+ return join28(getConfigDir(), CACHE_FILENAME3);
15984
16114
  }
15985
16115
  function readCache() {
15986
16116
  try {
15987
16117
  const path3 = getCachePath3();
15988
- if (!existsSync17(path3))
16118
+ if (!existsSync18(path3))
15989
16119
  return null;
15990
- const data = JSON.parse(readFileSync15(path3, "utf-8"));
16120
+ const data = JSON.parse(readFileSync16(path3, "utf-8"));
15991
16121
  if (typeof data.lastCheck === "number" && typeof data.latestVersion === "string") {
15992
16122
  return data;
15993
16123
  }
@@ -15999,10 +16129,10 @@ function readCache() {
15999
16129
  function writeCache(cache) {
16000
16130
  try {
16001
16131
  const dir = getConfigDir();
16002
- if (!existsSync17(dir)) {
16132
+ if (!existsSync18(dir)) {
16003
16133
  mkdirSync9(dir, { recursive: true, mode: 448 });
16004
16134
  }
16005
- writeFileSync8(getCachePath3(), JSON.stringify(cache), { mode: 384 });
16135
+ writeFileSync9(getCachePath3(), JSON.stringify(cache), { mode: 384 });
16006
16136
  } catch {}
16007
16137
  }
16008
16138
  async function fetchLatestVersion() {
@@ -16194,9 +16324,11 @@ Run 'dosu --help' to see available commands.`;
16194
16324
  token = result.token;
16195
16325
  }
16196
16326
  }
16197
- cfg.access_token = token.access_token;
16198
- cfg.refresh_token = token.refresh_token;
16199
- cfg.expires_at = Math.floor(Date.now() / 1000) + token.expires_in;
16327
+ replaceLoginSession(cfg, {
16328
+ access_token: token.access_token,
16329
+ refresh_token: token.refresh_token,
16330
+ expires_at: Math.floor(Date.now() / 1000) + token.expires_in
16331
+ });
16200
16332
  saveConfig(cfg);
16201
16333
  console.log("Successfully authenticated!");
16202
16334
  console.log(`Credentials saved to ${getConfigPath()}`);
@@ -16366,7 +16498,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
16366
16498
  if (opts.tail !== undefined) {
16367
16499
  const n2 = typeof opts.tail === "string" ? parseInt(opts.tail, 10) || 50 : 50;
16368
16500
  try {
16369
- const content = readFileSync16(logPath, "utf-8");
16501
+ const content = readFileSync17(logPath, "utf-8");
16370
16502
  const lines = content.split(`
16371
16503
  `);
16372
16504
  console.log(lines.slice(-n2).join(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosu/cli",
3
- "version": "0.32.1",
3
+ "version": "0.33.1",
4
4
  "type": "module",
5
5
  "description": "Dosu CLI - Manage MCP servers for AI tools",
6
6
  "license": "MIT",