@dosu/cli 0.35.4 → 0.37.0-alpha.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 +506 -184
  2. package/package.json +1 -1
package/bin/dosu.js CHANGED
@@ -4759,7 +4759,7 @@ var init_dist4 = __esm(() => {
4759
4759
  function getVersionString() {
4760
4760
  return `v${VERSION}`;
4761
4761
  }
4762
- var VERSION = "0.35.4", INSTALL_CHANNEL = "npm";
4762
+ var VERSION = "0.37.0-alpha.1", INSTALL_CHANNEL = "npm";
4763
4763
 
4764
4764
  // src/debug/logger.ts
4765
4765
  import {
@@ -4893,7 +4893,7 @@ var init_logger = __esm(() => {
4893
4893
  });
4894
4894
 
4895
4895
  // src/client/contract.ts
4896
- var CLI_CONTRACT_HASH = "8735e566b0ed";
4896
+ var CLI_CONTRACT_HASH = "663c43d2937c";
4897
4897
 
4898
4898
  // src/client/trpc.ts
4899
4899
  var exports_trpc = {};
@@ -10449,13 +10449,24 @@ var init_skill_update_check = __esm(() => {
10449
10449
 
10450
10450
  // src/commands/skill.ts
10451
10451
  import { execSync as execSync2 } from "node:child_process";
10452
- function supportedSkillAgentArgs() {
10453
- return SUPPORTED_SKILL_AGENTS.map((agent) => `-a ${agent}`).join(" ");
10452
+ function skillAgentIDsForProviders(providerIDs) {
10453
+ return [
10454
+ ...new Set(providerIDs.map((providerID) => SKILL_AGENT_BY_PROVIDER[providerID]).filter((agent) => Boolean(agent)))
10455
+ ];
10456
+ }
10457
+ function skillAgentArgs(providerIDs) {
10458
+ const agents = providerIDs === undefined ? SUPPORTED_SKILL_AGENTS : skillAgentIDsForProviders(providerIDs);
10459
+ return agents.map((agent) => `-a ${agent}`).join(" ");
10454
10460
  }
10455
- async function installSkill() {
10461
+ async function installSkill(providerIDs, options = {}) {
10462
+ const agentArgs = skillAgentArgs(providerIDs);
10463
+ if (!agentArgs) {
10464
+ logger.debug("skill", "No selected providers support the Dosu skill");
10465
+ return { success: true };
10466
+ }
10456
10467
  try {
10457
- execSync2(`npx skills add ${SKILL_REPO} -g ${supportedSkillAgentArgs()} -s ${SKILL_NAME} -y`, {
10458
- stdio: "inherit"
10468
+ execSync2(`npx skills add ${SKILL_REPO} -g ${agentArgs} -s ${SKILL_NAME} -y`, {
10469
+ stdio: options.quiet ? "pipe" : "inherit"
10459
10470
  });
10460
10471
  } catch (err) {
10461
10472
  logger.error("skill", `Failed to install skill: ${err}`);
@@ -10514,7 +10525,7 @@ Failed to update skill.`));
10514
10525
  });
10515
10526
  return cmd;
10516
10527
  }
10517
- var import_picocolors13, SKILL_REPO = "dosu-ai/dosu-skill", SKILL_NAME = "dosu", SUPPORTED_SKILL_AGENTS;
10528
+ var import_picocolors13, SKILL_REPO = "dosu-ai/dosu-skill", SKILL_NAME = "dosu", SUPPORTED_SKILL_AGENTS, SKILL_AGENT_BY_PROVIDER;
10518
10529
  var init_skill = __esm(() => {
10519
10530
  init_commander();
10520
10531
  init_logger();
@@ -10532,6 +10543,20 @@ var init_skill = __esm(() => {
10532
10543
  "opencode",
10533
10544
  "antigravity"
10534
10545
  ];
10546
+ SKILL_AGENT_BY_PROVIDER = {
10547
+ claude: "claude-code",
10548
+ cursor: "cursor",
10549
+ vscode: "github-copilot",
10550
+ gemini: "gemini-cli",
10551
+ codex: "codex",
10552
+ windsurf: "windsurf",
10553
+ zed: "zed",
10554
+ cline: "cline",
10555
+ "cline-cli": "cline",
10556
+ copilot: "github-copilot",
10557
+ opencode: "opencode",
10558
+ antigravity: "antigravity"
10559
+ };
10535
10560
  });
10536
10561
 
10537
10562
  // src/mcp/constants.ts
@@ -11163,78 +11188,185 @@ var init_providers = __esm(() => {
11163
11188
  init_zed();
11164
11189
  });
11165
11190
 
11166
- // src/setup/audit-handoff.ts
11167
- import { spawnSync as spawnSync2 } from "node:child_process";
11168
- function buildAuditHandoffPrompt(dosuCmd = dosuInvocation()) {
11169
- return [
11170
- "Audit this repository to see what docs Dosu can generate for it.",
11171
- "1. Run the codebase audit from the Dosu skill. It writes findings to .dosu/audit.json.",
11172
- "2. Show me the findings and which docs you recommend Dosu generate.",
11173
- `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.`
11174
- ].join(`
11175
- `);
11191
+ // src/rules/installer.ts
11192
+ import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync14, unlinkSync, writeFileSync as writeFileSync7 } from "node:fs";
11193
+ import { homedir as homedir3 } from "node:os";
11194
+ import { dirname as dirname5, join as join26 } from "node:path";
11195
+ function claudeConfigDir() {
11196
+ return process.env.CLAUDE_CONFIG_DIR || join26(homedir3(), ".claude");
11176
11197
  }
11177
- function binOnPath(bin) {
11178
- const cmd = process.platform === "win32" ? "where" : "which";
11179
- try {
11180
- return spawnSync2(cmd, [bin], { stdio: "ignore" }).status === 0;
11181
- } catch {
11182
- return false;
11198
+ function codexHome2() {
11199
+ return process.env.CODEX_HOME || join26(homedir3(), ".codex");
11200
+ }
11201
+ function isRuleAgent(agent) {
11202
+ return Object.hasOwn(RULE_TARGETS, agent);
11203
+ }
11204
+ function rulePathForAgent(agent) {
11205
+ return isRuleAgent(agent) ? RULE_TARGETS[agent].path() : null;
11206
+ }
11207
+ function normalizeRule(content) {
11208
+ return `${content.trim()}
11209
+ `;
11210
+ }
11211
+ async function fetchDosuRule(fetchImpl = globalThis.fetch) {
11212
+ for (const url of DOSU_RULE_URLS) {
11213
+ try {
11214
+ const response = await fetchImpl(url);
11215
+ if (!response.ok)
11216
+ continue;
11217
+ const content = await response.text();
11218
+ if (content.trim())
11219
+ return normalizeRule(content);
11220
+ } catch {}
11183
11221
  }
11222
+ return normalizeRule(FALLBACK_DOSU_RULE);
11184
11223
  }
11185
- function claudeCliAvailable() {
11186
- return binOnPath("claude");
11224
+ function ensureParent(path3) {
11225
+ mkdirSync8(dirname5(path3), { recursive: true });
11187
11226
  }
11188
- function dosuInvocation() {
11189
- return binOnPath("dosu") ? "dosu" : "npx -y @dosu/cli";
11227
+ function writeIfChanged(path3, content) {
11228
+ if (!existsSync16(path3)) {
11229
+ ensureParent(path3);
11230
+ writeFileSync7(path3, content, "utf-8");
11231
+ return "created";
11232
+ }
11233
+ if (readFileSync14(path3, "utf-8") === content)
11234
+ return "unchanged";
11235
+ writeFileSync7(path3, content, "utf-8");
11236
+ return "updated";
11190
11237
  }
11191
- function printManualAuditNudge() {
11192
- log.message(`See what docs Dosu can generate for this repo:
11193
-
11194
- ${info("Ask Dosu to audit this repo and show what docs it can generate.")}
11195
-
11196
- Or run ${info(`${dosuInvocation()} audit`)} after your agent writes the audit.`);
11238
+ function findSection(content) {
11239
+ const startMatch = content.match(DOSU_RULE_SECTION_START_RE);
11240
+ const start = startMatch?.index ?? -1;
11241
+ const end = content.indexOf(DOSU_RULE_SECTION_END, Math.max(start, 0));
11242
+ if (start === -1 && end === -1)
11243
+ return null;
11244
+ if (start === -1 || end < start) {
11245
+ throw new Error("Dosu rule markers are incomplete; refusing to overwrite the instruction file");
11246
+ }
11247
+ return { start, end };
11197
11248
  }
11198
- async function offerAuditHandoff() {
11199
- if (!detectGitRepo())
11200
- return false;
11201
- if (!claudeCliAvailable()) {
11202
- printManualAuditNudge();
11203
- return false;
11249
+ function lineEndingFor(content) {
11250
+ return content.includes(`\r
11251
+ `) ? `\r
11252
+ ` : `
11253
+ `;
11254
+ }
11255
+ function buildSection(content, lineEnding = `
11256
+ `) {
11257
+ const body = content.trimEnd().replace(/\r?\n/g, lineEnding);
11258
+ return `${DOSU_RULE_SECTION_START}${lineEnding}${body}${lineEnding}${DOSU_RULE_SECTION_END}`;
11259
+ }
11260
+ function upsertSection(path3, content) {
11261
+ if (!existsSync16(path3)) {
11262
+ const section2 = buildSection(content);
11263
+ ensureParent(path3);
11264
+ writeFileSync7(path3, `${section2}
11265
+ `, "utf-8");
11266
+ return "created";
11204
11267
  }
11205
- const go = await confirm({
11206
- message: "Kick off the codebase audit in Claude Code now?",
11207
- initialValue: true
11208
- });
11209
- if (isCancel(go) || !go) {
11210
- printManualAuditNudge();
11211
- return false;
11268
+ const existing = readFileSync14(path3, "utf-8");
11269
+ const lineEnding = lineEndingFor(existing);
11270
+ const section = buildSection(content, lineEnding);
11271
+ const location = findSection(existing);
11272
+ let next;
11273
+ if (location) {
11274
+ next = existing.slice(0, location.start) + section + existing.slice(location.end + DOSU_RULE_SECTION_END.length);
11275
+ } else {
11276
+ const separator = existing.length > 0 && !existing.endsWith(lineEnding) ? `${lineEnding}${lineEnding}` : existing.length > 0 ? lineEnding : "";
11277
+ next = `${existing}${separator}${section}${lineEnding}`;
11212
11278
  }
11213
- return true;
11279
+ if (next === existing)
11280
+ return "unchanged";
11281
+ writeFileSync7(path3, next, "utf-8");
11282
+ return "updated";
11214
11283
  }
11215
- function launchAuditAgent() {
11216
- logger.info("setup", "Handing off to Claude Code for the codebase audit");
11217
- const result = spawnSync2("claude", ["--model", AUDIT_AGENT_MODEL, buildAuditHandoffPrompt()], {
11218
- stdio: "inherit",
11219
- shell: process.platform === "win32"
11220
- });
11221
- if (result.error) {
11222
- logger.warn("setup", `Claude Code launch failed: ${result.error.message}`);
11223
- printManualAuditNudge();
11284
+ function installRuleForAgent(agent, content) {
11285
+ if (!isRuleAgent(agent))
11286
+ return null;
11287
+ const target = RULE_TARGETS[agent];
11288
+ const path3 = target.path();
11289
+ const normalized = normalizeRule(content);
11290
+ const action = target.kind === "file" ? writeIfChanged(path3, target.cursorFrontmatter ? `${CURSOR_FRONTMATTER}${normalized}` : normalized) : upsertSection(path3, normalized);
11291
+ return { agent, path: path3, action };
11292
+ }
11293
+ function removeRuleForAgent(agent) {
11294
+ if (!isRuleAgent(agent))
11295
+ return null;
11296
+ const target = RULE_TARGETS[agent];
11297
+ const path3 = target.path();
11298
+ if (!existsSync16(path3))
11299
+ return { agent, path: path3, action: "not_found" };
11300
+ if (target.kind === "file") {
11301
+ unlinkSync(path3);
11302
+ return { agent, path: path3, action: "removed" };
11303
+ }
11304
+ const existing = readFileSync14(path3, "utf-8");
11305
+ const lineEnding = lineEndingFor(existing);
11306
+ const location = findSection(existing);
11307
+ if (!location)
11308
+ return { agent, path: path3, action: "not_found" };
11309
+ 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();
11310
+ if (!next) {
11311
+ unlinkSync(path3);
11312
+ } else {
11313
+ writeFileSync7(path3, `${next}${lineEnding}`, "utf-8");
11224
11314
  }
11315
+ return { agent, path: path3, action: "removed" };
11225
11316
  }
11226
- var AUDIT_AGENT_MODEL = "haiku";
11227
- var init_audit_handoff = __esm(() => {
11228
- init_dist8();
11229
- init_logger();
11230
- init_github_step();
11231
- init_styles();
11317
+ var DOSU_RULE_SECTION_VERSION = 1, DOSU_RULE_SECTION_START, DOSU_RULE_SECTION_END = "<!-- dosu:rules:end -->", DOSU_RULE_SECTION_START_RE, CURSOR_FRONTMATTER = `---
11318
+ alwaysApply: true
11319
+ ---
11320
+
11321
+ `, 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.
11322
+
11323
+ 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. Treat results as leads and verify them against current code and state.
11324
+
11325
+ 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.**
11326
+
11327
+ 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.
11328
+ `, RULE_TARGETS, SUPPORTED_RULE_AGENT_IDS;
11329
+ var init_installer = __esm(() => {
11330
+ DOSU_RULE_SECTION_START = `<!-- dosu:rules:start v${DOSU_RULE_SECTION_VERSION} -->`;
11331
+ DOSU_RULE_SECTION_START_RE = /<!-- dosu:rules:start(?: v\d+)? -->/;
11332
+ DOSU_RULE_URLS = [
11333
+ "https://raw.githubusercontent.com/dosu-ai/dosu-cli/main/rules/dosu.md",
11334
+ "https://raw.githubusercontent.com/dosu-ai/dosu-cli/master/rules/dosu.md"
11335
+ ];
11336
+ RULE_TARGETS = {
11337
+ claude: {
11338
+ kind: "file",
11339
+ path: () => join26(claudeConfigDir(), "rules", "dosu.md")
11340
+ },
11341
+ cursor: {
11342
+ kind: "file",
11343
+ path: () => join26(homedir3(), ".cursor", "rules", "dosu.mdc"),
11344
+ cursorFrontmatter: true
11345
+ },
11346
+ codex: {
11347
+ kind: "section",
11348
+ path: () => join26(codexHome2(), "AGENTS.md")
11349
+ },
11350
+ opencode: {
11351
+ kind: "section",
11352
+ path: () => join26(homedir3(), ".config", "opencode", "AGENTS.md")
11353
+ },
11354
+ gemini: {
11355
+ kind: "section",
11356
+ path: () => join26(homedir3(), ".gemini", "GEMINI.md")
11357
+ },
11358
+ antigravity: {
11359
+ kind: "section",
11360
+ path: () => join26(homedir3(), ".gemini", "GEMINI.md")
11361
+ }
11362
+ };
11363
+ SUPPORTED_RULE_AGENT_IDS = Object.keys(RULE_TARGETS);
11232
11364
  });
11233
11365
 
11234
11366
  // src/setup/agents-md-step.ts
11235
11367
  import { execSync as execSync3 } from "node:child_process";
11236
- import { existsSync as existsSync16, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "node:fs";
11237
- import { join as join26 } from "node:path";
11368
+ import { existsSync as existsSync17, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
11369
+ import { join as join27 } from "node:path";
11238
11370
  function inGitWorkTree(cwd = process.cwd()) {
11239
11371
  try {
11240
11372
  const stdout2 = execSync3("git rev-parse --is-inside-work-tree", {
@@ -11246,51 +11378,58 @@ function inGitWorkTree(cwd = process.cwd()) {
11246
11378
  return false;
11247
11379
  }
11248
11380
  }
11249
- function buildDosuAgentsSection(dosuCmd = dosuInvocation()) {
11250
- return [
11251
- DOSU_SECTION_START,
11252
- "## Dosu",
11253
- "",
11254
- "Shared team knowledge lives in [Dosu](https://dosu.dev), via the Dosu MCP server.",
11255
- "",
11256
- "- Before a task, and for any codebase or docs questions: pull context with `read_knowledge` before digging through source.",
11257
- "- After a task: save durable learnings with `write_knowledge`.",
11258
- "",
11259
- `Missing these tools? Run \`${dosuCmd} setup --help\` — it covers agent-assisted setup.`,
11260
- DOSU_SECTION_END
11261
- ].join(`
11381
+ function lineEndingFor2(content) {
11382
+ return content.includes(`\r
11383
+ `) ? `\r
11384
+ ` : `
11385
+ `;
11386
+ }
11387
+ function renderDosuAgentsSection(content, lineEnding) {
11388
+ const body = content.trim().replace(/\r?\n/g, lineEnding);
11389
+ return `${DOSU_SECTION_START}${lineEnding}${body}${lineEnding}${DOSU_SECTION_END}`;
11390
+ }
11391
+ function buildDosuAgentsSection(content = FALLBACK_DOSU_RULE) {
11392
+ return renderDosuAgentsSection(content, `
11262
11393
  `);
11263
11394
  }
11264
- function upsertDosuAgentsSection(cwd = process.cwd(), dosuCmd = dosuInvocation()) {
11265
- const path3 = join26(cwd, "AGENTS.md");
11266
- const section = buildDosuAgentsSection(dosuCmd);
11267
- if (!existsSync16(path3)) {
11268
- writeFileSync7(path3, `${section}
11395
+ function findSection2(content) {
11396
+ const startMatch = content.match(SECTION_START_RE);
11397
+ const start = startMatch?.index ?? -1;
11398
+ const end = content.indexOf(DOSU_SECTION_END, Math.max(start, 0));
11399
+ if (start === -1 && end === -1)
11400
+ return null;
11401
+ if (start === -1 || end < start) {
11402
+ throw new Error("Dosu AGENTS.md markers are incomplete; refusing to overwrite the file");
11403
+ }
11404
+ return { start, end };
11405
+ }
11406
+ function upsertDosuAgentsSection(cwd = process.cwd(), content = FALLBACK_DOSU_RULE) {
11407
+ const path3 = join27(cwd, "AGENTS.md");
11408
+ if (!existsSync17(path3)) {
11409
+ const section2 = buildDosuAgentsSection(content);
11410
+ writeFileSync8(path3, `${section2}
11269
11411
  `);
11270
11412
  return { path: path3, action: "created" };
11271
11413
  }
11272
- const existing = readFileSync14(path3, "utf-8");
11273
- const startMatch = existing.match(SECTION_START_RE);
11274
- const start = startMatch?.index ?? -1;
11275
- const end = existing.indexOf(DOSU_SECTION_END);
11414
+ const existing = readFileSync15(path3, "utf-8");
11415
+ const lineEnding = lineEndingFor2(existing);
11416
+ const section = renderDosuAgentsSection(content, lineEnding);
11417
+ const location = findSection2(existing);
11276
11418
  let next;
11277
- if (start !== -1 && end !== -1 && end >= start) {
11278
- next = existing.slice(0, start) + section + existing.slice(end + DOSU_SECTION_END.length);
11419
+ if (location) {
11420
+ next = existing.slice(0, location.start) + section + existing.slice(location.end + DOSU_SECTION_END.length);
11279
11421
  } else {
11280
- next = `${existing.trimEnd()}
11281
-
11282
- ${section}
11283
- `;
11422
+ next = `${existing.trimEnd()}${lineEnding}${lineEnding}${section}${lineEnding}`;
11284
11423
  }
11285
11424
  if (next === existing)
11286
11425
  return { path: path3, action: "unchanged" };
11287
- writeFileSync7(path3, next);
11426
+ writeFileSync8(path3, next);
11288
11427
  return { path: path3, action: "updated" };
11289
11428
  }
11290
- function stepUpdateAgentsMd(cwd = process.cwd()) {
11429
+ async function stepUpdateAgentsMd(cwd = process.cwd(), content) {
11291
11430
  logger.info("setup", "Step: update AGENTS.md");
11292
11431
  try {
11293
- const result = upsertDosuAgentsSection(cwd);
11432
+ const result = upsertDosuAgentsSection(cwd, content ?? await fetchDosuRule());
11294
11433
  logger.info("setup", `AGENTS.md ${result.action} at ${result.path}`);
11295
11434
  if (result.action === "unchanged") {
11296
11435
  log.success(`AGENTS.md
@@ -11307,11 +11446,11 @@ ${dim(`Dosu section ${result.action} — ${result.path}`)}`);
11307
11446
  return false;
11308
11447
  }
11309
11448
  }
11310
- var DOSU_SECTION_VERSION = 1, DOSU_SECTION_START, DOSU_SECTION_END = "<!-- dosu:mcp:end -->", SECTION_START_RE;
11449
+ var DOSU_SECTION_VERSION = 2, DOSU_SECTION_START, DOSU_SECTION_END = "<!-- dosu:mcp:end -->", SECTION_START_RE;
11311
11450
  var init_agents_md_step = __esm(() => {
11312
11451
  init_dist8();
11313
11452
  init_logger();
11314
- init_audit_handoff();
11453
+ init_installer();
11315
11454
  init_styles();
11316
11455
  DOSU_SECTION_START = `<!-- dosu:mcp:start v${DOSU_SECTION_VERSION} -->`;
11317
11456
  SECTION_START_RE = /<!-- dosu:mcp:start(?: v(\d+))? -->/;
@@ -11403,6 +11542,153 @@ var init_analytics = __esm(() => {
11403
11542
  init_logger();
11404
11543
  });
11405
11544
 
11545
+ // src/setup/audit-handoff.ts
11546
+ import { spawnSync as spawnSync2 } from "node:child_process";
11547
+ function buildAuditHandoffPrompt(dosuCmd = dosuInvocation()) {
11548
+ return [
11549
+ "Audit this repository to see what docs Dosu can generate for it.",
11550
+ "1. Run the codebase audit from the Dosu skill. It writes findings to .dosu/audit.json.",
11551
+ "2. Show me the findings and which docs you recommend Dosu generate.",
11552
+ `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.`
11553
+ ].join(`
11554
+ `);
11555
+ }
11556
+ function binOnPath(bin) {
11557
+ const cmd = process.platform === "win32" ? "where" : "which";
11558
+ try {
11559
+ return spawnSync2(cmd, [bin], { stdio: "ignore" }).status === 0;
11560
+ } catch {
11561
+ return false;
11562
+ }
11563
+ }
11564
+ function claudeCliAvailable() {
11565
+ return binOnPath("claude");
11566
+ }
11567
+ function dosuInvocation() {
11568
+ return binOnPath("dosu") ? "dosu" : "npx -y @dosu/cli";
11569
+ }
11570
+ function printManualAuditNudge() {
11571
+ log.message(`See what docs Dosu can generate for this repo:
11572
+
11573
+ ${info("Ask Dosu to audit this repo and show what docs it can generate.")}
11574
+
11575
+ Or run ${info(`${dosuInvocation()} audit`)} after your agent writes the audit.`);
11576
+ }
11577
+ async function offerAuditHandoff() {
11578
+ if (!detectGitRepo())
11579
+ return false;
11580
+ if (!claudeCliAvailable()) {
11581
+ printManualAuditNudge();
11582
+ return false;
11583
+ }
11584
+ const go = await confirm({
11585
+ message: "Kick off the codebase audit in Claude Code now?",
11586
+ initialValue: true
11587
+ });
11588
+ if (isCancel(go) || !go) {
11589
+ printManualAuditNudge();
11590
+ return false;
11591
+ }
11592
+ return true;
11593
+ }
11594
+ function launchAuditAgent() {
11595
+ logger.info("setup", "Handing off to Claude Code for the codebase audit");
11596
+ const result = spawnSync2("claude", ["--model", AUDIT_AGENT_MODEL, buildAuditHandoffPrompt()], {
11597
+ stdio: "inherit",
11598
+ shell: process.platform === "win32"
11599
+ });
11600
+ if (result.error) {
11601
+ logger.warn("setup", `Claude Code launch failed: ${result.error.message}`);
11602
+ printManualAuditNudge();
11603
+ }
11604
+ }
11605
+ var AUDIT_AGENT_MODEL = "haiku";
11606
+ var init_audit_handoff = __esm(() => {
11607
+ init_dist8();
11608
+ init_logger();
11609
+ init_github_step();
11610
+ init_styles();
11611
+ });
11612
+
11613
+ // src/setup/rules-step.ts
11614
+ function successfulAgentIDs(results, action) {
11615
+ return new Set(results.filter((result) => result.action === action && !result.error).map((result) => result.provider.id()));
11616
+ }
11617
+ function logRuleResults(results) {
11618
+ const ready = results.filter((result) => !result.error && (result.action === "created" || result.action === "updated" || result.action === "unchanged"));
11619
+ const removed = results.filter((result) => !result.error && result.action === "removed");
11620
+ if (ready.length > 0) {
11621
+ const lines = ready.map((result) => `+ ${result.provider.name()}
11622
+ ${dim(result.path)}`).join(`
11623
+ `);
11624
+ log.success(`Rules ready for ${ready.length} agent(s):
11625
+ ${lines}`);
11626
+ }
11627
+ if (removed.length > 0) {
11628
+ const lines = removed.map((result) => `- ${result.provider.name()}
11629
+ ${dim(result.path)}`).join(`
11630
+ `);
11631
+ log.info(`Rules removed from ${removed.length} agent(s):
11632
+ ${lines}`);
11633
+ }
11634
+ }
11635
+ async function stepConfigureAgentRules(selection, mcpResults) {
11636
+ const successfulInstalls = successfulAgentIDs(mcpResults, "install");
11637
+ const successfulRemovals = successfulAgentIDs(mcpResults, "remove");
11638
+ const toInstall = selection.toInstall.filter((provider) => successfulInstalls.has(provider.id()) && isRuleAgent(provider.id()));
11639
+ const toRemove = selection.toRemove.filter((provider) => successfulRemovals.has(provider.id()) && isRuleAgent(provider.id()));
11640
+ const results = [];
11641
+ if (toInstall.length > 0) {
11642
+ const content = await fetchDosuRule();
11643
+ for (const provider of toInstall) {
11644
+ try {
11645
+ const installed = installRuleForAgent(provider.id(), content);
11646
+ if (installed)
11647
+ results.push({ provider, action: installed.action, path: installed.path });
11648
+ } catch (err) {
11649
+ const error = err instanceof Error ? err : new Error(String(err));
11650
+ logger.error("setup", `Rule install failed for ${provider.name()}: ${error.stack ?? error.message}`);
11651
+ log.error(`Failed to install ${provider.name()} rule: ${error.message}`);
11652
+ results.push({
11653
+ provider,
11654
+ action: "not_found",
11655
+ path: rulePathForAgent(provider.id()) ?? "",
11656
+ error
11657
+ });
11658
+ }
11659
+ }
11660
+ }
11661
+ const retainedPaths = new Set(toInstall.map((provider) => rulePathForAgent(provider.id())).filter((path3) => path3 !== null));
11662
+ for (const provider of toRemove) {
11663
+ const path3 = rulePathForAgent(provider.id());
11664
+ if (path3 && retainedPaths.has(path3))
11665
+ continue;
11666
+ try {
11667
+ const removed = removeRuleForAgent(provider.id());
11668
+ if (removed)
11669
+ results.push({ provider, action: removed.action, path: removed.path });
11670
+ } catch (err) {
11671
+ const error = err instanceof Error ? err : new Error(String(err));
11672
+ logger.error("setup", `Rule removal failed for ${provider.name()}: ${error.stack ?? error.message}`);
11673
+ log.error(`Failed to remove ${provider.name()} rule: ${error.message}`);
11674
+ results.push({
11675
+ provider,
11676
+ action: "not_found",
11677
+ path: path3 ?? "",
11678
+ error
11679
+ });
11680
+ }
11681
+ }
11682
+ logRuleResults(results);
11683
+ return results;
11684
+ }
11685
+ var init_rules_step = __esm(() => {
11686
+ init_dist8();
11687
+ init_logger();
11688
+ init_installer();
11689
+ init_styles();
11690
+ });
11691
+
11406
11692
  // src/auth/errors.ts
11407
11693
  var exports_errors = {};
11408
11694
  __export(exports_errors, {
@@ -11989,48 +12275,33 @@ async function runSetup(opts = {}) {
11989
12275
  }
11990
12276
  updateTarget(cfg, { api_key: apiKey });
11991
12277
  saveConfig(cfg);
11992
- const choices = await stepOneShotConfirm(inGitWorkTree());
11993
- if (!choices) {
12278
+ const configured = await stepConfigureMcpTools(cfg);
12279
+ if (configured === null) {
11994
12280
  await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_cancelled", {
11995
- reason: "options_cancelled"
12281
+ reason: "mcp_selection_cancelled"
11996
12282
  });
11997
12283
  return;
11998
12284
  }
11999
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_options_selected", {
12000
- configure_mcp: choices.configureMcp,
12001
- install_skill: choices.installSkill,
12002
- update_agents_md: choices.updateAgentsMd
12003
- });
12004
- let mcpConfiguredThisRun = false;
12005
- let mcpCompleted = false;
12006
- let skillCompleted = false;
12007
- if (choices.configureMcp) {
12008
- const configured = await stepConfigureMcpTools(cfg);
12009
- if (configured === null) {
12010
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_cancelled", {
12011
- reason: "mcp_selection_cancelled"
12012
- });
12013
- return;
12014
- }
12015
- mcpConfiguredThisRun = configured.some((r2) => r2.action === "install" || r2.action === "skip");
12016
- const configuredProviders = configured.filter((r2) => (r2.action === "install" || r2.action === "skip") && !r2.error);
12017
- mcpCompleted = configuredProviders.length > 0;
12018
- if (mcpCompleted) {
12019
- await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_mcp_configured", {
12020
- provider_count: configuredProviders.length,
12021
- providers: configuredProviders.map((r2) => r2.provider.id())
12022
- });
12023
- }
12285
+ const mcpConfiguredThisRun = configured.some((result) => result.action === "install" || result.action === "skip");
12286
+ const configuredProviders = configured.filter((result) => (result.action === "install" || result.action === "skip") && !result.error);
12287
+ const mcpCompleted = configuredProviders.length > 0;
12288
+ if (mcpCompleted) {
12289
+ await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_mcp_configured", {
12290
+ provider_count: configuredProviders.length,
12291
+ providers: configuredProviders.map((result) => result.provider.id())
12292
+ });
12024
12293
  }
12025
- if (choices.installSkill) {
12026
- skillCompleted = await runInstallSkill();
12294
+ let skillCompleted = false;
12295
+ const selectedProviderIDs = configuredProviders.map((result) => result.provider.id());
12296
+ if (skillAgentIDsForProviders(selectedProviderIDs).length > 0) {
12297
+ skillCompleted = await runInstallSkill(selectedProviderIDs);
12027
12298
  if (skillCompleted) {
12028
12299
  await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_skill_installed");
12029
12300
  }
12030
12301
  }
12031
12302
  let agentsMdCompleted = false;
12032
- if (choices.updateAgentsMd) {
12033
- agentsMdCompleted = stepUpdateAgentsMd();
12303
+ if (mcpCompleted && inGitWorkTree()) {
12304
+ agentsMdCompleted = await stepUpdateAgentsMd();
12034
12305
  }
12035
12306
  let handoffToAudit = false;
12036
12307
  if (mcpConfiguredThisRun && cfg.mode !== MODE_OSS) {
@@ -12071,31 +12342,6 @@ function applyModeOverride(cfg, opts) {
12071
12342
  }
12072
12343
  saveConfig(cfg);
12073
12344
  }
12074
- async function stepOneShotConfirm(offerAgentsMd) {
12075
- const items = [
12076
- { value: "configureMcp", label: "Install Dosu MCP" },
12077
- { value: "installSkill", label: "Install Dosu skill" }
12078
- ];
12079
- if (offerAgentsMd) {
12080
- items.push({ value: "updateAgentsMd", label: "Add Dosu instructions to AGENTS.md" });
12081
- }
12082
- const selected = await multiselect({
12083
- message: "Dosu will set these up — press Enter to accept, space to toggle",
12084
- options: items.map((it) => ({ value: it.value, label: it.label })),
12085
- initialValues: items.map((it) => it.value),
12086
- required: false
12087
- });
12088
- if (isCancel(selected)) {
12089
- logger.info("setup", "One-shot confirm cancelled");
12090
- return null;
12091
- }
12092
- const chosen = new Set(selected);
12093
- return {
12094
- configureMcp: chosen.has("configureMcp"),
12095
- installSkill: chosen.has("installSkill"),
12096
- updateAgentsMd: chosen.has("updateAgentsMd")
12097
- };
12098
- }
12099
12345
  async function stepConfigureMcpTools(cfg) {
12100
12346
  const detected = stepDetectTools();
12101
12347
  if (detected.length === 0) {
@@ -12108,12 +12354,13 @@ Run ${info("dosu mcp add <agent>")} to manually configure an agent.`);
12108
12354
  return null;
12109
12355
  const results = stepConfigureTools(cfg, selection);
12110
12356
  stepShowSummary(results);
12357
+ await stepConfigureAgentRules(selection, results);
12111
12358
  return results;
12112
12359
  }
12113
- async function runInstallSkill() {
12360
+ async function runInstallSkill(providerIDs) {
12114
12361
  logger.info("setup", "Step: install skill");
12115
12362
  try {
12116
- const result = await installSkill();
12363
+ const result = await installSkill(providerIDs);
12117
12364
  if (result.success) {
12118
12365
  logger.info("setup", `Skill installed${result.sha ? ` sha=${result.sha}` : ""}`);
12119
12366
  log.success("Skill installed");
@@ -12574,12 +12821,13 @@ var init_flow2 = __esm(() => {
12574
12821
  init_agents_md_step();
12575
12822
  init_analytics();
12576
12823
  init_audit_handoff();
12824
+ init_rules_step();
12577
12825
  init_styles();
12578
12826
  });
12579
12827
 
12580
12828
  // src/commands/insights.ts
12581
- import { existsSync as existsSync17, mkdirSync as mkdirSync8, readdirSync, unlinkSync, writeFileSync as writeFileSync8 } from "node:fs";
12582
- import { dirname as dirname5, join as join27 } from "node:path";
12829
+ import { existsSync as existsSync18, mkdirSync as mkdirSync9, readdirSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "node:fs";
12830
+ import { dirname as dirname6, join as join28 } from "node:path";
12583
12831
  function shuffled(arr) {
12584
12832
  const copy2 = [...arr];
12585
12833
  for (let i2 = copy2.length - 1;i2 > 0; i2--) {
@@ -12683,17 +12931,17 @@ function makeAskFn(cfg) {
12683
12931
  };
12684
12932
  }
12685
12933
  function insightsDir() {
12686
- return join27(getConfigDir(), "insights");
12934
+ return join28(getConfigDir(), "insights");
12687
12935
  }
12688
12936
  function reportPath(timestamp) {
12689
12937
  const dir = insightsDir();
12690
12938
  if (!timestamp)
12691
- return join27(dir, "latest.html");
12939
+ return join28(dir, "latest.html");
12692
12940
  const iso = timestamp.toISOString().slice(0, 19).replace(/:/g, "-");
12693
- return join27(dir, `report-${iso}Z.html`);
12941
+ return join28(dir, `report-${iso}Z.html`);
12694
12942
  }
12695
12943
  function pruneOldReports(dir, keepN) {
12696
- if (!existsSync17(dir))
12944
+ if (!existsSync18(dir))
12697
12945
  return;
12698
12946
  let entries;
12699
12947
  try {
@@ -12705,7 +12953,7 @@ function pruneOldReports(dir, keepN) {
12705
12953
  const reports = entries.filter((f2) => REPORT_FILE_PATTERN.test(f2)).sort().reverse();
12706
12954
  for (const f2 of reports.slice(keepN)) {
12707
12955
  try {
12708
- unlinkSync(join27(dir, f2));
12956
+ unlinkSync2(join28(dir, f2));
12709
12957
  } catch (err) {
12710
12958
  logger.debug("insights", `failed to prune ${f2}: ${err instanceof Error ? err.message : err}`);
12711
12959
  }
@@ -12777,10 +13025,10 @@ function defaultRunner(cfg) {
12777
13025
  build: buildInsights,
12778
13026
  render: renderHTML,
12779
13027
  writeFile: (path3, content) => {
12780
- const dir = dirname5(path3);
12781
- if (!existsSync17(dir))
12782
- mkdirSync8(dir, { recursive: true, mode: 448 });
12783
- writeFileSync8(path3, content, { mode: 384 });
13028
+ const dir = dirname6(path3);
13029
+ if (!existsSync18(dir))
13030
+ mkdirSync9(dir, { recursive: true, mode: 448 });
13031
+ writeFileSync9(path3, content, { mode: 384 });
12784
13032
  },
12785
13033
  prune: pruneOldReports,
12786
13034
  openInBrowser: async (path3) => {
@@ -13274,9 +13522,71 @@ async function runAgentSetup(opts) {
13274
13522
  });
13275
13523
  return 1;
13276
13524
  }
13525
+ let instruction;
13526
+ try {
13527
+ if (isRuleAgent(provider.id())) {
13528
+ instruction = await fetchDosuRule();
13529
+ const rule = installRuleForAgent(provider.id(), instruction);
13530
+ if (rule) {
13531
+ emitStep({
13532
+ step: "rule_install",
13533
+ tool: provider.id(),
13534
+ tool_name: provider.name(),
13535
+ rule_path: rule.path,
13536
+ action: rule.action
13537
+ });
13538
+ }
13539
+ }
13540
+ } catch (err) {
13541
+ emitError({
13542
+ step: "rule_install",
13543
+ reason: "install_failed",
13544
+ agent_next_steps: `Dosu MCP was installed, but its rule could not be installed for ${provider.name()}: ${err instanceof Error ? err.message : String(err)}. Re-run this setup command; installation is idempotent.`
13545
+ });
13546
+ return 1;
13547
+ }
13548
+ if (skillAgentIDsForProviders([provider.id()]).length > 0) {
13549
+ try {
13550
+ const skill = await installSkill([provider.id()], { quiet: true });
13551
+ if (!skill.success) {
13552
+ throw new Error("the skills installer failed");
13553
+ }
13554
+ emitStep({
13555
+ step: "skill_install",
13556
+ tool: provider.id(),
13557
+ tool_name: provider.name(),
13558
+ source: "dosu-ai/dosu-skill"
13559
+ });
13560
+ } catch (err) {
13561
+ emitError({
13562
+ step: "skill_install",
13563
+ reason: "install_failed",
13564
+ agent_next_steps: `Dosu MCP and its rule were installed, but the skill could not be installed for ${provider.name()}: ${err instanceof Error ? err.message : String(err)}. Re-run this setup command; installation is idempotent.`
13565
+ });
13566
+ return 1;
13567
+ }
13568
+ }
13569
+ if (inGitWorkTree()) {
13570
+ try {
13571
+ instruction ??= await fetchDosuRule();
13572
+ const agentsMd = upsertDosuAgentsSection(process.cwd(), instruction);
13573
+ emitStep({
13574
+ step: "agents_md_install",
13575
+ path: agentsMd.path,
13576
+ action: agentsMd.action
13577
+ });
13578
+ } catch (err) {
13579
+ emitError({
13580
+ step: "agents_md_install",
13581
+ reason: "install_failed",
13582
+ agent_next_steps: `Dosu's agent integrations were installed, but AGENTS.md could not be updated: ${err instanceof Error ? err.message : String(err)}. Re-run this setup command; installation is idempotent.`
13583
+ });
13584
+ return 1;
13585
+ }
13586
+ }
13277
13587
  emitStep({
13278
13588
  step: "done",
13279
- agent_next_steps: `Dosu MCP is configured for ${provider.name()}. Tell the user setup is complete and they can ask their agent a Dosu question. Run 'dosu status' to verify.`
13589
+ agent_next_steps: `Dosu is configured for ${provider.name()}. Tell the user setup is complete and they can ask their agent a Dosu question. Run 'dosu status' to verify.`
13280
13590
  });
13281
13591
  return 0;
13282
13592
  }
@@ -13508,16 +13818,19 @@ var NPX_INVOCATION = "npx @dosu/cli@latest";
13508
13818
  var init_flow3 = __esm(() => {
13509
13819
  init_ticket();
13510
13820
  init_client();
13821
+ init_skill();
13511
13822
  init_config();
13512
13823
  init_logger();
13513
13824
  init_providers();
13825
+ init_installer();
13826
+ init_agents_md_step();
13514
13827
  init_flow2();
13515
13828
  });
13516
13829
 
13517
13830
  // src/cli/cli.ts
13518
13831
  init_commander();
13519
13832
  init_client();
13520
- import { readFileSync as readFileSync17, unlinkSync as unlinkSync2 } from "node:fs";
13833
+ import { readFileSync as readFileSync18, unlinkSync as unlinkSync3 } from "node:fs";
13521
13834
 
13522
13835
  // src/commands/analytics.ts
13523
13836
  init_commander();
@@ -13644,6 +13957,7 @@ init_commander();
13644
13957
  init_config();
13645
13958
  init_logger();
13646
13959
  var import_picocolors5 = __toESM(require_picocolors(), 1);
13960
+ var DEFAULT_TIMEOUT_SECONDS = 600;
13647
13961
  function requireConfig2() {
13648
13962
  const cfg = loadConfig();
13649
13963
  if (!cfg.active_account?.target?.api_key) {
@@ -13657,16 +13971,24 @@ function requireConfig2() {
13657
13971
  return cfg;
13658
13972
  }
13659
13973
  function askCommand() {
13660
- const cmd = new Command("ask").description("Ask a question and get an AI-generated answer").argument("<question>", "The question to ask").option("--session <id>", "Continue a previous ask session").option("--json", "Output as JSON").action(async (question, opts) => {
13974
+ const cmd = new Command("ask").description("Ask a question and get an AI-generated answer").argument("<question>", "The question to ask").option("--session <id>", "Continue a previous ask session").option("--timeout <seconds>", `Request timeout in seconds (default: ${DEFAULT_TIMEOUT_SECONDS})`).option("--json", "Output as JSON").action(async (question, opts) => {
13661
13975
  const cfg = requireConfig2();
13662
13976
  const backendURL = getBackendURL();
13663
13977
  if (!backendURL) {
13664
13978
  console.error(import_picocolors5.default.red("Backend URL not configured."));
13665
13979
  process.exit(1);
13666
13980
  }
13981
+ let timeoutSeconds = DEFAULT_TIMEOUT_SECONDS;
13982
+ if (opts.timeout !== undefined) {
13983
+ timeoutSeconds = Number(opts.timeout);
13984
+ if (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0) {
13985
+ console.error(import_picocolors5.default.red("--timeout must be a positive number of seconds."));
13986
+ process.exit(1);
13987
+ }
13988
+ }
13667
13989
  logger.debug("ask", `Asking: ${question}`);
13668
13990
  const controller = new AbortController;
13669
- const timeout = setTimeout(() => controller.abort(), 120000);
13991
+ const timeout = setTimeout(() => controller.abort(), timeoutSeconds * 1000);
13670
13992
  try {
13671
13993
  const resp = await fetch(`${backendURL}/ask`, {
13672
13994
  method: "POST",
@@ -15600,7 +15922,7 @@ init_dist();
15600
15922
  init_commander();
15601
15923
  init_trpc();
15602
15924
  var import_picocolors19 = __toESM(require_picocolors(), 1);
15603
- import { readFileSync as readFileSync15 } from "node:fs";
15925
+ import { readFileSync as readFileSync16 } from "node:fs";
15604
15926
  function requireConfig10() {
15605
15927
  return requireLoginConfig();
15606
15928
  }
@@ -15776,7 +16098,7 @@ function reviewCommand() {
15776
16098
  let body = opts.body;
15777
16099
  if (opts.bodyFile) {
15778
16100
  try {
15779
- body = readFileSync15(opts.bodyFile, "utf-8");
16101
+ body = readFileSync16(opts.bodyFile, "utf-8");
15780
16102
  } catch (err) {
15781
16103
  console.error(import_picocolors19.default.red(`Failed to read --body-file: ${err.message}`));
15782
16104
  process.exit(1);
@@ -16296,8 +16618,8 @@ init_skill_update_check();
16296
16618
  init_config();
16297
16619
  init_logger();
16298
16620
  var import_picocolors24 = __toESM(require_picocolors(), 1);
16299
- import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "node:fs";
16300
- import { join as join28 } from "node:path";
16621
+ import { existsSync as existsSync19, mkdirSync as mkdirSync10, readFileSync as readFileSync17, writeFileSync as writeFileSync10 } from "node:fs";
16622
+ import { join as join29 } from "node:path";
16301
16623
  var CACHE_FILENAME3 = "update-check.json";
16302
16624
  var CHECK_INTERVAL_MS3 = 24 * 60 * 60 * 1000;
16303
16625
  var FETCH_TIMEOUT_MS3 = 5000;
@@ -16320,14 +16642,14 @@ function isNewerVersion(latest, current) {
16320
16642
  return false;
16321
16643
  }
16322
16644
  function getCachePath3() {
16323
- return join28(getConfigDir(), CACHE_FILENAME3);
16645
+ return join29(getConfigDir(), CACHE_FILENAME3);
16324
16646
  }
16325
16647
  function readCache() {
16326
16648
  try {
16327
16649
  const path3 = getCachePath3();
16328
- if (!existsSync18(path3))
16650
+ if (!existsSync19(path3))
16329
16651
  return null;
16330
- const data = JSON.parse(readFileSync16(path3, "utf-8"));
16652
+ const data = JSON.parse(readFileSync17(path3, "utf-8"));
16331
16653
  if (typeof data.lastCheck === "number" && typeof data.latestVersion === "string") {
16332
16654
  return data;
16333
16655
  }
@@ -16339,10 +16661,10 @@ function readCache() {
16339
16661
  function writeCache(cache) {
16340
16662
  try {
16341
16663
  const dir = getConfigDir();
16342
- if (!existsSync18(dir)) {
16343
- mkdirSync9(dir, { recursive: true, mode: 448 });
16664
+ if (!existsSync19(dir)) {
16665
+ mkdirSync10(dir, { recursive: true, mode: 448 });
16344
16666
  }
16345
- writeFileSync9(getCachePath3(), JSON.stringify(cache), { mode: 384 });
16667
+ writeFileSync10(getCachePath3(), JSON.stringify(cache), { mode: 384 });
16346
16668
  } catch {}
16347
16669
  }
16348
16670
  async function fetchLatestVersion() {
@@ -16691,7 +17013,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
16691
17013
  const logPath = logger.getLogPath();
16692
17014
  if (opts.clear) {
16693
17015
  try {
16694
- unlinkSync2(logPath);
17016
+ unlinkSync3(logPath);
16695
17017
  console.log("Log file deleted.");
16696
17018
  } catch {
16697
17019
  console.log("No log file to delete.");
@@ -16701,7 +17023,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
16701
17023
  if (opts.tail !== undefined) {
16702
17024
  const n2 = typeof opts.tail === "string" ? parseInt(opts.tail, 10) || 50 : 50;
16703
17025
  try {
16704
- const content = readFileSync17(logPath, "utf-8");
17026
+ const content = readFileSync18(logPath, "utf-8");
16705
17027
  const lines = content.split(`
16706
17028
  `);
16707
17029
  console.log(lines.slice(-n2).join(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosu/cli",
3
- "version": "0.35.4",
3
+ "version": "0.37.0-alpha.1",
4
4
  "type": "module",
5
5
  "description": "Dosu CLI - Manage MCP servers for AI tools",
6
6
  "license": "MIT",