@dosu/cli 0.19.2 → 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 +282 -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.2";
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 });
@@ -7667,9 +7782,12 @@ var init_skill_update_check = __esm(() => {
7667
7782
 
7668
7783
  // src/commands/skill.ts
7669
7784
  import { execSync } from "node:child_process";
7785
+ function supportedSkillAgentArgs() {
7786
+ return SUPPORTED_SKILL_AGENTS.map((agent) => `-a ${agent}`).join(" ");
7787
+ }
7670
7788
  async function installSkill() {
7671
7789
  try {
7672
- execSync(`npx skills add ${SKILL_REPO} -g -s ${SKILL_NAME} -y`, {
7790
+ execSync(`npx skills add ${SKILL_REPO} -g ${supportedSkillAgentArgs()} -s ${SKILL_NAME} -y`, {
7673
7791
  stdio: "inherit"
7674
7792
  });
7675
7793
  } catch (err) {
@@ -7733,33 +7851,45 @@ Failed to update skill.`));
7733
7851
  });
7734
7852
  return cmd;
7735
7853
  }
7736
- var import_picocolors10, SKILL_REPO = "dosu-ai/dosu-skill", SKILL_NAME = "dosu";
7854
+ var import_picocolors10, SKILL_REPO = "dosu-ai/dosu-skill", SKILL_NAME = "dosu", SUPPORTED_SKILL_AGENTS;
7737
7855
  var init_skill = __esm(() => {
7738
7856
  init_esm();
7739
7857
  init_logger();
7740
7858
  init_skill_update_check();
7741
7859
  import_picocolors10 = __toESM(require_picocolors(), 1);
7860
+ SUPPORTED_SKILL_AGENTS = [
7861
+ "claude-code",
7862
+ "cursor",
7863
+ "gemini-cli",
7864
+ "codex",
7865
+ "windsurf",
7866
+ "zed",
7867
+ "cline",
7868
+ "github-copilot",
7869
+ "opencode",
7870
+ "antigravity"
7871
+ ];
7742
7872
  });
7743
7873
 
7744
7874
  // src/mcp/constants.ts
7745
7875
  var MCP_PROVIDER_SLUG = "dosu_mcp";
7746
7876
 
7747
7877
  // src/mcp/detect.ts
7748
- import { existsSync as existsSync8 } from "node:fs";
7878
+ import { existsSync as existsSync9 } from "node:fs";
7749
7879
  import { homedir, platform } from "node:os";
7750
- import { join as join7 } from "node:path";
7880
+ import { join as join8 } from "node:path";
7751
7881
  function isInstalled(paths) {
7752
- return paths.some((p2) => existsSync8(expandHome(p2)));
7882
+ return paths.some((p2) => existsSync9(expandHome(p2)));
7753
7883
  }
7754
7884
  function expandHome(path) {
7755
7885
  if (!path.startsWith("~"))
7756
7886
  return path;
7757
- return join7(homedir(), path.slice(1));
7887
+ return join8(homedir(), path.slice(1));
7758
7888
  }
7759
7889
  function appSupportDir() {
7760
7890
  switch (platform()) {
7761
7891
  case "darwin": {
7762
- return join7(homedir(), "Library", "Application Support");
7892
+ return join8(homedir(), "Library", "Application Support");
7763
7893
  }
7764
7894
  case "win32": {
7765
7895
  return process.env.APPDATA ?? "";
@@ -7768,7 +7898,7 @@ function appSupportDir() {
7768
7898
  const xdg = process.env.XDG_CONFIG_HOME;
7769
7899
  if (xdg)
7770
7900
  return xdg;
7771
- return join7(homedir(), ".config");
7901
+ return join8(homedir(), ".config");
7772
7902
  }
7773
7903
  }
7774
7904
  }
@@ -7849,7 +7979,7 @@ var init_antigravity = __esm(() => {
7849
7979
  });
7850
7980
 
7851
7981
  // src/mcp/providers/claude.ts
7852
- import { join as join8 } from "node:path";
7982
+ import { join as join9 } from "node:path";
7853
7983
  var ClaudeProvider = () => createJSONProvider({
7854
7984
  providerName: "Claude Code",
7855
7985
  providerID: "claude",
@@ -7858,22 +7988,22 @@ var ClaudeProvider = () => createJSONProvider({
7858
7988
  paths: ["~/.claude"],
7859
7989
  globalPath: "~/.claude.json",
7860
7990
  topKey: "mcpServers",
7861
- localConfigPath: (cwd) => join8(cwd, ".mcp.json")
7991
+ localConfigPath: (cwd) => join9(cwd, ".mcp.json")
7862
7992
  });
7863
7993
  var init_claude = __esm(() => {
7864
7994
  init_base();
7865
7995
  });
7866
7996
 
7867
7997
  // src/mcp/providers/claude-desktop.ts
7868
- import { join as join9 } from "node:path";
7998
+ import { join as join10 } from "node:path";
7869
7999
  var ClaudeDesktopProvider = () => ({
7870
8000
  name: () => "Claude Desktop",
7871
8001
  id: () => "claude-desktop",
7872
8002
  supportsLocal: () => false,
7873
8003
  priority: () => 2,
7874
- detectPaths: () => [join9(appSupportDir(), "Claude")],
7875
- isInstalled: () => isInstalled([join9(appSupportDir(), "Claude")]),
7876
- 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"),
7877
8007
  isConfigured: () => false,
7878
8008
  install() {
7879
8009
  throw new Error("this tool only supports local (stdio) servers and cannot be configured for remote MCP");
@@ -7887,14 +8017,14 @@ var init_claude_desktop = __esm(() => {
7887
8017
  });
7888
8018
 
7889
8019
  // src/mcp/providers/cline.ts
7890
- import { join as join10 } from "node:path";
7891
- 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({
7892
8022
  providerName: "Cline",
7893
8023
  providerID: "cline",
7894
8024
  local: false,
7895
8025
  priorityValue: 11,
7896
8026
  paths: [extensionDir()],
7897
- globalPath: join10(extensionDir(), "settings", "cline_mcp_settings.json"),
8027
+ globalPath: join11(extensionDir(), "settings", "cline_mcp_settings.json"),
7898
8028
  topKey: "mcpServers",
7899
8029
  buildServer: (cfg) => ({
7900
8030
  url: mcpURL(cfg.deployment_id),
@@ -7910,7 +8040,7 @@ var init_cline = __esm(() => {
7910
8040
  });
7911
8041
 
7912
8042
  // src/mcp/providers/cline-cli.ts
7913
- import { join as join11 } from "node:path";
8043
+ import { join as join12 } from "node:path";
7914
8044
  function clineDir() {
7915
8045
  return process.env.CLINE_DIR ?? expandHome("~/.cline");
7916
8046
  }
@@ -7920,7 +8050,7 @@ var ClineCliProvider = () => createJSONProvider({
7920
8050
  local: false,
7921
8051
  priorityValue: 12,
7922
8052
  paths: [clineDir()],
7923
- globalPath: join11(clineDir(), "data", "settings", "cline_mcp_settings.json"),
8053
+ globalPath: join12(clineDir(), "data", "settings", "cline_mcp_settings.json"),
7924
8054
  topKey: "mcpServers",
7925
8055
  buildServer: (cfg) => ({
7926
8056
  url: mcpURL(cfg.deployment_id),
@@ -7936,20 +8066,20 @@ var init_cline_cli = __esm(() => {
7936
8066
  });
7937
8067
 
7938
8068
  // src/mcp/providers/codex.ts
7939
- import { existsSync as existsSync9, readFileSync as readFileSync10 } from "node:fs";
7940
- 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";
7941
8071
  function codexHome() {
7942
8072
  return process.env.CODEX_HOME ?? expandHome("~/.codex");
7943
8073
  }
7944
8074
  function getConfigPath2(global) {
7945
8075
  if (global)
7946
- return join12(codexHome(), "config.toml");
7947
- return join12(process.cwd(), ".codex", "config.toml");
8076
+ return join13(codexHome(), "config.toml");
8077
+ return join13(process.cwd(), ".codex", "config.toml");
7948
8078
  }
7949
8079
  function readTOML(path) {
7950
- if (!existsSync9(path))
8080
+ if (!existsSync10(path))
7951
8081
  return "";
7952
- return readFileSync10(path, "utf-8");
8082
+ return readFileSync11(path, "utf-8");
7953
8083
  }
7954
8084
  function writeTOML(path, content) {
7955
8085
  writeSecureFile(path, content);
@@ -8007,9 +8137,9 @@ var CodexProvider = () => ({
8007
8137
  priority: () => 8,
8008
8138
  detectPaths: () => ["~/.codex"],
8009
8139
  isInstalled: () => isInstalled(["~/.codex"]),
8010
- globalConfigPath: () => join12(codexHome(), "config.toml"),
8140
+ globalConfigPath: () => join13(codexHome(), "config.toml"),
8011
8141
  isConfigured: () => {
8012
- const content = readTOML(join12(codexHome(), "config.toml"));
8142
+ const content = readTOML(join13(codexHome(), "config.toml"));
8013
8143
  return content.includes("[mcp_servers.dosu]");
8014
8144
  },
8015
8145
  install(cfg, global) {
@@ -8031,10 +8161,10 @@ var init_codex2 = __esm(() => {
8031
8161
  });
8032
8162
 
8033
8163
  // src/mcp/providers/copilot.ts
8034
- import { join as join13 } from "node:path";
8164
+ import { join as join14 } from "node:path";
8035
8165
  function globalPath() {
8036
8166
  if (process.env.XDG_CONFIG_HOME) {
8037
- return join13(process.env.XDG_CONFIG_HOME, "mcp-config.json");
8167
+ return join14(process.env.XDG_CONFIG_HOME, "mcp-config.json");
8038
8168
  }
8039
8169
  return expandHome("~/.copilot/mcp-config.json");
8040
8170
  }
@@ -8065,7 +8195,7 @@ var CopilotProvider = () => ({
8065
8195
  };
8066
8196
  installJSONServer(globalPath(), "mcpServers", server);
8067
8197
  } else {
8068
- const configPath = join13(process.cwd(), ".vscode", "mcp.json");
8198
+ const configPath = join14(process.cwd(), ".vscode", "mcp.json");
8069
8199
  const server = {
8070
8200
  type: "http",
8071
8201
  url,
@@ -8078,7 +8208,7 @@ var CopilotProvider = () => ({
8078
8208
  if (global) {
8079
8209
  removeJSONServer(globalPath(), "mcpServers");
8080
8210
  } else {
8081
- const configPath = join13(process.cwd(), ".vscode", "mcp.json");
8211
+ const configPath = join14(process.cwd(), ".vscode", "mcp.json");
8082
8212
  removeJSONServer(configPath, "servers");
8083
8213
  }
8084
8214
  }
@@ -8090,7 +8220,7 @@ var init_copilot = __esm(() => {
8090
8220
  });
8091
8221
 
8092
8222
  // src/mcp/providers/cursor.ts
8093
- import { join as join14 } from "node:path";
8223
+ import { join as join15 } from "node:path";
8094
8224
  var CursorProvider = () => createJSONProvider({
8095
8225
  providerName: "Cursor",
8096
8226
  providerID: "cursor",
@@ -8103,15 +8233,31 @@ var CursorProvider = () => createJSONProvider({
8103
8233
  url: mcpURL(cfg.deployment_id),
8104
8234
  headers: mcpHeaders(cfg.api_key)
8105
8235
  }),
8106
- localConfigPath: (cwd) => join14(cwd, ".cursor", "mcp.json")
8236
+ localConfigPath: (cwd) => join15(cwd, ".cursor", "mcp.json")
8107
8237
  });
8108
8238
  var init_cursor = __esm(() => {
8109
8239
  init_config_helpers();
8110
8240
  init_base();
8111
8241
  });
8112
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
+
8113
8259
  // src/mcp/providers/gemini.ts
8114
- import { join as join15 } from "node:path";
8260
+ import { join as join17 } from "node:path";
8115
8261
  var GeminiProvider = () => createJSONProvider({
8116
8262
  providerName: "Gemini CLI",
8117
8263
  providerID: "gemini",
@@ -8120,7 +8266,7 @@ var GeminiProvider = () => createJSONProvider({
8120
8266
  paths: ["~/.gemini"],
8121
8267
  globalPath: "~/.gemini/settings.json",
8122
8268
  topKey: "mcpServers",
8123
- localConfigPath: (cwd) => join15(cwd, ".gemini", "settings.json")
8269
+ localConfigPath: (cwd) => join17(cwd, ".gemini", "settings.json")
8124
8270
  });
8125
8271
  var init_gemini = __esm(() => {
8126
8272
  init_base();
@@ -8170,14 +8316,14 @@ var init_manual = __esm(() => {
8170
8316
  });
8171
8317
 
8172
8318
  // src/mcp/providers/mcporter.ts
8173
- import { existsSync as existsSync10 } from "node:fs";
8174
- import { join as join16 } from "node:path";
8319
+ import { existsSync as existsSync11 } from "node:fs";
8320
+ import { join as join18 } from "node:path";
8175
8321
  function resolveGlobalConfigPath() {
8176
8322
  const jsonPath = expandHome("~/.mcporter/mcporter.json");
8177
- if (existsSync10(jsonPath))
8323
+ if (existsSync11(jsonPath))
8178
8324
  return jsonPath;
8179
8325
  const jsoncPath = expandHome("~/.mcporter/mcporter.jsonc");
8180
- if (existsSync10(jsoncPath))
8326
+ if (existsSync11(jsoncPath))
8181
8327
  return jsoncPath;
8182
8328
  return jsonPath;
8183
8329
  }
@@ -8198,7 +8344,7 @@ var MCPorterProvider = () => ({
8198
8344
  globalConfigPath: () => resolveGlobalConfigPath(),
8199
8345
  isConfigured: () => isJSONKeyConfigured(resolveGlobalConfigPath(), "mcpServers"),
8200
8346
  install(cfg, global) {
8201
- const configPath = global ? resolveGlobalConfigPath() : join16(process.cwd(), "config", "mcporter.json");
8347
+ const configPath = global ? resolveGlobalConfigPath() : join18(process.cwd(), "config", "mcporter.json");
8202
8348
  const server = {
8203
8349
  type: "http",
8204
8350
  url: mcpEndpoint4(cfg),
@@ -8207,7 +8353,7 @@ var MCPorterProvider = () => ({
8207
8353
  installJSONServer(configPath, "mcpServers", server);
8208
8354
  },
8209
8355
  remove(global) {
8210
- const configPath = global ? resolveGlobalConfigPath() : join16(process.cwd(), "config", "mcporter.json");
8356
+ const configPath = global ? resolveGlobalConfigPath() : join18(process.cwd(), "config", "mcporter.json");
8211
8357
  removeJSONServer(configPath, "mcpServers");
8212
8358
  }
8213
8359
  });
@@ -8218,7 +8364,7 @@ var init_mcporter = __esm(() => {
8218
8364
  });
8219
8365
 
8220
8366
  // src/mcp/providers/opencode.ts
8221
- import { join as join17 } from "node:path";
8367
+ import { join as join19 } from "node:path";
8222
8368
  var OpenCodeProvider = () => createJSONProvider({
8223
8369
  providerName: "OpenCode",
8224
8370
  providerID: "opencode",
@@ -8233,7 +8379,7 @@ var OpenCodeProvider = () => createJSONProvider({
8233
8379
  enabled: true,
8234
8380
  headers: mcpHeaders(cfg.api_key)
8235
8381
  }),
8236
- localConfigPath: (cwd) => join17(cwd, "opencode.json")
8382
+ localConfigPath: (cwd) => join19(cwd, "opencode.json")
8237
8383
  });
8238
8384
  var init_opencode = __esm(() => {
8239
8385
  init_config_helpers();
@@ -8241,16 +8387,16 @@ var init_opencode = __esm(() => {
8241
8387
  });
8242
8388
 
8243
8389
  // src/mcp/providers/vscode.ts
8244
- import { join as join18 } from "node:path";
8390
+ import { join as join20 } from "node:path";
8245
8391
  var VSCodeProvider = () => createJSONProvider({
8246
8392
  providerName: "VS Code",
8247
8393
  providerID: "vscode",
8248
8394
  local: true,
8249
8395
  priorityValue: 6,
8250
- paths: [join18(appSupportDir(), "Code")],
8251
- globalPath: join18(appSupportDir(), "Code", "User", "mcp.json"),
8396
+ paths: [join20(appSupportDir(), "Code")],
8397
+ globalPath: join20(appSupportDir(), "Code", "User", "mcp.json"),
8252
8398
  topKey: "servers",
8253
- localConfigPath: (cwd) => join18(cwd, ".vscode", "mcp.json")
8399
+ localConfigPath: (cwd) => join20(cwd, ".vscode", "mcp.json")
8254
8400
  });
8255
8401
  var init_vscode = __esm(() => {
8256
8402
  init_detect();
@@ -8259,14 +8405,14 @@ var init_vscode = __esm(() => {
8259
8405
 
8260
8406
  // src/mcp/providers/windsurf.ts
8261
8407
  import { homedir as homedir2 } from "node:os";
8262
- import { join as join19 } from "node:path";
8408
+ import { join as join21 } from "node:path";
8263
8409
  var WindsurfProvider = () => createJSONProvider({
8264
8410
  providerName: "Windsurf",
8265
8411
  providerID: "windsurf",
8266
8412
  local: false,
8267
8413
  priorityValue: 9,
8268
- paths: [join19(homedir2(), ".codeium", "windsurf")],
8269
- globalPath: join19(homedir2(), ".codeium", "windsurf", "mcp_config.json"),
8414
+ paths: [join21(homedir2(), ".codeium", "windsurf")],
8415
+ globalPath: join21(homedir2(), ".codeium", "windsurf", "mcp_config.json"),
8270
8416
  topKey: "mcpServers"
8271
8417
  });
8272
8418
  var init_windsurf = __esm(() => {
@@ -8275,12 +8421,12 @@ var init_windsurf = __esm(() => {
8275
8421
 
8276
8422
  // src/mcp/providers/zed.ts
8277
8423
  import { platform as platform2 } from "node:os";
8278
- import { join as join20 } from "node:path";
8424
+ import { join as join22 } from "node:path";
8279
8425
  function zedConfigDir() {
8280
8426
  const os = platform2();
8281
8427
  if (os === "darwin" || os === "win32")
8282
- return join20(appSupportDir(), "Zed");
8283
- return join20(appSupportDir(), "zed");
8428
+ return join22(appSupportDir(), "Zed");
8429
+ return join22(appSupportDir(), "zed");
8284
8430
  }
8285
8431
  var ZedProvider = () => createJSONProvider({
8286
8432
  providerName: "Zed",
@@ -8288,7 +8434,7 @@ var ZedProvider = () => createJSONProvider({
8288
8434
  local: true,
8289
8435
  priorityValue: 10,
8290
8436
  paths: [zedConfigDir()],
8291
- globalPath: join20(zedConfigDir(), "settings.json"),
8437
+ globalPath: join22(zedConfigDir(), "settings.json"),
8292
8438
  topKey: "context_servers",
8293
8439
  buildServer: (cfg) => ({
8294
8440
  source: "custom",
@@ -8296,7 +8442,7 @@ var ZedProvider = () => createJSONProvider({
8296
8442
  url: mcpURL(cfg.deployment_id),
8297
8443
  headers: mcpHeaders(cfg.api_key)
8298
8444
  }),
8299
- localConfigPath: (cwd) => join20(cwd, ".zed", "settings.json")
8445
+ localConfigPath: (cwd) => join22(cwd, ".zed", "settings.json")
8300
8446
  });
8301
8447
  var init_zed = __esm(() => {
8302
8448
  init_config_helpers();
@@ -8321,6 +8467,7 @@ function allProviders() {
8321
8467
  OpenCodeProvider(),
8322
8468
  AntigravityProvider(),
8323
8469
  MCPorterProvider(),
8470
+ FactoryProvider(),
8324
8471
  ManualProvider()
8325
8472
  ];
8326
8473
  }
@@ -8343,6 +8490,7 @@ var init_providers = __esm(() => {
8343
8490
  init_codex2();
8344
8491
  init_copilot();
8345
8492
  init_cursor();
8493
+ init_factory2();
8346
8494
  init_gemini();
8347
8495
  init_manual();
8348
8496
  init_mcporter();
@@ -10889,8 +11037,8 @@ __export(exports_flow2, {
10889
11037
  isStdioOnly: () => isStdioOnly
10890
11038
  });
10891
11039
  import { randomUUID } from "node:crypto";
10892
- import { existsSync as existsSync11 } from "node:fs";
10893
- import { join as join21 } from "node:path";
11040
+ import { existsSync as existsSync12 } from "node:fs";
11041
+ import { join as join23 } from "node:path";
10894
11042
  async function runSetup(opts = {}) {
10895
11043
  const onboardingRunID = randomUUID();
10896
11044
  logger.info("setup", `Setup flow started${opts.deploymentID ? ` deployment=${opts.deploymentID}` : ""}${opts.mode ? ` mode=${opts.mode}` : ""}`);
@@ -11062,7 +11210,7 @@ async function runSetup(opts = {}) {
11062
11210
  showTryItOutPrompt({
11063
11211
  mode: cfg.mode,
11064
11212
  docsImported: choices.connectGitHub && githubOnboardingDone,
11065
- hasAgentsMd: existsSync11(join21(process.cwd(), "AGENTS.md"))
11213
+ hasAgentsMd: existsSync12(join23(process.cwd(), "AGENTS.md"))
11066
11214
  });
11067
11215
  }
11068
11216
  if (mcpCompleted || skillCompleted || docsImported) {
@@ -11556,8 +11704,8 @@ var init_flow2 = __esm(() => {
11556
11704
  });
11557
11705
 
11558
11706
  // src/commands/insights.ts
11559
- import { existsSync as existsSync12, mkdirSync as mkdirSync6, readdirSync, unlinkSync, writeFileSync as writeFileSync5 } from "node:fs";
11560
- 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";
11561
11709
  function shuffled(arr) {
11562
11710
  const copy2 = [...arr];
11563
11711
  for (let i = copy2.length - 1;i > 0; i--) {
@@ -11661,17 +11809,17 @@ function makeAskFn(cfg) {
11661
11809
  };
11662
11810
  }
11663
11811
  function insightsDir() {
11664
- return join22(getConfigDir(), "insights");
11812
+ return join24(getConfigDir(), "insights");
11665
11813
  }
11666
11814
  function reportPath(timestamp) {
11667
11815
  const dir = insightsDir();
11668
11816
  if (!timestamp)
11669
- return join22(dir, "latest.html");
11817
+ return join24(dir, "latest.html");
11670
11818
  const iso = timestamp.toISOString().slice(0, 19).replace(/:/g, "-");
11671
- return join22(dir, `report-${iso}Z.html`);
11819
+ return join24(dir, `report-${iso}Z.html`);
11672
11820
  }
11673
11821
  function pruneOldReports(dir, keepN) {
11674
- if (!existsSync12(dir))
11822
+ if (!existsSync13(dir))
11675
11823
  return;
11676
11824
  let entries;
11677
11825
  try {
@@ -11683,7 +11831,7 @@ function pruneOldReports(dir, keepN) {
11683
11831
  const reports = entries.filter((f) => REPORT_FILE_PATTERN.test(f)).sort().reverse();
11684
11832
  for (const f of reports.slice(keepN)) {
11685
11833
  try {
11686
- unlinkSync(join22(dir, f));
11834
+ unlinkSync(join24(dir, f));
11687
11835
  } catch (err) {
11688
11836
  logger.debug("insights", `failed to prune ${f}: ${err instanceof Error ? err.message : err}`);
11689
11837
  }
@@ -11756,7 +11904,7 @@ function defaultRunner(cfg) {
11756
11904
  render: renderHTML,
11757
11905
  writeFile: (path2, content) => {
11758
11906
  const dir = dirname3(path2);
11759
- if (!existsSync12(dir))
11907
+ if (!existsSync13(dir))
11760
11908
  mkdirSync6(dir, { recursive: true, mode: 448 });
11761
11909
  writeFileSync5(path2, content, { mode: 384 });
11762
11910
  },
@@ -12421,7 +12569,7 @@ var init_flow3 = __esm(() => {
12421
12569
  // src/cli/cli.ts
12422
12570
  init_esm();
12423
12571
  init_client();
12424
- import { readFileSync as readFileSync12, unlinkSync as unlinkSync2 } from "node:fs";
12572
+ import { readFileSync as readFileSync13, unlinkSync as unlinkSync2 } from "node:fs";
12425
12573
 
12426
12574
  // src/commands/analytics.ts
12427
12575
  init_esm();
@@ -13107,7 +13255,7 @@ init_esm();
13107
13255
  init_config();
13108
13256
  init_logger();
13109
13257
  init_prompts();
13110
- import { readFileSync as readFileSync8 } from "node:fs";
13258
+ import { readFileSync as readFileSync9 } from "node:fs";
13111
13259
  import { basename } from "node:path";
13112
13260
 
13113
13261
  // src/hooks/state.ts
@@ -13146,7 +13294,7 @@ var COOLDOWN_DEFAULT_MS = 3000;
13146
13294
  var TTL_DEFAULT_MS = 10 * 60 * 1000;
13147
13295
  var STOP_WAIT_DEFAULT_MS = 8000;
13148
13296
  var STOP_POLL_DEFAULT_MS = 1000;
13149
- var SUPPORTED_AGENTS = ["claude-code", "codex"];
13297
+ var SUPPORTED_AGENTS = ["claude-code", "codex", "factory"];
13150
13298
  var DEFAULT_AGENT = "claude-code";
13151
13299
  function cooldownMs() {
13152
13300
  const n = Number.parseInt(process.env.DOSU_HOOK_CHECK_COOLDOWN_MS ?? "", 10);
@@ -13376,7 +13524,7 @@ async function runHookEntrypoint(event, raw, now = Date.now(), agent = DEFAULT_A
13376
13524
  }
13377
13525
  function readStdinRaw() {
13378
13526
  try {
13379
- return readFileSync8(0, "utf8");
13527
+ return readFileSync9(0, "utf8");
13380
13528
  } catch {
13381
13529
  return "";
13382
13530
  }
@@ -13424,6 +13572,20 @@ async function runInstall(agent, opts) {
13424
13572
  }
13425
13573
  return;
13426
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
+ }
13427
13589
  const { installClaudeHooks: installClaudeHooks2, claudeLocalSettingsPath: claudeLocalSettingsPath2 } = await Promise.resolve().then(() => (init_claude_code(), exports_claude_code));
13428
13590
  const configPath = claudeLocalSettingsPath2(resolveDir(opts.dir));
13429
13591
  const { events } = installClaudeHooks2(configPath, { stop: opts.stop });
@@ -13461,6 +13623,10 @@ async function runUninstall(agent, opts) {
13461
13623
  const { removeCodexHooks: removeCodexHooks2, codexHooksPath: codexHooksPath2 } = await Promise.resolve().then(() => (init_codex(), exports_codex));
13462
13624
  configPath = codexHooksPath2(resolveDir(opts.dir));
13463
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));
13464
13630
  } else {
13465
13631
  const { removeClaudeHooks: removeClaudeHooks2, claudeLocalSettingsPath: claudeLocalSettingsPath2 } = await Promise.resolve().then(() => (init_claude_code(), exports_claude_code));
13466
13632
  configPath = claudeLocalSettingsPath2(resolveDir(opts.dir));
@@ -13479,15 +13645,20 @@ async function collectDoctorChecks(opts) {
13479
13645
  const checks = [];
13480
13646
  const { claudeLocalSettingsPath: claudeLocalSettingsPath2, inspectClaudeHooks: inspectClaudeHooks2 } = await Promise.resolve().then(() => (init_claude_code(), exports_claude_code));
13481
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));
13482
13649
  const configPath = claudeLocalSettingsPath2(resolveDir(opts.dir));
13483
13650
  const inspection = inspectClaudeHooks2(configPath);
13484
13651
  const codexPath = codexHooksPath2(resolveDir(opts.dir));
13485
13652
  const codex = inspectCodexHooks2(codexPath);
13653
+ const factoryPath = factoryHooksPath2(resolveDir(opts.dir));
13654
+ const factory = inspectFactoryHooks2(factoryPath);
13486
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;
13487
13658
  if (!inspection.fileExists) {
13488
13659
  checks.push({
13489
13660
  name: "config",
13490
- status: codexInstalled ? "warn" : "fail",
13661
+ status: alternativeInstalled ? "warn" : "fail",
13491
13662
  detail: `not found: ${configPath} (run 'dosu hooks install claude-code')`
13492
13663
  });
13493
13664
  } else if (inspection.parseError) {
@@ -13506,7 +13677,7 @@ async function collectDoctorChecks(opts) {
13506
13677
  } else {
13507
13678
  checks.push({
13508
13679
  name: "hooks",
13509
- status: codexInstalled ? "warn" : "fail",
13680
+ status: alternativeInstalled ? "warn" : "fail",
13510
13681
  detail: "UserPromptSubmit + PostToolUse not both installed"
13511
13682
  });
13512
13683
  }
@@ -13532,6 +13703,27 @@ async function collectDoctorChecks(opts) {
13532
13703
  });
13533
13704
  }
13534
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
+ }
13535
13727
  const cfg = loadConfig();
13536
13728
  if (isAuthenticated(cfg)) {
13537
13729
  checks.push({ name: "auth", status: "ok", detail: "logged in" });
@@ -13586,7 +13778,7 @@ async function runDoctor(opts) {
13586
13778
  }
13587
13779
  }
13588
13780
  function hooksCommand() {
13589
- 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)");
13590
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) => {
13591
13783
  await runHookEntrypoint("user-prompt-submit", readStdinRaw(), Date.now(), opts.agent);
13592
13784
  });
@@ -13613,6 +13805,7 @@ function hooksCommand() {
13613
13805
  "Examples:",
13614
13806
  " $ dosu hooks install claude-code",
13615
13807
  " $ dosu hooks install codex",
13808
+ " $ dosu hooks install factory",
13616
13809
  " $ dosu hooks install claude-code --no-stop",
13617
13810
  " $ dosu hooks install codex --dir ./my-project",
13618
13811
  "",
@@ -13626,7 +13819,8 @@ function hooksCommand() {
13626
13819
  "",
13627
13820
  "Examples:",
13628
13821
  " $ dosu hooks uninstall claude-code",
13629
- " $ dosu hooks uninstall codex"
13822
+ " $ dosu hooks uninstall codex",
13823
+ " $ dosu hooks uninstall factory"
13630
13824
  ].join(`
13631
13825
  `)).action((agent, opts) => runUninstall(agent, opts));
13632
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));
@@ -14462,8 +14656,8 @@ init_skill_update_check();
14462
14656
  init_config();
14463
14657
  init_logger();
14464
14658
  var import_picocolors24 = __toESM(require_picocolors(), 1);
14465
- import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
14466
- 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";
14467
14661
  var CACHE_FILENAME2 = "update-check.json";
14468
14662
  var CHECK_INTERVAL_MS2 = 24 * 60 * 60 * 1000;
14469
14663
  var FETCH_TIMEOUT_MS2 = 5000;
@@ -14486,14 +14680,14 @@ function isNewerVersion(latest, current) {
14486
14680
  return false;
14487
14681
  }
14488
14682
  function getCachePath2() {
14489
- return join23(getConfigDir(), CACHE_FILENAME2);
14683
+ return join25(getConfigDir(), CACHE_FILENAME2);
14490
14684
  }
14491
14685
  function readCache() {
14492
14686
  try {
14493
14687
  const path2 = getCachePath2();
14494
- if (!existsSync13(path2))
14688
+ if (!existsSync14(path2))
14495
14689
  return null;
14496
- const data = JSON.parse(readFileSync11(path2, "utf-8"));
14690
+ const data = JSON.parse(readFileSync12(path2, "utf-8"));
14497
14691
  if (typeof data.lastCheck === "number" && typeof data.latestVersion === "string") {
14498
14692
  return data;
14499
14693
  }
@@ -14505,7 +14699,7 @@ function readCache() {
14505
14699
  function writeCache(cache) {
14506
14700
  try {
14507
14701
  const dir = getConfigDir();
14508
- if (!existsSync13(dir)) {
14702
+ if (!existsSync14(dir)) {
14509
14703
  mkdirSync7(dir, { recursive: true, mode: 448 });
14510
14704
  }
14511
14705
  writeFileSync6(getCachePath2(), JSON.stringify(cache), { mode: 384 });
@@ -14795,7 +14989,7 @@ Use 'dosu mcp add <agent>' to add Dosu MCP to a tool.`);
14795
14989
  if (opts.tail !== undefined) {
14796
14990
  const n = typeof opts.tail === "string" ? parseInt(opts.tail, 10) || 50 : 50;
14797
14991
  try {
14798
- const content = readFileSync12(logPath, "utf-8");
14992
+ const content = readFileSync13(logPath, "utf-8");
14799
14993
  const lines = content.split(`
14800
14994
  `);
14801
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.2",
3
+ "version": "0.20.0",
4
4
  "type": "module",
5
5
  "description": "Dosu CLI - Manage MCP servers for AI tools",
6
6
  "license": "MIT",