@phuetz/code-buddy 1.1.0 → 1.3.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 (381) hide show
  1. package/README.md +211 -163
  2. package/dist/agent/autonomous/agentic-coding-contract.d.ts +12 -12
  3. package/dist/agent/base-agent.d.ts +4 -0
  4. package/dist/agent/base-agent.js +6 -0
  5. package/dist/agent/codebuddy-agent.d.ts +1 -1
  6. package/dist/agent/codebuddy-agent.js +69 -14
  7. package/dist/agent/execution/agent-executor.js +122 -12
  8. package/dist/agent/execution/tool-dependency-graph.js +7 -0
  9. package/dist/agent/execution/tool-hooks.d.ts +6 -0
  10. package/dist/agent/execution/tool-hooks.js +13 -5
  11. package/dist/agent/execution/tool-selection-strategy.d.ts +11 -2
  12. package/dist/agent/execution/tool-selection-strategy.js +67 -10
  13. package/dist/agent/extended-thinking.d.ts +8 -0
  14. package/dist/agent/extended-thinking.js +27 -0
  15. package/dist/agent/facades/infrastructure-facade.d.ts +9 -2
  16. package/dist/agent/facades/infrastructure-facade.js +15 -6
  17. package/dist/agent/hermes-browser-backends.js +151 -11
  18. package/dist/agent/hermes-claw-migrate.d.ts +59 -0
  19. package/dist/agent/hermes-claw-migrate.js +427 -26
  20. package/dist/agent/hermes-parity-manifest.js +78 -71
  21. package/dist/agent/hermes-runtime-lifecycle.d.ts +67 -0
  22. package/dist/agent/hermes-runtime-lifecycle.js +468 -0
  23. package/dist/agent/middleware/index.d.ts +1 -0
  24. package/dist/agent/middleware/index.js +1 -0
  25. package/dist/agent/middleware/visual-validation-middleware.d.ts +17 -0
  26. package/dist/agent/middleware/visual-validation-middleware.js +67 -0
  27. package/dist/agent/model-benchmark.d.ts +77 -0
  28. package/dist/agent/model-benchmark.js +309 -0
  29. package/dist/agent/model-tier.d.ts +14 -0
  30. package/dist/agent/model-tier.js +71 -0
  31. package/dist/agent/multi-agent/agents/orchestrator-agent.js +17 -6
  32. package/dist/agent/multi-agent/multi-agent-system.js +3 -1
  33. package/dist/agent/multi-agent/session-fleet-bridge.d.ts +2 -0
  34. package/dist/agent/multi-agent/session-fleet-bridge.js +9 -1
  35. package/dist/agent/multi-agent/session-tools.js +22 -5
  36. package/dist/agent/reasoning/mcts.js +20 -6
  37. package/dist/agent/self-improvement/authored-artifact-gate.d.ts +18 -0
  38. package/dist/agent/self-improvement/authored-artifact-gate.js +42 -0
  39. package/dist/agent/self-improvement/authored-tool-runtime.d.ts +27 -0
  40. package/dist/agent/self-improvement/authored-tool-runtime.js +57 -0
  41. package/dist/agent/self-improvement/authored-tool-store.d.ts +24 -0
  42. package/dist/agent/self-improvement/authored-tool-store.js +57 -0
  43. package/dist/agent/self-improvement/llm-tool-proposer.d.ts +41 -0
  44. package/dist/agent/self-improvement/llm-tool-proposer.js +136 -0
  45. package/dist/agent/self-improvement/sandbox-scorer.d.ts +17 -0
  46. package/dist/agent/self-improvement/sandbox-scorer.js +43 -0
  47. package/dist/agent/self-improvement/self-knowledge.d.ts +8 -0
  48. package/dist/agent/self-improvement/self-knowledge.js +24 -0
  49. package/dist/agent/self-improvement/skill-benchmark.d.ts +9 -0
  50. package/dist/agent/self-improvement/skill-benchmark.js +22 -0
  51. package/dist/agent/self-improvement/skill-consolidator.d.ts +71 -0
  52. package/dist/agent/self-improvement/skill-consolidator.js +137 -0
  53. package/dist/agent/self-improvement/skill-engine.d.ts +42 -0
  54. package/dist/agent/self-improvement/skill-engine.js +87 -0
  55. package/dist/agent/self-improvement/skill-gate.d.ts +19 -0
  56. package/dist/agent/self-improvement/skill-gate.js +62 -0
  57. package/dist/agent/self-improvement/skill-mutator.d.ts +74 -0
  58. package/dist/agent/self-improvement/skill-mutator.js +223 -0
  59. package/dist/agent/self-improvement/skill-proposer.d.ts +40 -0
  60. package/dist/agent/self-improvement/skill-proposer.js +82 -0
  61. package/dist/agent/self-improvement/skill-types.d.ts +41 -0
  62. package/dist/agent/self-improvement/skill-types.js +13 -0
  63. package/dist/agent/self-improvement/tool-benchmark.d.ts +10 -0
  64. package/dist/agent/self-improvement/tool-benchmark.js +37 -0
  65. package/dist/agent/self-improvement/tool-engine.d.ts +54 -0
  66. package/dist/agent/self-improvement/tool-engine.js +101 -0
  67. package/dist/agent/self-improvement/tool-gate.d.ts +20 -0
  68. package/dist/agent/self-improvement/tool-gate.js +78 -0
  69. package/dist/agent/self-improvement/tool-proposer.d.ts +31 -0
  70. package/dist/agent/self-improvement/tool-proposer.js +34 -0
  71. package/dist/agent/self-improvement/tool-skill-mutator.d.ts +40 -0
  72. package/dist/agent/self-improvement/tool-skill-mutator.js +79 -0
  73. package/dist/agent/self-improvement/tool-types.d.ts +48 -0
  74. package/dist/agent/self-improvement/tool-types.js +9 -0
  75. package/dist/agent/self-improvement/types.d.ts +3 -1
  76. package/dist/agent/session-end-flush.d.ts +6 -1
  77. package/dist/agent/session-end-flush.js +20 -4
  78. package/dist/agent/streaming/streaming-handler.d.ts +7 -0
  79. package/dist/agent/streaming/streaming-handler.js +8 -1
  80. package/dist/agent/tool-executor.js +1 -0
  81. package/dist/agent/tool-handler.d.ts +5 -0
  82. package/dist/agent/tool-handler.js +133 -4
  83. package/dist/browser-automation/browser-operator-executor.js +15 -0
  84. package/dist/browser-automation/browser-use-runner.d.ts +96 -0
  85. package/dist/browser-automation/browser-use-runner.js +492 -0
  86. package/dist/browser-automation/camofox-runner.d.ts +107 -0
  87. package/dist/browser-automation/camofox-runner.js +287 -0
  88. package/dist/channels/dingtalk/index.js +3 -0
  89. package/dist/channels/discord/client.d.ts +9 -0
  90. package/dist/channels/discord/client.js +12 -0
  91. package/dist/channels/feishu/index.d.ts +167 -1
  92. package/dist/channels/feishu/index.js +432 -7
  93. package/dist/channels/gateway-lifecycle.d.ts +132 -0
  94. package/dist/channels/gateway-lifecycle.js +219 -0
  95. package/dist/channels/google-chat/index.d.ts +5 -1
  96. package/dist/channels/google-chat/index.js +5 -1
  97. package/dist/channels/imessage/index.d.ts +14 -0
  98. package/dist/channels/imessage/index.js +64 -17
  99. package/dist/channels/index.d.ts +4 -0
  100. package/dist/channels/index.js +4 -0
  101. package/dist/channels/irc/index.d.ts +73 -3
  102. package/dist/channels/irc/index.js +446 -11
  103. package/dist/channels/line/index.js +3 -0
  104. package/dist/channels/mattermost/index.d.ts +51 -4
  105. package/dist/channels/mattermost/index.js +303 -20
  106. package/dist/channels/nextcloud-talk/index.d.ts +89 -8
  107. package/dist/channels/nextcloud-talk/index.js +367 -16
  108. package/dist/channels/nostr/index.d.ts +121 -1
  109. package/dist/channels/nostr/index.js +396 -8
  110. package/dist/channels/ntfy/index.js +3 -0
  111. package/dist/channels/qq/index.js +3 -0
  112. package/dist/channels/slash-parity.d.ts +117 -0
  113. package/dist/channels/slash-parity.js +185 -0
  114. package/dist/channels/synology-chat/index.js +3 -0
  115. package/dist/channels/teams/index.d.ts +3 -0
  116. package/dist/channels/teams/index.js +3 -0
  117. package/dist/channels/telegram/client.d.ts +9 -0
  118. package/dist/channels/telegram/client.js +12 -0
  119. package/dist/channels/twilio-voice/index.js +3 -0
  120. package/dist/channels/webchat/index.d.ts +7 -1
  121. package/dist/channels/webchat/index.js +7 -1
  122. package/dist/channels/wecom/index.js +3 -0
  123. package/dist/channels/weixin/index.js +3 -0
  124. package/dist/channels/zalo/index.js +3 -0
  125. package/dist/cli/config-loader.js +1 -1
  126. package/dist/codebuddy/client.d.ts +30 -1
  127. package/dist/codebuddy/client.js +167 -20
  128. package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +5 -0
  129. package/dist/codebuddy/providers/provider-chatgpt-responses.js +7 -1
  130. package/dist/codebuddy/providers/provider-gemini-native.js +1 -1
  131. package/dist/codebuddy/providers/provider-openai-compat.d.ts +5 -0
  132. package/dist/codebuddy/providers/provider-openai-compat.js +82 -2
  133. package/dist/codebuddy/tool-definitions/computer-control-tools.js +32 -2
  134. package/dist/codebuddy/tool-definitions/core-tools.js +3 -3
  135. package/dist/codebuddy/tool-definitions/index.d.ts +2 -1
  136. package/dist/codebuddy/tool-definitions/index.js +2 -1
  137. package/dist/codebuddy/tool-definitions/multimodal-tools.d.ts +1 -0
  138. package/dist/codebuddy/tool-definitions/multimodal-tools.js +44 -1
  139. package/dist/codebuddy/tool-definitions/windows-tools.d.ts +2 -0
  140. package/dist/codebuddy/tool-definitions/windows-tools.js +34 -0
  141. package/dist/codebuddy/tools.d.ts +8 -1
  142. package/dist/codebuddy/tools.js +67 -24
  143. package/dist/commands/cli/hermes-commands.js +108 -0
  144. package/dist/commands/cli/improve-command.js +123 -0
  145. package/dist/commands/cli/native-engine-commands.js +102 -3
  146. package/dist/commands/cli/utility-commands.js +45 -0
  147. package/dist/commands/enhanced-command-handler.js +2 -2
  148. package/dist/commands/goal-cli.d.ts +30 -0
  149. package/dist/commands/goal-cli.js +210 -27
  150. package/dist/commands/handlers/goal-handler.d.ts +9 -2
  151. package/dist/commands/handlers/goal-handler.js +36 -9
  152. package/dist/commands/handlers/infra-handlers.js +36 -0
  153. package/dist/commands/handlers/memory-handlers.js +96 -1
  154. package/dist/commands/handlers/missing-handlers.d.ts +1 -1
  155. package/dist/commands/handlers/missing-handlers.js +26 -3
  156. package/dist/commands/headless-slash.d.ts +2 -0
  157. package/dist/commands/headless-slash.js +12 -0
  158. package/dist/commands/llm-provider-resolution.d.ts +5 -3
  159. package/dist/commands/llm-provider-resolution.js +87 -33
  160. package/dist/commands/ollama.d.ts +25 -0
  161. package/dist/commands/ollama.js +100 -0
  162. package/dist/commands/provider.d.ts +5 -0
  163. package/dist/commands/provider.js +106 -55
  164. package/dist/commands/skills-cli/index.js +123 -0
  165. package/dist/commands/slash/builtin-commands.js +1 -1
  166. package/dist/commands/spec-next.js +2 -1
  167. package/dist/commands/spec-plan.js +5 -16
  168. package/dist/commands/tunnel.d.ts +2 -0
  169. package/dist/commands/tunnel.js +48 -0
  170. package/dist/companion/percepts.js +11 -1
  171. package/dist/config/config-resolver.d.ts +2 -1
  172. package/dist/config/config-resolver.js +54 -41
  173. package/dist/config/constants.d.ts +28 -0
  174. package/dist/config/constants.js +7 -0
  175. package/dist/config/env-schema.js +756 -0
  176. package/dist/config/model-tools.js +4 -4
  177. package/dist/config/toml-config.d.ts +16 -0
  178. package/dist/config/toml-config.js +3 -0
  179. package/dist/context/bootstrap-loader.js +6 -23
  180. package/dist/context/context-manager-v2.js +1 -0
  181. package/dist/context/import-directive-parser.d.ts +4 -0
  182. package/dist/context/import-directive-parser.js +51 -6
  183. package/dist/context/instruction-excludes.d.ts +30 -1
  184. package/dist/context/instruction-excludes.js +71 -1
  185. package/dist/context/jit-context.d.ts +8 -10
  186. package/dist/context/jit-context.js +28 -106
  187. package/dist/context/project-context.d.ts +90 -0
  188. package/dist/context/project-context.js +295 -0
  189. package/dist/daemon/autonomous-daemon.d.ts +1 -1
  190. package/dist/daemon/autonomous-daemon.js +3 -3
  191. package/dist/daemon/autonomous-loop.d.ts +31 -1
  192. package/dist/daemon/autonomous-loop.js +91 -4
  193. package/dist/daemon/colab-goal.js +9 -1
  194. package/dist/daemon/cron-agent-bridge.d.ts +12 -3
  195. package/dist/daemon/cron-agent-bridge.js +25 -9
  196. package/dist/desktop/codebuddy-engine-adapter.d.ts +32 -0
  197. package/dist/desktop/codebuddy-engine-adapter.js +257 -80
  198. package/dist/desktop/engine-adapter.d.ts +14 -0
  199. package/dist/desktop-automation/automation-manager.js +16 -0
  200. package/dist/desktop-automation/omniparser-runner.d.ts +63 -0
  201. package/dist/desktop-automation/omniparser-runner.js +115 -0
  202. package/dist/desktop-automation/smart-snapshot.d.ts +11 -0
  203. package/dist/desktop-automation/smart-snapshot.js +72 -5
  204. package/dist/doctor/index.js +40 -23
  205. package/dist/events/types.d.ts +11 -0
  206. package/dist/fleet/colab-store.js +14 -2
  207. package/dist/fleet/peer-session-bridge.js +50 -12
  208. package/dist/fleet/peer-tool-bridge.js +14 -0
  209. package/dist/goals/goal-decomposer.d.ts +27 -0
  210. package/dist/goals/goal-decomposer.js +293 -0
  211. package/dist/goals/goal-judge-client.d.ts +15 -0
  212. package/dist/goals/goal-judge-client.js +45 -0
  213. package/dist/goals/goal-judge.d.ts +2 -0
  214. package/dist/goals/goal-judge.js +35 -6
  215. package/dist/goals/goal-loop.d.ts +14 -0
  216. package/dist/goals/goal-loop.js +47 -2
  217. package/dist/goals/goal-manager.d.ts +6 -0
  218. package/dist/goals/goal-manager.js +67 -14
  219. package/dist/goals/goal-state.d.ts +13 -0
  220. package/dist/goals/goal-state.js +83 -13
  221. package/dist/goals/goal-store.d.ts +3 -0
  222. package/dist/goals/goal-store.js +31 -15
  223. package/dist/harness/contract.d.ts +28 -28
  224. package/dist/hooks/user-hooks.js +17 -3
  225. package/dist/identity/identity-manager.js +3 -2
  226. package/dist/index.js +481 -26
  227. package/dist/input/text-to-speech.d.ts +2 -6
  228. package/dist/input/text-to-speech.js +2 -27
  229. package/dist/integrations/screenpipe/screenpipe-client.d.ts +10 -0
  230. package/dist/integrations/screenpipe/screenpipe-client.js +12 -2
  231. package/dist/integrations/tailscale.d.ts +13 -0
  232. package/dist/integrations/tailscale.js +87 -34
  233. package/dist/knowledge/workspace-indexer.js +53 -9
  234. package/dist/mcp/client.js +1 -0
  235. package/dist/mcp/mcp-desktop-tools.d.ts +23 -0
  236. package/dist/mcp/mcp-desktop-tools.js +158 -0
  237. package/dist/mcp/mcp-resources.js +2 -3
  238. package/dist/mcp/mcp-server.js +2 -0
  239. package/dist/memory/enhanced-memory.d.ts +10 -0
  240. package/dist/memory/enhanced-memory.js +33 -2
  241. package/dist/memory/index.d.ts +3 -1
  242. package/dist/memory/index.js +4 -1
  243. package/dist/memory/memory-auto-proposer.d.ts +23 -0
  244. package/dist/memory/memory-auto-proposer.js +308 -0
  245. package/dist/memory/memory-candidate-queue.d.ts +92 -0
  246. package/dist/memory/memory-candidate-queue.js +261 -0
  247. package/dist/memory/persistent-memory.d.ts +45 -2
  248. package/dist/memory/persistent-memory.js +236 -40
  249. package/dist/ml/bayesian-qualifier.d.ts +1 -1
  250. package/dist/ml/bayesian-qualifier.js +35 -2
  251. package/dist/openclaw/gateway-bridge.js +5 -0
  252. package/dist/plugins/marketplace.d.ts +1 -0
  253. package/dist/plugins/marketplace.js +7 -0
  254. package/dist/prompts/execution-discipline.d.ts +14 -0
  255. package/dist/prompts/execution-discipline.js +29 -0
  256. package/dist/prompts/variation-injector.js +9 -2
  257. package/dist/protocols/acp/acp-agentic-runner.d.ts +7 -6
  258. package/dist/protocols/acp/acp-agentic-runner.js +115 -18
  259. package/dist/protocols/acp/acp-session-store.d.ts +23 -0
  260. package/dist/protocols/acp/acp-session-store.js +77 -0
  261. package/dist/protocols/acp/acp-stdio-server.d.ts +7 -0
  262. package/dist/protocols/acp/acp-stdio-server.js +71 -16
  263. package/dist/providers/active-llm-registry.d.ts +37 -0
  264. package/dist/providers/active-llm-registry.js +186 -0
  265. package/dist/providers/auxiliary-provider.d.ts +25 -0
  266. package/dist/providers/auxiliary-provider.js +192 -0
  267. package/dist/providers/codex-oauth.d.ts +1 -1
  268. package/dist/providers/codex-oauth.js +27 -4
  269. package/dist/providers/index.d.ts +3 -0
  270. package/dist/providers/index.js +4 -0
  271. package/dist/providers/provider-catalog.d.ts +70 -0
  272. package/dist/providers/provider-catalog.js +738 -0
  273. package/dist/providers/provider-fallback.d.ts +35 -0
  274. package/dist/providers/provider-fallback.js +171 -0
  275. package/dist/providers/xai-oauth.d.ts +128 -0
  276. package/dist/providers/xai-oauth.js +735 -0
  277. package/dist/scheduler/cron-scheduler.d.ts +15 -3
  278. package/dist/scheduler/cron-scheduler.js +53 -7
  279. package/dist/scheduler/script-runner.d.ts +2 -0
  280. package/dist/scheduler/script-runner.js +19 -4
  281. package/dist/scheduler/watchdog-handlers.js +7 -20
  282. package/dist/search/usearch-index.js +7 -2
  283. package/dist/security/tool-policy/tool-groups.js +2 -0
  284. package/dist/sensory/dreaming.d.ts +45 -0
  285. package/dist/sensory/dreaming.js +114 -0
  286. package/dist/sensory/heartbeat-scheduler.d.ts +38 -0
  287. package/dist/sensory/heartbeat-scheduler.js +72 -0
  288. package/dist/sensory/reactions.d.ts +24 -0
  289. package/dist/sensory/reactions.js +31 -0
  290. package/dist/sensory/screen-reaction.d.ts +23 -0
  291. package/dist/sensory/screen-reaction.js +59 -0
  292. package/dist/sensory/sensory-bridge.d.ts +23 -0
  293. package/dist/sensory/sensory-bridge.js +85 -0
  294. package/dist/sensory/sensory-memory.d.ts +20 -0
  295. package/dist/sensory/sensory-memory.js +39 -0
  296. package/dist/sensory/speech-reaction.d.ts +21 -0
  297. package/dist/sensory/speech-reaction.js +83 -0
  298. package/dist/sensory/vision-reaction.d.ts +31 -0
  299. package/dist/sensory/vision-reaction.js +74 -0
  300. package/dist/server/index.js +106 -2
  301. package/dist/server/routes/mobile.d.ts +12 -4
  302. package/dist/server/routes/mobile.js +116 -1
  303. package/dist/server/tls-config.d.ts +35 -0
  304. package/dist/server/tls-config.js +142 -0
  305. package/dist/server/tunnel-manager.d.ts +20 -0
  306. package/dist/server/tunnel-manager.js +58 -0
  307. package/dist/server/websocket/fleet-bridge.js +5 -0
  308. package/dist/services/prompt-builder.d.ts +11 -0
  309. package/dist/services/prompt-builder.js +124 -17
  310. package/dist/shared/engine-types.d.ts +15 -1
  311. package/dist/sidecar/sidecar-bridge.d.ts +1 -0
  312. package/dist/sidecar/sidecar-bridge.js +21 -10
  313. package/dist/skills/parser.js +3 -0
  314. package/dist/skills/skill-importer.d.ts +58 -0
  315. package/dist/skills/skill-importer.js +261 -0
  316. package/dist/skills/skill-sources.d.ts +20 -0
  317. package/dist/skills/skill-sources.js +102 -0
  318. package/dist/skills/types.d.ts +6 -0
  319. package/dist/spec/spec-store.js +8 -1
  320. package/dist/talk-mode/providers/audioreader-tts.d.ts +1 -1
  321. package/dist/talk-mode/providers/audioreader-tts.js +8 -8
  322. package/dist/templates/project-scaffolding.js +3 -2
  323. package/dist/themes/theme-schema.d.ts +10 -10
  324. package/dist/tools/application-profiles.js +38 -0
  325. package/dist/tools/bash/bash-tool.d.ts +1 -0
  326. package/dist/tools/bash/bash-tool.js +16 -2
  327. package/dist/tools/bash/command-validator.js +3 -0
  328. package/dist/tools/bash/rtk-rewrite.d.ts +16 -0
  329. package/dist/tools/bash/rtk-rewrite.js +100 -0
  330. package/dist/tools/bash/streaming-executor.js +14 -1
  331. package/dist/tools/computer-control-tool.d.ts +16 -1
  332. package/dist/tools/computer-control-tool.js +317 -5
  333. package/dist/tools/document-generator.d.ts +14 -0
  334. package/dist/tools/document-generator.js +79 -0
  335. package/dist/tools/execute-code-rpc-invoker.d.ts +26 -2
  336. package/dist/tools/execute-code-rpc-invoker.js +46 -5
  337. package/dist/tools/metadata.js +28 -0
  338. package/dist/tools/office-macro-tool.d.ts +10 -0
  339. package/dist/tools/office-macro-tool.js +93 -0
  340. package/dist/tools/register-tool-handler.d.ts +25 -0
  341. package/dist/tools/register-tool-handler.js +100 -0
  342. package/dist/tools/registry/index.d.ts +2 -1
  343. package/dist/tools/registry/index.js +2 -1
  344. package/dist/tools/registry/memory-tools.d.ts +20 -0
  345. package/dist/tools/registry/memory-tools.js +225 -3
  346. package/dist/tools/registry/multimodal-tools.js +2 -2
  347. package/dist/tools/registry/vision-tools.d.ts +33 -0
  348. package/dist/tools/registry/vision-tools.js +308 -6
  349. package/dist/tools/registry/windows-tools.d.ts +2 -0
  350. package/dist/tools/registry/windows-tools.js +47 -0
  351. package/dist/tools/registry.d.ts +6 -0
  352. package/dist/tools/registry.js +8 -0
  353. package/dist/tools/route-peer-tool.js +14 -0
  354. package/dist/tools/screenshot-tool.js +14 -2
  355. package/dist/tools/text-to-speech-tool.d.ts +1 -1
  356. package/dist/tools/text-to-speech-tool.js +2 -12
  357. package/dist/tools/tool-selector.js +3 -59
  358. package/dist/utils/config-validation/schema.d.ts +40 -7
  359. package/dist/utils/config-validation/schema.js +130 -1
  360. package/dist/utils/cost-tracker.js +21 -1
  361. package/dist/utils/disk-guard.d.ts +180 -0
  362. package/dist/utils/disk-guard.js +404 -0
  363. package/dist/utils/init-project.d.ts +7 -0
  364. package/dist/utils/init-project.js +37 -0
  365. package/dist/utils/interactive-setup.js +35 -2
  366. package/dist/utils/model-utils.js +11 -2
  367. package/dist/utils/output-sanitizer.js +11 -0
  368. package/dist/utils/provider-detector.d.ts +8 -7
  369. package/dist/utils/provider-detector.js +19 -77
  370. package/dist/utils/settings-manager.d.ts +19 -0
  371. package/dist/utils/token-display.d.ts +3 -3
  372. package/dist/utils/token-display.js +22 -4
  373. package/dist/wizard/onboarding.d.ts +39 -0
  374. package/dist/wizard/onboarding.js +433 -21
  375. package/dist/wizard/provider-onboarding.d.ts +8 -23
  376. package/dist/wizard/provider-onboarding.js +39 -158
  377. package/package.json +4 -2
  378. package/dist/plugins/provider-onboarding.d.ts +0 -23
  379. package/dist/plugins/provider-onboarding.js +0 -116
  380. package/dist/utils/kokoro-tts.d.ts +0 -8
  381. package/dist/utils/kokoro-tts.js +0 -67
