@springbrand/cli 0.1.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 (222) hide show
  1. package/README.md +167 -0
  2. package/dist/commands/auth.d.ts +4 -0
  3. package/dist/commands/auth.d.ts.map +1 -0
  4. package/dist/commands/auth.js +241 -0
  5. package/dist/commands/auth.js.map +1 -0
  6. package/dist/commands/check-version.d.ts +3 -0
  7. package/dist/commands/check-version.d.ts.map +1 -0
  8. package/dist/commands/check-version.js +112 -0
  9. package/dist/commands/check-version.js.map +1 -0
  10. package/dist/commands/config-claude.d.ts +4 -0
  11. package/dist/commands/config-claude.d.ts.map +1 -0
  12. package/dist/commands/config-claude.js +78 -0
  13. package/dist/commands/config-claude.js.map +1 -0
  14. package/dist/commands/connect.d.ts +41 -0
  15. package/dist/commands/connect.d.ts.map +1 -0
  16. package/dist/commands/connect.js +901 -0
  17. package/dist/commands/connect.js.map +1 -0
  18. package/dist/commands/doctor.d.ts +6 -0
  19. package/dist/commands/doctor.d.ts.map +1 -0
  20. package/dist/commands/doctor.js +124 -0
  21. package/dist/commands/doctor.js.map +1 -0
  22. package/dist/commands/upgrade.d.ts +9 -0
  23. package/dist/commands/upgrade.d.ts.map +1 -0
  24. package/dist/commands/upgrade.js +133 -0
  25. package/dist/commands/upgrade.js.map +1 -0
  26. package/dist/index.d.ts +4 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +15 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/lib/agent-detect.d.ts +33 -0
  31. package/dist/lib/agent-detect.d.ts.map +1 -0
  32. package/dist/lib/agent-detect.js +81 -0
  33. package/dist/lib/agent-detect.js.map +1 -0
  34. package/dist/lib/agent-launcher.d.ts +22 -0
  35. package/dist/lib/agent-launcher.d.ts.map +1 -0
  36. package/dist/lib/agent-launcher.js +67 -0
  37. package/dist/lib/agent-launcher.js.map +1 -0
  38. package/dist/lib/auth.d.ts +90 -0
  39. package/dist/lib/auth.d.ts.map +1 -0
  40. package/dist/lib/auth.js +327 -0
  41. package/dist/lib/auth.js.map +1 -0
  42. package/dist/lib/bundled-skill.d.ts +5 -0
  43. package/dist/lib/bundled-skill.d.ts.map +1 -0
  44. package/dist/lib/bundled-skill.js +13 -0
  45. package/dist/lib/bundled-skill.js.map +1 -0
  46. package/dist/lib/claude-config.d.ts +19 -0
  47. package/dist/lib/claude-config.d.ts.map +1 -0
  48. package/dist/lib/claude-config.js +202 -0
  49. package/dist/lib/claude-config.js.map +1 -0
  50. package/dist/lib/cli-install.d.ts +40 -0
  51. package/dist/lib/cli-install.d.ts.map +1 -0
  52. package/dist/lib/cli-install.js +42 -0
  53. package/dist/lib/cli-install.js.map +1 -0
  54. package/dist/lib/command-code-config.d.ts +5 -0
  55. package/dist/lib/command-code-config.d.ts.map +1 -0
  56. package/dist/lib/command-code-config.js +16 -0
  57. package/dist/lib/command-code-config.js.map +1 -0
  58. package/dist/lib/connect-all-auth.d.ts +21 -0
  59. package/dist/lib/connect-all-auth.d.ts.map +1 -0
  60. package/dist/lib/connect-all-auth.js +113 -0
  61. package/dist/lib/connect-all-auth.js.map +1 -0
  62. package/dist/lib/connect-all-summary.d.ts +10 -0
  63. package/dist/lib/connect-all-summary.d.ts.map +1 -0
  64. package/dist/lib/connect-all-summary.js +153 -0
  65. package/dist/lib/connect-all-summary.js.map +1 -0
  66. package/dist/lib/connect-all-uninstall.d.ts +7 -0
  67. package/dist/lib/connect-all-uninstall.d.ts.map +1 -0
  68. package/dist/lib/connect-all-uninstall.js +249 -0
  69. package/dist/lib/connect-all-uninstall.js.map +1 -0
  70. package/dist/lib/connect-all.d.ts +42 -0
  71. package/dist/lib/connect-all.d.ts.map +1 -0
  72. package/dist/lib/connect-all.js +438 -0
  73. package/dist/lib/connect-all.js.map +1 -0
  74. package/dist/lib/connect-auth.d.ts +7 -0
  75. package/dist/lib/connect-auth.d.ts.map +1 -0
  76. package/dist/lib/connect-auth.js +50 -0
  77. package/dist/lib/connect-auth.js.map +1 -0
  78. package/dist/lib/connect-clients.d.ts +108 -0
  79. package/dist/lib/connect-clients.d.ts.map +1 -0
  80. package/dist/lib/connect-clients.js +358 -0
  81. package/dist/lib/connect-clients.js.map +1 -0
  82. package/dist/lib/connect-fallback.d.ts +27 -0
  83. package/dist/lib/connect-fallback.d.ts.map +1 -0
  84. package/dist/lib/connect-fallback.js +166 -0
  85. package/dist/lib/connect-fallback.js.map +1 -0
  86. package/dist/lib/connect-picker.d.ts +19 -0
  87. package/dist/lib/connect-picker.d.ts.map +1 -0
  88. package/dist/lib/connect-picker.js +155 -0
  89. package/dist/lib/connect-picker.js.map +1 -0
  90. package/dist/lib/connect-preflight.d.ts +17 -0
  91. package/dist/lib/connect-preflight.d.ts.map +1 -0
  92. package/dist/lib/connect-preflight.js +81 -0
  93. package/dist/lib/connect-preflight.js.map +1 -0
  94. package/dist/lib/connect-runtime.d.ts +109 -0
  95. package/dist/lib/connect-runtime.d.ts.map +1 -0
  96. package/dist/lib/connect-runtime.js +360 -0
  97. package/dist/lib/connect-runtime.js.map +1 -0
  98. package/dist/lib/constants.d.ts +23 -0
  99. package/dist/lib/constants.d.ts.map +1 -0
  100. package/dist/lib/constants.js +87 -0
  101. package/dist/lib/constants.js.map +1 -0
  102. package/dist/lib/cursor-config.d.ts +16 -0
  103. package/dist/lib/cursor-config.d.ts.map +1 -0
  104. package/dist/lib/cursor-config.js +34 -0
  105. package/dist/lib/cursor-config.js.map +1 -0
  106. package/dist/lib/doctor-checks.d.ts +21 -0
  107. package/dist/lib/doctor-checks.d.ts.map +1 -0
  108. package/dist/lib/doctor-checks.js +312 -0
  109. package/dist/lib/doctor-checks.js.map +1 -0
  110. package/dist/lib/doctor-repairs.d.ts +11 -0
  111. package/dist/lib/doctor-repairs.d.ts.map +1 -0
  112. package/dist/lib/doctor-repairs.js +130 -0
  113. package/dist/lib/doctor-repairs.js.map +1 -0
  114. package/dist/lib/doctor-report.d.ts +195 -0
  115. package/dist/lib/doctor-report.d.ts.map +1 -0
  116. package/dist/lib/doctor-report.js +102 -0
  117. package/dist/lib/doctor-report.js.map +1 -0
  118. package/dist/lib/doctor-telemetry.d.ts +9 -0
  119. package/dist/lib/doctor-telemetry.d.ts.map +1 -0
  120. package/dist/lib/doctor-telemetry.js +63 -0
  121. package/dist/lib/doctor-telemetry.js.map +1 -0
  122. package/dist/lib/harness-detect.d.ts +33 -0
  123. package/dist/lib/harness-detect.d.ts.map +1 -0
  124. package/dist/lib/harness-detect.js +70 -0
  125. package/dist/lib/harness-detect.js.map +1 -0
  126. package/dist/lib/harness-spec.d.ts +285 -0
  127. package/dist/lib/harness-spec.d.ts.map +1 -0
  128. package/dist/lib/harness-spec.js +268 -0
  129. package/dist/lib/harness-spec.js.map +1 -0
  130. package/dist/lib/harness.d.ts +27 -0
  131. package/dist/lib/harness.d.ts.map +1 -0
  132. package/dist/lib/harness.js +125 -0
  133. package/dist/lib/harness.js.map +1 -0
  134. package/dist/lib/hermes-config.d.ts +22 -0
  135. package/dist/lib/hermes-config.d.ts.map +1 -0
  136. package/dist/lib/hermes-config.js +76 -0
  137. package/dist/lib/hermes-config.js.map +1 -0
  138. package/dist/lib/hermes-env.d.ts +19 -0
  139. package/dist/lib/hermes-env.d.ts.map +1 -0
  140. package/dist/lib/hermes-env.js +169 -0
  141. package/dist/lib/hermes-env.js.map +1 -0
  142. package/dist/lib/hermes-plugin.d.ts +40 -0
  143. package/dist/lib/hermes-plugin.d.ts.map +1 -0
  144. package/dist/lib/hermes-plugin.js +168 -0
  145. package/dist/lib/hermes-plugin.js.map +1 -0
  146. package/dist/lib/install-root.d.ts +18 -0
  147. package/dist/lib/install-root.d.ts.map +1 -0
  148. package/dist/lib/install-root.js +55 -0
  149. package/dist/lib/install-root.js.map +1 -0
  150. package/dist/lib/mcp-config.d.ts +36 -0
  151. package/dist/lib/mcp-config.d.ts.map +1 -0
  152. package/dist/lib/mcp-config.js +90 -0
  153. package/dist/lib/mcp-config.js.map +1 -0
  154. package/dist/lib/mcp-endpoint.d.ts +16 -0
  155. package/dist/lib/mcp-endpoint.d.ts.map +1 -0
  156. package/dist/lib/mcp-endpoint.js +41 -0
  157. package/dist/lib/mcp-endpoint.js.map +1 -0
  158. package/dist/lib/mcp-json-config.d.ts +43 -0
  159. package/dist/lib/mcp-json-config.d.ts.map +1 -0
  160. package/dist/lib/mcp-json-config.js +164 -0
  161. package/dist/lib/mcp-json-config.js.map +1 -0
  162. package/dist/lib/notice.d.ts +13 -0
  163. package/dist/lib/notice.d.ts.map +1 -0
  164. package/dist/lib/notice.js +73 -0
  165. package/dist/lib/notice.js.map +1 -0
  166. package/dist/lib/omp-config.d.ts +18 -0
  167. package/dist/lib/omp-config.d.ts.map +1 -0
  168. package/dist/lib/omp-config.js +73 -0
  169. package/dist/lib/omp-config.js.map +1 -0
  170. package/dist/lib/output.d.ts +22 -0
  171. package/dist/lib/output.d.ts.map +1 -0
  172. package/dist/lib/output.js +92 -0
  173. package/dist/lib/output.js.map +1 -0
  174. package/dist/lib/pi-config.d.ts +27 -0
  175. package/dist/lib/pi-config.d.ts.map +1 -0
  176. package/dist/lib/pi-config.js +113 -0
  177. package/dist/lib/pi-config.js.map +1 -0
  178. package/dist/lib/registration-detect.d.ts +28 -0
  179. package/dist/lib/registration-detect.d.ts.map +1 -0
  180. package/dist/lib/registration-detect.js +563 -0
  181. package/dist/lib/registration-detect.js.map +1 -0
  182. package/dist/lib/setup-telemetry.d.ts +273 -0
  183. package/dist/lib/setup-telemetry.d.ts.map +1 -0
  184. package/dist/lib/setup-telemetry.js +309 -0
  185. package/dist/lib/setup-telemetry.js.map +1 -0
  186. package/dist/lib/signals.d.ts +14 -0
  187. package/dist/lib/signals.d.ts.map +1 -0
  188. package/dist/lib/signals.js +29 -0
  189. package/dist/lib/signals.js.map +1 -0
  190. package/dist/lib/skill-install.d.ts +10 -0
  191. package/dist/lib/skill-install.d.ts.map +1 -0
  192. package/dist/lib/skill-install.js +94 -0
  193. package/dist/lib/skill-install.js.map +1 -0
  194. package/dist/lib/skill-paths.d.ts +7 -0
  195. package/dist/lib/skill-paths.d.ts.map +1 -0
  196. package/dist/lib/skill-paths.js +37 -0
  197. package/dist/lib/skill-paths.js.map +1 -0
  198. package/dist/lib/skill-vendor.d.ts +20 -0
  199. package/dist/lib/skill-vendor.d.ts.map +1 -0
  200. package/dist/lib/skill-vendor.js +215 -0
  201. package/dist/lib/skill-vendor.js.map +1 -0
  202. package/dist/lib/skill.d.ts +14 -0
  203. package/dist/lib/skill.d.ts.map +1 -0
  204. package/dist/lib/skill.js +87 -0
  205. package/dist/lib/skill.js.map +1 -0
  206. package/dist/lib/verify.d.ts +35 -0
  207. package/dist/lib/verify.d.ts.map +1 -0
  208. package/dist/lib/verify.js +169 -0
  209. package/dist/lib/verify.js.map +1 -0
  210. package/dist/program.d.ts +8 -0
  211. package/dist/program.d.ts.map +1 -0
  212. package/dist/program.js +54 -0
  213. package/dist/program.js.map +1 -0
  214. package/package.json +55 -0
  215. package/skill/springbrand-action-api/SKILL.md +49 -0
  216. package/skill/springbrand-action-api/references/action-aliases.md +156 -0
  217. package/skill/springbrand-action-api/references/action-discovery.md +95 -0
  218. package/skill/springbrand-connector/SKILL.md +49 -0
  219. package/skill/springbrand-gtm/SKILL.md +34 -0
  220. package/skill/springbrand-platform/SKILL.md +55 -0
  221. package/skill/springbrand-platform/references/plugin-discovery.md +94 -0
  222. package/skill/use-springbrand/SKILL.md +70 -0
