@dosu/cli 0.19.3 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/dosu.js +281 -88
  2. package/package.json +1 -1
package/bin/dosu.js CHANGED
@@ -4548,7 +4548,7 @@ var init_dist4 = __esm(() => {
4548
4548
  function getVersionString() {
4549
4549
  return `v${VERSION}`;
4550
4550
  }
4551
- var VERSION = "0.19.3";
4551
+ var VERSION = "0.20.1";
4552
4552
 
4553
4553
  // src/debug/logger.ts
4554
4554
  import {
@@ -6473,6 +6473,121 @@ var init_codex = __esm(() => {
6473
6473
  };
6474
6474
  });
6475
6475
 
6476
+ // src/hooks/factory.ts
6477
+ var exports_factory = {};
6478
+ __export(exports_factory, {
6479
+ removeFactoryHooks: () => removeFactoryHooks,
6480
+ installFactoryHooks: () => installFactoryHooks,
6481
+ inspectFactoryHooks: () => inspectFactoryHooks,
6482
+ factoryHooksPath: () => factoryHooksPath,
6483
+ factoryHookCommand: () => factoryHookCommand
6484
+ });
6485
+ import { existsSync as existsSync7, readFileSync as readFileSync8 } from "node:fs";
6486
+ import { join as join6 } from "node:path";
6487
+ function factoryHooksPath(dir) {
6488
+ return join6(dir, ".factory", "hooks.json");
6489
+ }
6490
+ function factoryHookCommand(subcommand) {
6491
+ return `dosu hooks ${subcommand} --agent factory`;
6492
+ }
6493
+ function dosuGroup3(event) {
6494
+ const group = {
6495
+ hooks: [{ type: "command", command: factoryHookCommand(EVENT_SUBCOMMAND3[event]) }]
6496
+ };
6497
+ if (event === "PostToolUse")
6498
+ group.matcher = "*";
6499
+ return group;
6500
+ }
6501
+ function readHooksFileOrThrow2(path) {
6502
+ if (!existsSync7(path))
6503
+ return {};
6504
+ const raw = readFileSync8(path, "utf-8").trim();
6505
+ if (!raw)
6506
+ return {};
6507
+ try {
6508
+ return JSON.parse(raw);
6509
+ } catch {
6510
+ throw new Error(`refusing to modify ${path}: file exists but is not valid JSON`);
6511
+ }
6512
+ }
6513
+ function installFactoryHooks(configPath, opts = {}) {
6514
+ const config = readHooksFileOrThrow2(configPath);
6515
+ const events = DEFAULT_HOOK_EVENTS.filter((e2) => e2 !== "Stop" || opts.stop !== false);
6516
+ const hooks = typeof config.hooks === "object" && config.hooks ? config.hooks : {};
6517
+ for (const event of events) {
6518
+ const groups = Array.isArray(hooks[event]) ? hooks[event] : [];
6519
+ const kept = groups.filter((g2) => !isDosuGroup(g2));
6520
+ kept.push(dosuGroup3(event));
6521
+ hooks[event] = kept;
6522
+ }
6523
+ for (const event of DEFAULT_HOOK_EVENTS) {
6524
+ if (!events.includes(event) && Array.isArray(hooks[event])) {
6525
+ const kept = hooks[event].filter((g2) => !isDosuGroup(g2));
6526
+ if (kept.length > 0)
6527
+ hooks[event] = kept;
6528
+ else
6529
+ delete hooks[event];
6530
+ }
6531
+ }
6532
+ config.hooks = hooks;
6533
+ saveJSONConfig(configPath, config);
6534
+ return { events: [...events] };
6535
+ }
6536
+ function removeFactoryHooks(configPath) {
6537
+ if (!existsSync7(configPath))
6538
+ return { removed: false };
6539
+ let config;
6540
+ try {
6541
+ config = readHooksFileOrThrow2(configPath);
6542
+ } catch {
6543
+ return { removed: false };
6544
+ }
6545
+ const hooks = config.hooks;
6546
+ if (typeof hooks !== "object" || !hooks)
6547
+ return { removed: false };
6548
+ let removed = false;
6549
+ for (const event of Object.keys(hooks)) {
6550
+ if (!Array.isArray(hooks[event]))
6551
+ continue;
6552
+ const kept = hooks[event].filter((g2) => !isDosuGroup(g2));
6553
+ if (kept.length !== hooks[event].length) {
6554
+ removed = true;
6555
+ if (kept.length > 0)
6556
+ hooks[event] = kept;
6557
+ else
6558
+ delete hooks[event];
6559
+ }
6560
+ }
6561
+ if (removed)
6562
+ saveJSONConfig(configPath, config);
6563
+ return { removed };
6564
+ }
6565
+ function inspectFactoryHooks(configPath) {
6566
+ if (!existsSync7(configPath))
6567
+ return { fileExists: false, events: [] };
6568
+ let config;
6569
+ try {
6570
+ config = readHooksFileOrThrow2(configPath);
6571
+ } catch {
6572
+ return { fileExists: true, parseError: true, events: [] };
6573
+ }
6574
+ const hooks = config.hooks;
6575
+ if (typeof hooks !== "object" || !hooks)
6576
+ return { fileExists: true, events: [] };
6577
+ const events = Object.keys(hooks).filter((event) => Array.isArray(hooks[event]) && hooks[event].some((g2) => isDosuGroup(g2)));
6578
+ return { fileExists: true, events };
6579
+ }
6580
+ var EVENT_SUBCOMMAND3;
6581
+ var init_factory = __esm(() => {
6582
+ init_config_helpers();
6583
+ init_claude_code();
6584
+ EVENT_SUBCOMMAND3 = {
6585
+ UserPromptSubmit: "user-prompt-submit",
6586
+ PostToolUse: "post-tool-use",
6587
+ Stop: "stop"
6588
+ };
6589
+ });
6590
+
6476
6591
  // src/insights/insights.ts
6477
6592
  async function buildInsights({
6478
6593
  client,
@@ -7561,17 +7676,17 @@ var init_insights2 = __esm(() => {
7561
7676
  });
7562
7677
 
7563
7678
  // src/version/skill-update-check.ts
7564
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "node:fs";
7565
- import { join as join6 } from "node:path";
7679
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "node:fs";
7680
+ import { join as join7 } from "node:path";
7566
7681
  function getCachePath() {
7567
- return join6(getConfigDir(), CACHE_FILENAME);
7682
+ return join7(getConfigDir(), CACHE_FILENAME);
7568
7683
  }
7569
7684
  function readSkillCache() {
7570
7685
  try {
7571
7686
  const path = getCachePath();
7572
- if (!existsSync7(path))
7687
+ if (!existsSync8(path))
7573
7688
  return null;
7574
- const data = JSON.parse(readFileSync9(path, "utf-8"));
7689
+ const data = JSON.parse(readFileSync10(path, "utf-8"));
7575
7690
  if (typeof data.lastCheck === "number" && typeof data.latestSha === "string" && typeof data.installedSha === "string") {
7576
7691
  return data;
7577
7692
  }
@@ -7583,7 +7698,7 @@ function readSkillCache() {
7583
7698
  function writeSkillCache(cache) {
7584
7699
  try {
7585
7700
  const dir = getConfigDir();
7586
- if (!existsSync7(dir)) {
7701
+ if (!existsSync8(dir)) {
7587
7702
  mkdirSync5(dir, { recursive: true, mode: 448 });
7588
7703
  }
7589
7704
  writeFileSync4(getCachePath(), JSON.stringify(cache), { mode: 384 });
@@ -7760,21 +7875,21 @@ var init_skill = __esm(() => {
7760
7875
  var MCP_PROVIDER_SLUG = "dosu_mcp";
7761
7876
 
7762
7877
  // src/mcp/detect.ts
7763
- import { existsSync as existsSync8 } from "node:fs";
7878
+ import { existsSync as existsSync9 } from "node:fs";
7764
7879
  import { homedir, platform } from "node:os";
7765
- import { join as join7 } from "node:path";
7880
+ import { join as join8 } from "node:path";
7766
7881
  function isInstalled(paths) {
7767
- return paths.some((p2) => existsSync8(expandHome(p2)));
7882
+ return paths.some((p2) => existsSync9(expandHome(p2)));
7768
7883
  }
7769
7884
  function expandHome(path) {
7770
7885
  if (!path.startsWith("~"))
7771
7886
  return path;
7772
- return join7(homedir(), path.slice(1));
7887
+ return join8(homedir(), path.slice(1));
7773
7888
  }
7774
7889
  function appSupportDir() {
7775
7890
  switch (platform()) {
7776
7891
  case "darwin": {
7777
- return join7(homedir(), "Library", "Application Support");
7892
+ return join8(homedir(), "Library", "Application Support");
7778
7893
  }
7779
7894
  case "win32": {
7780
7895
  return process.env.APPDATA ?? "";
@@ -7783,7 +7898,7 @@ function appSupportDir() {
7783
7898
  const xdg = process.env.XDG_CONFIG_HOME;
7784
7899
  if (xdg)
7785
7900
  return xdg;
7786
- return join7(homedir(), ".config");
7901
+ return join8(homedir(), ".config");
7787
7902
  }
7788
7903
  }
7789
7904
  }
@@ -7864,7 +7979,7 @@ var init_antigravity = __esm(() => {
7864
7979
  });
7865
7980
 
7866
7981
  // src/mcp/providers/claude.ts
7867
- import { join as join8 } from "node:path";
7982
+ import { join as join9 } from "node:path";
7868
7983
  var ClaudeProvider = () => createJSONProvider({
7869
7984
  providerName: "Claude Code",
7870
7985
  providerID: "claude",
@@ -7873,22 +7988,22 @@ var ClaudeProvider = () => createJSONProvider({
7873
7988
  paths: ["~/.claude"],
7874
7989
  globalPath: "~/.claude.json",
7875
7990
  topKey: "mcpServers",
7876
- localConfigPath: (cwd) => join8(cwd, ".mcp.json")
7991
+ localConfigPath: (cwd) => join9(cwd, ".mcp.json")
7877
7992
  });
7878
7993
  var init_claude = __esm(() => {
7879
7994
  init_base();
7880
7995
  });
7881
7996
 
7882
7997
  // src/mcp/providers/claude-desktop.ts
7883
- import { join as join9 } from "node:path";
7998
+ import { join as join10 } from "node:path";
7884
7999
  var ClaudeDesktopProvider = () => ({
7885
8000
  name: () => "Claude Desktop",
7886
8001
  id: () => "claude-desktop",
7887
8002
  supportsLocal: () => false,
7888
8003
  priority: () => 2,
7889
- detectPaths: () => [join9(appSupportDir(), "Claude")],
7890
- isInstalled: () => isInstalled([join9(appSupportDir(), "Claude")]),
7891
- globalConfigPath: () => join9(appSupportDir(), "Claude", "claude_desktop_config.json"),
8004
+ detectPaths: () => [join10(appSupportDir(), "Claude")],
8005
+ isInstalled: () => isInstalled([join10(appSupportDir(), "Claude")]),
8006
+ globalConfigPath: () => join10(appSupportDir(), "Claude", "claude_desktop_config.json"),
7892
8007
  isConfigured: () => false,
7893
8008
  install() {
7894
8009
  throw new Error("this tool only supports local (stdio) servers and cannot be configured for remote MCP");
@@ -7902,14 +8017,14 @@ var init_claude_desktop = __esm(() => {
7902
8017
  });
7903
8018
 
7904
8019
  // src/mcp/providers/cline.ts
7905
- import { join as join10 } from "node:path";
7906
- var extensionDir = () => join10(appSupportDir(), "Code", "User", "globalStorage", "saoudrizwan.claude-dev"), ClineProvider = () => createJSONProvider({
8020
+ import { join as join11 } from "node:path";
8021
+ var extensionDir = () => join11(appSupportDir(), "Code", "User", "globalStorage", "saoudrizwan.claude-dev"), ClineProvider = () => createJSONProvider({
7907
8022
  providerName: "Cline",
7908
8023
  providerID: "cline",
7909
8024
  local: false,
7910
8025
  priorityValue: 11,
7911
8026
  paths: [extensionDir()],
7912
- globalPath: join10(extensionDir(), "settings", "cline_mcp_settings.json"),
8027
+ globalPath: join11(extensionDir(), "settings", "cline_mcp_settings.json"),
7913
8028
  topKey: "mcpServers",
7914
8029
  buildServer: (cfg) => ({
7915
8030
  url: mcpURL(cfg.deployment_id),
@@ -7925,7 +8040,7 @@ var init_cline = __esm(() => {
7925
8040
  });
7926
8041
 
7927
8042
  // src/mcp/providers/cline-cli.ts
7928
- import { join as join11 } from "node:path";
8043
+ import { join as join12 } from "node:path";
7929
8044
  function clineDir() {
7930
8045
  return process.env.CLINE_DIR ?? expandHome("~/.cline");
7931
8046
  }
@@ -7935,7 +8050,7 @@ var ClineCliProvider = () => createJSONProvider({
7935
8050
  local: false,
7936
8051
  priorityValue: 12,
7937
8052
  paths: [clineDir()],
7938
- globalPath: join11(clineDir(), "data", "settings", "cline_mcp_settings.json"),
8053
+ globalPath: join12(clineDir(), "data", "settings", "cline_mcp_settings.json"),
7939
8054
  topKey: "mcpServers",
7940
8055
  buildServer: (cfg) => ({
7941
8056
  url: mcpURL(cfg.deployment_id),
@@ -7951,20 +8066,20 @@ var init_cline_cli = __esm(() => {
7951
8066
  });
7952
8067
 
7953
8068
  // src/mcp/providers/codex.ts
7954
- import { existsSync as existsSync9, readFileSync as readFileSync10 } from "node:fs";
7955
- import { join as join12 } from "node:path";
8069
+ import { existsSync as existsSync10, readFileSync as readFileSync11 } from "node:fs";
8070
+ import { join as join13 } from "node:path";
7956
8071
  function codexHome() {
7957
8072
  return process.env.CODEX_HOME ?? expandHome("~/.codex");
7958
8073
  }
7959
8074
  function getConfigPath2(global) {
7960
8075
  if (global)
7961
- return join12(codexHome(), "config.toml");
7962
- return join12(process.cwd(), ".codex", "config.toml");
8076
+ return join13(codexHome(), "config.toml");
8077
+ return join13(process.cwd(), ".codex", "config.toml");
7963
8078
  }
7964
8079
  function readTOML(path) {
7965
- if (!existsSync9(path))
8080
+ if (!existsSync10(path))
7966
8081
  return "";
7967
- return readFileSync10(path, "utf-8");
8082
+ return readFileSync11(path, "utf-8");
7968
8083
  }
7969
8084
  function writeTOML(path, content) {
7970
8085
  writeSecureFile(path, content);
@@ -8022,9 +8137,9 @@ var CodexProvider = () => ({
8022
8137
  priority: () => 8,
8023
8138
  detectPaths: () => ["~/.codex"],
8024
8139
  isInstalled: () => isInstalled(["~/.codex"]),
8025
- globalConfigPath: () => join12(codexHome(), "config.toml"),
8140
+ globalConfigPath: () => join13(codexHome(), "config.toml"),
8026
8141
  isConfigured: () => {
8027
- const content = readTOML(join12(codexHome(), "config.toml"));
8142
+ const content = readTOML(join13(codexHome(), "config.toml"));
8028
8143
  return content.includes("[mcp_servers.dosu]");
8029
8144
  },
8030
8145
  install(cfg, global) {
@@ -8046,10 +8161,10 @@ var init_codex2 = __esm(() => {
8046
8161
  });
8047
8162
 
8048
8163
  // src/mcp/providers/copilot.ts
8049
- import { join as join13 } from "node:path";
8164
+ import { join as join14 } from "node:path";
8050
8165
  function globalPath() {
8051
8166
  if (process.env.XDG_CONFIG_HOME) {
8052
- return join13(process.env.XDG_CONFIG_HOME, "mcp-config.json");
8167
+ return join14(process.env.XDG_CONFIG_HOME, "mcp-config.json");
8053
8168
  }
8054
8169
  return expandHome("~/.copilot/mcp-config.json");
8055
8170
  }
@@ -8080,7 +8195,7 @@ var CopilotProvider = () => ({
8080
8195
  };
8081
8196
  installJSONServer(globalPath(), "mcpServers", server);
8082
8197
  } else {
8083
- const configPath = join13(process.cwd(), ".vscode", "mcp.json");
8198
+ const configPath = join14(process.cwd(), ".vscode", "mcp.json");
8084
8199
  const server = {
8085
8200
  type: "http",
8086
8201
  url,
@@ -8093,7 +8208,7 @@ var CopilotProvider = () => ({
8093
8208
  if (global) {
8094
8209
  removeJSONServer(globalPath(), "mcpServers");
8095
8210
  } else {
8096
- const configPath = join13(process.cwd(), ".vscode", "mcp.json");
8211
+ const configPath = join14(process.cwd(), ".vscode", "mcp.json");
8097
8212
  removeJSONServer(configPath, "servers");
8098
8213
  }
8099
8214
  }
@@ -8105,7 +8220,7 @@ var init_copilot = __esm(() => {
8105
8220
  });
8106
8221
 
8107
8222
  // src/mcp/providers/cursor.ts
8108
- import { join as join14 } from "node:path";
8223
+ import { join as join15 } from "node:path";
8109
8224
  var CursorProvider = () => createJSONProvider({
8110
8225
  providerName: "Cursor",
8111
8226
  providerID: "cursor",
@@ -8118,15 +8233,31 @@ var CursorProvider = () => createJSONProvider({
8118
8233
  url: mcpURL(cfg.deployment_id),
8119
8234
  headers: mcpHeaders(cfg.api_key)
8120
8235
  }),
8121
- localConfigPath: (cwd) => join14(cwd, ".cursor", "mcp.json")
8236
+ localConfigPath: (cwd) => join15(cwd, ".cursor", "mcp.json")
8122
8237
  });
8123
8238
  var init_cursor = __esm(() => {
8124
8239
  init_config_helpers();
8125
8240
  init_base();
8126
8241
  });
8127
8242
 
8243
+ // src/mcp/providers/factory.ts
8244
+ import { join as join16 } from "node:path";
8245
+ var FactoryProvider = () => createJSONProvider({
8246
+ providerName: "Factory",
8247
+ providerID: "factory",
8248
+ local: true,
8249
+ priorityValue: 17,
8250
+ paths: ["~/.factory"],
8251
+ globalPath: "~/.factory/mcp.json",
8252
+ topKey: "mcpServers",
8253
+ localConfigPath: (cwd) => join16(cwd, ".factory", "mcp.json")
8254
+ });
8255
+ var init_factory2 = __esm(() => {
8256
+ init_base();
8257
+ });
8258
+
8128
8259
  // src/mcp/providers/gemini.ts
8129
- import { join as join15 } from "node:path";
8260
+ import { join as join17 } from "node:path";
8130
8261
  var GeminiProvider = () => createJSONProvider({
8131
8262
  providerName: "Gemini CLI",
8132
8263
  providerID: "gemini",
@@ -8135,7 +8266,7 @@ var GeminiProvider = () => createJSONProvider({
8135
8266
  paths: ["~/.gemini"],
8136
8267
  globalPath: "~/.gemini/settings.json",
8137
8268
  topKey: "mcpServers",
8138
- localConfigPath: (cwd) => join15(cwd, ".gemini", "settings.json")
8269
+ localConfigPath: (cwd) => join17(cwd, ".gemini", "settings.json")
8139
8270
  });
8140
8271
  var init_gemini = __esm(() => {
8141
8272
  init_base();
@@ -8185,14 +8316,14 @@ var init_manual = __esm(() => {
8185
8316
  });
8186
8317
 
8187
8318
  // src/mcp/providers/mcporter.ts
8188
- import { existsSync as existsSync10 } from "node:fs";
8189
- import { join as join16 } from "node:path";
8319
+ import { existsSync as existsSync11 } from "node:fs";
8320
+ import { join as join18 } from "node:path";
8190
8321
  function resolveGlobalConfigPath() {
8191
8322
  const jsonPath = expandHome("~/.mcporter/mcporter.json");
8192
- if (existsSync10(jsonPath))
8323
+ if (existsSync11(jsonPath))
8193
8324
  return jsonPath;
8194
8325
  const jsoncPath = expandHome("~/.mcporter/mcporter.jsonc");
8195
- if (existsSync10(jsoncPath))
8326
+ if (existsSync11(jsoncPath))
8196
8327
  return jsoncPath;
8197
8328
  return jsonPath;
8198
8329
  }
@@ -8213,7 +8344,7 @@ var MCPorterProvider = () => ({
8213
8344
  globalConfigPath: () => resolveGlobalConfigPath(),
8214
8345
  isConfigured: () => isJSONKeyConfigured(resolveGlobalConfigPath(), "mcpServers"),
8215
8346
  install(cfg, global) {
8216
- const configPath = global ? resolveGlobalConfigPath() : join16(process.cwd(), "config", "mcporter.json");
8347
+ const configPath = global ? resolveGlobalConfigPath() : join18(process.cwd(), "config", "mcporter.json");
8217
8348
  const server = {
8218
8349
  type: "http",
8219
8350
  url: mcpEndpoint4(cfg),
@@ -8222,7 +8353,7 @@ var MCPorterProvider = () => ({
8222
8353
  installJSONServer(configPath, "mcpServers", server);
8223
8354
  },
8224
8355
  remove(global) {
8225
- const configPath = global ? resolveGlobalConfigPath() : join16(process.cwd(), "config", "mcporter.json");
8356
+ const configPath = global ? resolveGlobalConfigPath() : join18(process.cwd(), "config", "mcporter.json");
8226
8357
  removeJSONServer(configPath, "mcpServers");
8227
8358
  }
8228
8359
  });
@@ -8233,7 +8364,7 @@ var init_mcporter = __esm(() => {
8233
8364
  });
8234
8365
 
8235
8366
  // src/mcp/providers/opencode.ts
8236
- import { join as join17 } from "node:path";
8367
+ import { join as join19 } from "node:path";
8237
8368
  var OpenCodeProvider = () => createJSONProvider({
8238
8369
  providerName: "OpenCode",
8239
8370
  providerID: "opencode",
@@ -8248,7 +8379,7 @@ var OpenCodeProvider = () => createJSONProvider({
8248
8379
  enabled: true,
8249
8380
  headers: mcpHeaders(cfg.api_key)
8250
8381
  }),
8251
- localConfigPath: (cwd) => join17(cwd, "opencode.json")
8382
+ localConfigPath: (cwd) => join19(cwd, "opencode.json")
8252
8383
  });
8253
8384
  var init_opencode = __esm(() => {
8254
8385
  init_config_helpers();
@@ -8256,16 +8387,16 @@ var init_opencode = __esm(() => {
8256
8387
  });
8257
8388
 
8258
8389
  // src/mcp/providers/vscode.ts
8259
- import { join as join18 } from "node:path";
8390
+ import { join as join20 } from "node:path";
8260
8391
  var VSCodeProvider = () => createJSONProvider({
8261
8392
  providerName: "VS Code",
8262
8393
  providerID: "vscode",
8263
8394
  local: true,
8264
8395
  priorityValue: 6,
8265
- paths: [join18(appSupportDir(), "Code")],
8266
- globalPath: join18(appSupportDir(), "Code", "User", "mcp.json"),
8396
+ paths: [join20(appSupportDir(), "Code")],
8397
+ globalPath: join20(appSupportDir(), "Code", "User", "mcp.json"),
8267
8398
  topKey: "servers",
8268
- localConfigPath: (cwd) => join18(cwd, ".vscode", "mcp.json")
8399
+ localConfigPath: (cwd) => join20(cwd, ".vscode", "mcp.json")
8269
8400
  });
8270
8401
  var init_vscode = __esm(() => {
8271
8402
  init_detect();
@@ -8274,14 +8405,14 @@ var init_vscode = __esm(() => {
8274
8405
 
8275
8406
  // src/mcp/providers/windsurf.ts
8276
8407
  import { homedir as homedir2 } from "node:os";
8277
- import { join as join19 } from "node:path";
8408
+ import { join as join21 } from "node:path";
8278
8409
  var WindsurfProvider = () => createJSONProvider({
8279
8410
  providerName: "Windsurf",
8280
8411
  providerID: "windsurf",
8281
8412
  local: false,
8282
8413
  priorityValue: 9,
8283
- paths: [join19(homedir2(), ".codeium", "windsurf")],
8284
- globalPath: join19(homedir2(), ".codeium", "windsurf", "mcp_config.json"),
8414
+ paths: [join21(homedir2(), ".codeium", "windsurf")],
8415
+ globalPath: join21(homedir2(), ".codeium", "windsurf", "mcp_config.json"),
8285
8416
  topKey: "mcpServers"
8286
8417
  });
8287
8418
  var init_windsurf = __esm(() => {
@@ -8290,12 +8421,12 @@ var init_windsurf = __esm(() => {
8290
8421
 
8291
8422
  // src/mcp/providers/zed.ts
8292
8423
  import { platform as platform2 } from "node:os";
8293
- import { join as join20 } from "node:path";
8424
+ import { join as join22 } from "node:path";
8294
8425
  function zedConfigDir() {
8295
8426
  const os = platform2();
8296
8427
  if (os === "darwin" || os === "win32")
8297
- return join20(appSupportDir(), "Zed");
8298
- return join20(appSupportDir(), "zed");
8428
+ return join22(appSupportDir(), "Zed");
8429
+ return join22(appSupportDir(), "zed");
8299
8430
  }
8300
8431
  var ZedProvider = () => createJSONProvider({
8301
8432
  providerName: "Zed",
@@ -8303,7 +8434,7 @@ var ZedProvider = () => createJSONProvider({
8303
8434
  local: true,
8304
8435
  priorityValue: 10,
8305
8436
  paths: [zedConfigDir()],
8306
- globalPath: join20(zedConfigDir(), "settings.json"),
8437
+ globalPath: join22(zedConfigDir(), "settings.json"),
8307
8438
  topKey: "context_servers",
8308
8439
  buildServer: (cfg) => ({
8309
8440
  source: "custom",
@@ -8311,7 +8442,7 @@ var ZedProvider = () => createJSONProvider({
8311
8442
  url: mcpURL(cfg.deployment_id),
8312
8443
  headers: mcpHeaders(cfg.api_key)
8313
8444
  }),
8314
- localConfigPath: (cwd) => join20(cwd, ".zed", "settings.json")
8445
+ localConfigPath: (cwd) => join22(cwd, ".zed", "settings.json")
8315
8446
  });
8316
8447
  var init_zed = __esm(() => {
8317
8448
  init_config_helpers();
@@ -8336,6 +8467,7 @@ function allProviders() {
8336
8467
  OpenCodeProvider(),
8337
8468
  AntigravityProvider(),
8338
8469
  MCPorterProvider(),
8470
+ FactoryProvider(),
8339
8471
  ManualProvider()
8340
8472
  ];
8341
8473
  }
@@ -8358,6 +8490,7 @@ var init_providers = __esm(() => {
8358
8490
  init_codex2();
8359
8491
  init_copilot();
8360
8492
  init_cursor();
8493
+ init_factory2();
8361
8494
  init_gemini();
8362
8495
  init_manual();
8363
8496
  init_mcporter();
@@ -10859,8 +10992,22 @@ async function startOAuthFlow(signal, path2 = "/cli/auth", params = {}) {
10859
10992
  const authURL = buildAuthURL(callbackURL, path2, params);
10860
10993
  logger.info("auth.flow", `Auth URL: ${authURL}`);
10861
10994
  const open2 = await Promise.resolve().then(() => (init_open(), exports_open));
10862
- await open2.default(authURL);
10863
- logger.info("auth.flow", "Browser open command executed");
10995
+ let browserOpened = false;
10996
+ try {
10997
+ await open2.default(authURL);
10998
+ browserOpened = true;
10999
+ logger.info("auth.flow", "Browser open command executed");
11000
+ } catch (openErr) {
11001
+ logger.warn("auth.flow", `Could not open browser automatically: ${openErr instanceof Error ? openErr.message : String(openErr)}`);
11002
+ }
11003
+ if (!browserOpened) {
11004
+ console.log(`
11005
+ Could not open a browser automatically.`);
11006
+ console.log(`Please open this URL in your browser to log in:
11007
+ `);
11008
+ console.log(` ${authURL}
11009
+ `);
11010
+ }
10864
11011
  const timeout = new Promise((_3, reject) => {
10865
11012
  timeoutId = setTimeout(() => {
10866
11013
  logger.warn("auth.flow", "Authentication timed out (8min)");
@@ -10904,8 +11051,8 @@ __export(exports_flow2, {
10904
11051
  isStdioOnly: () => isStdioOnly
10905
11052
  });
10906
11053
  import { randomUUID } from "node:crypto";
10907
- import { existsSync as existsSync11 } from "node:fs";
10908
- import { join as join21 } from "node:path";
11054
+ import { existsSync as existsSync12 } from "node:fs";
11055
+ import { join as join23 } from "node:path";
10909
11056
  async function runSetup(opts = {}) {
10910
11057
  const onboardingRunID = randomUUID();
10911
11058
  logger.info("setup", `Setup flow started${opts.deploymentID ? ` deployment=${opts.deploymentID}` : ""}${opts.mode ? ` mode=${opts.mode}` : ""}`);
@@ -11077,7 +11224,7 @@ async function runSetup(opts = {}) {
11077
11224
  showTryItOutPrompt({
11078
11225
  mode: cfg.mode,
11079
11226
  docsImported: choices.connectGitHub && githubOnboardingDone,
11080
- hasAgentsMd: existsSync11(join21(process.cwd(), "AGENTS.md"))
11227
+ hasAgentsMd: existsSync12(join23(process.cwd(), "AGENTS.md"))
11081
11228
  });
11082
11229
  }
11083
11230
  if (mcpCompleted || skillCompleted || docsImported) {
@@ -11571,8 +11718,8 @@ var init_flow2 = __esm(() => {
11571
11718
  });
11572
11719
 
11573
11720
  // src/commands/insights.ts
11574
- import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync, unlinkSync, writeFileSync as writeFileSync5 } from "node:fs";
11575
- import { dirname as dirname3, join as join22 } from "node:path";
11721
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, readdirSync, unlinkSync, writeFileSync as writeFileSync5 } from "node:fs";
11722
+ import { dirname as dirname3, join as join24 } from "node:path";
11576
11723
  function shuffled(arr) {
11577
11724
  const copy2 = [...arr];
11578
11725
  for (let i = copy2.length - 1;i > 0; i--) {
@@ -11676,17 +11823,17 @@ function makeAskFn(cfg) {
11676
11823
  };
11677
11824
  }
11678
11825
  function insightsDir() {
11679
- return join22(getConfigDir(), "insights");
11826
+ return join24(getConfigDir(), "insights");
11680
11827
  }
11681
11828
  function reportPath(timestamp) {
11682
11829
  const dir = insightsDir();
11683
11830
  if (!timestamp)
11684
- return join22(dir, "latest.html");
11831
+ return join24(dir, "latest.html");
11685
11832
  const iso = timestamp.toISOString().slice(0, 19).replace(/:/g, "-");
11686
- return join22(dir, `report-${iso}Z.html`);
11833
+ return join24(dir, `report-${iso}Z.html`);
11687
11834
  }
11688
11835
  function pruneOldReports(dir, keepN) {
11689
- if (!existsSync12(dir))
11836
+ if (!existsSync13(dir))
11690
11837
  return;
11691
11838
  let entries;
11692
11839
  try {
@@ -11698,7 +11845,7 @@ function pruneOldReports(dir, keepN) {
11698
11845
  const reports = entries.filter((f) => REPORT_FILE_PATTERN.test(f)).sort().reverse();
11699
11846
  for (const f of reports.slice(keepN)) {
11700
11847
  try {
11701
- unlinkSync(join22(dir, f));
11848
+ unlinkSync(join24(dir, f));
11702
11849
  } catch (err) {
11703
11850
  logger.debug("insights", `failed to prune ${f}: ${err instanceof Error ? err.message : err}`);
11704
11851
  }
@@ -11771,7 +11918,7 @@ function defaultRunner(cfg) {
11771
11918
  render: renderHTML,
11772
11919
  writeFile: (path2, content) => {
11773
11920
  const dir = dirname3(path2);
11774
- if (!existsSync12(dir))
11921
+ if (!existsSync13(dir))
11775
11922
  mkdirSync6(dir, { recursive: true, mode: 448 });
11776
11923
  writeFileSync5(path2, content, { mode: 384 });
11777
11924
  },
@@ -12436,7 +12583,7 @@ var init_flow3 = __esm(() => {
12436
12583
  // src/cli/cli.ts
12437
12584
  init_esm();
12438
12585
  init_client();
12439
- import { readFileSync as readFileSync12, unlinkSync as unlinkSync2 } from "node:fs";
12586
+ import { readFileSync as readFileSync13, unlinkSync as unlinkSync2 } from "node:fs";
12440
12587
 
12441
12588
  // src/commands/analytics.ts
12442
12589
  init_esm();
@@ -13122,7 +13269,7 @@ init_esm();
13122
13269
  init_config();
13123
13270
  init_logger();
13124
13271
  init_prompts();
13125
- import { readFileSync as readFileSync8 } from "node:fs";
13272
+ import { readFileSync as readFileSync9 } from "node:fs";
13126
13273
  import { basename } from "node:path";
13127
13274
 
13128
13275
  // src/hooks/state.ts
@@ -13161,7 +13308,7 @@ var COOLDOWN_DEFAULT_MS = 3000;
13161
13308
  var TTL_DEFAULT_MS = 10 * 60 * 1000;
13162
13309
  var STOP_WAIT_DEFAULT_MS = 8000;
13163
13310
  var STOP_POLL_DEFAULT_MS = 1000;
13164
- var SUPPORTED_AGENTS = ["claude-code", "codex"];
13311
+ var SUPPORTED_AGENTS = ["claude-code", "codex", "factory"];
13165
13312
  var DEFAULT_AGENT = "claude-code";
13166
13313
  function cooldownMs() {
13167
13314
  const n = Number.parseInt(process.env.DOSU_HOOK_CHECK_COOLDOWN_MS ?? "", 10);
@@ -13391,7 +13538,7 @@ async function runHookEntrypoint(event, raw, now = Date.now(), agent = DEFAULT_A
13391
13538
  }
13392
13539
  function readStdinRaw() {
13393
13540
  try {
13394
- return readFileSync8(0, "utf8");
13541
+ return readFileSync9(0, "utf8");
13395
13542
  } catch {
13396
13543
  return "";
13397
13544
  }
@@ -13439,6 +13586,20 @@ async function runInstall(agent, opts) {
13439
13586
  }
13440
13587
  return;
13441
13588
  }
13589
+ if (agent === "factory") {
13590
+ const { installFactoryHooks: installFactoryHooks2, factoryHooksPath: factoryHooksPath2 } = await Promise.resolve().then(() => (init_factory(), exports_factory));
13591
+ const configPath2 = factoryHooksPath2(resolveDir(opts.dir));
13592
+ const { events: events2 } = installFactoryHooks2(configPath2, { stop: opts.stop });
13593
+ if (opts.json) {
13594
+ const { emitStep: emitStep2 } = await Promise.resolve().then(() => exports_output);
13595
+ emitStep2({ step: "hooks-install", agent, path: configPath2, events: events2 });
13596
+ } else {
13597
+ console.log(`✓ Installed Dosu hooks for Factory (${events2.join(", ")}).`);
13598
+ console.log(` → ${configPath2}`);
13599
+ console.log(" Start a new Factory session in this project to use them.");
13600
+ }
13601
+ return;
13602
+ }
13442
13603
  const { installClaudeHooks: installClaudeHooks2, claudeLocalSettingsPath: claudeLocalSettingsPath2 } = await Promise.resolve().then(() => (init_claude_code(), exports_claude_code));
13443
13604
  const configPath = claudeLocalSettingsPath2(resolveDir(opts.dir));
13444
13605
  const { events } = installClaudeHooks2(configPath, { stop: opts.stop });
@@ -13476,6 +13637,10 @@ async function runUninstall(agent, opts) {
13476
13637
  const { removeCodexHooks: removeCodexHooks2, codexHooksPath: codexHooksPath2 } = await Promise.resolve().then(() => (init_codex(), exports_codex));
13477
13638
  configPath = codexHooksPath2(resolveDir(opts.dir));
13478
13639
  ({ removed } = removeCodexHooks2(configPath));
13640
+ } else if (agent === "factory") {
13641
+ const { removeFactoryHooks: removeFactoryHooks2, factoryHooksPath: factoryHooksPath2 } = await Promise.resolve().then(() => (init_factory(), exports_factory));
13642
+ configPath = factoryHooksPath2(resolveDir(opts.dir));
13643
+ ({ removed } = removeFactoryHooks2(configPath));
13479
13644
  } else {
13480
13645
  const { removeClaudeHooks: removeClaudeHooks2, claudeLocalSettingsPath: claudeLocalSettingsPath2 } = await Promise.resolve().then(() => (init_claude_code(), exports_claude_code));
13481
13646
  configPath = claudeLocalSettingsPath2(resolveDir(opts.dir));
@@ -13494,15 +13659,20 @@ async function collectDoctorChecks(opts) {
13494
13659
  const checks = [];
13495
13660
  const { claudeLocalSettingsPath: claudeLocalSettingsPath2, inspectClaudeHooks: inspectClaudeHooks2 } = await Promise.resolve().then(() => (init_claude_code(), exports_claude_code));
13496
13661
  const { codexHooksPath: codexHooksPath2, inspectCodexHooks: inspectCodexHooks2 } = await Promise.resolve().then(() => (init_codex(), exports_codex));
13662
+ const { factoryHooksPath: factoryHooksPath2, inspectFactoryHooks: inspectFactoryHooks2 } = await Promise.resolve().then(() => (init_factory(), exports_factory));
13497
13663
  const configPath = claudeLocalSettingsPath2(resolveDir(opts.dir));
13498
13664
  const inspection = inspectClaudeHooks2(configPath);
13499
13665
  const codexPath = codexHooksPath2(resolveDir(opts.dir));
13500
13666
  const codex = inspectCodexHooks2(codexPath);
13667
+ const factoryPath = factoryHooksPath2(resolveDir(opts.dir));
13668
+ const factory = inspectFactoryHooks2(factoryPath);
13501
13669
  const codexInstalled = codex.events.includes("UserPromptSubmit") && codex.events.includes("PostToolUse");
13670
+ const factoryInstalled = factory.events.includes("UserPromptSubmit") && factory.events.includes("PostToolUse");
13671
+ const alternativeInstalled = codexInstalled || factoryInstalled;
13502
13672
  if (!inspection.fileExists) {
13503
13673
  checks.push({
13504
13674
  name: "config",
13505
- status: codexInstalled ? "warn" : "fail",
13675
+ status: alternativeInstalled ? "warn" : "fail",
13506
13676
  detail: `not found: ${configPath} (run 'dosu hooks install claude-code')`
13507
13677
  });
13508
13678
  } else if (inspection.parseError) {
@@ -13521,7 +13691,7 @@ async function collectDoctorChecks(opts) {
13521
13691
  } else {
13522
13692
  checks.push({
13523
13693
  name: "hooks",
13524
- status: codexInstalled ? "warn" : "fail",
13694
+ status: alternativeInstalled ? "warn" : "fail",
13525
13695
  detail: "UserPromptSubmit + PostToolUse not both installed"
13526
13696
  });
13527
13697
  }
@@ -13547,6 +13717,27 @@ async function collectDoctorChecks(opts) {
13547
13717
  });
13548
13718
  }
13549
13719
  }
13720
+ if (factory.fileExists) {
13721
+ if (factory.parseError) {
13722
+ checks.push({
13723
+ name: "factory-config",
13724
+ status: "fail",
13725
+ detail: `invalid JSON: ${factoryPath}`
13726
+ });
13727
+ } else if (factoryInstalled) {
13728
+ checks.push({
13729
+ name: "factory-config",
13730
+ status: "ok",
13731
+ detail: `installed: ${factory.events.join(", ")} (${factoryPath})`
13732
+ });
13733
+ } else {
13734
+ checks.push({
13735
+ name: "factory-config",
13736
+ status: "fail",
13737
+ detail: "UserPromptSubmit + PostToolUse not both installed"
13738
+ });
13739
+ }
13740
+ }
13550
13741
  const cfg = loadConfig();
13551
13742
  if (isAuthenticated(cfg)) {
13552
13743
  checks.push({ name: "auth", status: "ok", detail: "logged in" });
@@ -13601,7 +13792,7 @@ async function runDoctor(opts) {
13601
13792
  }
13602
13793
  }
13603
13794
  function hooksCommand() {
13604
- const cmd = new Command("hooks").description("Dosu knowledge-injection hooks for coding agents (Claude Code, Codex)");
13795
+ const cmd = new Command("hooks").description("Dosu knowledge-injection hooks for coding agents (Claude Code, Codex, Factory)");
13605
13796
  cmd.command("user-prompt-submit").description("Hook entrypoint: create a knowledge ticket on prompt submit").option("--agent <agent>", "Calling agent for ticket attribution", DEFAULT_AGENT).action(async (opts) => {
13606
13797
  await runHookEntrypoint("user-prompt-submit", readStdinRaw(), Date.now(), opts.agent);
13607
13798
  });
@@ -13628,6 +13819,7 @@ function hooksCommand() {
13628
13819
  "Examples:",
13629
13820
  " $ dosu hooks install claude-code",
13630
13821
  " $ dosu hooks install codex",
13822
+ " $ dosu hooks install factory",
13631
13823
  " $ dosu hooks install claude-code --no-stop",
13632
13824
  " $ dosu hooks install codex --dir ./my-project",
13633
13825
  "",
@@ -13641,7 +13833,8 @@ function hooksCommand() {
13641
13833
  "",
13642
13834
  "Examples:",
13643
13835
  " $ dosu hooks uninstall claude-code",
13644
- " $ dosu hooks uninstall codex"
13836
+ " $ dosu hooks uninstall codex",
13837
+ " $ dosu hooks uninstall factory"
13645
13838
  ].join(`
13646
13839
  `)).action((agent, opts) => runUninstall(agent, opts));
13647
13840
  cmd.command("doctor").description("Diagnose Dosu hook config, auth, deployment, and backend connectivity").option("--dir <path>", "Project root (defaults to current directory)").option("--json", "Emit machine-readable JSON", false).action((opts) => runDoctor(opts));
@@ -14477,8 +14670,8 @@ init_skill_update_check();
14477
14670
  init_config();
14478
14671
  init_logger();
14479
14672
  var import_picocolors24 = __toESM(require_picocolors(), 1);
14480
- import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
14481
- import { join as join23 } from "node:path";
14673
+ import { existsSync as existsSync14, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "node:fs";
14674
+ import { join as join25 } from "node:path";
14482
14675
  var CACHE_FILENAME2 = "update-check.json";
14483
14676
  var CHECK_INTERVAL_MS2 = 24 * 60 * 60 * 1000;
14484
14677
  var FETCH_TIMEOUT_MS2 = 5000;
@@ -14501,14 +14694,14 @@ function isNewerVersion(latest, current) {
14501
14694
  return false;
14502
14695
  }
14503
14696
  function getCachePath2() {
14504
- return join23(getConfigDir(), CACHE_FILENAME2);
14697
+ return join25(getConfigDir(), CACHE_FILENAME2);
14505
14698
  }
14506
14699
  function readCache() {
14507
14700
  try {
14508
14701
  const path2 = getCachePath2();
14509
- if (!existsSync13(path2))
14702
+ if (!existsSync14(path2))
14510
14703
  return null;
14511
- const data = JSON.parse(readFileSync11(path2, "utf-8"));
14704
+ const data = JSON.parse(readFileSync12(path2, "utf-8"));
14512
14705
  if (typeof data.lastCheck === "number" && typeof data.latestVersion === "string") {
14513
14706
  return data;
14514
14707
  }
@@ -14520,7 +14713,7 @@ function readCache() {
14520
14713
  function writeCache(cache) {
14521
14714
  try {
14522
14715
  const dir = getConfigDir();
14523
- if (!existsSync13(dir)) {
14716
+ if (!existsSync14(dir)) {
14524
14717
  mkdirSync7(dir, { recursive: true, mode: 448 });
14525
14718
  }
14526
14719
  writeFileSync6(getCachePath2(), JSON.stringify(cache), { mode: 384 });
@@ -14810,7 +15003,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
14810
15003
  if (opts.tail !== undefined) {
14811
15004
  const n = typeof opts.tail === "string" ? parseInt(opts.tail, 10) || 50 : 50;
14812
15005
  try {
14813
- const content = readFileSync12(logPath, "utf-8");
15006
+ const content = readFileSync13(logPath, "utf-8");
14814
15007
  const lines = content.split(`
14815
15008
  `);
14816
15009
  console.log(lines.slice(-n).join(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosu/cli",
3
- "version": "0.19.3",
3
+ "version": "0.20.1",
4
4
  "type": "module",
5
5
  "description": "Dosu CLI - Manage MCP servers for AI tools",
6
6
  "license": "MIT",