@dosu/cli 0.41.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 +90 -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.41.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)) {
@@ -12223,8 +12279,7 @@ __export(exports_flow2, {
12223
12279
  stepDetectTools: () => stepDetectTools,
12224
12280
  stepConfigureTools: () => stepConfigureTools,
12225
12281
  runSetup: () => runSetup,
12226
- runInstallSkill: () => runInstallSkill,
12227
- isStdioOnly: () => isStdioOnly
12282
+ runInstallSkill: () => runInstallSkill
12228
12283
  });
12229
12284
  import { randomUUID } from "node:crypto";
12230
12285
  async function runSetup(opts = {}) {
@@ -12771,11 +12826,8 @@ ${dim("created")}`);
12771
12826
  return null;
12772
12827
  }
12773
12828
  }
12774
- function isStdioOnly(p2) {
12775
- return p2.id() === "claude-desktop";
12776
- }
12777
12829
  function stepDetectTools() {
12778
- return allSetupProviders().filter((p2) => p2.isInstalled() && !isStdioOnly(p2));
12830
+ return allSetupProviders().filter((p2) => p2.isInstalled());
12779
12831
  }
12780
12832
  async function stepSelectTools(detected) {
12781
12833
  const configuredMap = new Map;
@@ -12877,7 +12929,7 @@ var init_flow2 = __esm(() => {
12877
12929
 
12878
12930
  // src/commands/insights.ts
12879
12931
  import { existsSync as existsSync18, mkdirSync as mkdirSync9, readdirSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "node:fs";
12880
- import { dirname as dirname6, join as join29 } from "node:path";
12932
+ import { dirname as dirname7, join as join29 } from "node:path";
12881
12933
  function shuffled(arr) {
12882
12934
  const copy2 = [...arr];
12883
12935
  for (let i2 = copy2.length - 1;i2 > 0; i2--) {
@@ -13075,7 +13127,7 @@ function defaultRunner(cfg) {
13075
13127
  build: buildInsights,
13076
13128
  render: renderHTML,
13077
13129
  writeFile: (path3, content) => {
13078
- const dir = dirname6(path3);
13130
+ const dir = dirname7(path3);
13079
13131
  if (!existsSync18(dir))
13080
13132
  mkdirSync9(dir, { recursive: true, mode: 448 });
13081
13133
  writeFileSync9(path3, content, { mode: 384 });
@@ -13519,7 +13571,7 @@ __export(exports_flow3, {
13519
13571
  async function runAgentSetup(opts) {
13520
13572
  const provider = allSetupProviders().find((p2) => p2.id() === opts.tool.toLowerCase());
13521
13573
  if (!provider) {
13522
- const available = allSetupProviders().filter((p2) => !isStdioOnly(p2)).map((p2) => p2.id()).join(", ");
13574
+ const available = allSetupProviders().map((p2) => p2.id()).join(", ");
13523
13575
  emitError({
13524
13576
  step: "setup",
13525
13577
  reason: "unknown_tool",
@@ -13527,14 +13579,6 @@ async function runAgentSetup(opts) {
13527
13579
  });
13528
13580
  return 2;
13529
13581
  }
13530
- if (isStdioOnly(provider)) {
13531
- emitError({
13532
- step: "setup",
13533
- reason: "tool_unsupported_in_agent_mode",
13534
- 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.`
13535
- });
13536
- return 2;
13537
- }
13538
13582
  let cfg = loadConfig();
13539
13583
  if (opts.loginTicket) {
13540
13584
  const redeemed = await redeemTicket(opts.loginTicket, cfg);
@@ -13862,7 +13906,7 @@ function buildResumeCommand(tool, ticket, deploymentID) {
13862
13906
  return parts.join(" ");
13863
13907
  }
13864
13908
  function listAgentSupportedToolIDs() {
13865
- return allSetupProviders().filter((p2) => !isStdioOnly(p2)).map((p2) => p2.id());
13909
+ return allSetupProviders().map((p2) => p2.id());
13866
13910
  }
13867
13911
  var NPX_INVOCATION = "npx @dosu/cli@latest";
13868
13912
  var init_flow3 = __esm(() => {
@@ -13874,7 +13918,6 @@ var init_flow3 = __esm(() => {
13874
13918
  init_providers();
13875
13919
  init_installer();
13876
13920
  init_agents_md_step();
13877
- init_flow2();
13878
13921
  });
13879
13922
 
13880
13923
  // src/cli/cli.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosu/cli",
3
- "version": "0.41.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",