@@ -1,23 +1,157 @@
1
1
  import * as readline from 'readline';
2
2
  import { mkdirSync, writeFileSync } from 'fs';
3
3
  import { join } from 'path';
4
+ import { getValidationConfigForGuide, validateProviderKey, } from './provider-onboarding.js';
4
5
  export const PROVIDER_ENV_MAP = {
6
+ chatgpt: '',
5
7
  grok: 'GROK_API_KEY',
6
8
  claude: 'ANTHROPIC_API_KEY',
7
- chatgpt: 'OPENAI_API_KEY',
8
9
  gemini: 'GEMINI_API_KEY',
10
+ openai: 'OPENAI_API_KEY',
11
+ openrouter: 'OPENROUTER_API_KEY',
9
12
  ollama: '',
10
13
  lmstudio: '',
11
14
  };
12
15
  export const PROVIDER_DEFAULT_MODEL = {
16
+ chatgpt: 'gpt-5.5',
13
17
  grok: 'grok-3',
14
18
  claude: 'claude-sonnet-4-20250514',
15
- chatgpt: 'gpt-4o',
16
19
  gemini: 'gemini-2.0-flash',
20
+ openai: 'gpt-4o',
21
+ openrouter: 'openai/gpt-4o',
17
22
  ollama: 'llama3',
18
23
  lmstudio: 'default',
19
24
  };
