@dosu/cli 0.40.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/bin/dosu.js +92 -47
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,13 +54,16 @@ dosu setup
|
|
|
54
54
|
brew install dosu-ai/dosu/dosu
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
The fully qualified name works on every Homebrew version: it taps the repo if needed and, on Homebrew 6.0+, records trust for just the `dosu` formula. To use tapped short names instead:
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
60
|
brew tap dosu-ai/dosu
|
|
61
|
+
brew trust dosu-ai/dosu # Homebrew 6.0+ only — skip on older versions
|
|
61
62
|
brew install dosu
|
|
62
63
|
```
|
|
63
64
|
|
|
65
|
+
**Homebrew 6.0+ only** ([tap trust](https://docs.brew.sh/Tap-Trust), since June 2026): third-party taps start untrusted, so short-name installs or upgrades — including on machines that tapped `dosu-ai/dosu` before upgrading Homebrew — fail with `Error: Refusing to load formula dosu-ai/dosu/dosu from untrusted tap dosu-ai/dosu` until a one-time `brew trust dosu-ai/dosu` (the tap also ships [decant](https://github.com/dosu-ai/decant)). Homebrew 5 and earlier has no trust step and needs none. Tap trust is Homebrew's own consent step, separate from the macOS Gatekeeper warning below — Homebrew installs never trigger Gatekeeper.
|
|
66
|
+
|
|
64
67
|
### Manual Download
|
|
65
68
|
|
|
66
69
|
Download the appropriate archive from the [Releases](https://github.com/dosu-ai/dosu-cli/releases) page.
|
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.42.0", INSTALL_CHANNEL = "npm";
|
|
4763
4763
|
|
|
4764
4764
|
// src/debug/logger.ts
|
|
4765
4765
|
import {
|
|
@@ -8744,6 +8744,25 @@ function mcpHeaders(apiKey) {
|
|
|
8744
8744
|
}
|
|
8745
8745
|
return { "X-Dosu-API-Key": apiKey };
|
|
8746
8746
|
}
|
|
8747
|
+
function mcpRemoteServer(url, apiKey) {
|
|
8748
|
+
const env = {};
|
|
8749
|
+
const headerArgs = Object.entries(mcpHeaders(apiKey)).flatMap(([key, value]) => {
|
|
8750
|
+
const envKey = key.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
8751
|
+
env[envKey] = value;
|
|
8752
|
+
return ["--header", `${key}:\${${envKey}}`];
|
|
8753
|
+
});
|
|
8754
|
+
return {
|
|
8755
|
+
args: [
|
|
8756
|
+
"-y",
|
|
8757
|
+
`mcp-remote@${MCP_REMOTE_VERSION}`,
|
|
8758
|
+
url,
|
|
8759
|
+
...headerArgs,
|
|
8760
|
+
"--transport",
|
|
8761
|
+
"http-only"
|
|
8762
|
+
],
|
|
8763
|
+
env
|
|
8764
|
+
};
|
|
8765
|
+
}
|
|
8747
8766
|
function loadJSONConfig(path3) {
|
|
8748
8767
|
if (!existsSync6(path3))
|
|
8749
8768
|
return {};
|
|
@@ -8845,7 +8864,7 @@ function removeJSONServer(configPath, topKey) {
|
|
|
8845
8864
|
}
|
|
8846
8865
|
saveJSONConfig(configPath, jsonCfg);
|
|
8847
8866
|
}
|
|
8848
|
-
var import_write_file_atomic, writeFileAtomic;
|
|
8867
|
+
var import_write_file_atomic, writeFileAtomic, MCP_REMOTE_VERSION = "0.1.38";
|
|
8849
8868
|
var init_config_helpers = __esm(() => {
|
|
8850
8869
|
import_write_file_atomic = __toESM(require_lib(), 1);
|
|
8851
8870
|
writeFileAtomic = import_write_file_atomic.default;
|
|
@@ -10610,7 +10629,7 @@ var MCP_PROVIDER_SLUG = "dosu_mcp";
|
|
|
10610
10629
|
// src/mcp/detect.ts
|
|
10611
10630
|
import { existsSync as existsSync13 } from "node:fs";
|
|
10612
10631
|
import { homedir as homedir2, platform as platform2 } from "node:os";
|
|
10613
|
-
import { join as join12 } from "node:path";
|
|
10632
|
+
import { delimiter, dirname as dirname5, join as join12 } from "node:path";
|
|
10614
10633
|
function isInstalled(paths) {
|
|
10615
10634
|
return paths.some((p2) => existsSync13(expandHome(p2)));
|
|
10616
10635
|
}
|
|
@@ -10635,6 +10654,20 @@ function appSupportDir() {
|
|
|
10635
10654
|
}
|
|
10636
10655
|
}
|
|
10637
10656
|
}
|
|
10657
|
+
function findNpx() {
|
|
10658
|
+
const bin = platform2() === "win32" ? "npx.cmd" : "npx";
|
|
10659
|
+
for (const dir of (process.env.PATH ?? "").split(delimiter)) {
|
|
10660
|
+
if (!dir)
|
|
10661
|
+
continue;
|
|
10662
|
+
const candidate = join12(dir, bin);
|
|
10663
|
+
if (existsSync13(candidate))
|
|
10664
|
+
return candidate;
|
|
10665
|
+
}
|
|
10666
|
+
throw new Error("npx not found on PATH — Node.js is required (the MCP entry runs `npx mcp-remote`).");
|
|
10667
|
+
}
|
|
10668
|
+
function npxPathEnv(npx) {
|
|
10669
|
+
return [dirname5(npx), "/usr/bin", "/bin"].join(delimiter);
|
|
10670
|
+
}
|
|
10638
10671
|
var init_detect = () => {};
|
|
10639
10672
|
|
|
10640
10673
|
// src/mcp/providers/base.ts
|
|
@@ -10729,6 +10762,9 @@ var init_claude = __esm(() => {
|
|
|
10729
10762
|
|
|
10730
10763
|
// src/mcp/providers/claude-desktop.ts
|
|
10731
10764
|
import { join as join14 } from "node:path";
|
|
10765
|
+
function configPath() {
|
|
10766
|
+
return join14(appSupportDir(), "Claude", "claude_desktop_config.json");
|
|
10767
|
+
}
|
|
10732
10768
|
var ClaudeDesktopProvider = () => ({
|
|
10733
10769
|
name: () => "Claude Desktop",
|
|
10734
10770
|
id: () => "claude-desktop",
|
|
@@ -10736,16 +10772,31 @@ var ClaudeDesktopProvider = () => ({
|
|
|
10736
10772
|
priority: () => 2,
|
|
10737
10773
|
detectPaths: () => [join14(appSupportDir(), "Claude")],
|
|
10738
10774
|
isInstalled: () => isInstalled([join14(appSupportDir(), "Claude")]),
|
|
10739
|
-
globalConfigPath: () =>
|
|
10740
|
-
isConfigured: () =>
|
|
10741
|
-
install() {
|
|
10742
|
-
|
|
10775
|
+
globalConfigPath: () => configPath(),
|
|
10776
|
+
isConfigured: () => isJSONKeyConfigured(configPath(), "mcpServers"),
|
|
10777
|
+
install(cfg, global) {
|
|
10778
|
+
if (!global)
|
|
10779
|
+
throw new Error("Claude Desktop does not support local installation");
|
|
10780
|
+
if (cfg.mode !== MODE_OSS && !cfg.active_account?.target?.deployment_id)
|
|
10781
|
+
throw new Error("deployment ID is required");
|
|
10782
|
+
const url = cfg.mode === MODE_OSS ? mcpBaseURL() : mcpURL(cfg.active_account.target.deployment_id);
|
|
10783
|
+
const npx = findNpx();
|
|
10784
|
+
const remote = mcpRemoteServer(url, cfg.active_account?.target?.api_key);
|
|
10785
|
+
installJSONServer(configPath(), "mcpServers", {
|
|
10786
|
+
command: npx,
|
|
10787
|
+
args: remote.args,
|
|
10788
|
+
env: { PATH: npxPathEnv(npx), ...remote.env }
|
|
10789
|
+
});
|
|
10743
10790
|
},
|
|
10744
|
-
remove() {
|
|
10745
|
-
|
|
10791
|
+
remove(global) {
|
|
10792
|
+
if (!global)
|
|
10793
|
+
throw new Error("Claude Desktop does not support local removal");
|
|
10794
|
+
removeJSONServer(configPath(), "mcpServers");
|
|
10746
10795
|
}
|
|
10747
10796
|
});
|
|
10748
10797
|
var init_claude_desktop = __esm(() => {
|
|
10798
|
+
init_config();
|
|
10799
|
+
init_config_helpers();
|
|
10749
10800
|
init_detect();
|
|
10750
10801
|
});
|
|
10751
10802
|
|
|
@@ -10824,20 +10875,25 @@ function mcpEndpoint(cfg) {
|
|
|
10824
10875
|
throw new Error("deployment ID is required");
|
|
10825
10876
|
return mcpURL(cfg.active_account?.target?.deployment_id);
|
|
10826
10877
|
}
|
|
10878
|
+
function tomlString(value) {
|
|
10879
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
10880
|
+
}
|
|
10827
10881
|
function installDosuToTOML(path3, cfg) {
|
|
10828
10882
|
let content = readTOML(path3);
|
|
10829
10883
|
content = removeDosuFromTOML(content);
|
|
10830
|
-
const
|
|
10831
|
-
const
|
|
10832
|
-
const
|
|
10884
|
+
const npx = findNpx();
|
|
10885
|
+
const remote = mcpRemoteServer(mcpEndpoint(cfg), cfg.active_account?.target?.api_key);
|
|
10886
|
+
const env = { PATH: npxPathEnv(npx), ...remote.env };
|
|
10887
|
+
const envEntries = Object.entries(env).map(([key, value]) => `${key} = ${tomlString(value)}`).join(`
|
|
10833
10888
|
`);
|
|
10889
|
+
const args = remote.args.map(tomlString).join(", ");
|
|
10834
10890
|
const section = `
|
|
10835
10891
|
[mcp_servers.dosu]
|
|
10836
|
-
|
|
10837
|
-
|
|
10838
|
-
|
|
10839
|
-
[mcp_servers.dosu.
|
|
10840
|
-
${
|
|
10892
|
+
command = ${tomlString(npx)}
|
|
10893
|
+
args = [${args}]
|
|
10894
|
+
` + `
|
|
10895
|
+
[mcp_servers.dosu.env]
|
|
10896
|
+
${envEntries}
|
|
10841
10897
|
`;
|
|
10842
10898
|
content += section;
|
|
10843
10899
|
writeTOML(path3, content);
|
|
@@ -10864,7 +10920,7 @@ function removeDosuFromTOML(content) {
|
|
|
10864
10920
|
`);
|
|
10865
10921
|
}
|
|
10866
10922
|
var CodexProvider = () => ({
|
|
10867
|
-
name: () => "Codex CLI",
|
|
10923
|
+
name: () => "Codex (CLI + Desktop)",
|
|
10868
10924
|
id: () => "codex",
|
|
10869
10925
|
supportsLocal: () => true,
|
|
10870
10926
|
priority: () => 8,
|
|
@@ -10928,21 +10984,21 @@ var CopilotProvider = () => ({
|
|
|
10928
10984
|
};
|
|
10929
10985
|
installJSONServer(globalPath(), "mcpServers", server);
|
|
10930
10986
|
} else {
|
|
10931
|
-
const
|
|
10987
|
+
const configPath2 = join18(process.cwd(), ".vscode", "mcp.json");
|
|
10932
10988
|
const server = {
|
|
10933
10989
|
type: "http",
|
|
10934
10990
|
url,
|
|
10935
10991
|
headers: mcpHeaders(cfg.active_account.target.api_key)
|
|
10936
10992
|
};
|
|
10937
|
-
installJSONServer(
|
|
10993
|
+
installJSONServer(configPath2, "servers", server);
|
|
10938
10994
|
}
|
|
10939
10995
|
},
|
|
10940
10996
|
remove(global) {
|
|
10941
10997
|
if (global) {
|
|
10942
10998
|
removeJSONServer(globalPath(), "mcpServers");
|
|
10943
10999
|
} else {
|
|
10944
|
-
const
|
|
10945
|
-
removeJSONServer(
|
|
11000
|
+
const configPath2 = join18(process.cwd(), ".vscode", "mcp.json");
|
|
11001
|
+
removeJSONServer(configPath2, "servers");
|
|
10946
11002
|
}
|
|
10947
11003
|
}
|
|
10948
11004
|
});
|
|
@@ -11077,17 +11133,17 @@ var MCPorterProvider = () => ({
|
|
|
11077
11133
|
globalConfigPath: () => resolveGlobalConfigPath(),
|
|
11078
11134
|
isConfigured: () => isJSONKeyConfigured(resolveGlobalConfigPath(), "mcpServers"),
|
|
11079
11135
|
install(cfg, global) {
|
|
11080
|
-
const
|
|
11136
|
+
const configPath2 = global ? resolveGlobalConfigPath() : join22(process.cwd(), "config", "mcporter.json");
|
|
11081
11137
|
const server = {
|
|
11082
11138
|
type: "http",
|
|
11083
11139
|
url: mcpEndpoint4(cfg),
|
|
11084
11140
|
headers: mcpHeaders(cfg.active_account.target.api_key)
|
|
11085
11141
|
};
|
|
11086
|
-
installJSONServer(
|
|
11142
|
+
installJSONServer(configPath2, "mcpServers", server);
|
|
11087
11143
|
},
|
|
11088
11144
|
remove(global) {
|
|
11089
|
-
const
|
|
11090
|
-
removeJSONServer(
|
|
11145
|
+
const configPath2 = global ? resolveGlobalConfigPath() : join22(process.cwd(), "config", "mcporter.json");
|
|
11146
|
+
removeJSONServer(configPath2, "mcpServers");
|
|
11091
11147
|
}
|
|
11092
11148
|
});
|
|
11093
11149
|
var init_mcporter = __esm(() => {
|
|
@@ -11236,7 +11292,7 @@ var init_providers = __esm(() => {
|
|
|
11236
11292
|
// src/rules/installer.ts
|
|
11237
11293
|
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync14, unlinkSync, writeFileSync as writeFileSync7 } from "node:fs";
|
|
11238
11294
|
import { homedir as homedir4 } from "node:os";
|
|
11239
|
-
import { dirname as
|
|
11295
|
+
import { dirname as dirname6, join as join27 } from "node:path";
|
|
11240
11296
|
function claudeConfigDir() {
|
|
11241
11297
|
return process.env.CLAUDE_CONFIG_DIR || join27(homedir4(), ".claude");
|
|
11242
11298
|
}
|
|
@@ -11267,7 +11323,7 @@ async function fetchDosuRule(fetchImpl = globalThis.fetch) {
|
|
|
11267
11323
|
return normalizeRule(FALLBACK_DOSU_RULE);
|
|
11268
11324
|
}
|
|
11269
11325
|
function ensureParent(path3) {
|
|
11270
|
-
mkdirSync8(
|
|
11326
|
+
mkdirSync8(dirname6(path3), { recursive: true });
|
|
11271
11327
|
}
|
|
11272
11328
|
function writeIfChanged(path3, content) {
|
|
11273
11329
|
if (!existsSync16(path3)) {
|
|
@@ -11370,6 +11426,8 @@ When \`read_knowledge\` is listed, call it before non-trivial code or documentat
|
|
|
11370
11426
|
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.**
|
|
11371
11427
|
|
|
11372
11428
|
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.
|
|
11429
|
+
|
|
11430
|
+
When \`read_knowledge\` or \`write_knowledge\` returned a \`receipt_item_id\` this turn, call \`finalize_session_knowledge\` exactly once at the end of the turn — after completing the task, immediately before your final reply — passing all receipt_item_ids from this turn. Never call it when the current turn produced no receipt_item_id, and never call it more than once per turn.
|
|
11373
11431
|
`, RULE_TARGETS, SUPPORTED_RULE_AGENT_IDS;
|
|
11374
11432
|
var init_installer = __esm(() => {
|
|
11375
11433
|
DOSU_RULE_SECTION_START = `<!-- dosu:rules:start v${DOSU_RULE_SECTION_VERSION} -->`;
|
|
@@ -12221,8 +12279,7 @@ __export(exports_flow2, {
|
|
|
12221
12279
|
stepDetectTools: () => stepDetectTools,
|
|
12222
12280
|
stepConfigureTools: () => stepConfigureTools,
|
|
12223
12281
|
runSetup: () => runSetup,
|
|
12224
|
-
runInstallSkill: () => runInstallSkill
|
|
12225
|
-
isStdioOnly: () => isStdioOnly
|
|
12282
|
+
runInstallSkill: () => runInstallSkill
|
|
12226
12283
|
});
|
|
12227
12284
|
import { randomUUID } from "node:crypto";
|
|
12228
12285
|
async function runSetup(opts = {}) {
|
|
@@ -12769,11 +12826,8 @@ ${dim("created")}`);
|
|
|
12769
12826
|
return null;
|
|
12770
12827
|
}
|
|
12771
12828
|
}
|
|
12772
|
-
function isStdioOnly(p2) {
|
|
12773
|
-
return p2.id() === "claude-desktop";
|
|
12774
|
-
}
|
|
12775
12829
|
function stepDetectTools() {
|
|
12776
|
-
return allSetupProviders().filter((p2) => p2.isInstalled()
|
|
12830
|
+
return allSetupProviders().filter((p2) => p2.isInstalled());
|
|
12777
12831
|
}
|
|
12778
12832
|
async function stepSelectTools(detected) {
|
|
12779
12833
|
const configuredMap = new Map;
|
|
@@ -12875,7 +12929,7 @@ var init_flow2 = __esm(() => {
|
|
|
12875
12929
|
|
|
12876
12930
|
// src/commands/insights.ts
|
|
12877
12931
|
import { existsSync as existsSync18, mkdirSync as mkdirSync9, readdirSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "node:fs";
|
|
12878
|
-
import { dirname as
|
|
12932
|
+
import { dirname as dirname7, join as join29 } from "node:path";
|
|
12879
12933
|
function shuffled(arr) {
|
|
12880
12934
|
const copy2 = [...arr];
|
|
12881
12935
|
for (let i2 = copy2.length - 1;i2 > 0; i2--) {
|
|
@@ -13073,7 +13127,7 @@ function defaultRunner(cfg) {
|
|
|
13073
13127
|
build: buildInsights,
|
|
13074
13128
|
render: renderHTML,
|
|
13075
13129
|
writeFile: (path3, content) => {
|
|
13076
|
-
const dir =
|
|
13130
|
+
const dir = dirname7(path3);
|
|
13077
13131
|
if (!existsSync18(dir))
|
|
13078
13132
|
mkdirSync9(dir, { recursive: true, mode: 448 });
|
|
13079
13133
|
writeFileSync9(path3, content, { mode: 384 });
|
|
@@ -13517,7 +13571,7 @@ __export(exports_flow3, {
|
|
|
13517
13571
|
async function runAgentSetup(opts) {
|
|
13518
13572
|
const provider = allSetupProviders().find((p2) => p2.id() === opts.tool.toLowerCase());
|
|
13519
13573
|
if (!provider) {
|
|
13520
|
-
const available = allSetupProviders().
|
|
13574
|
+
const available = allSetupProviders().map((p2) => p2.id()).join(", ");
|
|
13521
13575
|
emitError({
|
|
13522
13576
|
step: "setup",
|
|
13523
13577
|
reason: "unknown_tool",
|
|
@@ -13525,14 +13579,6 @@ async function runAgentSetup(opts) {
|
|
|
13525
13579
|
});
|
|
13526
13580
|
return 2;
|
|
13527
13581
|
}
|
|
13528
|
-
if (isStdioOnly(provider)) {
|
|
13529
|
-
emitError({
|
|
13530
|
-
step: "setup",
|
|
13531
|
-
reason: "tool_unsupported_in_agent_mode",
|
|
13532
|
-
agent_next_steps: `${provider.name()} is not supported by agent setup. Tell the user to run 'dosu mcp add ${provider.id()}' manually after signing in.`
|
|
13533
|
-
});
|
|
13534
|
-
return 2;
|
|
13535
|
-
}
|
|
13536
13582
|
let cfg = loadConfig();
|
|
13537
13583
|
if (opts.loginTicket) {
|
|
13538
13584
|
const redeemed = await redeemTicket(opts.loginTicket, cfg);
|
|
@@ -13860,7 +13906,7 @@ function buildResumeCommand(tool, ticket, deploymentID) {
|
|
|
13860
13906
|
return parts.join(" ");
|
|
13861
13907
|
}
|
|
13862
13908
|
function listAgentSupportedToolIDs() {
|
|
13863
|
-
return allSetupProviders().
|
|
13909
|
+
return allSetupProviders().map((p2) => p2.id());
|
|
13864
13910
|
}
|
|
13865
13911
|
var NPX_INVOCATION = "npx @dosu/cli@latest";
|
|
13866
13912
|
var init_flow3 = __esm(() => {
|
|
@@ -13872,7 +13918,6 @@ var init_flow3 = __esm(() => {
|
|
|
13872
13918
|
init_providers();
|
|
13873
13919
|
init_installer();
|
|
13874
13920
|
init_agents_md_step();
|
|
13875
|
-
init_flow2();
|
|
13876
13921
|
});
|
|
13877
13922
|
|
|
13878
13923
|
// src/cli/cli.ts
|