@dosu/cli 0.32.1 → 0.33.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 (2) hide show
  1. package/bin/dosu.js +192 -95
  2. package/package.json +1 -1
package/bin/dosu.js CHANGED
@@ -4577,7 +4577,7 @@ var init_dist4 = __esm(() => {
4577
4577
  function getVersionString() {
4578
4578
  return `v${VERSION}`;
4579
4579
  }
4580
- var VERSION = "0.32.1", INSTALL_CHANNEL = "npm";
4580
+ var VERSION = "0.33.0", INSTALL_CHANNEL = "npm";
4581
4581
 
4582
4582
  // src/debug/logger.ts
4583
4583
  import {
@@ -10981,6 +10981,160 @@ var init_providers = __esm(() => {
10981
10981
  init_zed();
10982
10982
  });
10983
10983
 
10984
+ // src/setup/audit-handoff.ts
10985
+ import { spawnSync as spawnSync2 } from "node:child_process";
10986
+ function buildAuditHandoffPrompt(dosuCmd = dosuInvocation()) {
10987
+ return [
10988
+ "Audit this repository to see what docs Dosu can generate for it.",
10989
+ "1. Run the codebase audit from the Dosu skill. It writes findings to .dosu/audit.json.",
10990
+ "2. Show me the findings and which docs you recommend Dosu generate.",
10991
+ `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.`
10992
+ ].join(`
10993
+ `);
10994
+ }
10995
+ function binOnPath(bin) {
10996
+ const cmd = process.platform === "win32" ? "where" : "which";
10997
+ try {
10998
+ return spawnSync2(cmd, [bin], { stdio: "ignore" }).status === 0;
10999
+ } catch {
11000
+ return false;
11001
+ }
11002
+ }
11003
+ function claudeCliAvailable() {
11004
+ return binOnPath("claude");
11005
+ }
11006
+ function dosuInvocation() {
11007
+ return binOnPath("dosu") ? "dosu" : "npx -y @dosu/cli";
11008
+ }
11009
+ function printManualAuditNudge() {
11010
+ log.message(`See what docs Dosu can generate for this repo:
11011
+
11012
+ ${info("Ask Dosu to audit this repo and show what docs it can generate.")}
11013
+
11014
+ Or run ${info(`${dosuInvocation()} audit`)} after your agent writes the audit.`);
11015
+ }
11016
+ async function offerAuditHandoff() {
11017
+ if (!detectGitRepo())
11018
+ return false;
11019
+ if (!claudeCliAvailable()) {
11020
+ printManualAuditNudge();
11021
+ return false;
11022
+ }
11023
+ const go = await confirm({
11024
+ message: "Kick off the codebase audit in Claude Code now?",
11025
+ initialValue: true
11026
+ });
11027
+ if (isCancel(go) || !go) {
11028
+ printManualAuditNudge();
11029
+ return false;
11030
+ }
11031
+ return true;
11032
+ }
11033
+ function launchAuditAgent() {
11034
+ logger.info("setup", "Handing off to Claude Code for the codebase audit");
11035
+ const result = spawnSync2("claude", ["--model", AUDIT_AGENT_MODEL, buildAuditHandoffPrompt()], {
11036
+ stdio: "inherit",
11037
+ shell: process.platform === "win32"
11038
+ });
11039
+ if (result.error) {
11040
+ logger.warn("setup", `Claude Code launch failed: ${result.error.message}`);
11041
+ printManualAuditNudge();
11042
+ }
11043
+ }
11044
+ var AUDIT_AGENT_MODEL = "haiku";
11045
+ var init_audit_handoff = __esm(() => {
11046
+ init_dist8();
11047
+ init_logger();
11048
+ init_github_step();
11049
+ init_styles();
11050
+ });
11051
+
11052
+ // src/setup/agents-md-step.ts
11053
+ import { execSync as execSync3 } from "node:child_process";
11054
+ import { existsSync as existsSync16, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "node:fs";
11055
+ import { join as join26 } from "node:path";
11056
+ function inGitWorkTree(cwd = process.cwd()) {
11057
+ try {
11058
+ const stdout2 = execSync3("git rev-parse --is-inside-work-tree", {
11059
+ cwd,
11060
+ stdio: ["ignore", "pipe", "ignore"]
11061
+ });
11062
+ return stdout2.toString().trim() === "true";
11063
+ } catch {
11064
+ return false;
11065
+ }
11066
+ }
11067
+ function buildDosuAgentsSection(dosuCmd = dosuInvocation()) {
11068
+ return [
11069
+ DOSU_SECTION_START,
11070
+ "## Dosu",
11071
+ "",
11072
+ "Shared team knowledge lives in [Dosu](https://dosu.dev), via the Dosu MCP server.",
11073
+ "",
11074
+ "- Before a task, and for any codebase or docs questions: pull context with `read_knowledge` before digging through source.",
11075
+ "- After a task: save durable learnings with `write_knowledge`.",
11076
+ "",
11077
+ `Missing these tools? Run \`${dosuCmd} setup --help\` — it covers agent-assisted setup.`,
11078
+ DOSU_SECTION_END
11079
+ ].join(`
11080
+ `);
11081
+ }
11082
+ function upsertDosuAgentsSection(cwd = process.cwd(), dosuCmd = dosuInvocation()) {
11083
+ const path3 = join26(cwd, "AGENTS.md");
11084
+ const section = buildDosuAgentsSection(dosuCmd);
11085
+ if (!existsSync16(path3)) {
11086
+ writeFileSync7(path3, `${section}
11087
+ `);
11088
+ return { path: path3, action: "created" };
11089
+ }
11090
+ const existing = readFileSync14(path3, "utf-8");
11091
+ const startMatch = existing.match(SECTION_START_RE);
11092
+ const start = startMatch?.index ?? -1;
11093
+ const end = existing.indexOf(DOSU_SECTION_END);
11094
+ let next;
11095
+ if (start !== -1 && end !== -1 && end >= start) {
11096
+ next = existing.slice(0, start) + section + existing.slice(end + DOSU_SECTION_END.length);
11097
+ } else {
11098
+ next = `${existing.trimEnd()}
11099
+
11100
+ ${section}
11101
+ `;
11102
+ }
11103
+ if (next === existing)
11104
+ return { path: path3, action: "unchanged" };
11105
+ writeFileSync7(path3, next);
11106
+ return { path: path3, action: "updated" };
11107
+ }
11108
+ function stepUpdateAgentsMd(cwd = process.cwd()) {
11109
+ logger.info("setup", "Step: update AGENTS.md");
11110
+ try {
11111
+ const result = upsertDosuAgentsSection(cwd);
11112
+ logger.info("setup", `AGENTS.md ${result.action} at ${result.path}`);
11113
+ if (result.action === "unchanged") {
11114
+ log.success(`AGENTS.md
11115
+ ${dim("Dosu section already up to date")}`);
11116
+ } else {
11117
+ log.success(`AGENTS.md
11118
+ ${dim(`Dosu section ${result.action} — ${result.path}`)}`);
11119
+ }
11120
+ return true;
11121
+ } catch (err) {
11122
+ const msg = err instanceof Error ? err.message : String(err);
11123
+ logger.error("setup", `AGENTS.md update failed: ${msg}`);
11124
+ log.error(`Could not update AGENTS.md: ${msg}`);
11125
+ return false;
11126
+ }
11127
+ }
11128
+ var DOSU_SECTION_VERSION = 1, DOSU_SECTION_START, DOSU_SECTION_END = "<!-- dosu:mcp:end -->", SECTION_START_RE;
11129
+ var init_agents_md_step = __esm(() => {
11130
+ init_dist8();
11131
+ init_logger();
11132
+ init_audit_handoff();
11133
+ init_styles();
11134
+ DOSU_SECTION_START = `<!-- dosu:mcp:start v${DOSU_SECTION_VERSION} -->`;
11135
+ SECTION_START_RE = /<!-- dosu:mcp:start(?: v(\d+))? -->/;
11136
+ });
11137
+
10984
11138
  // src/setup/analytics.ts
10985
11139
  async function trackCliOnboardingEvent(cfg, onboardingRunID, event, properties = {}) {
10986
11140
  if (!cfg.access_token)
@@ -11066,74 +11220,6 @@ var init_analytics = __esm(() => {
11066
11220
  init_logger();
11067
11221
  });
11068
11222
 
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
11223
  // src/auth/errors.ts
11138
11224
  var exports_errors = {};
11139
11225
  __export(exports_errors, {
@@ -11718,7 +11804,7 @@ async function runSetup(opts = {}) {
11718
11804
  }
11719
11805
  cfg.api_key = apiKey;
11720
11806
  saveConfig(cfg);
11721
- const choices = await stepOneShotConfirm();
11807
+ const choices = await stepOneShotConfirm(inGitWorkTree());
11722
11808
  if (!choices) {
11723
11809
  await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_cancelled", {
11724
11810
  reason: "options_cancelled"
@@ -11727,7 +11813,8 @@ async function runSetup(opts = {}) {
11727
11813
  }
11728
11814
  await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_options_selected", {
11729
11815
  configure_mcp: choices.configureMcp,
11730
- install_skill: choices.installSkill
11816
+ install_skill: choices.installSkill,
11817
+ update_agents_md: choices.updateAgentsMd
11731
11818
  });
11732
11819
  let mcpConfiguredThisRun = false;
11733
11820
  let mcpCompleted = false;
@@ -11756,14 +11843,19 @@ async function runSetup(opts = {}) {
11756
11843
  await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_skill_installed");
11757
11844
  }
11758
11845
  }
11846
+ let agentsMdCompleted = false;
11847
+ if (choices.updateAgentsMd) {
11848
+ agentsMdCompleted = stepUpdateAgentsMd();
11849
+ }
11759
11850
  let handoffToAudit = false;
11760
11851
  if (mcpConfiguredThisRun && cfg.mode !== MODE_OSS) {
11761
11852
  handoffToAudit = await offerAuditHandoff();
11762
11853
  }
11763
- if (mcpCompleted || skillCompleted) {
11854
+ if (mcpCompleted || skillCompleted || agentsMdCompleted) {
11764
11855
  await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_completed", {
11765
11856
  completed_mcp: mcpCompleted,
11766
- completed_skill: skillCompleted
11857
+ completed_skill: skillCompleted,
11858
+ completed_agents_md: agentsMdCompleted
11767
11859
  });
11768
11860
  }
11769
11861
  if (cfg.mode === MODE_OSS) {
@@ -11792,11 +11884,14 @@ function applyModeOverride(cfg, opts) {
11792
11884
  }
11793
11885
  saveConfig(cfg);
11794
11886
  }
11795
- async function stepOneShotConfirm() {
11887
+ async function stepOneShotConfirm(offerAgentsMd) {
11796
11888
  const items = [
11797
11889
  { value: "configureMcp", label: "Install Dosu MCP" },
11798
11890
  { value: "installSkill", label: "Install Dosu skill" }
11799
11891
  ];
11892
+ if (offerAgentsMd) {
11893
+ items.push({ value: "updateAgentsMd", label: "Add Dosu instructions to AGENTS.md" });
11894
+ }
11800
11895
  const selected = await multiselect({
11801
11896
  message: "Dosu will set these up — press Enter to accept, space to toggle",
11802
11897
  options: items.map((it) => ({ value: it.value, label: it.label })),
@@ -11810,7 +11905,8 @@ async function stepOneShotConfirm() {
11810
11905
  const chosen = new Set(selected);
11811
11906
  return {
11812
11907
  configureMcp: chosen.has("configureMcp"),
11813
- installSkill: chosen.has("installSkill")
11908
+ installSkill: chosen.has("installSkill"),
11909
+ updateAgentsMd: chosen.has("updateAgentsMd")
11814
11910
  };
11815
11911
  }
11816
11912
  async function stepConfigureMcpTools(cfg) {
@@ -12263,14 +12359,15 @@ var init_flow2 = __esm(() => {
12263
12359
  init_config();
12264
12360
  init_logger();
12265
12361
  init_providers();
12362
+ init_agents_md_step();
12266
12363
  init_analytics();
12267
12364
  init_audit_handoff();
12268
12365
  init_styles();
12269
12366
  });
12270
12367
 
12271
12368
  // 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";
12369
+ import { existsSync as existsSync17, mkdirSync as mkdirSync8, readdirSync, unlinkSync, writeFileSync as writeFileSync8 } from "node:fs";
12370
+ import { dirname as dirname5, join as join27 } from "node:path";
12274
12371
  function shuffled(arr) {
12275
12372
  const copy2 = [...arr];
12276
12373
  for (let i2 = copy2.length - 1;i2 > 0; i2--) {
@@ -12374,17 +12471,17 @@ function makeAskFn(cfg) {
12374
12471
  };
12375
12472
  }
12376
12473
  function insightsDir() {
12377
- return join26(getConfigDir(), "insights");
12474
+ return join27(getConfigDir(), "insights");
12378
12475
  }
12379
12476
  function reportPath(timestamp) {
12380
12477
  const dir = insightsDir();
12381
12478
  if (!timestamp)
12382
- return join26(dir, "latest.html");
12479
+ return join27(dir, "latest.html");
12383
12480
  const iso = timestamp.toISOString().slice(0, 19).replace(/:/g, "-");
12384
- return join26(dir, `report-${iso}Z.html`);
12481
+ return join27(dir, `report-${iso}Z.html`);
12385
12482
  }
12386
12483
  function pruneOldReports(dir, keepN) {
12387
- if (!existsSync16(dir))
12484
+ if (!existsSync17(dir))
12388
12485
  return;
12389
12486
  let entries;
12390
12487
  try {
@@ -12396,7 +12493,7 @@ function pruneOldReports(dir, keepN) {
12396
12493
  const reports = entries.filter((f2) => REPORT_FILE_PATTERN.test(f2)).sort().reverse();
12397
12494
  for (const f2 of reports.slice(keepN)) {
12398
12495
  try {
12399
- unlinkSync(join26(dir, f2));
12496
+ unlinkSync(join27(dir, f2));
12400
12497
  } catch (err) {
12401
12498
  logger.debug("insights", `failed to prune ${f2}: ${err instanceof Error ? err.message : err}`);
12402
12499
  }
@@ -12469,9 +12566,9 @@ function defaultRunner(cfg) {
12469
12566
  render: renderHTML,
12470
12567
  writeFile: (path3, content) => {
12471
12568
  const dir = dirname5(path3);
12472
- if (!existsSync16(dir))
12569
+ if (!existsSync17(dir))
12473
12570
  mkdirSync8(dir, { recursive: true, mode: 448 });
12474
- writeFileSync7(path3, content, { mode: 384 });
12571
+ writeFileSync8(path3, content, { mode: 384 });
12475
12572
  },
12476
12573
  prune: pruneOldReports,
12477
12574
  openInBrowser: async (path3) => {
@@ -13198,7 +13295,7 @@ var init_flow3 = __esm(() => {
13198
13295
  // src/cli/cli.ts
13199
13296
  init_commander();
13200
13297
  init_client();
13201
- import { readFileSync as readFileSync16, unlinkSync as unlinkSync2 } from "node:fs";
13298
+ import { readFileSync as readFileSync17, unlinkSync as unlinkSync2 } from "node:fs";
13202
13299
 
13203
13300
  // src/commands/analytics.ts
13204
13301
  init_commander();
@@ -15260,7 +15357,7 @@ init_dist();
15260
15357
  init_commander();
15261
15358
  init_trpc();
15262
15359
  var import_picocolors19 = __toESM(require_picocolors(), 1);
15263
- import { readFileSync as readFileSync14 } from "node:fs";
15360
+ import { readFileSync as readFileSync15 } from "node:fs";
15264
15361
  function requireConfig10() {
15265
15362
  return requireLoginConfig();
15266
15363
  }
@@ -15436,7 +15533,7 @@ function reviewCommand() {
15436
15533
  let body = opts.body;
15437
15534
  if (opts.bodyFile) {
15438
15535
  try {
15439
- body = readFileSync14(opts.bodyFile, "utf-8");
15536
+ body = readFileSync15(opts.bodyFile, "utf-8");
15440
15537
  } catch (err) {
15441
15538
  console.error(import_picocolors19.default.red(`Failed to read --body-file: ${err.message}`));
15442
15539
  process.exit(1);
@@ -15956,8 +16053,8 @@ init_skill_update_check();
15956
16053
  init_config();
15957
16054
  init_logger();
15958
16055
  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";
16056
+ import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "node:fs";
16057
+ import { join as join28 } from "node:path";
15961
16058
  var CACHE_FILENAME3 = "update-check.json";
15962
16059
  var CHECK_INTERVAL_MS3 = 24 * 60 * 60 * 1000;
15963
16060
  var FETCH_TIMEOUT_MS3 = 5000;
@@ -15980,14 +16077,14 @@ function isNewerVersion(latest, current) {
15980
16077
  return false;
15981
16078
  }
15982
16079
  function getCachePath3() {
15983
- return join27(getConfigDir(), CACHE_FILENAME3);
16080
+ return join28(getConfigDir(), CACHE_FILENAME3);
15984
16081
  }
15985
16082
  function readCache() {
15986
16083
  try {
15987
16084
  const path3 = getCachePath3();
15988
- if (!existsSync17(path3))
16085
+ if (!existsSync18(path3))
15989
16086
  return null;
15990
- const data = JSON.parse(readFileSync15(path3, "utf-8"));
16087
+ const data = JSON.parse(readFileSync16(path3, "utf-8"));
15991
16088
  if (typeof data.lastCheck === "number" && typeof data.latestVersion === "string") {
15992
16089
  return data;
15993
16090
  }
@@ -15999,10 +16096,10 @@ function readCache() {
15999
16096
  function writeCache(cache) {
16000
16097
  try {
16001
16098
  const dir = getConfigDir();
16002
- if (!existsSync17(dir)) {
16099
+ if (!existsSync18(dir)) {
16003
16100
  mkdirSync9(dir, { recursive: true, mode: 448 });
16004
16101
  }
16005
- writeFileSync8(getCachePath3(), JSON.stringify(cache), { mode: 384 });
16102
+ writeFileSync9(getCachePath3(), JSON.stringify(cache), { mode: 384 });
16006
16103
  } catch {}
16007
16104
  }
16008
16105
  async function fetchLatestVersion() {
@@ -16366,7 +16463,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
16366
16463
  if (opts.tail !== undefined) {
16367
16464
  const n2 = typeof opts.tail === "string" ? parseInt(opts.tail, 10) || 50 : 50;
16368
16465
  try {
16369
- const content = readFileSync16(logPath, "utf-8");
16466
+ const content = readFileSync17(logPath, "utf-8");
16370
16467
  const lines = content.split(`
16371
16468
  `);
16372
16469
  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.0",
4
4
  "type": "module",
5
5
  "description": "Dosu CLI - Manage MCP servers for AI tools",
6
6
  "license": "MIT",