@dosu/cli 0.36.0 → 0.37.0-alpha.2
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/README.md +1 -1
- package/bin/dosu.js +527 -183
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -92,7 +92,7 @@ Or right-click the binary, select "Open", and click "Open" in the dialog.
|
|
|
92
92
|
| `dosu setup` | Run the setup wizard (auth → deployment → detect tools → configure) |
|
|
93
93
|
| `dosu login` | Authenticate with Dosu via browser OAuth |
|
|
94
94
|
| `dosu logout` | Clear saved credentials |
|
|
95
|
-
| `dosu status` | Show current authentication and MCP status |
|
|
95
|
+
| `dosu status [--json]` | Show current authentication and MCP status |
|
|
96
96
|
| `dosu mcp list` | List supported AI tools |
|
|
97
97
|
| `dosu mcp add <tool>` | Add the Dosu MCP server to a specific tool |
|
|
98
98
|
| `dosu logs` | View or manage debug logs (`--tail`, `--clear`) |
|
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.2", INSTALL_CHANNEL = "npm";
|
|
4763
4763
|
|
|
4764
4764
|
// src/debug/logger.ts
|
|
4765
4765
|
import {
|
|
@@ -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 --json' 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();
|
|
@@ -15609,7 +15922,7 @@ init_dist();
|
|
|
15609
15922
|
init_commander();
|
|
15610
15923
|
init_trpc();
|
|
15611
15924
|
var import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
15612
|
-
import { readFileSync as
|
|
15925
|
+
import { readFileSync as readFileSync16 } from "node:fs";
|
|
15613
15926
|
function requireConfig10() {
|
|
15614
15927
|
return requireLoginConfig();
|
|
15615
15928
|
}
|
|
@@ -15785,7 +16098,7 @@ function reviewCommand() {
|
|
|
15785
16098
|
let body = opts.body;
|
|
15786
16099
|
if (opts.bodyFile) {
|
|
15787
16100
|
try {
|
|
15788
|
-
body =
|
|
16101
|
+
body = readFileSync16(opts.bodyFile, "utf-8");
|
|
15789
16102
|
} catch (err) {
|
|
15790
16103
|
console.error(import_picocolors19.default.red(`Failed to read --body-file: ${err.message}`));
|
|
15791
16104
|
process.exit(1);
|
|
@@ -16305,8 +16618,8 @@ init_skill_update_check();
|
|
|
16305
16618
|
init_config();
|
|
16306
16619
|
init_logger();
|
|
16307
16620
|
var import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
16308
|
-
import { existsSync as
|
|
16309
|
-
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";
|
|
16310
16623
|
var CACHE_FILENAME3 = "update-check.json";
|
|
16311
16624
|
var CHECK_INTERVAL_MS3 = 24 * 60 * 60 * 1000;
|
|
16312
16625
|
var FETCH_TIMEOUT_MS3 = 5000;
|
|
@@ -16329,14 +16642,14 @@ function isNewerVersion(latest, current) {
|
|
|
16329
16642
|
return false;
|
|
16330
16643
|
}
|
|
16331
16644
|
function getCachePath3() {
|
|
16332
|
-
return
|
|
16645
|
+
return join29(getConfigDir(), CACHE_FILENAME3);
|
|
16333
16646
|
}
|
|
16334
16647
|
function readCache() {
|
|
16335
16648
|
try {
|
|
16336
16649
|
const path3 = getCachePath3();
|
|
16337
|
-
if (!
|
|
16650
|
+
if (!existsSync19(path3))
|
|
16338
16651
|
return null;
|
|
16339
|
-
const data = JSON.parse(
|
|
16652
|
+
const data = JSON.parse(readFileSync17(path3, "utf-8"));
|
|
16340
16653
|
if (typeof data.lastCheck === "number" && typeof data.latestVersion === "string") {
|
|
16341
16654
|
return data;
|
|
16342
16655
|
}
|
|
@@ -16348,10 +16661,10 @@ function readCache() {
|
|
|
16348
16661
|
function writeCache(cache) {
|
|
16349
16662
|
try {
|
|
16350
16663
|
const dir = getConfigDir();
|
|
16351
|
-
if (!
|
|
16352
|
-
|
|
16664
|
+
if (!existsSync19(dir)) {
|
|
16665
|
+
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
16353
16666
|
}
|
|
16354
|
-
|
|
16667
|
+
writeFileSync10(getCachePath3(), JSON.stringify(cache), { mode: 384 });
|
|
16355
16668
|
} catch {}
|
|
16356
16669
|
}
|
|
16357
16670
|
async function fetchLatestVersion() {
|
|
@@ -16562,14 +16875,45 @@ Run 'dosu --help' to see available commands.`;
|
|
|
16562
16875
|
saveConfig(cfg);
|
|
16563
16876
|
console.log("Successfully logged out.");
|
|
16564
16877
|
});
|
|
16565
|
-
program2.command("status").description("Show current authentication and MCP status").action(async () => {
|
|
16878
|
+
program2.command("status").description("Show current authentication and MCP status").option("--json", "Output as JSON").action(async (opts) => {
|
|
16566
16879
|
const cfg = loadConfig();
|
|
16880
|
+
const mode = cfg.mode === MODE_OSS ? "oss" : "cloud";
|
|
16567
16881
|
if (!isAuthenticated(cfg)) {
|
|
16882
|
+
if (opts.json) {
|
|
16883
|
+
console.log(JSON.stringify({
|
|
16884
|
+
authenticated: false,
|
|
16885
|
+
session_status: "missing",
|
|
16886
|
+
mode,
|
|
16887
|
+
mcp: {
|
|
16888
|
+
status: "not_configured",
|
|
16889
|
+
deployment_id: null,
|
|
16890
|
+
deployment_name: null
|
|
16891
|
+
}
|
|
16892
|
+
}, null, 2));
|
|
16893
|
+
return;
|
|
16894
|
+
}
|
|
16568
16895
|
console.log("Status: Not logged in");
|
|
16569
16896
|
console.log("Run 'dosu login' to authenticate.");
|
|
16570
16897
|
return;
|
|
16571
16898
|
}
|
|
16572
|
-
|
|
16899
|
+
const sessionStatus = isTokenExpired(cfg) && !await ensureFreshSession(cfg) ? "expired" : "valid";
|
|
16900
|
+
if (opts.json) {
|
|
16901
|
+
const deploymentId = cfg.active_account.target?.deployment_id ?? null;
|
|
16902
|
+
const deploymentName = cfg.active_account.target?.deployment_name ?? null;
|
|
16903
|
+
const mcpStatus = cfg.mode === MODE_OSS ? "public_libraries" : deploymentId ? "configured" : "not_configured";
|
|
16904
|
+
console.log(JSON.stringify({
|
|
16905
|
+
authenticated: sessionStatus === "valid",
|
|
16906
|
+
session_status: sessionStatus,
|
|
16907
|
+
mode,
|
|
16908
|
+
mcp: {
|
|
16909
|
+
status: mcpStatus,
|
|
16910
|
+
deployment_id: deploymentId,
|
|
16911
|
+
deployment_name: deploymentName
|
|
16912
|
+
}
|
|
16913
|
+
}, null, 2));
|
|
16914
|
+
return;
|
|
16915
|
+
}
|
|
16916
|
+
if (sessionStatus === "expired") {
|
|
16573
16917
|
console.log("Status: Token expired");
|
|
16574
16918
|
console.log("Run 'dosu login' to re-authenticate.");
|
|
16575
16919
|
} else {
|
|
@@ -16700,7 +17044,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
|
|
|
16700
17044
|
const logPath = logger.getLogPath();
|
|
16701
17045
|
if (opts.clear) {
|
|
16702
17046
|
try {
|
|
16703
|
-
|
|
17047
|
+
unlinkSync3(logPath);
|
|
16704
17048
|
console.log("Log file deleted.");
|
|
16705
17049
|
} catch {
|
|
16706
17050
|
console.log("No log file to delete.");
|
|
@@ -16710,7 +17054,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
|
|
|
16710
17054
|
if (opts.tail !== undefined) {
|
|
16711
17055
|
const n2 = typeof opts.tail === "string" ? parseInt(opts.tail, 10) || 50 : 50;
|
|
16712
17056
|
try {
|
|
16713
|
-
const content =
|
|
17057
|
+
const content = readFileSync18(logPath, "utf-8");
|
|
16714
17058
|
const lines = content.split(`
|
|
16715
17059
|
`);
|
|
16716
17060
|
console.log(lines.slice(-n2).join(`
|