@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.
- package/bin/dosu.js +506 -184
- 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.
|
|
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 = "
|
|
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
|
|
10453
|
-
return
|
|
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 ${
|
|
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/
|
|
11167
|
-
import {
|
|
11168
|
-
|
|
11169
|
-
|
|
11170
|
-
|
|
11171
|
-
|
|
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
|
|
11178
|
-
|
|
11179
|
-
|
|
11180
|
-
|
|
11181
|
-
|
|
11182
|
-
|
|
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
|
|
11186
|
-
|
|
11224
|
+
function ensureParent(path3) {
|
|
11225
|
+
mkdirSync8(dirname5(path3), { recursive: true });
|
|
11187
11226
|
}
|
|
11188
|
-
function
|
|
11189
|
-
|
|
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
|
|
11192
|
-
|
|
11193
|
-
|
|
11194
|
-
|
|
11195
|
-
|
|
11196
|
-
|
|
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
|
-
|
|
11199
|
-
|
|
11200
|
-
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
|
|
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
|
|
11206
|
-
|
|
11207
|
-
|
|
11208
|
-
|
|
11209
|
-
|
|
11210
|
-
|
|
11211
|
-
|
|
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
|
-
|
|
11279
|
+
if (next === existing)
|
|
11280
|
+
return "unchanged";
|
|
11281
|
+
writeFileSync7(path3, next, "utf-8");
|
|
11282
|
+
return "updated";
|
|
11214
11283
|
}
|
|
11215
|
-
function
|
|
11216
|
-
|
|
11217
|
-
|
|
11218
|
-
|
|
11219
|
-
|
|
11220
|
-
|
|
11221
|
-
|
|
11222
|
-
|
|
11223
|
-
|
|
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
|
|
11227
|
-
|
|
11228
|
-
|
|
11229
|
-
|
|
11230
|
-
|
|
11231
|
-
|
|
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
|
|
11237
|
-
import { join as
|
|
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
|
|
11250
|
-
return
|
|
11251
|
-
|
|
11252
|
-
|
|
11253
|
-
|
|
11254
|
-
|
|
11255
|
-
|
|
11256
|
-
|
|
11257
|
-
|
|
11258
|
-
|
|
11259
|
-
|
|
11260
|
-
|
|
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
|
|
11265
|
-
const
|
|
11266
|
-
const
|
|
11267
|
-
|
|
11268
|
-
|
|
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 =
|
|
11273
|
-
const
|
|
11274
|
-
const
|
|
11275
|
-
const
|
|
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 (
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
11993
|
-
if (
|
|
12278
|
+
const configured = await stepConfigureMcpTools(cfg);
|
|
12279
|
+
if (configured === null) {
|
|
11994
12280
|
await trackCliOnboardingEvent(cfg, onboardingRunID, "cli_onboarding_cancelled", {
|
|
11995
|
-
reason: "
|
|
12281
|
+
reason: "mcp_selection_cancelled"
|
|
11996
12282
|
});
|
|
11997
12283
|
return;
|
|
11998
12284
|
}
|
|
11999
|
-
|
|
12000
|
-
|
|
12001
|
-
|
|
12002
|
-
|
|
12003
|
-
|
|
12004
|
-
|
|
12005
|
-
|
|
12006
|
-
|
|
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
|
-
|
|
12026
|
-
|
|
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 (
|
|
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
|
|
12582
|
-
import { dirname as
|
|
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
|
|
12934
|
+
return join28(getConfigDir(), "insights");
|
|
12687
12935
|
}
|
|
12688
12936
|
function reportPath(timestamp) {
|
|
12689
12937
|
const dir = insightsDir();
|
|
12690
12938
|
if (!timestamp)
|
|
12691
|
-
return
|
|
12939
|
+
return join28(dir, "latest.html");
|
|
12692
12940
|
const iso = timestamp.toISOString().slice(0, 19).replace(/:/g, "-");
|
|
12693
|
-
return
|
|
12941
|
+
return join28(dir, `report-${iso}Z.html`);
|
|
12694
12942
|
}
|
|
12695
12943
|
function pruneOldReports(dir, keepN) {
|
|
12696
|
-
if (!
|
|
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
|
-
|
|
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 =
|
|
12781
|
-
if (!
|
|
12782
|
-
|
|
12783
|
-
|
|
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
|
|
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
|
|
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(),
|
|
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
|
|
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 =
|
|
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
|
|
16300
|
-
import { join as
|
|
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
|
|
16645
|
+
return join29(getConfigDir(), CACHE_FILENAME3);
|
|
16324
16646
|
}
|
|
16325
16647
|
function readCache() {
|
|
16326
16648
|
try {
|
|
16327
16649
|
const path3 = getCachePath3();
|
|
16328
|
-
if (!
|
|
16650
|
+
if (!existsSync19(path3))
|
|
16329
16651
|
return null;
|
|
16330
|
-
const data = JSON.parse(
|
|
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 (!
|
|
16343
|
-
|
|
16664
|
+
if (!existsSync19(dir)) {
|
|
16665
|
+
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
16344
16666
|
}
|
|
16345
|
-
|
|
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
|
-
|
|
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 =
|
|
17026
|
+
const content = readFileSync18(logPath, "utf-8");
|
|
16705
17027
|
const lines = content.split(`
|
|
16706
17028
|
`);
|
|
16707
17029
|
console.log(lines.slice(-n2).join(`
|