20
- const PROVIDERS = ['grok', 'claude', 'chatgpt', 'gemini', 'ollama', 'lmstudio'];
25
+ export const PROVIDER_AUTH_MODE = {
26
+ chatgpt: 'oauth',
27
+ grok: 'api-key',
28
+ claude: 'api-key',
29
+ gemini: 'api-key',
30
+ openai: 'api-key',
31
+ openrouter: 'api-key',
32
+ ollama: 'local',
33
+ lmstudio: 'local',
34
+ };
35
+ export const PROVIDER_GUIDES = [
36
+ {
37
+ id: 'chatgpt',
38
+ label: 'ChatGPT subscription (OAuth)',
39
+ authMode: 'oauth',
40
+ envVar: '',
41
+ defaultModel: 'gpt-5.5',
42
+ setupCommand: 'buddy login',
43
+ verifyCommand: 'buddy whoami',
44
+ help: 'One browser login unlocks the ChatGPT-backed Codex route; no OPENAI_API_KEY is required.',
45
+ },
46
+ {
47
+ id: 'grok',
48
+ label: 'Grok / xAI API key',
49
+ authMode: 'api-key',
50
+ envVar: 'GROK_API_KEY',
51
+ defaultModel: 'grok-3',
52
+ baseURL: 'https://api.x.ai/v1',
53
+ verifyCommand: 'buddy doctor',
54
+ help: 'Set GROK_API_KEY in your shell or secret manager.',
55
+ },
56
+ {
57
+ id: 'claude',
58
+ label: 'Anthropic Claude API key',
59
+ authMode: 'api-key',
60
+ envVar: 'ANTHROPIC_API_KEY',
61
+ defaultModel: 'claude-sonnet-4-20250514',
62
+ baseURL: 'https://api.anthropic.com/v1',
63
+ verifyCommand: 'buddy doctor',
64
+ help: 'Set ANTHROPIC_API_KEY in your shell or secret manager.',
65
+ },
66
+ {
67
+ id: 'gemini',
68
+ label: 'Google Gemini API key',
69
+ authMode: 'api-key',
70
+ envVar: 'GEMINI_API_KEY',
71
+ defaultModel: 'gemini-2.0-flash',
72
+ baseURL: 'https://generativelanguage.googleapis.com/v1beta',
73
+ verifyCommand: 'buddy doctor',
74
+ help: 'Set GEMINI_API_KEY in your shell or secret manager.',
75
+ },
76
+ {
77
+ id: 'openai',
78
+ label: 'OpenAI API key',
79
+ authMode: 'api-key',
80
+ envVar: 'OPENAI_API_KEY',
81
+ defaultModel: 'gpt-4o',
82
+ baseURL: 'https://api.openai.com/v1',
83
+ verifyCommand: 'buddy doctor',
84
+ help: 'Set OPENAI_API_KEY (https://platform.openai.com/api-keys).',
85
+ },
86
+ {
87
+ id: 'openrouter',
88
+ label: 'OpenRouter API key',
89
+ authMode: 'api-key',
90
+ envVar: 'OPENROUTER_API_KEY',
91
+ defaultModel: 'openai/gpt-4o',
92
+ baseURL: 'https://openrouter.ai/api/v1',
93
+ verifyCommand: 'buddy doctor',
94
+ help: 'Set OPENROUTER_API_KEY (https://openrouter.ai/keys).',
95
+ },
96
+ {
97
+ id: 'ollama',
98
+ label: 'Ollama local model',
99
+ authMode: 'local',
100
+ envVar: '',
101
+ defaultModel: 'llama3',
102
+ baseURL: 'http://localhost:11434/v1',
103
+ setupCommand: 'ollama serve',
104
+ verifyCommand: 'curl http://localhost:11434/api/tags',
105
+ help: 'Run Ollama locally and pull the model you selected.',
106
+ },
107
+ {
108
+ id: 'lmstudio',
109
+ label: 'LM Studio local server',
110
+ authMode: 'local',
111
+ envVar: '',
112
+ defaultModel: 'default',
113
+ baseURL: 'http://localhost:1234/v1',
114
+ verifyCommand: 'curl http://localhost:1234/v1/models',
115
+ help: 'Start the LM Studio local server before launching Code Buddy.',
116
+ },
117
+ ];
118
+ export const ONBOARDING_PHASES = [
119
+ {
120
+ id: 'install',
121
+ title: 'Install and diagnose',
122
+ hermesPhase: 'Install Hermes Agent, then run doctor when anything looks off.',
123
+ codeBuddyAction: 'Install Code Buddy, then run buddy doctor.',
124
+ successCheck: 'doctor reports Node.js and core dependencies as usable.',
125
+ },
126
+ {
127
+ id: 'provider',
128
+ title: 'Choose provider and authenticate',
129
+ hermesPhase: 'Choose a provider; the fastest path is hermes setup --portal.',
130
+ codeBuddyAction: 'Prefer buddy login for ChatGPT OAuth, or configure an API/local provider.',
131
+ successCheck: 'buddy whoami or buddy doctor confirms the selected credential source.',
132
+ },
133
+ {
134
+ id: 'first-chat',
135
+ title: 'Run a verifiable first chat',
136
+ hermesPhase: 'Start the CLI/TUI and ask a specific prompt with observable success.',
137
+ codeBuddyAction: 'Run buddy with a repo summary prompt.',
138
+ successCheck: 'the answer names files or tools from the current workspace.',
139
+ },
140
+ {
141
+ id: 'session-resume',
142
+ title: 'Verify session resume',
143
+ hermesPhase: 'Run --continue before moving to advanced workflows.',
144
+ codeBuddyAction: 'Run buddy --continue or buddy session list.',
145
+ successCheck: 'the previous session is visible and resumable.',
146
+ },
147
+ {
148
+ id: 'next-layer',
149
+ title: 'Add the next layer',
150
+ hermesPhase: 'Only after base chat works, add tools, skills, gateway, MCP, voice, or sandboxing.',
151
+ codeBuddyAction: 'Pick one: buddy --init, buddy server, buddy skills, Cowork, Fleet, companion, or sandbox mode.',
152
+ successCheck: 'the selected layer has a doctor/status command or a focused smoke prompt.',
153
+ },
154
+ ];
21
155
  const TTS_PROVIDERS = ['edge-tts', 'espeak', 'audioreader'];
