@dosu/cli 0.19.3 → 0.20.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 (2) hide show
  1. package/bin/dosu.js +265 -86
  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.0";
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();
@@ -10904,8 +11037,8 @@ __export(exports_flow2, {
10904
11037
  isStdioOnly: () => isStdioOnly
10905
11038
  });
10906
11039
  import { randomUUID } from "node:crypto";
10907
- import { existsSync as existsSync11 } from "node:fs";
10908
- import { join as join21 } from "node:path";
11040
+ import { existsSync as existsSync12 } from "node:fs";
11041
+ import { join as join23 } from "node:path";
10909
11042
  async function runSetup(opts = {}) {
10910
11043
  const onboardingRunID = randomUUID();
10911
11044
  logger.info("setup", `Setup flow started${opts.deploymentID ? ` deployment=${opts.deploymentID}` : ""}${opts.mode ? ` mode=${opts.mode}` : ""}`);
@@ -11077,7 +11210,7 @@ async function runSetup(opts = {}) {
11077
11210
  showTryItOutPrompt({
11078
11211
  mode: cfg.mode,
11079
11212
  docsImported: choices.connectGitHub && githubOnboardingDone,
11080
- hasAgentsMd: existsSync11(join21(process.cwd(), "AGENTS.md"))
11213
+ hasAgentsMd: existsSync12(join23(process.cwd(), "AGENTS.md"))
11081
11214
  });
11082
11215
  }
11083
11216
  if (mcpCompleted || skillCompleted || docsImported) {
@@ -11571,8 +11704,8 @@ var init_flow2 = __esm(() => {
11571
11704
  });
11572
11705
 
11573
11706
  // 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";
11707
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, readdirSync, unlinkSync, writeFileSync as writeFileSync5 } from "node:fs";
11708
+ import { dirname as dirname3, join as join24 } from "node:path";
11576
11709
  function shuffled(arr) {
11577
11710
  const copy2 = [...arr];
11578
11711
  for (let i = copy2.length - 1;i > 0; i--) {
@@ -11676,17 +11809,17 @@ function makeAskFn(cfg) {
11676
11809
  };
11677
11810
  }
11678
11811
  function insightsDir() {
11679
- return join22(getConfigDir(), "insights");
11812
+ return join24(getConfigDir(), "insights");
11680
11813
  }
11681
11814
  function reportPath(timestamp) {
11682
11815
  const dir = insightsDir();
11683
11816
  if (!timestamp)
11684
- return join22(dir, "latest.html");
11817
+ return join24(dir, "latest.html");
11685
11818
  const iso = timestamp.toISOString().slice(0, 19).replace(/:/g, "-");
11686
- return join22(dir, `report-${iso}Z.html`);
11819
+ return join24(dir, `report-${iso}Z.html`);
11687
11820
  }
11688
11821
  function pruneOldReports(dir, keepN) {
11689
- if (!existsSync12(dir))
11822
+ if (!existsSync13(dir))
11690
11823
  return;
11691
11824
  let entries;
11692
11825
  try {
@@ -11698,7 +11831,7 @@ function pruneOldReports(dir, keepN) {
11698
11831
  const reports = entries.filter((f) => REPORT_FILE_PATTERN.test(f)).sort().reverse();
11699
11832
  for (const f of reports.slice(keepN)) {
11700
11833
  try {
11701
- unlinkSync(join22(dir, f));
11834
+ unlinkSync(join24(dir, f));
11702
11835
  } catch (err) {
11703
11836
  logger.debug("insights", `failed to prune ${f}: ${err instanceof Error ? err.message : err}`);
11704
11837
  }
@@ -11771,7 +11904,7 @@ function defaultRunner(cfg) {
11771
11904
  render: renderHTML,
11772
11905
  writeFile: (path2, content) => {
11773
11906
  const dir = dirname3(path2);
11774
- if (!existsSync12(dir))
11907
+ if (!existsSync13(dir))
11775
11908
  mkdirSync6(dir, { recursive: true, mode: 448 });
11776
11909
  writeFileSync5(path2, content, { mode: 384 });
11777
11910
  },
@@ -12436,7 +12569,7 @@ var init_flow3 = __esm(() => {
12436
12569
  // src/cli/cli.ts
12437
12570
  init_esm();
12438
12571
  init_client();
12439
- import { readFileSync as readFileSync12, unlinkSync as unlinkSync2 } from "node:fs";
12572
+ import { readFileSync as readFileSync13, unlinkSync as unlinkSync2 } from "node:fs";
12440
12573
 
12441
12574
  // src/commands/analytics.ts
12442
12575
  init_esm();
@@ -13122,7 +13255,7 @@ init_esm();
13122
13255
  init_config();
13123
13256
  init_logger();
13124
13257
  init_prompts();
13125
- import { readFileSync as readFileSync8 } from "node:fs";
13258
+ import { readFileSync as readFileSync9 } from "node:fs";
13126
13259
  import { basename } from "node:path";
13127
13260
 
13128
13261
  // src/hooks/state.ts
@@ -13161,7 +13294,7 @@ var COOLDOWN_DEFAULT_MS = 3000;
13161
13294
  var TTL_DEFAULT_MS = 10 * 60 * 1000;
13162
13295
  var STOP_WAIT_DEFAULT_MS = 8000;
13163
13296
  var STOP_POLL_DEFAULT_MS = 1000;
13164
- var SUPPORTED_AGENTS = ["claude-code", "codex"];
13297
+ var SUPPORTED_AGENTS = ["claude-code", "codex", "factory"];
13165
13298
  var DEFAULT_AGENT = "claude-code";
13166
13299
  function cooldownMs() {
13167
13300
  const n = Number.parseInt(process.env.DOSU_HOOK_CHECK_COOLDOWN_MS ?? "", 10);
@@ -13391,7 +13524,7 @@ async function runHookEntrypoint(event, raw, now = Date.now(), agent = DEFAULT_A
13391
13524
  }
13392
13525
  function readStdinRaw() {
13393
13526
  try {
13394
- return readFileSync8(0, "utf8");
13527
+ return readFileSync9(0, "utf8");
13395
13528
  } catch {
13396
13529
  return "";
13397
13530
  }
@@ -13439,6 +13572,20 @@ async function runInstall(agent, opts) {
13439
13572
  }
13440
13573
  return;
13441
13574
  }
13575
+ if (agent === "factory") {
13576
+ const { installFactoryHooks: installFactoryHooks2, factoryHooksPath: factoryHooksPath2 } = await Promise.resolve().then(() => (init_factory(), exports_factory));
13577
+ const configPath2 = factoryHooksPath2(resolveDir(opts.dir));
13578
+ const { events: events2 } = installFactoryHooks2(configPath2, { stop: opts.stop });
13579
+ if (opts.json) {
13580
+ const { emitStep: emitStep2 } = await Promise.resolve().then(() => exports_output);
13581
+ emitStep2({ step: "hooks-install", agent, path: configPath2, events: events2 });
13582
+ } else {
13583
+ console.log(`✓ Installed Dosu hooks for Factory (${events2.join(", ")}).`);
13584
+ console.log(` → ${configPath2}`);
13585
+ console.log(" Start a new Factory session in this project to use them.");
13586
+ }
13587
+ return;
13588
+ }
13442
13589
  const { installClaudeHooks: installClaudeHooks2, claudeLocalSettingsPath: claudeLocalSettingsPath2 } = await Promise.resolve().then(() => (init_claude_code(), exports_claude_code));
13443
13590
  const configPath = claudeLocalSettingsPath2(resolveDir(opts.dir));
13444
13591
  const { events } = installClaudeHooks2(configPath, { stop: opts.stop });
@@ -13476,6 +13623,10 @@ async function runUninstall(agent, opts) {
13476
13623
  const { removeCodexHooks: removeCodexHooks2, codexHooksPath: codexHooksPath2 } = await Promise.resolve().then(() => (init_codex(), exports_codex));
13477
13624
  configPath = codexHooksPath2(resolveDir(opts.dir));
13478
13625
  ({ removed } = removeCodexHooks2(configPath));
13626
+ } else if (agent === "factory") {
13627
+ const { removeFactoryHooks: removeFactoryHooks2, factoryHooksPath: factoryHooksPath2 } = await Promise.resolve().then(() => (init_factory(), exports_factory));
13628
+ configPath = factoryHooksPath2(resolveDir(opts.dir));
13629
+ ({ removed } = removeFactoryHooks2(configPath));
13479
13630
  } else {
13480
13631
  const { removeClaudeHooks: removeClaudeHooks2, claudeLocalSettingsPath: claudeLocalSettingsPath2 } = await Promise.resolve().then(() => (init_claude_code(), exports_claude_code));
13481
13632
  configPath = claudeLocalSettingsPath2(resolveDir(opts.dir));
@@ -13494,15 +13645,20 @@ async function collectDoctorChecks(opts) {
13494
13645
  const checks = [];
13495
13646
  const { claudeLocalSettingsPath: claudeLocalSettingsPath2, inspectClaudeHooks: inspectClaudeHooks2 } = await Promise.resolve().then(() => (init_claude_code(), exports_claude_code));
13496
13647
  const { codexHooksPath: codexHooksPath2, inspectCodexHooks: inspectCodexHooks2 } = await Promise.resolve().then(() => (init_codex(), exports_codex));
13648
+ const { factoryHooksPath: factoryHooksPath2, inspectFactoryHooks: inspectFactoryHooks2 } = await Promise.resolve().then(() => (init_factory(), exports_factory));
13497
13649
  const configPath = claudeLocalSettingsPath2(resolveDir(opts.dir));
13498
13650
  const inspection = inspectClaudeHooks2(configPath);
13499
13651
  const codexPath = codexHooksPath2(resolveDir(opts.dir));
13500
13652
  const codex = inspectCodexHooks2(codexPath);
13653
+ const factoryPath = factoryHooksPath2(resolveDir(opts.dir));
13654
+ const factory = inspectFactoryHooks2(factoryPath);
13501
13655
  const codexInstalled = codex.events.includes("UserPromptSubmit") && codex.events.includes("PostToolUse");
13656
+ const factoryInstalled = factory.events.includes("UserPromptSubmit") && factory.events.includes("PostToolUse");
13657
+ const alternativeInstalled = codexInstalled || factoryInstalled;
13502
13658
  if (!inspection.fileExists) {
13503
13659
  checks.push({
13504
13660
  name: "config",
13505
- status: codexInstalled ? "warn" : "fail",
13661
+ status: alternativeInstalled ? "warn" : "fail",
13506
13662
  detail: `not found: ${configPath} (run 'dosu hooks install claude-code')`
13507
13663
  });
13508
13664
  } else if (inspection.parseError) {
@@ -13521,7 +13677,7 @@ async function collectDoctorChecks(opts) {
13521
13677
  } else {
13522
13678
  checks.push({
13523
13679
  name: "hooks",
13524
- status: codexInstalled ? "warn" : "fail",
13680
+ status: alternativeInstalled ? "warn" : "fail",
13525
13681
  detail: "UserPromptSubmit + PostToolUse not both installed"
13526
13682
  });
13527
13683
  }
@@ -13547,6 +13703,27 @@ async function collectDoctorChecks(opts) {
13547
13703
  });
13548
13704
  }
13549
13705
  }
13706
+ if (factory.fileExists) {
13707
+ if (factory.parseError) {
13708
+ checks.push({
13709
+ name: "factory-config",
13710
+ status: "fail",
13711
+ detail: `invalid JSON: ${factoryPath}`
13712
+ });
13713
+ } else if (factoryInstalled) {
13714
+ checks.push({
13715
+ name: "factory-config",
13716
+ status: "ok",
13717
+ detail: `installed: ${factory.events.join(", ")} (${factoryPath})`
13718
+ });
13719
+ } else {
13720
+ checks.push({
13721
+ name: "factory-config",
13722
+ status: "fail",
13723
+ detail: "UserPromptSubmit + PostToolUse not both installed"
13724
+ });
13725
+ }
13726
+ }
13550
13727
  const cfg = loadConfig();
13551
13728
  if (isAuthenticated(cfg)) {
13552
13729
  checks.push({ name: "auth", status: "ok", detail: "logged in" });
@@ -13601,7 +13778,7 @@ async function runDoctor(opts) {
13601
13778
  }
13602
13779
  }
13603
13780
  function hooksCommand() {
13604
- const cmd = new Command("hooks").description("Dosu knowledge-injection hooks for coding agents (Claude Code, Codex)");
13781
+ const cmd = new Command("hooks").description("Dosu knowledge-injection hooks for coding agents (Claude Code, Codex, Factory)");
13605
13782
  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
13783
  await runHookEntrypoint("user-prompt-submit", readStdinRaw(), Date.now(), opts.agent);
13607
13784
  });
@@ -13628,6 +13805,7 @@ function hooksCommand() {
13628
13805
  "Examples:",
13629
13806
  " $ dosu hooks install claude-code",
13630
13807
  " $ dosu hooks install codex",
13808
+ " $ dosu hooks install factory",
13631
13809
  " $ dosu hooks install claude-code --no-stop",
13632
13810
  " $ dosu hooks install codex --dir ./my-project",
13633
13811
  "",
@@ -13641,7 +13819,8 @@ function hooksCommand() {
13641
13819
  "",
13642
13820
  "Examples:",
13643
13821
  " $ dosu hooks uninstall claude-code",
13644
- " $ dosu hooks uninstall codex"
13822
+ " $ dosu hooks uninstall codex",
13823
+ " $ dosu hooks uninstall factory"
13645
13824
  ].join(`
13646
13825
  `)).action((agent, opts) => runUninstall(agent, opts));
13647
13826
  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 +14656,8 @@ init_skill_update_check();
14477
14656
  init_config();
14478
14657
  init_logger();
14479
14658
  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";
14659
+ import { existsSync as existsSync14, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "node:fs";
14660
+ import { join as join25 } from "node:path";
14482
14661
  var CACHE_FILENAME2 = "update-check.json";
14483
14662
  var CHECK_INTERVAL_MS2 = 24 * 60 * 60 * 1000;
14484
14663
  var FETCH_TIMEOUT_MS2 = 5000;
@@ -14501,14 +14680,14 @@ function isNewerVersion(latest, current) {
14501
14680
  return false;
14502
14681
  }
14503
14682
  function getCachePath2() {
14504
- return join23(getConfigDir(), CACHE_FILENAME2);
14683
+ return join25(getConfigDir(), CACHE_FILENAME2);
14505
14684
  }
14506
14685
  function readCache() {
14507
14686
  try {
14508
14687
  const path2 = getCachePath2();
14509
- if (!existsSync13(path2))
14688
+ if (!existsSync14(path2))
14510
14689
  return null;
14511
- const data = JSON.parse(readFileSync11(path2, "utf-8"));
14690
+ const data = JSON.parse(readFileSync12(path2, "utf-8"));
14512
14691
  if (typeof data.lastCheck === "number" && typeof data.latestVersion === "string") {
14513
14692
  return data;
14514
14693
  }
@@ -14520,7 +14699,7 @@ function readCache() {
14520
14699
  function writeCache(cache) {
14521
14700
  try {
14522
14701
  const dir = getConfigDir();
14523
- if (!existsSync13(dir)) {
14702
+ if (!existsSync14(dir)) {
14524
14703
  mkdirSync7(dir, { recursive: true, mode: 448 });
14525
14704
  }
14526
14705
  writeFileSync6(getCachePath2(), JSON.stringify(cache), { mode: 384 });
@@ -14810,7 +14989,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
14810
14989
  if (opts.tail !== undefined) {
14811
14990
  const n = typeof opts.tail === "string" ? parseInt(opts.tail, 10) || 50 : 50;
14812
14991
  try {
14813
- const content = readFileSync12(logPath, "utf-8");
14992
+ const content = readFileSync13(logPath, "utf-8");
14814
14993
  const lines = content.split(`
14815
14994
  `);
14816
14995
  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.0",
4
4
  "type": "module",
5
5
  "description": "Dosu CLI - Manage MCP servers for AI tools",
6
6
  "license": "MIT",