@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.
Files changed (3) hide show
  1. package/README.md +4 -1
  2. package/bin/dosu.js +92 -47
  3. 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
- Or tap first:
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.40.0", INSTALL_CHANNEL = "npm";
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: () => join14(appSupportDir(), "Claude", "claude_desktop_config.json"),
10740
- isConfigured: () => false,
10741
- install() {
10742
- throw new Error("this tool only supports local (stdio) servers and cannot be configured for remote MCP");
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
- throw new Error("this tool only supports local (stdio) servers and cannot be configured for remote MCP");
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 url = mcpEndpoint(cfg);
10831
- const headers = mcpHeaders(cfg.active_account?.target?.api_key);
10832
- const headerEntries = Object.entries(headers).map(([k, v]) => `${k} = "${v}"`).join(`
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
- type = "http"
10837
- url = "${url}"
10838
-
10839
- [mcp_servers.dosu.http_headers]
10840
- ${headerEntries}
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 configPath = join18(process.cwd(), ".vscode", "mcp.json");
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(configPath, "servers", server);
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 configPath = join18(process.cwd(), ".vscode", "mcp.json");
10945
- removeJSONServer(configPath, "servers");
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 configPath = global ? resolveGlobalConfigPath() : join22(process.cwd(), "config", "mcporter.json");
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(configPath, "mcpServers", server);
11142
+ installJSONServer(configPath2, "mcpServers", server);
11087
11143
  },
11088
11144
  remove(global) {
11089
- const configPath = global ? resolveGlobalConfigPath() : join22(process.cwd(), "config", "mcporter.json");
11090
- removeJSONServer(configPath, "mcpServers");
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 dirname5, join as join27 } from "node:path";
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(dirname5(path3), { recursive: true });
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() && !isStdioOnly(p2));
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 dirname6, join as join29 } from "node:path";
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 = dirname6(path3);
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().filter((p2) => !isStdioOnly(p2)).map((p2) => p2.id()).join(", ");
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().filter((p2) => !isStdioOnly(p2)).map((p2) => p2.id());
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosu/cli",
3
- "version": "0.40.0",
3
+ "version": "0.42.0",
4
4
  "type": "module",
5
5
  "description": "Dosu CLI - Manage MCP servers for AI tools",
6
6
  "license": "MIT",