22
156
  function ask(rl, question, defaultValue) {
23
157
  return new Promise((resolve) => {
@@ -38,18 +172,217 @@ function askChoice(rl, question, choices, defaultIdx) {
38
172
  });
39
173
  });
40
174
  }
175
+ /** Read a secret without echoing it (masks keystrokes with '*'). The captured
176
+ * value is always correct regardless of echo; masking is cosmetic, and falls
177
+ * back to a visible prompt if the terminal doesn't support muting. */
178
+ function askSecret(rl, question) {
179
+ return new Promise((resolve) => {
180
+ const rlAny = rl;
181
+ const original = rlAny._writeToOutput?.bind(rl);
182
+ let muted = false;
183
+ if (typeof original === 'function') {
184
+ rlAny._writeToOutput = (str) => {
185
+ if (!muted || /[\r\n]/.test(str))
186
+ original(str);
187
+ else
188
+ original('*'.repeat(str.length || 1));
189
+ };
190
+ }
191
+ rl.question(` ${question}: `, (answer) => {
192
+ if (typeof original === 'function')
193
+ rlAny._writeToOutput = original;
194
+ resolve(answer.trim());
195
+ });
196
+ muted = true;
197
+ });
198
+ }
199
+ /**
200
+ * Persist the chosen provider so the session works now AND on the next run:
201
+ * - provider + baseURL + model → `~/.codebuddy/user-settings.json`
202
+ * (loadBaseURL/loadModel read it; the provider strategy is picked from baseURL).
203
+ * - an entered API key → encrypted credential store (never plaintext in settings),
204
+ * plus `process.env[envVar]` so the current process resolves immediately.
205
+ * - ollama → default `OLLAMA_HOST` so auto-detection picks it up.
206
+ * All writes are best-effort; the wizard summary still prints on failure.
207
+ */
208
+ export async function persistProviderSelection(guide, model, apiKey) {
209
+ try {
210
+ const { getSettingsManager } = await import('../utils/settings-manager.js');
211
+ getSettingsManager().saveUserSettings({
212
+ provider: guide.id,
213
+ defaultModel: model,
214
+ model,
215
+ ...(guide.baseURL ? { baseURL: guide.baseURL } : {}),
216
+ });
217
+ }
218
+ catch { /* non-fatal */ }
219
+ if (guide.authMode === 'api-key' && apiKey) {
220
+ try {
221
+ const { getCredentialManager } = await import('../security/credential-manager.js');
222
+ getCredentialManager().setApiKey(apiKey);
223
+ }
224
+ catch { /* non-fatal */ }
225
+ if (guide.envVar)
226
+ process.env[guide.envVar] = apiKey;
227
+ }
228
+ if (guide.id === 'ollama' && !process.env.OLLAMA_HOST) {
229
+ process.env.OLLAMA_HOST = 'http://localhost:11434';
230
+ }
231
+ }
41
232
  export function writeConfig(configDir, result) {
42
233
  mkdirSync(configDir, { recursive: true });
234
+ const authMode = result.authMode ?? getProviderGuide(result.provider).authMode;
235
+ const recommendedNextCommands = result.recommendedNextCommands ?? buildRecommendedNextCommands(result);
43
236
  const config = {
44
237
  provider: result.provider,
45
238
  model: result.model,
239
+ authMode,
46
240
  ttsEnabled: result.ttsEnabled,
241
+ onboarding: {
242
+ version: 1,
243
+ phases: ONBOARDING_PHASES.map((phase) => phase.id),
244
+ recommendedNextCommands,
245
+ },
47
246
  };
48
247
  if (result.ttsProvider) {
49
248
  config.ttsProvider = result.ttsProvider;
50
249
  }
51
250
  writeFileSync(join(configDir, 'config.json'), JSON.stringify(config, null, 2) + '\n');
52
251
  }