package/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # SpringBrand CLI
2
+
3
+ 把 AI 编码客户端连接到 SpringBrand Gateway 的命令行工具。
4
+
5
+ **当前定位:只做两件事** —— 往客户端写 MCP 配置,以及安装随 CLI 分发的 SpringBrand Skill 集合。集合以 `use-springbrand` 为通用入口,并包含 Platform、Action API、Connector、GTM 的业务指导;其他能力(Web 搜索、抓取、浏览器自动化等)暂不提供,见文末 TODO。
6
+
7
+ 包名 `@springbrand/cli`,命令名 `sp`。
8
+
9
+ ## 安装
10
+
11
+ ```bash
12
+ cd cli
13
+ npm install
14
+ npm run build
15
+ npm link
16
+ ```
17
+
18
+ ## 命令
19
+
20
+ ```bash
21
+ sp connect [client] # 把 SpringBrand 写入客户端的 MCP 配置并安装 Skill 集合
22
+ sp doctor # 诊断安装状态,输出 JSON 报告与修复建议
23
+ sp auth login|set|status|logout # 管理 API key
24
+ sp auth status --verify # 额外拿这把 key 探一次 /mcp,报告账号、keyId 与环境
25
+ sp upgrade # 更新 CLI 并刷新随包分发的 Skill 集合
26
+ sp check-version # 报告是否需要升级,供 agent 调用
27
+ ```
28
+
29
+ `config-claude` 已从命令列表中取消注册,原因见 TODO。
30
+
31
+ ### sp connect
32
+
33
+ ```bash
34
+ sp connect # 从本机检测到的客户端里挑一个
35
+ sp connect cursor # 指定客户端
36
+ sp connect --all # 连接本机所有支持的客户端
37
+ sp connect --all --dry-run # 只打印将要写入什么,不落盘
38
+ sp connect --all --uninstall # 移除 connect 写入的条目
39
+ sp connect cursor --url http://127.0.0.1:8789/mcp # 覆盖端点(本地开发)
40
+ ```
41
+
42
+ 支持 10 个客户端。Gateway 同时提供 OAuth 与 API key,实际可用情况分三档:
43
+
44
+ | 客户端 | 状态 | 验证方式 |
45
+ |---|---|---|
46
+ | `cursor` | 可用 | 真实写入 `~/.cursor/mcp.json` + skill,OAuth 与 API key 两种形态 |
47
+ | `claude-code` | 可用 | 真实 `claude mcp add`,条目落入 `~/.claude/.claude.json`,OAuth 由客户端接手 |
48
+ | `codex` | 可用 | 真实 `codex mcp add`,条目落入 `~/.codex/config.toml` |
49
+ | `pi` | 可用 | 真实写入 `~/.pi/agent/mcp.json` + skill |
50
+ | `omp` | 可用 | 真实写入 `~/.omp/mcp.json`(不装 skill) |
51
+ | `opencode` | 可用 | stub 校验命令构造:`mcp add <name> --url ...` 与 `mcp auth` |
52
+ | `grok` | 可用 | stub 校验命令构造:`mcp remove` 后 `mcp add --transport http` |
53
+ | `openclaw` | MCP 可用,skill 失败 | skill 需 ClawHub 包 `@springbrand/springbrand`,尚未发布 |
54
+ | `command-code` | 可用 | `keyRequired`,只接受 API key;端到端断言 `mcp add-json` 的 Bearer 请求头 |
55
+ | `hermes` | 不可用 | `keyRequired`,且插件仓库不存在 |
56
+
57
+ `hermes` 仍被插件仓库阻塞,见 TODO。
58
+
59
+ 两种认证形态由客户端能力决定:
60
+
61
+ - **OAuth(不带 key)**:写入的条目只有 URL,由客户端自己走 OAuth 授权。
62
+ - **API key**:CLI 把 key 写进 `Authorization: Bearer`,客户端不再需要浏览器登录。需要 `--api-key` 或已保存的 key。
63
+
64
+ **`Authorization: Bearer` 是 Gateway 唯一读取 key 的位置**(`src/mcp-api-key.ts` 的 `classifyMcpAuthorization`),key 写进别的请求头等于没带凭据,客户端会被推回 OAuth。Codex 与 Grok 例外地传环境变量名而非 key 本身:前者用 `--bearer-token-env-var`,后者在加载时展开 `${SPRINGBRAND_API_KEY}`,两者都由客户端自己拼成 Bearer,key 因此不进 argv 与配置文件。
65
+
66
+ key 形态是 `sbk_v1_<dev|production>_<id>_<secret>`,环境段决定它能用在哪个端点:生产端点 `connector.springbrand.ai` 只收 production key,其余(dev、本机)只收 dev key。不匹配的组合在写任何配置之前就被拒绝,不必等服务端的 401。
67
+
68
+ 写入前会把原配置备份成带时间戳的副本。`claude-code` 和 `codex` 不由 CLI 直接改文件,而是调用它们各自的 `mcp add` 命令。
69
+
70
+ ### sp doctor
71
+
72
+ 输出机器可读的 JSON,包含逐项检查、每个客户端的注册状态,以及可执行的修复命令。退出码非 0 表示存在需要处理的问题。
73
+
74
+ ## 端点与配置
75
+
76
+ MCP 端点默认 `https://connector.springbrand.ai/mcp`。写入客户端配置时的服务名固定为 `springbrand`,与 Gateway 声明的服务别名一致。
77
+
78
+ | 环境变量 | 作用 |
79
+ |---|---|
80
+ | `SPRINGBRAND_API_KEY` | API key,优先级高于已保存的配置 |
81
+ | `SPRINGBRAND_API_URL` | 覆盖端点,必须是 https 或 loopback http |
82
+ | `SPRINGBRAND_WEB_URL` | 覆盖 `sp auth login` 打开的网页地址;与端点覆盖相互独立 |
83
+ | `SPRINGBRAND_DEBUG=1` | 打印 HTTP 请求与响应到 stderr(等同 `--debug`) |
84
+
85
+ ### 版本提示
86
+
87
+ `sp check-version` 查询 `GET /api/cli/version`,输出结构化结果供 agent 解析:
88
+
89
+ ```json
90
+ {"current":"0.1.0","latest":"0.9.0","minimum":null,"update":"recommended"}
91
+ ```
92
+
93
+ `update` 为 `null`、`recommended` 或 `required`。**退出码承载强制语义**:`required` 退 `1`,其余退 `0`,agent 与脚本无需解析文案即可判断。结果在 `~/.springbrand/version-check.json` 缓存 6 小时(`--force` 跳过),因为它设计上会被 agent 在每次任务收尾时调用。查不到版本时输出 `update: null` 并退 `0`——检查版本绝不能挡住用户干活。
94
+
95
+ 另有一条被动通道:`connect` 期间会向所连接端点的 `/api/cli/connect-event` 上报六次安装进度,并从响应头 `x-sp-notice` 系列读取版本提示,`doctor` 与 `upgrade` 同样会读。但用户装完就不再跑这些命令,触达有限,所以主动通道才是主要手段。Gateway 已实现该端点(见 `src/cli-events.ts`):事件不落库,作用是承载版本通告——CLI 装在用户机器上,服务端只能借它主动发起的请求把「该升级了」捎回去。版本门槛由 Wrangler 的 `CLI_LATEST_VERSION`(与可选的 `CLI_MIN_VERSION`)配置,**发布新版 CLI 后需同步该变量**,否则老版本收不到提示。
96
+
97
+ CLI 自己的配置在 `~/.springbrand/config.json`。
98
+
99
+ skill 的安装位置随客户端而定:默认 `~/.agents/skills/`,`claude-code` 用 `~/.claude/skills/`,`pi` 用 `~/.pi/agent/skills/`,`command-code` 用 `~/.commandcode/skills/`,`hermes` 用 `~/.hermes/skills/`。`openclaw` 例外,走它自己的 ClawHub 包管理器。
100
+
101
+ ## 代码来源
102
+
103
+ `src/` 复刻自 `@tiny-fish/cli@0.43.0` 的编译产物:`dist/**/*.js`(实现)与 `dist/**/*.d.ts`(类型声明)合并成 TypeScript,再替换品牌标识。`scripts/` 下是这条管线,可重跑:
104
+
105
+ ```bash
106
+ node scripts/convert-libs.ts # 合并 .d.ts + .impl.js 成 .ts
107
+ node scripts/rebrand.ts # 替换品牌标识(命令名/服务名/端点/环境变量分层处理)
108
+ node scripts/apply-signatures.ts # 从 .d.ts 套回导出函数与常量的类型
109
+ node scripts/check-imports.ts # 校验相对导入
110
+ ```
111
+
112
+ 重跑管线会覆盖手工修改,重跑后需要重新应用。
113
+
114
+ ## 开发
115
+
116
+ ```bash
117
+ npm run build # tsc
118
+ npm run dev # tsc --watch
119
+ npm run typecheck # tsc --noEmit,当前 strict 全绿
120
+ ```
121
+
122
+ ## 测试
123
+
124
+ ```bash
125
+ npm test # 先编译,再跑全部用例(含端到端,约 10 秒)
126
+ npm run test:unit # 只跑单元用例,秒级,不需要编译
127
+ npm run test:watch
128
+ ```
129
+
130
+ 端到端用例会真的执行 `sp connect <client>`,覆盖 cursor、pi、omp、claude-code、codex、opencode、grok 与 command-code,OAuth 与 API key 两种形态都断言写出的配置内容或传给客户端的命令参数。**不需要本机装任何客户端,也不会碰你的真实配置**,靠三点做到:
131
+
132
+ - `HOME` 指向临时目录,配置写进沙箱
133
+ - `PATH` 前置 stub 目录,用假的 harness 命令记录收到的参数;`open` 也被顶掉,否则会真的弹浏览器
134
+ - `--url` 指向 `127.0.0.1:1`,凭证探测立即失败,不访问外网
135
+
136
+ 改动安装流程后跑 `npm test` 即可,不必再逐个平台手工装一遍。Hermes 与 OpenClaw 依赖尚未就绪的外部资源,不在覆盖范围内。
137
+
138
+ 给 stub 加能力时注意:各客户端的能力探测用的是锚定行首的正则(如 `^\s*add(?:\s|\[)`),子命令名必须各占一行。`--header` 与 `--bearer-token-env-var` 决定 key 形态是否可用——stub 的 `--help` 里缺了它们,`--api-key` 会被无声降级成 OAuth 或直接拒绝。
139
+
140
+ ## TODO
141
+
142
+ 以下能力尚未支持,按需要程度排列。
143
+
144
+ ### 会影响现有安装流程
145
+
146
+ - [x] **`--launch` 的引导语引用了不存在的工具**。`connect-clients.ts` 里的 `DEFAULT_ONBOARDING_PROMPT` 曾让 agent 调用上游的 `guide_next_step`,`OPENCLAW_ONBOARDING_PROMPT` 引用了已移除的 Search / Fetch / Agent 能力;`connect-all.ts` 的 `FIRST_TASK_PROMPT` 也还在让 agent 使用不存在的「SpringBrand search/fetch tool」。三处均已按 `search_tools` → `get_tool_schemas` →(需要时)`manage_connections` → `execute_tools` → `get_execution` 的元工具流程重写,并有 `branding.test.ts` 锁定。
147
+ - [x] **`config-claude` 已取消注册**。它的作用是改写 `~/.claude/CLAUDE.md` 与 `~/.claude/settings.json`,把 Claude Code 内置的 `WebSearch`/`WebFetch` 引导到 `sp search` / `sp fetch`,而这两个命令已随业务能力一并移除;写入的权限规则 `Bash(springbrand:*)` 里的名字也不是实际的 bin 名 `sp`。已从 `program.ts` 的命令表移除,`commands/config-claude.ts` 与 `lib/claude-config.ts` 保留,等 Web 能力落地后改掉命令名即可重新挂上。
148
+
149
+ ### 外部资源未就绪
150
+
151
+ - [ ] **Hermes 插件仓库不存在**。`hermes-plugin.ts` 会从 `springbrand-lab/springbrand-web-agent-integrations.git` 克隆插件,并锚定着上游的 commit SHA `496cd63`。仓库建立前,`sp connect hermes` 的插件安装步骤必然失败。
152
+ - [ ] **OpenClaw skill 包未发布**。OpenClaw 不接受外部往目录里写 skill,只能通过 ClawHub 安装 `@springbrand/springbrand`。该包尚未发布,`sp connect openclaw` 的 skill 步骤会失败。内容可直接复用 `skill/use-springbrand/`。
153
+
154
+ ### 能力待补
155
+
156
+ - [ ] **`onboard` 命令**(首次使用引导)。上游版本的主体是 Search → Fetch 的现场演示,依赖已移除的业务模块。Gateway 的能力在 MCP 层、只有 agent 能调用,CLI 自身无法当场演示,因此引导流程需要重新设计——要么把演示交给 agent 完成,要么退化成「检测 → 连接 → 启动」。与上面的引导语重写是同一件事。
157
+ - [ ] **key 管理页地址未定**。`sp auth login` 需要打开一个网页让用户创建并复制 key,那个页面属于 sp-platform 的 Web 应用,路径尚未确定。当前 `constants.ts` 用占位值 `https://springbrand.ai/account-settings`,可由 `SPRINGBRAND_WEB_URL` 覆盖;页面上线后改这一个常量即可。`sp auth set`(手动粘贴或管道输入)不受影响。
158
+ - [ ] **其他工具命令**。上游的 `search`、`fetch`、`browser`、`run`、`runs`、`batch`、`profile`、`vault`、`wallet` 均未复刻。按当前定位不做,后续按需增加。
159
+
160
+ ### 工程链路
161
+
162
+ - [x] **自动化测试**。已覆盖配置写入安全、品牌标识回归、key 格式与错误分类,以及 8 个客户端在 OAuth 与 API key 两种形态下的端到端安装(见「测试」一节)。尚未覆盖:Hermes 与 OpenClaw(外部资源未就绪)、与真实平台验钥的联调。
163
+ - [ ] **缺少 lint / format 脚本**,也没有发布流程,`@springbrand/cli` 尚未发布。
164
+
165
+ ### 文档
166
+
167
+ 早期自动生成的一批文档与脚本已清理(描述的命令与路径均不存在),本 README 为唯一权威入口。仓库根目录下的 `PROJECT-OVERVIEW.md`、`TASK-COMPLETE.md` 与 `docs/cli-*.md` 出自同一批生成,尚待确认是否保留。
@@ -0,0 +1,4 @@
1
+ /** 复刻自 tiny-fish CLI 编译产物:auth.d.ts + auth.impl.js */
2
+ import type { Command } from 'commander';
3
+ export declare function registerAuth(program: Command): void;
4
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,sDAAsD;AAEtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8HzC,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsHnD"}
@@ -0,0 +1,241 @@
1
+ /** 复刻自 tiny-fish CLI 编译产物:auth.d.ts + auth.impl.js */
2
+ import { spawn } from 'child_process';
3
+ import * as readline from 'readline';
4
+ import { Option } from 'commander';
5
+ import { CONNECT_ATTEMPT_ENV, KEY_FORMAT_HINT, clearConfig, configFile, getDashboardUrl, loadConfig, maskKey, parseKeyIdentity, savePendingConnectAttempt, saveConfig, validateKeyFormat, } from '../lib/auth.js';
6
+ import { SPRINGBRAND_API_KEY_VAR } from '../lib/constants.js';
7
+ import { err, errLine, out, outLine, warnLine } from '../lib/output.js';
8
+ import { verifyMcpAuth } from '../lib/verify.js';
9
+ /**
10
+ * Read a key from stdin without echoing it to the terminal.
11
+ * Uses setRawMode on TTY so characters are never written to the screen.
12
+ * Falls back to plain readline when stdin is not a TTY (piped CI/CD input).
13
+ */
14
+ async function readKeyHidden(prompt) {
15
+ process.stderr.write(prompt);
16
+ if (!process.stdin.isTTY) {
17
+ // Non-interactive (piped): read stdin line as-is — no echo suppression needed
18
+ const rl = readline.createInterface({ input: process.stdin, terminal: false });
19
+ return new Promise((resolve) => {
20
+ rl.once('line', (line) => {
21
+ rl.close();
22
+ resolve(line.trim());
23
+ });
24
+ });
25
+ }
26
+ // Interactive TTY: use raw mode so keystrokes are never echoed
27
+ return new Promise((resolve) => {
28
+ let input = '';
29
+ process.stdin.setRawMode(true);
30
+ process.stdin.resume();
31
+ process.stdin.setEncoding('utf8');
32
+ const onData = (char) => {
33
+ if (char === '\r' || char === '\n') {
34
+ process.stdin.setRawMode(false);
35
+ process.stdin.pause();
36
+ process.stdin.removeListener('data', onData);
37
+ process.stderr.write('\n');
38
+ resolve(input);
39
+ }
40
+ else if (char === '\u0003') {
41
+ // Ctrl+C — restore terminal state before exiting
42
+ process.stdin.setRawMode(false);
43
+ process.exit(130);
44
+ }
45
+ else if (char === '\u007f' || char === '\b') {
46
+ // Backspace / Delete
47
+ input = input.slice(0, -1);
48
+ }
49
+ else {
50
+ input += char;
51
+ }
52
+ };
53
+ process.stdin.on('data', onData);
54
+ });
55
+ }
56
+ /**
57
+ * Read key from piped stdin (CI/CD) or hidden TTY prompt (interactive).
58
+ * Never reads from a positional arg — avoids shell history / process list exposure.
59
+ */
60
+ async function readKeyFromStdinOrPrompt() {
61
+ if (!process.stdin.isTTY) {
62
+ // Piped input: echo $KEY | sp auth set
63
+ return new Promise((resolve) => {
64
+ let data = '';
65
+ process.stdin.setEncoding('utf8');
66
+ process.stdin.on('data', (chunk) => (data += chunk));
67
+ process.stdin.on('end', () => resolve(data.trim()));
68
+ });
69
+ }
70
+ return readKeyHidden('Paste your API key: ');
71
+ }
72
+ // Sign-in and the connect that follows are separate copied commands, so a page-minted id
73
+ // handed to this process has to outlive it.
74
+ function parkSeedAttempt() {
75
+ const seed = process.env[CONNECT_ATTEMPT_ENV];
76
+ if (seed)
77
+ savePendingConnectAttempt(seed);
78
+ }
79
+ // Env var wins every later lookup; a silent save misleads.
80
+ function warnIfEnvShadows(savedKey) {
81
+ const envKey = process.env[SPRINGBRAND_API_KEY_VAR];
82
+ if (!envKey || envKey === savedKey)
83
+ return;
84
+ warnLine(`Warning: ${SPRINGBRAND_API_KEY_VAR} is set in your environment and overrides the key just saved. ` +
85
+ `Unset it or update your shell profile, then check with 'sp auth status'.`);
86
+ }
87
+ function keyFromConfig() {
88
+ const stored = loadConfig().api_key;
89
+ return stored ? { key: stored, source: 'config', where: configFile() } : undefined;
90
+ }
91
+ /** `--verify` only: says the endpoint accepts the key, never that an agent has loaded SpringBrand. */
92
+ async function checkKey(key) {
93
+ const result = await verifyMcpAuth(key);
94
+ if (result.ok) {
95
+ return {
96
+ verified: 'ok',
97
+ ...(result.identity
98
+ ? { account_id: result.identity.accountId, verified_environment: result.identity.environment }
99
+ : {}),
100
+ };
101
+ }
102
+ return {
103
+ verified: result.kind ?? 'retryable',
104
+ verify_detail: result.code ?? 'the authenticated call failed',
105
+ ...(result.errorCode ? { verify_code: result.errorCode } : {}),
106
+ };
107
+ }
108
+ function renderStatus(where, key, identity, verified) {
109
+ const lines = [`Source: ${where}`, `Key: ${maskKey(key)}`];
110
+ if (identity) {
111
+ lines.push(`Env: ${identity.environment}`, `Key ID: ${identity.keyId}`);
112
+ }
113
+ if (verified?.verified === 'ok') {
114
+ const account = verified.account_id ? ` (account ${verified.account_id})` : '';
115
+ lines.push(`Checked: SpringBrand accepted this key${account}`);
116
+ }
117
+ else if (verified) {
118
+ lines.push(`Checked: ${verified.verify_detail}`);
119
+ }
120
+ return lines.join('\n');
121
+ }
122
+ export function registerAuth(program) {
123
+ const auth = program.command('auth').description('Manage your SpringBrand API key');
124
+ auth
125
+ .command('login')
126
+ .description('Open the API keys page and save your key interactively')
127
+ .addOption(new Option('--source <source>', 'API keys page source attribution').default('cli').hideHelp())
128
+ .action(async (options) => {
129
+ const dashboardUrl = getDashboardUrl(options.source);
130
+ errLine(`Opening ${dashboardUrl} in your browser...`);
131
+ try {
132
+ // Use spawn with an explicit args array — no shell, no interpolation
133
+ let cmd;
134
+ let args;
135
+ if (process.platform === 'darwin') {
136
+ cmd = 'open';
137
+ args = [dashboardUrl];
138
+ }
139
+ else if (process.platform === 'win32') {
140
+ // "start" treats the first quoted arg as a window title — empty string avoids it
141
+ cmd = 'cmd';
142
+ args = ['/c', 'start', '', dashboardUrl];
143
+ }
144
+ else {
145
+ cmd = 'xdg-open';
146
+ args = [dashboardUrl];
147
+ }
148
+ const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
149
+ child.unref();
150
+ child.on('error', () => { }); // headless — ignore
151
+ }
152
+ catch {
153
+ // headless — ignore
154
+ }
155
+ process.stderr.write(`If the browser didn't open, visit: ${dashboardUrl}\n\n`);
156
+ const key = await readKeyHidden('Paste your API key: ');
157
+ if (!key) {
158
+ err('API key cannot be empty');
159
+ process.exit(1);
160
+ }
161
+ if (!validateKeyFormat(key)) {
162
+ err(`Invalid key format. ${KEY_FORMAT_HINT}`);
163
+ process.exit(1);
164
+ }
165
+ saveConfig(key);
166
+ parkSeedAttempt();
167
+ out({ status: 'ok', message: 'API key saved', key_preview: maskKey(key) });
168
+ warnIfEnvShadows(key);
169
+ });
170
+ auth
171
+ .command('set')
172
+ .description('Save an API key. Pass via stdin: echo $KEY | sp auth set (safe for CI/CD)')
173
+ .action(async () => {
174
+ // Read from stdin pipe (CI/CD) or hidden TTY prompt (interactive)
175
+ const key = await readKeyFromStdinOrPrompt();
176
+ if (!key) {
177
+ // `echo $KEY | sp auth set` with KEY unset pipes a blank line, not a key.
178
+ err(process.stdin.isTTY
179
+ ? 'API key cannot be empty'
180
+ : "No key received on stdin. Example: echo 'sbk_v1_production_...' | sp auth set");
181
+ process.exit(1);
182
+ }
183
+ if (!validateKeyFormat(key)) {
184
+ err(`Invalid key format. ${KEY_FORMAT_HINT}`);
185
+ process.exit(1);
186
+ }
187
+ saveConfig(key);
188
+ parkSeedAttempt();
189
+ out({ status: 'ok', message: 'API key saved', key_preview: maskKey(key) });
190
+ warnIfEnvShadows(key);
191
+ });
192
+ auth
193
+ .command('status')
194
+ .description('Show which API key is active and its source')
195
+ .option('--pretty', 'Human-readable output')
196
+ .option('--verify', 'Also check the key against the SpringBrand MCP endpoint')
197
+ .action(async (opts) => {
198
+ const envKey = process.env[SPRINGBRAND_API_KEY_VAR];
199
+ const active = envKey
200
+ ? { key: envKey, source: 'env', where: `environment variable (${SPRINGBRAND_API_KEY_VAR})` }
201
+ : keyFromConfig();
202
+ if (!active) {
203
+ const result = { source: 'none', key_preview: null, authenticated: false };
204
+ if (opts.pretty)
205
+ outLine('No API key configured. Run: sp auth login');
206
+ else
207
+ out(result);
208
+ process.exit(1);
209
+ }
210
+ const authenticated = validateKeyFormat(active.key);
211
+ const identity = parseKeyIdentity(active.key);
212
+ // Local shape only unless --verify: a status query must not cost a round-trip.
213
+ const verified = opts.verify ? await checkKey(active.key) : undefined;
214
+ const result = {
215
+ source: active.source,
216
+ key_preview: maskKey(active.key),
217
+ authenticated,
218
+ ...(identity ? { environment: identity.environment, key_id: identity.keyId } : {}),
219
+ ...(verified ?? {}),
220
+ };
221
+ if (opts.pretty) {
222
+ outLine(renderStatus(active.where, active.key, identity, verified));
223
+ }
224
+ else {
225
+ out(result);
226
+ }
227
+ if (verified?.verified === 'credential_rejected')
228
+ process.exit(1);
229
+ });
230
+ auth
231
+ .command('logout')
232
+ .description('Remove stored API key')
233
+ .action(() => {
234
+ if (!clearConfig()) {
235
+ out({ status: 'ok', message: 'No API key stored' });
236
+ return;
237
+ }
238
+ out({ status: 'ok', message: 'API key removed' });
239
+ });
240
+ }
241
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerCheckVersion(program: Command): void;
3
+ //# sourceMappingURL=check-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-version.d.ts","sourceRoot":"","sources":["../../src/commands/check-version.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6FzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6B3D"}
@@ -0,0 +1,112 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { configDir } from '../lib/auth.js';
4
+ import { BASE_URL, CLI_VERSION } from '../lib/constants.js';
5
+ import { out } from '../lib/output.js';
6
+ /**
7
+ * `sp check-version` 供 agent 在任务收尾时调用。
8
+ *
9
+ * 独立于 connect / doctor 的通告:那条路搭在安装上报上,而用户装完就不再跑安装
10
+ * 命令,触达不到。这条由 skill 指引 agent 主动调用,不依赖用户的主动性,也不需要
11
+ * 改 MCP 契约。
12
+ *
13
+ * 因此必须便宜:结果带本地缓存,否则 agent 每完成一次任务就给 Gateway 加一次请求。
14
+ */
15
+ const CACHE_TTL_MS = 6 * 60 * 60 * 1000;
16
+ const REQUEST_TIMEOUT_MS = 3_000;
17
+ function cacheFile() {
18
+ return path.join(configDir(), 'version-check.json');
19
+ }
20
+ function readCache() {
21
+ try {
22
+ const raw = JSON.parse(fs.readFileSync(cacheFile(), 'utf8'));
23
+ if (typeof raw.checkedAt !== 'number')
24
+ return undefined;
25
+ return {
26
+ checkedAt: raw.checkedAt,
27
+ latest: typeof raw.latest === 'string' ? raw.latest : null,
28
+ minimum: typeof raw.minimum === 'string' ? raw.minimum : null,
29
+ };
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ }
35
+ function writeCache(entry) {
36
+ try {
37
+ fs.mkdirSync(configDir(), { recursive: true });
38
+ fs.writeFileSync(cacheFile(), JSON.stringify(entry));
39
+ }
40
+ catch {
41
+ // 缓存写不进去不影响结论,静默即可
42
+ }
43
+ }
44
+ const PLAIN_VERSION = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
45
+ function compareVersions(left, right) {
46
+ const a = left.split('.').map(Number);
47
+ const b = right.split('.').map(Number);
48
+ for (let i = 0; i < 3; i++) {
49
+ if (a[i] !== b[i])
50
+ return (a[i] ?? 0) < (b[i] ?? 0) ? -1 : 1;
51
+ }
52
+ return 0;
53
+ }
54
+ function verdict(latest, minimum) {
55
+ if (!PLAIN_VERSION.test(CLI_VERSION))
56
+ return null;
57
+ if (minimum && PLAIN_VERSION.test(minimum) && compareVersions(CLI_VERSION, minimum) < 0)
58
+ return 'required';
59
+ if (latest && PLAIN_VERSION.test(latest) && compareVersions(CLI_VERSION, latest) < 0)
60
+ return 'recommended';
61
+ return null;
62
+ }
63
+ async function fetchVersions(baseUrl) {
64
+ const response = await fetch(new URL('/api/cli/version', baseUrl), {
65
+ headers: { accept: 'application/json' },
66
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
67
+ });
68
+ if (!response.ok)
69
+ throw new Error(`the endpoint returned HTTP ${response.status}`);
70
+ const body = (await response.json());
71
+ return {
72
+ latest: typeof body.latest === 'string' ? body.latest : null,
73
+ minimum: typeof body.minimum === 'string' ? body.minimum : null,
74
+ };
75
+ }
76
+ export function registerCheckVersion(program) {
77
+ program
78
+ .command('check-version')
79
+ .description('Report whether this CLI is current; exits 1 only when an upgrade is required')
80
+ .option('--force', 'Skip the local cache and query the server')
81
+ .option('--url <baseUrl>', 'Gateway base URL override')
82
+ .action(async (options) => {
83
+ const cached = options.force ? undefined : readCache();
84
+ if (cached && Date.now() - cached.checkedAt < CACHE_TTL_MS) {
85
+ const update = verdict(cached.latest, cached.minimum);
86
+ report({ current: CLI_VERSION, latest: cached.latest, minimum: cached.minimum, update, cached: true });
87
+ return;
88
+ }
89
+ try {
90
+ const { latest, minimum } = await fetchVersions(options.url ?? BASE_URL);
91
+ writeCache({ checkedAt: Date.now(), latest, minimum });
92
+ report({ current: CLI_VERSION, latest, minimum, update: verdict(latest, minimum) });
93
+ }
94
+ catch (e) {
95
+ // 查不到版本绝不能挡住用户干活,也不该让 agent 以为需要升级
96
+ report({
97
+ current: CLI_VERSION,
98
+ latest: null,
99
+ minimum: null,
100
+ update: null,
101
+ unavailable: e instanceof Error ? e.message : String(e),
102
+ });
103
+ }
104
+ });
105
+ }
106
+ /** 退出码承载强制语义,agent 与脚本都能据此判断,无需解析文案 */
107
+ function report(result) {
108
+ out(result);
109
+ if (result.update === 'required')
110
+ process.exit(1);
111
+ }
112
+ //# sourceMappingURL=check-version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-version.js","sourceRoot":"","sources":["../../src/commands/check-version.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** 复刻自 tiny-fish CLI 编译产物:config-claude.d.ts + config-claude.impl.js */
2
+ import type { Command } from 'commander';
3
+ export declare function registerConfigureClaude(program: Command): void;
4
+ //# sourceMappingURL=config-claude.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-claude.d.ts","sourceRoot":"","sources":["../../src/commands/config-claude.ts"],"names":[],"mappings":"AAAA,wEAAwE;AAExE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8DzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAc9D"}
@@ -0,0 +1,78 @@
1
+ /** 复刻自 tiny-fish CLI 编译产物:config-claude.d.ts + config-claude.impl.js */
2
+ import { err, errLine, out } from '../lib/output.js';
3
+ import { validatedApiKey } from '../lib/auth.js';
4
+ import { readSettingsJson, writeSettingsJson, readClaudeMd, writeClaudeMd, mergeSettings, removeFromSettings, mergeClaudeMd, removeFromClaudeMd, claudeSettingsPath, claudeMdPath, isSpringbrandConfiguredInSettings, isSpringbrandConfiguredInClaudeMd, } from '../lib/claude-config.js';
5
+ function loadExistingConfig() {
6
+ let settings;
7
+ try {
8
+ settings = readSettingsJson();
9
+ }
10
+ catch (e) {
11
+ err({
12
+ error: `Cannot read ${claudeSettingsPath()}: ${e instanceof Error ? e.message : String(e)}. Fix it manually before running this command.`,
13
+ });
14
+ process.exit(1);
15
+ }
16
+ let claudeMd;
17
+ try {
18
+ claudeMd = readClaudeMd();
19
+ }
20
+ catch (e) {
21
+ err({
22
+ error: `Cannot read ${claudeMdPath()}: ${e instanceof Error ? e.message : String(e)}`,
23
+ });
24
+ process.exit(1);
25
+ }
26
+ return { settings, claudeMd };
27
+ }
28
+ function isSignedIn() {
29
+ return !!validatedApiKey();
30
+ }
31
+ function install(settings, claudeMd) {
32
+ errLine('Configuring Claude Code to use SpringBrand...\n');
33
+ writeSettingsJson(mergeSettings(settings));
34
+ errLine(' Updated settings.json (permission + hooks)');
35
+ const { content, replaced } = mergeClaudeMd(claudeMd);
36
+ writeClaudeMd(content);
37
+ errLine(replaced
38
+ ? ' Updated CLAUDE.md (replaced existing SpringBrand block)'
39
+ : ' Updated CLAUDE.md (added SpringBrand instructions)');
40
+ errLine('\nClaude Code is now configured to use SpringBrand for web search and fetch! 🚀');
41
+ const signedIn = isSignedIn();
42
+ if (!signedIn) {
43
+ errLine('\nNote: You are not signed in to SpringBrand yet. The configuration is in place,');
44
+ errLine('but search and fetch will not work until you sign in. Run:\n');
45
+ errLine(' sp auth login');
46
+ }
47
+ out({ status: 'ok', action: 'configured', signed_in: signedIn });
48
+ }
49
+ function remove(settings, claudeMd) {
50
+ if (!isSpringbrandConfiguredInSettings(settings) && !isSpringbrandConfiguredInClaudeMd(claudeMd)) {
51
+ errLine('SpringBrand is not configured in Claude Code. Nothing to remove.');
52
+ out({ status: 'ok', action: 'noop' });
53
+ return;
54
+ }
55
+ errLine('Removing SpringBrand configuration from Claude Code...\n');
56
+ writeSettingsJson(removeFromSettings(settings));
57
+ errLine(' Cleaned settings.json');
58
+ writeClaudeMd(removeFromClaudeMd(claudeMd));
59
+ errLine(' Cleaned CLAUDE.md');
60
+ errLine('\nSpringBrand configuration removed from Claude Code.');
61
+ out({ status: 'ok', action: 'removed' });
62
+ }
63
+ export function registerConfigureClaude(program) {
64
+ program
65
+ .command('config-claude')
66
+ .description('Configure Claude Code to use SpringBrand for web search and fetch')
67
+ .option('--remove', 'Remove SpringBrand configuration from Claude Code')
68
+ .action(async (opts) => {
69
+ const { settings, claudeMd } = loadExistingConfig();
70
+ if (opts.remove) {
71
+ remove(settings, claudeMd);
72
+ }
73
+ else {
74
+ install(settings, claudeMd);
75
+ }
76
+ });
77
+ }
78
+ //# sourceMappingURL=config-claude.js.map