252
+ export function getProviderGuide(provider) {
253
+ return (PROVIDER_GUIDES.find((guide) => guide.id === provider)
254
+ ?? PROVIDER_GUIDES.find((guide) => guide.id === 'chatgpt')
255
+ ?? {
256
+ id: 'chatgpt',
257
+ label: 'ChatGPT subscription (OAuth)',
258
+ authMode: 'oauth',
259
+ envVar: '',
260
+ defaultModel: 'gpt-5.5',
261
+ setupCommand: 'buddy login',
262
+ verifyCommand: 'buddy whoami',
263
+ help: 'Use ChatGPT OAuth.',
264
+ });
265
+ }
266
+ export function buildRecommendedNextCommands(result) {
267
+ const guide = getProviderGuide(result.provider);
268
+ const commands = [];
269
+ if (guide.authMode === 'oauth') {
270
+ commands.push(guide.setupCommand ?? 'buddy login');
271
+ commands.push(guide.verifyCommand);
272
+ }
273
+ else if (guide.authMode === 'api-key' && guide.envVar && !result.apiKey) {
274
+ commands.push(`export ${guide.envVar}=<your_api_key>`);
275
+ commands.push(guide.verifyCommand);
276
+ }
277
+ else if (guide.authMode === 'local') {
278
+ if (guide.setupCommand)
279
+ commands.push(guide.setupCommand);
280
+ commands.push(guide.verifyCommand);
281
+ }
282
+ else {
283
+ commands.push(guide.verifyCommand);
284
+ }
285
+ commands.push(`buddy --model ${result.model} -p "Summarize this repo in 5 bullets and name the main entry point."`);
286
+ commands.push('buddy --continue');
287
+ commands.push('buddy --init');
288
+ return Array.from(new Set(commands));
289
+ }
290
+ export function renderOnboardingRoadmap(result) {
291
+ const nextCommands = result ? buildRecommendedNextCommands(result) : [];
292
+ const lines = [
293
+ ' Hermes-style onboarding phases:',
294
+ ...ONBOARDING_PHASES.map((phase, index) => ` ${index + 1}. ${phase.title} — ${phase.codeBuddyAction}`),
295
+ ];
296
+ if (nextCommands.length) {
297
+ lines.push('', ' Recommended next commands:');
298
+ nextCommands.forEach((command) => lines.push(` ${command}`));
299
+ }
300
+ return lines.join('\n');
301
+ }
302
+ /**
303
+ * Capture an API key and verify it against the provider's `/models` endpoint
304
+ * before we persist it (Hermes parity: a bad key is rejected here, not saved to
305
+ * silently break the first chat). Up to 3 attempts. On a connectivity error
306
+ * (not an auth rejection) the last-entered key can be kept and verified later,
307
+ * so a transient network blip doesn't discard an otherwise-good key.
308
+ */
309
+ async function captureAndValidateKey(rl, guide, config) {
310
+ const maxAttempts = 3;
311
+ let lastKey = '';
312
+ let lastErrorWasAuth = true;
313
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
314
+ const key = await askSecret(rl, `Enter your ${guide.envVar} (or press Enter to set it later)`);
315
+ if (!key)
316
+ break;
317
+ lastKey = key;
318
+ if (!config) {
319
+ // No known validation endpoint — accept as entered, can't probe.
320
+ return { apiKey: key, verified: false };
321
+ }
322
+ console.log(' Validating…');
323
+ const result = await validateProviderKey(config, key);
324
+ if (result.valid) {
325
+ console.log(` ✓ Key verified — ${guide.label} reachable.`);
326
+ return { apiKey: key, verified: true, ...(result.models ? { models: result.models } : {}) };
327
+ }
328
+ lastErrorWasAuth = (result.error ?? '').toLowerCase().includes('invalid api key');
329
+ const left = maxAttempts - attempt;
330
+ console.log(` ✗ ${result.error}`);
331
+ if (left > 0) {
332
+ console.log(` Try again (${left} attempt${left === 1 ? '' : 's'} left), or press Enter to skip.`);
333
+ }
334
+ }
335
+ // Exhausted/skipped. If the failure was connectivity (not a rejected key),
336
+ // offer to keep the entered key and verify on the next run.
337
+ if (lastKey && !lastErrorWasAuth) {
338
+ const keep = (await ask(rl, 'Could not reach the provider. Save this key anyway and verify later? (y/N)', 'n'))
339
+ .toLowerCase();
340
+ if (keep === 'y' || keep === 'yes') {
341
+ return { apiKey: lastKey, verified: false };
342
+ }
343
+ }
344
+ return { apiKey: '', verified: false };
345
+ }
346
+ /**
347
+ * Pick a model. When the provider handed us a real model list (from the
348
+ * validation probe), choose from it; otherwise fall back to free-text entry.
349
+ */
350
+ async function selectModel(rl, guide, availableModels) {
351
+ const models = (availableModels ?? []).filter(Boolean);
352
+ if (models.length === 0) {
353
+ return ask(rl, 'Which model do you want to use?', guide.defaultModel);
354
+ }
355
+ const CUSTOM = '✏️ Enter a custom model id…';
356
+ const MAX_SHOWN = 30;
357
+ const shown = models.slice(0, MAX_SHOWN);
358
+ if (models.length > MAX_SHOWN) {
359
+ console.log(`\n (${models.length} models available; showing the first ${MAX_SHOWN} — pick "custom" to type any id.)`);
360
+ }
361
+ const choices = [...shown, CUSTOM];
362
+ const defaultIdx = shown.indexOf(guide.defaultModel) >= 0 ? shown.indexOf(guide.defaultModel) : 0;
363
+ const picked = await askChoice(rl, 'Which model do you want to use?', choices, defaultIdx);
364
+ if (picked === CUSTOM) {
365
+ return ask(rl, 'Enter the model id', guide.defaultModel);
366
+ }
367
+ return picked;
368
+ }
369
+ /**
370
+ * A light "what's enabled now / add later" capability snapshot for the closing
371
+ * summary — the Hermes-style reassurance that the agent is ready, derived from
372
+ * env vars actually present. Kept deliberately minimal (getting-started scope).
373
+ */
374
+ function renderCapabilitiesFooter() {
375
+ const has = (vars) => vars.some((v) => Boolean(process.env[v]));
376
+ const webSearch = has(['BRAVE_API_KEY', 'EXA_API_KEY', 'PERPLEXITY_API_KEY', 'TAVILY_API_KEY']);
377
+ return [
378
+ ' Capabilities:',
379
+ ' ✓ Code tools — file edit, shell, search',
380
+ ' ✓ Text-to-speech (edge-tts, offline fallback)',
381
+ webSearch
382
+ ? ' ✓ Web search'
383
+ : ' ○ Web search — add BRAVE_API_KEY or EXA_API_KEY to enable',
384
+ ].join('\n');
385
+ }
53
386
  export async function runOnboarding() {
54
387
  const rl = readline.createInterface({
55
388
  input: process.stdin,
@@ -63,21 +396,72 @@ export async function runOnboarding() {
63
396
  console.log('');
64
397
  console.log(' This wizard will help you configure Code Buddy.');
65
398
  console.log('');
399
+ console.log(renderOnboardingRoadmap());
400
+ console.log('');
66
401
  // 1. Provider selection
67
- const provider = await askChoice(rl, 'Which AI provider do you want to use?', PROVIDERS, 0);
68
- // 2. API Key
69
- const envVar = PROVIDER_ENV_MAP[provider];
402
+ const provider = await askChoice(rl, 'Which AI provider do you want to use?', PROVIDER_GUIDES.map((guide) => `${guide.id} — ${guide.label}`), 0).then((choice) => choice.split(/\s+—\s+/)[0] ?? choice);
403
+ const guide = getProviderGuide(provider);
404
+ const validationConfig = getValidationConfigForGuide(provider);
405
+ // 2. Authentication — captured AND verified inline so you leave the wizard
406
+ // ready to chat. A rejected key never gets persisted; a successful probe
407
+ // also hands us the provider's real model list for step 3.
408
+ const envVar = guide.envVar;
70
409
  let apiKey = '';
71
- if (envVar) {
72
- console.log(`\n You will need to set ${envVar} in your environment.`);
73
- apiKey = await ask(rl, `Enter your API key (or press Enter to set ${envVar} later)`);
410
+ let oauthDone = false;
411
+ let verified = false;
412
+ let availableModels;
413
+ if (guide.authMode === 'oauth') {
414
+ const doNow = (await ask(rl, 'Sign in with your ChatGPT account now? (Y/n)', 'y')).toLowerCase();
415
+ if (doNow === 'y' || doNow === 'yes') {
416
+ try {
417
+ const { loginInteractive } = await import('../providers/codex-oauth.js');
418
+ rl.pause();
419
+ const auth = await loginInteractive();
420
+ rl.resume();
421
+ oauthDone = true;
422
+ verified = true;
423
+ console.log(`\n ✅ Signed in${auth.email ? ` as ${auth.email}` : ''}.`);
424
+ }
425
+ catch (err) {
426
+ try {
427
+ rl.resume();
428
+ }
429
+ catch { /* ignore */ }
430
+ console.log(`\n ⚠️ Sign-in didn't complete (${err instanceof Error ? err.message : String(err)}).`);
431
+ console.log(` Finish later with: ${guide.setupCommand ?? 'buddy login'}`);
432
+ }
433
+ }
434
+ else {
435
+ console.log(` No problem — run \`${guide.setupCommand ?? 'buddy login'}\` when you're ready.`);
436
+ }
437
+ }
438
+ else if (guide.authMode === 'api-key' && envVar) {
439
+ console.log(`\n ${guide.help}`);
440
+ const captured = await captureAndValidateKey(rl, guide, validationConfig);
441
+ apiKey = captured.apiKey;
442
+ verified = captured.verified;
443
+ availableModels = captured.models;
444
+ }
445
+ else if (guide.authMode === 'local' && validationConfig) {
446
+ // Local providers: probe connectivity and list installed models. This is
447
+ // the real round-trip for the free path (no key required).
448
+ console.log(`\n Checking ${guide.label}…`);
449
+ const probe = await validateProviderKey(validationConfig, '');
450
+ if (probe.valid) {
451
+ verified = true;
452
+ availableModels = probe.models;
453
+ console.log(` ✓ ${guide.label} is running (${probe.models?.length ?? 0} model(s) available).`);
454
+ }
455
+ else {
456
+ console.log(` ⚠️ Could not reach ${guide.label}: ${probe.error}`);
457
+ console.log(` ${guide.help}`);
458
+ }
74
459
  }
75
460
  else {
76
- console.log(`\n No API key needed for ${provider} (local provider).`);
461
+ console.log(`\n ${guide.help}`);
77
462
  }
78
- // 3. Model selection
79
- const defaultModel = PROVIDER_DEFAULT_MODEL[provider];
80
- const model = await ask(rl, 'Which model do you want to use?', defaultModel);
463
+ // 3. Model selection — from the real list when the probe gave us one.
464
+ const model = await selectModel(rl, guide, availableModels);
81
465
  // 4. TTS setup
82
466
  const ttsAnswer = await ask(rl, 'Enable text-to-speech? (y/n)', 'n');
83
467
  const ttsEnabled = ttsAnswer.toLowerCase() === 'y' || ttsAnswer.toLowerCase() === 'yes';
@@ -85,23 +469,51 @@ export async function runOnboarding() {
85
469
  if (ttsEnabled) {
86
470
  ttsProvider = await askChoice(rl, 'Which TTS provider?', TTS_PROVIDERS, 0);
87
471
  }
88
- const result = { provider, apiKey, model, ttsEnabled, ttsProvider };
89
- // 5. Write config
90
- const configDir = join(process.cwd(), '.codebuddy');
91
- writeConfig(configDir, result);
92
- // 6. Summary
472
+ // 5. Persist credentials + provider so the session works now and next run.
473
+ await persistProviderSelection(guide, model, apiKey);
474
+ const result = {
475
+ provider,
476
+ apiKey,
477
+ model,
478
+ ttsEnabled,
479
+ authMode: guide.authMode,
480
+ recommendedNextCommands: buildRecommendedNextCommands({ provider, apiKey, model }),
481
+ ...(ttsProvider ? { ttsProvider } : {}),
482
+ };
483
+ // 6. Write project config
484
+ writeConfig(join(process.cwd(), '.codebuddy'), result);
485
+ // 7. Summary
486
+ const ready = verified || (guide.authMode === 'api-key' && Boolean(apiKey));
93
487
  console.log('');
94
- console.log(' Setup complete! Configuration saved to .codebuddy/config.json');
488
+ console.log(` ${ready ? 'Setup complete you\'re ready to go!' : 'Setup saved.'}`);
95
489
  console.log('');
96
490
  console.log(` Provider: ${provider}`);
491
+ console.log(` Auth: ${guide.authMode}${oauthDone ? ' (signed in)' : guide.authMode === 'api-key' && apiKey ? ' (key saved)' : ''}`);
97
492
  console.log(` Model: ${model}`);
98
493
  if (ttsEnabled && ttsProvider) {
99
494
  console.log(` TTS: ${ttsProvider}`);
100
495
  }
101
- if (envVar && !apiKey) {
496
+ if (verified) {
497
+ const modelNote = availableModels?.includes(model) ? `, model ${model} available` : '';
498
+ console.log('');
499
+ console.log(` ✓ Verified — ${provider} reachable${modelNote}.`);
500
+ }
501
+ console.log('');
502
+ console.log(renderCapabilitiesFooter());
503
+ if (guide.authMode === 'oauth' && !oauthDone) {
102
504
  console.log('');
103
- console.log(` Remember to set ${envVar} in your environment before using Code Buddy.`);
505
+ console.log(` Next: run \`${guide.setupCommand ?? 'buddy login'}\` to finish signing in.`);
104
506
  }
507
+ else if (guide.authMode === 'api-key' && !apiKey && envVar) {
508
+ console.log('');
509
+ console.log(` Next: set ${envVar} in your environment (or re-run \`buddy onboard\`).`);
510
+ }
511
+ else if (guide.authMode === 'local') {
512
+ console.log('');
513
+ console.log(` Next: make sure your local server is running (${guide.setupCommand ?? guide.verifyCommand}).`);
514
+ }
515
+ console.log('');
516
+ console.log(renderOnboardingRoadmap(result));
105
517
  console.log('');
106
518
  return result;
107
519
  }
@@ -1,9 +1,12 @@
1
1
  /**
2
- * Provider Onboarding
2
+ * Provider validation library
3
3
  *
4
- * Guided setup for AI providers with API key validation.
5
- * Each provider config defines the env key, base URL, and a validation
6
- * endpoint that is probed with a GET request to confirm the key works.
4
+ * Shared key/endpoint validation used by the onboarding wizard
5
+ * (`src/wizard/onboarding.ts`). Each provider config defines the env key, base
6
+ * URL, and a GET validation endpoint that is probed to confirm the key works
7
+ * AND to list the provider's available models — the same mechanism Hermes uses
8
+ * during `hermes setup` so onboarding fails fast on a bad key instead of
9
+ * persisting it and breaking the first chat.
7
10
  */
8
11
  export interface ProviderOnboardingConfig {
9
12
  /** Unique provider identifier */
@@ -18,12 +21,6 @@ export interface ProviderOnboardingConfig {
18
21
  validateEndpoint: string;
19
22
  /** User-facing instructions for obtaining an API key */
20
23
  instructions: string;
21
- /** Optional OAuth flow parameters */
22
- oauthFlow?: {
23
- authUrl: string;
24
- tokenUrl: string;
25
- scopes: string[];
26
- };
27
24
  }
28
25
  export interface ProviderValidationResult {
29
26
  valid: boolean;
@@ -31,25 +28,13 @@ export interface ProviderValidationResult {
31
28
  error?: string;
32
29
  }
33
30
  export declare const PROVIDER_CONFIGS: ProviderOnboardingConfig[];
31
+ export declare function getValidationConfigForGuide(guideId: string): ProviderOnboardingConfig | undefined;
34
32
  /**
35
33
  * Validate a provider API key by making a test request to the validation endpoint.
36
34
  * Returns the validation result including available models on success.
37
35
  */
38
36
  export declare function validateProviderKey(config: ProviderOnboardingConfig, apiKey: string): Promise<ProviderValidationResult>;
39
- /**
40
- * Run interactive onboarding for a specific provider.
41
- * Prompts for API key, validates it, and saves to the environment.
42
- */
43
- export declare function runProviderOnboarding(providerId: string): Promise<boolean>;
44
- /**
45
- * Run interactive onboarding that lets the user choose a provider.
46
- */
47
- export declare function runFullProviderOnboarding(): Promise<boolean>;
48
37
  /**
49
38
  * Get a provider config by ID.
50
39
  */
51
40
  export declare function getProviderConfig(providerId: string): ProviderOnboardingConfig | undefined;
52
- /**
53
- * List all configured providers (those with env keys set).
54
- */
55
- export declare function listConfiguredProviders(): ProviderOnboardingConfig[];