@phuetz/code-buddy 1.1.0 → 1.2.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 (291) hide show
  1. package/README.md +160 -165
  2. package/dist/agent/autonomous/agentic-coding-contract.d.ts +6 -6
  3. package/dist/agent/codebuddy-agent.d.ts +1 -1
  4. package/dist/agent/codebuddy-agent.js +69 -14
  5. package/dist/agent/execution/agent-executor.js +122 -12
  6. package/dist/agent/execution/tool-dependency-graph.js +7 -0
  7. package/dist/agent/execution/tool-hooks.d.ts +6 -0
  8. package/dist/agent/execution/tool-hooks.js +13 -5
  9. package/dist/agent/execution/tool-selection-strategy.d.ts +11 -2
  10. package/dist/agent/execution/tool-selection-strategy.js +67 -10
  11. package/dist/agent/extended-thinking.d.ts +8 -0
  12. package/dist/agent/extended-thinking.js +27 -0
  13. package/dist/agent/hermes-browser-backends.js +151 -11
  14. package/dist/agent/hermes-claw-migrate.d.ts +59 -0
  15. package/dist/agent/hermes-claw-migrate.js +427 -26
  16. package/dist/agent/hermes-parity-manifest.js +78 -71
  17. package/dist/agent/hermes-runtime-lifecycle.d.ts +67 -0
  18. package/dist/agent/hermes-runtime-lifecycle.js +468 -0
  19. package/dist/agent/middleware/index.d.ts +1 -0
  20. package/dist/agent/middleware/index.js +1 -0
  21. package/dist/agent/middleware/visual-validation-middleware.d.ts +17 -0
  22. package/dist/agent/middleware/visual-validation-middleware.js +67 -0
  23. package/dist/agent/model-benchmark.d.ts +77 -0
  24. package/dist/agent/model-benchmark.js +309 -0
  25. package/dist/agent/model-tier.d.ts +14 -0
  26. package/dist/agent/model-tier.js +71 -0
  27. package/dist/agent/multi-agent/agents/orchestrator-agent.js +17 -6
  28. package/dist/agent/multi-agent/multi-agent-system.js +3 -1
  29. package/dist/agent/multi-agent/session-fleet-bridge.d.ts +2 -0
  30. package/dist/agent/multi-agent/session-fleet-bridge.js +9 -1
  31. package/dist/agent/multi-agent/session-tools.js +22 -5
  32. package/dist/agent/reasoning/mcts.js +20 -6
  33. package/dist/agent/session-end-flush.d.ts +6 -1
  34. package/dist/agent/session-end-flush.js +20 -4
  35. package/dist/agent/streaming/streaming-handler.d.ts +7 -0
  36. package/dist/agent/streaming/streaming-handler.js +8 -1
  37. package/dist/agent/tool-executor.js +1 -0
  38. package/dist/agent/tool-handler.d.ts +5 -0
  39. package/dist/agent/tool-handler.js +130 -4
  40. package/dist/browser-automation/browser-operator-executor.js +15 -0
  41. package/dist/browser-automation/browser-use-runner.d.ts +96 -0
  42. package/dist/browser-automation/browser-use-runner.js +492 -0
  43. package/dist/browser-automation/camofox-runner.d.ts +107 -0
  44. package/dist/browser-automation/camofox-runner.js +287 -0
  45. package/dist/channels/dingtalk/index.js +3 -0
  46. package/dist/channels/discord/client.d.ts +9 -0
  47. package/dist/channels/discord/client.js +12 -0
  48. package/dist/channels/feishu/index.d.ts +167 -1
  49. package/dist/channels/feishu/index.js +432 -7
  50. package/dist/channels/gateway-lifecycle.d.ts +132 -0
  51. package/dist/channels/gateway-lifecycle.js +219 -0
  52. package/dist/channels/google-chat/index.d.ts +5 -1
  53. package/dist/channels/google-chat/index.js +5 -1
  54. package/dist/channels/imessage/index.d.ts +14 -0
  55. package/dist/channels/imessage/index.js +64 -17
  56. package/dist/channels/index.d.ts +4 -0
  57. package/dist/channels/index.js +4 -0
  58. package/dist/channels/irc/index.d.ts +73 -3
  59. package/dist/channels/irc/index.js +446 -11
  60. package/dist/channels/line/index.js +3 -0
  61. package/dist/channels/mattermost/index.d.ts +51 -4
  62. package/dist/channels/mattermost/index.js +303 -20
  63. package/dist/channels/nextcloud-talk/index.d.ts +89 -8
  64. package/dist/channels/nextcloud-talk/index.js +367 -16
  65. package/dist/channels/nostr/index.d.ts +121 -1
  66. package/dist/channels/nostr/index.js +396 -8
  67. package/dist/channels/ntfy/index.js +3 -0
  68. package/dist/channels/qq/index.js +3 -0
  69. package/dist/channels/slash-parity.d.ts +117 -0
  70. package/dist/channels/slash-parity.js +185 -0
  71. package/dist/channels/synology-chat/index.js +3 -0
  72. package/dist/channels/teams/index.d.ts +3 -0
  73. package/dist/channels/teams/index.js +3 -0
  74. package/dist/channels/telegram/client.d.ts +9 -0
  75. package/dist/channels/telegram/client.js +12 -0
  76. package/dist/channels/twilio-voice/index.js +3 -0
  77. package/dist/channels/webchat/index.d.ts +7 -1
  78. package/dist/channels/webchat/index.js +7 -1
  79. package/dist/channels/wecom/index.js +3 -0
  80. package/dist/channels/weixin/index.js +3 -0
  81. package/dist/channels/zalo/index.js +3 -0
  82. package/dist/cli/config-loader.js +1 -1
  83. package/dist/codebuddy/client.d.ts +30 -1
  84. package/dist/codebuddy/client.js +167 -20
  85. package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +5 -0
  86. package/dist/codebuddy/providers/provider-chatgpt-responses.js +1 -0
  87. package/dist/codebuddy/providers/provider-gemini-native.js +1 -1
  88. package/dist/codebuddy/providers/provider-openai-compat.d.ts +5 -0
  89. package/dist/codebuddy/providers/provider-openai-compat.js +82 -2
  90. package/dist/codebuddy/tool-definitions/computer-control-tools.js +32 -2
  91. package/dist/codebuddy/tool-definitions/core-tools.js +3 -3
  92. package/dist/codebuddy/tool-definitions/index.d.ts +2 -1
  93. package/dist/codebuddy/tool-definitions/index.js +2 -1
  94. package/dist/codebuddy/tool-definitions/multimodal-tools.d.ts +1 -0
  95. package/dist/codebuddy/tool-definitions/multimodal-tools.js +44 -1
  96. package/dist/codebuddy/tool-definitions/windows-tools.d.ts +2 -0
  97. package/dist/codebuddy/tool-definitions/windows-tools.js +34 -0
  98. package/dist/codebuddy/tools.d.ts +1 -1
  99. package/dist/codebuddy/tools.js +27 -24
  100. package/dist/commands/cli/hermes-commands.js +108 -0
  101. package/dist/commands/cli/native-engine-commands.js +102 -3
  102. package/dist/commands/cli/utility-commands.js +45 -0
  103. package/dist/commands/enhanced-command-handler.js +1 -1
  104. package/dist/commands/goal-cli.d.ts +30 -0
  105. package/dist/commands/goal-cli.js +210 -27
  106. package/dist/commands/handlers/goal-handler.d.ts +9 -2
  107. package/dist/commands/handlers/goal-handler.js +36 -9
  108. package/dist/commands/handlers/infra-handlers.js +36 -0
  109. package/dist/commands/handlers/memory-handlers.js +96 -1
  110. package/dist/commands/headless-slash.d.ts +2 -0
  111. package/dist/commands/headless-slash.js +12 -0
  112. package/dist/commands/llm-provider-resolution.d.ts +5 -3
  113. package/dist/commands/llm-provider-resolution.js +87 -33
  114. package/dist/commands/ollama.d.ts +25 -0
  115. package/dist/commands/ollama.js +100 -0
  116. package/dist/commands/provider.d.ts +5 -0
  117. package/dist/commands/provider.js +106 -55
  118. package/dist/commands/spec-next.js +2 -1
  119. package/dist/commands/spec-plan.js +5 -16
  120. package/dist/commands/tunnel.d.ts +2 -0
  121. package/dist/commands/tunnel.js +48 -0
  122. package/dist/config/config-resolver.d.ts +2 -1
  123. package/dist/config/config-resolver.js +54 -41
  124. package/dist/config/constants.d.ts +28 -0
  125. package/dist/config/constants.js +7 -0
  126. package/dist/config/env-schema.js +756 -0
  127. package/dist/config/model-tools.js +4 -4
  128. package/dist/config/toml-config.d.ts +16 -0
  129. package/dist/config/toml-config.js +3 -0
  130. package/dist/context/context-manager-v2.js +1 -0
  131. package/dist/daemon/autonomous-daemon.d.ts +1 -1
  132. package/dist/daemon/autonomous-daemon.js +3 -3
  133. package/dist/daemon/autonomous-loop.js +11 -2
  134. package/dist/daemon/colab-goal.js +9 -1
  135. package/dist/daemon/cron-agent-bridge.d.ts +12 -3
  136. package/dist/daemon/cron-agent-bridge.js +25 -9
  137. package/dist/desktop/codebuddy-engine-adapter.d.ts +32 -0
  138. package/dist/desktop/codebuddy-engine-adapter.js +257 -80
  139. package/dist/desktop/engine-adapter.d.ts +14 -0
  140. package/dist/desktop-automation/automation-manager.js +16 -0
  141. package/dist/desktop-automation/omniparser-runner.d.ts +63 -0
  142. package/dist/desktop-automation/omniparser-runner.js +115 -0
  143. package/dist/desktop-automation/smart-snapshot.d.ts +11 -0
  144. package/dist/desktop-automation/smart-snapshot.js +72 -5
  145. package/dist/doctor/index.js +40 -23
  146. package/dist/events/types.d.ts +11 -0
  147. package/dist/fleet/colab-store.js +14 -2
  148. package/dist/fleet/peer-session-bridge.js +50 -12
  149. package/dist/fleet/peer-tool-bridge.js +14 -0
  150. package/dist/goals/goal-decomposer.d.ts +27 -0
  151. package/dist/goals/goal-decomposer.js +293 -0
  152. package/dist/goals/goal-judge-client.d.ts +15 -0
  153. package/dist/goals/goal-judge-client.js +45 -0
  154. package/dist/goals/goal-judge.d.ts +2 -0
  155. package/dist/goals/goal-judge.js +35 -6
  156. package/dist/goals/goal-loop.d.ts +14 -0
  157. package/dist/goals/goal-loop.js +47 -2
  158. package/dist/goals/goal-manager.d.ts +6 -0
  159. package/dist/goals/goal-manager.js +67 -14
  160. package/dist/goals/goal-state.d.ts +13 -0
  161. package/dist/goals/goal-state.js +83 -13
  162. package/dist/goals/goal-store.d.ts +3 -0
  163. package/dist/goals/goal-store.js +31 -15
  164. package/dist/hooks/user-hooks.js +17 -3
  165. package/dist/index.js +464 -25
  166. package/dist/input/text-to-speech.d.ts +2 -6
  167. package/dist/input/text-to-speech.js +2 -27
  168. package/dist/integrations/screenpipe/screenpipe-client.d.ts +10 -0
  169. package/dist/integrations/screenpipe/screenpipe-client.js +12 -2
  170. package/dist/integrations/tailscale.d.ts +13 -0
  171. package/dist/integrations/tailscale.js +87 -34
  172. package/dist/knowledge/workspace-indexer.js +53 -9
  173. package/dist/mcp/client.js +1 -0
  174. package/dist/mcp/mcp-desktop-tools.d.ts +23 -0
  175. package/dist/mcp/mcp-desktop-tools.js +158 -0
  176. package/dist/mcp/mcp-server.js +2 -0
  177. package/dist/memory/enhanced-memory.d.ts +10 -0
  178. package/dist/memory/enhanced-memory.js +33 -2
  179. package/dist/memory/index.d.ts +3 -1
  180. package/dist/memory/index.js +4 -1
  181. package/dist/memory/memory-auto-proposer.d.ts +23 -0
  182. package/dist/memory/memory-auto-proposer.js +308 -0
  183. package/dist/memory/memory-candidate-queue.d.ts +92 -0
  184. package/dist/memory/memory-candidate-queue.js +261 -0
  185. package/dist/memory/persistent-memory.d.ts +45 -2
  186. package/dist/memory/persistent-memory.js +236 -40
  187. package/dist/ml/bayesian-qualifier.d.ts +1 -1
  188. package/dist/ml/bayesian-qualifier.js +35 -2
  189. package/dist/openclaw/gateway-bridge.js +5 -0
  190. package/dist/plugins/marketplace.d.ts +1 -0
  191. package/dist/plugins/marketplace.js +7 -0
  192. package/dist/prompts/execution-discipline.d.ts +14 -0
  193. package/dist/prompts/execution-discipline.js +29 -0
  194. package/dist/prompts/variation-injector.js +9 -2
  195. package/dist/protocols/acp/acp-agentic-runner.d.ts +7 -6
  196. package/dist/protocols/acp/acp-agentic-runner.js +115 -18
  197. package/dist/protocols/acp/acp-session-store.d.ts +23 -0
  198. package/dist/protocols/acp/acp-session-store.js +77 -0
  199. package/dist/protocols/acp/acp-stdio-server.d.ts +7 -0
  200. package/dist/protocols/acp/acp-stdio-server.js +71 -16
  201. package/dist/providers/active-llm-registry.d.ts +37 -0
  202. package/dist/providers/active-llm-registry.js +186 -0
  203. package/dist/providers/auxiliary-provider.d.ts +25 -0
  204. package/dist/providers/auxiliary-provider.js +192 -0
  205. package/dist/providers/codex-oauth.d.ts +1 -1
  206. package/dist/providers/codex-oauth.js +27 -4
  207. package/dist/providers/index.d.ts +3 -0
  208. package/dist/providers/index.js +4 -0
  209. package/dist/providers/provider-catalog.d.ts +70 -0
  210. package/dist/providers/provider-catalog.js +738 -0
  211. package/dist/providers/provider-fallback.d.ts +35 -0
  212. package/dist/providers/provider-fallback.js +171 -0
  213. package/dist/providers/xai-oauth.d.ts +128 -0
  214. package/dist/providers/xai-oauth.js +735 -0
  215. package/dist/scheduler/cron-scheduler.d.ts +15 -3
  216. package/dist/scheduler/cron-scheduler.js +53 -7
  217. package/dist/scheduler/script-runner.d.ts +2 -0
  218. package/dist/scheduler/script-runner.js +19 -4
  219. package/dist/scheduler/watchdog-handlers.js +7 -20
  220. package/dist/search/usearch-index.js +7 -2
  221. package/dist/security/tool-policy/tool-groups.js +2 -0
  222. package/dist/server/index.js +17 -2
  223. package/dist/server/routes/mobile.d.ts +12 -4
  224. package/dist/server/routes/mobile.js +116 -1
  225. package/dist/server/tls-config.d.ts +35 -0
  226. package/dist/server/tls-config.js +142 -0
  227. package/dist/server/tunnel-manager.d.ts +20 -0
  228. package/dist/server/tunnel-manager.js +58 -0
  229. package/dist/server/websocket/fleet-bridge.js +5 -0
  230. package/dist/services/prompt-builder.d.ts +1 -0
  231. package/dist/services/prompt-builder.js +49 -8
  232. package/dist/shared/engine-types.d.ts +15 -1
  233. package/dist/sidecar/sidecar-bridge.d.ts +1 -0
  234. package/dist/sidecar/sidecar-bridge.js +21 -10
  235. package/dist/spec/spec-store.js +8 -1
  236. package/dist/talk-mode/providers/audioreader-tts.d.ts +1 -1
  237. package/dist/talk-mode/providers/audioreader-tts.js +8 -8
  238. package/dist/templates/project-scaffolding.js +3 -2
  239. package/dist/themes/theme-schema.d.ts +10 -10
  240. package/dist/tools/application-profiles.js +38 -0
  241. package/dist/tools/bash/bash-tool.d.ts +1 -0
  242. package/dist/tools/bash/bash-tool.js +16 -2
  243. package/dist/tools/bash/command-validator.js +3 -0
  244. package/dist/tools/bash/rtk-rewrite.d.ts +16 -0
  245. package/dist/tools/bash/rtk-rewrite.js +100 -0
  246. package/dist/tools/bash/streaming-executor.js +14 -1
  247. package/dist/tools/computer-control-tool.d.ts +16 -1
  248. package/dist/tools/computer-control-tool.js +317 -5
  249. package/dist/tools/document-generator.d.ts +14 -0
  250. package/dist/tools/document-generator.js +79 -0
  251. package/dist/tools/execute-code-rpc-invoker.d.ts +26 -2
  252. package/dist/tools/execute-code-rpc-invoker.js +46 -5
  253. package/dist/tools/metadata.js +28 -0
  254. package/dist/tools/office-macro-tool.d.ts +10 -0
  255. package/dist/tools/office-macro-tool.js +93 -0
  256. package/dist/tools/registry/index.d.ts +2 -1
  257. package/dist/tools/registry/index.js +2 -1
  258. package/dist/tools/registry/memory-tools.d.ts +20 -0
  259. package/dist/tools/registry/memory-tools.js +225 -3
  260. package/dist/tools/registry/multimodal-tools.js +2 -2
  261. package/dist/tools/registry/vision-tools.d.ts +33 -0
  262. package/dist/tools/registry/vision-tools.js +308 -6
  263. package/dist/tools/registry/windows-tools.d.ts +2 -0
  264. package/dist/tools/registry/windows-tools.js +47 -0
  265. package/dist/tools/route-peer-tool.js +14 -0
  266. package/dist/tools/screenshot-tool.js +14 -2
  267. package/dist/tools/text-to-speech-tool.d.ts +1 -1
  268. package/dist/tools/text-to-speech-tool.js +2 -12
  269. package/dist/tools/tool-selector.js +3 -59
  270. package/dist/utils/config-validation/schema.d.ts +40 -7
  271. package/dist/utils/config-validation/schema.js +130 -1
  272. package/dist/utils/cost-tracker.js +21 -1
  273. package/dist/utils/disk-guard.d.ts +180 -0
  274. package/dist/utils/disk-guard.js +404 -0
  275. package/dist/utils/interactive-setup.js +35 -2
  276. package/dist/utils/model-utils.js +11 -2
  277. package/dist/utils/output-sanitizer.js +11 -0
  278. package/dist/utils/provider-detector.d.ts +8 -7
  279. package/dist/utils/provider-detector.js +19 -77
  280. package/dist/utils/settings-manager.d.ts +7 -0
  281. package/dist/utils/token-display.d.ts +3 -3
  282. package/dist/utils/token-display.js +22 -4
  283. package/dist/wizard/onboarding.d.ts +39 -0
  284. package/dist/wizard/onboarding.js +433 -21
  285. package/dist/wizard/provider-onboarding.d.ts +8 -23
  286. package/dist/wizard/provider-onboarding.js +39 -158
  287. package/package.json +4 -2
  288. package/dist/plugins/provider-onboarding.d.ts +0 -23
  289. package/dist/plugins/provider-onboarding.js +0 -116
  290. package/dist/utils/kokoro-tts.d.ts +0 -8
  291. package/dist/utils/kokoro-tts.js +0 -67
@@ -9,6 +9,16 @@ import { buildHeartbeatStatusReport } from '../../daemon/status-reports.js';
9
9
  // Heartbeat commands
10
10
  // ============================================================================
11
11
  export { buildHeartbeatStatusReport };
12
+ function parsePositiveIntegerCliOption(value, flagName) {
13
+ if (value === undefined)
14
+ return undefined;
15
+ const trimmed = value.trim();
16
+ const n = Number(trimmed);
17
+ if (!/^[1-9]\d*$/.test(trimmed) || !Number.isSafeInteger(n)) {
18
+ throw new Error(`${flagName} must be a positive integer`);
19
+ }
20
+ return n;
21
+ }
12
22
  export function registerHeartbeatCommands(program) {
13
23
  const heartbeat = program
14
24
  .command('heartbeat')
@@ -745,7 +755,7 @@ export function registerFleetAutonomyCommands(program) {
745
755
  .action(async (opts) => {
746
756
  const { createDefaultAutonomousLoop, FleetAutonomousDaemon, watchFleetTasks } = await import('../../daemon/autonomous-daemon.js');
747
757
  const path = await import('path');
748
- const loop = createDefaultAutonomousLoop({
758
+ const loop = await createDefaultAutonomousLoop({
749
759
  ...(opts.dir ? { dir: opts.dir } : {}),
750
760
  ...(opts.outputDir ? { outputDir: opts.outputDir } : {}),
751
761
  });
@@ -780,6 +790,86 @@ export function registerFleetAutonomyCommands(program) {
780
790
  console.log(`\nDone: ${summary.ticks} tick(s), ${JSON.stringify(summary.outcomes)} (${summary.stoppedReason}).`);
781
791
  }
782
792
  });
793
+ fleet
794
+ .command('bench')
795
+ .description('Benchmark live Tailnet Ollama peers and rank the network model tier')
796
+ .option('--peer <pattern>', 'only benchmark peers whose hostname or IP contains the pattern')
797
+ .option('--models <csv>', 'only benchmark models whose name contains one of the comma-separated patterns')
798
+ .option('--prompt-set <name>', 'prompt set: balanced, coding, or latency', 'coding')
799
+ .option('--runs <n>', 'repeat the prompt set N times per candidate', '1')
800
+ .option('--timeout <ms>', 'request timeout per prompt', '60000')
801
+ .option('--json', 'output JSON')
802
+ .option('--no-write-cache', 'do not persist the score cache used by the router')
803
+ .action(async (opts) => {
804
+ const { TailscaleManager } = await import('../../integrations/tailscale.js');
805
+ const { BENCHMARK_PROMPT_SETS, benchmarkCandidates, writeBenchmarkIndex, defaultBenchmarkIndexPath, } = await import('../../agent/model-benchmark.js');
806
+ const peers = await TailscaleManager.getInstance().discoverOllamaPeers();
807
+ const filteredPeers = opts.peer
808
+ ? peers.filter((peer) => peer.hostname.toLowerCase().includes(opts.peer.toLowerCase())
809
+ || peer.ip.includes(opts.peer.trim()))
810
+ : peers;
811
+ const modelFilters = opts.models
812
+ ? opts.models.split(',').map((part) => part.trim()).filter(Boolean)
813
+ : [];
814
+ const candidates = filteredPeers.flatMap((peer) => peer.models
815
+ .filter((model) => modelFilters.length === 0
816
+ ? true
817
+ : modelFilters.some((filter) => model.toLowerCase().includes(filter.toLowerCase())))
818
+ .map((model) => ({
819
+ model,
820
+ baseUrl: peer.baseURL,
821
+ label: peer.hostname,
822
+ })));
823
+ if (candidates.length === 0) {
824
+ const message = opts.peer
825
+ ? `No Tailnet Ollama peers matched "${opts.peer}".`
826
+ : 'No Tailnet Ollama peers were discovered.';
827
+ if (opts.json) {
828
+ console.log(JSON.stringify({ ok: false, error: message }, null, 2));
829
+ return;
830
+ }
831
+ console.log(message);
832
+ return;
833
+ }
834
+ const suite = BENCHMARK_PROMPT_SETS[opts.promptSet] ? opts.promptSet : 'coding';
835
+ const reports = await benchmarkCandidates(candidates, {
836
+ promptSet: suite,
837
+ runs: Math.max(1, parseInt(opts.runs, 10) || 1),
838
+ timeoutMs: Math.max(5_000, parseInt(opts.timeout, 10) || 60_000),
839
+ });
840
+ const ranked = [...reports].sort((a, b) => b.summary.score - a.summary.score);
841
+ let indexPath;
842
+ if (opts.writeCache !== false) {
843
+ indexPath = defaultBenchmarkIndexPath();
844
+ await writeBenchmarkIndex(ranked, suite, indexPath);
845
+ }
846
+ const best = ranked[0];
847
+ if (opts.json) {
848
+ console.log(JSON.stringify({
849
+ ok: true,
850
+ suite,
851
+ indexPath,
852
+ best,
853
+ reports: ranked,
854
+ }, null, 2));
855
+ return;
856
+ }
857
+ console.log(`\nTailnet Ollama benchmark (${suite})`);
858
+ if (indexPath)
859
+ console.log(` Cache: ${indexPath}`);
860
+ console.log(` Candidates: ${ranked.length}`);
861
+ if (best) {
862
+ console.log(` Best: ${best.candidate.label ?? best.candidate.model} / ${best.candidate.model}`);
863
+ console.log(` Score: ${best.summary.score.toFixed(1)} compliance ${(best.summary.complianceRate * 100).toFixed(0)}% ttft ${Math.round(best.summary.avgTtftMs)}ms total ${Math.round(best.summary.avgTotalMs)}ms`);
864
+ }
865
+ console.log('');
866
+ ranked.forEach((report, index) => {
867
+ console.log(`${String(index + 1).padStart(2, ' ')}. ${report.candidate.label ?? report.candidate.model} / ${report.candidate.model}`);
868
+ console.log(` ${report.candidate.baseUrl}`);
869
+ console.log(` score=${report.summary.score.toFixed(1)} compliance=${(report.summary.complianceRate * 100).toFixed(0)}% ttft=${Math.round(report.summary.avgTtftMs)}ms total=${Math.round(report.summary.avgTotalMs)}ms`);
870
+ });
871
+ console.log('');
872
+ });
783
873
  fleet
784
874
  .command('status')
785
875
  .description('Show the fleet task queue + presence')
@@ -822,6 +912,15 @@ export function registerFleetAutonomyCommands(program) {
822
912
  .option('--dir <path>', 'colab dir')
823
913
  .option('--json', 'output JSON')
824
914
  .action(async (title, opts) => {
915
+ let goalMaxTurns;
916
+ try {
917
+ goalMaxTurns = parsePositiveIntegerCliOption(opts.goalMaxTurns, '--goal-max-turns');
918
+ }
919
+ catch (error) {
920
+ console.error(error instanceof Error ? error.message : String(error));
921
+ process.exit(1);
922
+ return;
923
+ }
825
924
  const { FleetColabStore } = await import('../../fleet/colab-store.js');
826
925
  const store = new FleetColabStore({ ...(opts.dir ? { dir: opts.dir } : {}) });
827
926
  const priority = ['critical', 'high', 'medium', 'low'].find((p) => p === opts.priority) ?? 'medium';
@@ -831,7 +930,7 @@ export function registerFleetAutonomyCommands(program) {
831
930
  ...(opts.description ? { description: opts.description } : {}),
832
931
  ...(opts.dependsOn ? { dependsOn: opts.dependsOn.split(',').map((s) => s.trim()).filter(Boolean) } : {}),
833
932
  ...(opts.goalMode ? { goalMode: true } : {}),
834
- ...(opts.goalMaxTurns ? { goalMaxTurns: parseInt(opts.goalMaxTurns, 10) } : {}),
933
+ ...(goalMaxTurns !== undefined ? { goalMaxTurns } : {}),
835
934
  });
836
935
  if (opts.json) {
837
936
  console.log(JSON.stringify({ task }, null, 2));
@@ -1129,7 +1228,7 @@ export function registerCompanionCommands(program) {
1129
1228
  .option('--no-set-model', 'Do not set the project model to the ChatGPT companion default')
1130
1229
  .option('--language <lang>', 'Voice language', 'fr')
1131
1230
  .option('--stt-provider <provider>', 'Voice input provider (system, whisper-local, whisper-api)')
1132
- .option('--tts-provider <provider>', 'TTS provider (edge-tts, espeak, say, piper, audioreader, kokoro)')
1231
+ .option('--tts-provider <provider>', 'TTS provider (edge-tts, espeak, say, piper, audioreader)')
1133
1232
  .option('--tts-voice <voice>', 'TTS voice name')
1134
1233
  .option('--model <model>', 'ChatGPT model to use when OAuth credentials are present')
1135
1234
  .action(async (opts) => {
@@ -4,6 +4,7 @@
4
4
  * Extracted from index.ts for modularity.
5
5
  */
6
6
  import { resolve } from 'path';
7
+ import { buildOllamaUpdatePlan, fetchOllamaStatus, runOllamaUpdatePlan } from '../ollama.js';
7
8
  function resolveCommandDirectory(program) {
8
9
  const directory = program.opts().directory;
9
10
  return resolve(process.cwd(), directory || process.cwd());
@@ -93,6 +94,50 @@ export function registerUtilityCommands(program) {
93
94
  const { runOnboarding } = await import('../../wizard/onboarding.js');
94
95
  await runOnboarding();
95
96
  });
97
+ const ollamaCommand = program
98
+ .command('ollama')
99
+ .description('Inspect or update the local Ollama runtime');
100
+ ollamaCommand
101
+ .command('status')
102
+ .description('Show local Ollama version and models')
103
+ .option('--url <url>', 'Ollama base URL', process.env.OLLAMA_HOST ?? 'http://localhost:11434')
104
+ .option('--json', 'Output JSON')
105
+ .action(async (options) => {
106
+ const status = await fetchOllamaStatus(options.url);
107
+ if (options.json) {
108
+ console.log(JSON.stringify(status, null, 2));
109
+ return;
110
+ }
111
+ console.log('\nOllama status\n');
112
+ console.log(` Base URL: ${status.baseUrl}`);
113
+ console.log(` Reachable: ${status.reachable ? 'YES' : 'NO'}`);
114
+ console.log(` Version: ${status.version ?? '(unknown)'}`);
115
+ console.log(` Models: ${status.models.length > 0 ? status.models.join(', ') : '(none)'}`);
116
+ if (status.error) {
117
+ console.log(` Error: ${status.error}`);
118
+ }
119
+ console.log('');
120
+ });
121
+ ollamaCommand
122
+ .command('update')
123
+ .description('Run the official Ollama Windows update script')
124
+ .option('--script-url <url>', 'Official installer/update script URL', 'https://ollama.com/install.ps1')
125
+ .option('--repo-root <dir>', 'Repository root', resolveCommandDirectory(program))
126
+ .action(async (options) => {
127
+ const plan = buildOllamaUpdatePlan({
128
+ repoRoot: options.repoRoot,
129
+ scriptUrl: options.scriptUrl,
130
+ });
131
+ if (!plan.supported) {
132
+ console.error(plan.message);
133
+ console.error(`Use the update script directly on Windows: ${plan.scriptPath}`);
134
+ process.exit(1);
135
+ return;
136
+ }
137
+ console.log(`Running Ollama updater: ${plan.scriptPath}`);
138
+ await runOllamaUpdatePlan(plan);
139
+ console.log('Ollama update completed.');
140
+ });
96
141
  // Webhook command
97
142
  const webhookCommand = program
98
143
  .command('webhook')
@@ -340,7 +340,7 @@ export class EnhancedCommandHandler {
340
340
  // Heartbeat engine activation (fleet AUTONOMOUS-FLEET-PROTOCOL v0.1)
341
341
  ['__HEARTBEAT__', (args) => handleHeartbeat(args)],
342
342
  // Standing goal — judge + auto-continue loop (Hermes Agent parity)
343
- ['__GOAL__', (args) => handleGoal(args)],
343
+ ['__GOAL__', (args) => handleGoal(args, { client: this.codebuddyClient })],
344
344
  ['__SUBGOAL__', (args) => handleSubgoal(args)],
345
345
  // Daily reset scheduler (audit OpenClaw heritage activation)
346
346
  ['__DAILY_RESET__', (args) => handleDailyReset(args)],
@@ -15,14 +15,22 @@
15
15
  import { Command } from 'commander';
16
16
  import type { ChatEntry } from '../agent/codebuddy-agent.js';
17
17
  import type { CodeBuddyClient } from '../codebuddy/client.js';
18
+ import { type GoalJudgeProviderInfo } from '../goals/goal-judge-client.js';
18
19
  import { GoalStatus } from '../goals/goal-state.js';
19
20
  /** The slice of CodeBuddyAgent the loop needs — injectable for tests. */
20
21
  export interface GoalLoopAgent {
21
22
  processUserMessage(input: string): Promise<ChatEntry[]>;
22
23
  getClient(): CodeBuddyClient;
23
24
  }
25
+ export interface GoalCommandRuntimeAgent extends GoalLoopAgent {
26
+ systemPromptReady: Promise<unknown>;
27
+ setSystemPrompt?: (prompt: string) => void;
28
+ dispose?: () => void | Promise<void>;
29
+ }
24
30
  export interface GoalLoopRunOptions {
25
31
  maxTurns?: number;
32
+ /** Optional standalone judge client. Defaults to the agent client. */
33
+ judgeClient?: CodeBuddyClient;
26
34
  /** Progress sink (status lines ⊙/↻/✓/⏸). Defaults to silent. */
27
35
  onMessage?: (text: string) => void;
28
36
  }
@@ -31,6 +39,19 @@ export interface GoalLoopRunResult {
31
39
  turnsUsed: number;
32
40
  lastReason?: string;
33
41
  }
42
+ export interface GoalCommandRuntimeOptions {
43
+ maxTurns?: number;
44
+ judgeModel?: string;
45
+ provider: GoalJudgeProviderInfo;
46
+ workingDirectory?: string;
47
+ onMessage?: (text: string) => void;
48
+ createJudgeClient?: typeof createStandaloneJudgeClient;
49
+ runLoop?: typeof runGoalLoop;
50
+ }
51
+ export declare const LOCAL_GOAL_ACTOR_SYSTEM_PROMPT: string;
52
+ export declare function buildLocalGoalActorSystemPrompt(workingDirectory?: string): string;
53
+ export declare function shouldUseLocalGoalActorPrompt(provider: GoalJudgeProviderInfo): boolean;
54
+ export declare function resolveLocalGoalActorSystemPrompt(provider: GoalJudgeProviderInfo, workingDirectory?: string): string | undefined;
34
55
  /**
35
56
  * Drive the goal loop headlessly on an in-process agent. Sets the goal,
36
57
  * runs the first turn with the goal text (mirroring the interactive
@@ -38,4 +59,13 @@ export interface GoalLoopRunResult {
38
59
  * stops continuing.
39
60
  */
40
61
  export declare function runGoalLoop(agent: GoalLoopAgent, goalText: string, options?: GoalLoopRunOptions): Promise<GoalLoopRunResult>;
62
+ declare function createStandaloneJudgeClient(judgeModel: string | undefined, agentProvider: GoalJudgeProviderInfo, currentClient: CodeBuddyClient): Promise<CodeBuddyClient | undefined>;
63
+ export declare function runGoalCommandWithAgent(agent: GoalCommandRuntimeAgent, goal: string, options: GoalCommandRuntimeOptions): Promise<GoalLoopRunResult>;
64
+ export declare function resolveGoalCliJudgeModel(explicitJudgeModel: string | undefined): string | undefined;
65
+ export declare function resolveGoalCliMaxToolRounds(commandValue: number | undefined, command?: Command): number;
66
+ export declare function resolveGoalCliWorkingDirectory(command?: Command): string | undefined;
67
+ export declare function applyGoalCliWorkingDirectory(command?: Command): string;
68
+ export declare function parsePositiveIntegerOption(value: string, optionName: string): number;
69
+ export declare function validateGoalCommandNumericOptions(argv: readonly string[]): void;
41
70
  export declare function createGoalCommand(): Command;
71
+ export {};
@@ -12,10 +12,72 @@
12
12
  *
13
13
  * Exit codes: 0 = goal done, 1 = paused (budget/judge) or error.
14
14
  */
15
- import { Command } from 'commander';
15
+ import { Command, InvalidArgumentError } from 'commander';
16
+ import path from 'path';
17
+ import { resolveGoalJudgeClient } from '../goals/goal-judge-client.js';
16
18
  import { maybeContinueGoalAfterTurn } from '../goals/goal-loop.js';
17
- import { getGoalManager } from '../goals/goal-manager.js';
19
+ import { getGoalManager, resolveGoalsConfig } from '../goals/goal-manager.js';
18
20
  import { resolveCommandProvider } from './llm-provider-resolution.js';
21
+ const TOOL_RESULT_SNIPPET_CHARS = 1600;
22
+ export const LOCAL_GOAL_ACTOR_SYSTEM_PROMPT = 'You are Code Buddy running in headless goal mode on a local model. ' +
23
+ 'Work only toward the user standing goal. Use available tools when they are necessary, ' +
24
+ 'then report concrete progress or the final result. For simple answer-only goals, ' +
25
+ 'output exactly the requested answer and no extra text. Do not greet the user, add markdown, ' +
26
+ 'or explain unless the goal asks for it.';
27
+ export function buildLocalGoalActorSystemPrompt(workingDirectory = process.cwd()) {
28
+ return `${LOCAL_GOAL_ACTOR_SYSTEM_PROMPT}
29
+
30
+ Workspace:
31
+ - Current project directory: ${workingDirectory}
32
+ - Use paths relative to the current project directory whenever possible.
33
+ - Do not invent container paths such as /workspace or root paths such as /src unless a tool result proves they exist.`;
34
+ }
35
+ export function shouldUseLocalGoalActorPrompt(provider) {
36
+ const label = provider.providerLabel?.toLowerCase() ?? '';
37
+ const baseURL = provider.baseURL?.toLowerCase() ?? '';
38
+ return provider.apiKey === 'ollama' || label.includes('ollama') || baseURL.includes(':11434');
39
+ }
40
+ export function resolveLocalGoalActorSystemPrompt(provider, workingDirectory = process.cwd()) {
41
+ return shouldUseLocalGoalActorPrompt(provider)
42
+ ? buildLocalGoalActorSystemPrompt(workingDirectory)
43
+ : undefined;
44
+ }
45
+ function truncateGoalTurnPart(text) {
46
+ if (text.length <= TOOL_RESULT_SNIPPET_CHARS)
47
+ return text;
48
+ return `${text.slice(0, TOOL_RESULT_SNIPPET_CHARS)}... [truncated]`;
49
+ }
50
+ function buildGoalTurnSummary(entries) {
51
+ const parts = [];
52
+ let hasToolResult = false;
53
+ for (const entry of entries) {
54
+ if (entry.type === 'assistant' && entry.content.trim()) {
55
+ parts.push(entry.content.trim());
56
+ continue;
57
+ }
58
+ if (entry.type !== 'tool_result')
59
+ continue;
60
+ hasToolResult = true;
61
+ const toolOutput = (entry.toolResult?.success ? entry.toolResult.output : entry.toolResult?.error) ??
62
+ entry.content;
63
+ const trimmedOutput = String(toolOutput ?? '').trim();
64
+ if (!trimmedOutput)
65
+ continue;
66
+ const toolName = entry.toolCall?.function?.name ?? 'tool';
67
+ const status = entry.toolResult ? (entry.toolResult.success ? 'success' : 'error') : 'result';
68
+ parts.push(`[tool:${toolName} ${status}]\n${truncateGoalTurnPart(trimmedOutput)}`);
69
+ }
70
+ const summary = parts.join('\n\n');
71
+ if (summary && !hasToolResult) {
72
+ return `[tool evidence: none]\n\n${summary}`;
73
+ }
74
+ return summary;
75
+ }
76
+ function buildGoalRunSummary(turnSummaries) {
77
+ return turnSummaries
78
+ .map((summary, index) => `[Goal turn ${index + 1}]\n${summary}`)
79
+ .join('\n\n');
80
+ }
19
81
  /**
20
82
  * Drive the goal loop headlessly on an in-process agent. Sets the goal,
21
83
  * runs the first turn with the goal text (mirroring the interactive
@@ -24,25 +86,32 @@ import { resolveCommandProvider } from './llm-provider-resolution.js';
24
86
  */
25
87
  export async function runGoalLoop(agent, goalText, options = {}) {
26
88
  const manager = getGoalManager();
27
- const state = manager.set(goalText, options.maxTurns ? { maxTurns: options.maxTurns } : {});
89
+ const state = manager.set(goalText, options.maxTurns !== undefined ? { maxTurns: options.maxTurns } : {});
28
90
  const emit = options.onMessage ?? (() => { });
91
+ const judgeClient = options.judgeClient ?? agent.getClient();
29
92
  emit(`⊙ Goal set (${state.maxTurns}-turn budget): ${state.goal}`);
30
93
  let prompt = state.goal;
94
+ const turnSummaries = [];
31
95
  // Hard backstop on top of the manager's own budget/auto-pause guards.
32
96
  const maxIterations = state.maxTurns + 1;
33
97
  for (let i = 0; i < maxIterations; i++) {
34
98
  const entries = await agent.processUserMessage(prompt);
35
- const lastResponse = entries
36
- .filter(entry => entry.type === 'assistant' && entry.content)
37
- .map(entry => entry.content)
38
- .join('\n');
99
+ const lastResponse = buildGoalTurnSummary(entries);
100
+ if (lastResponse.trim()) {
101
+ turnSummaries.push(lastResponse);
102
+ }
39
103
  const outcome = await maybeContinueGoalAfterTurn({
40
- client: agent.getClient(),
41
- lastResponse,
104
+ client: judgeClient,
105
+ lastResponse: lastResponse.trim() ? buildGoalRunSummary(turnSummaries) : lastResponse,
42
106
  interrupted: false,
43
107
  });
44
108
  if (outcome?.message)
45
109
  emit(outcome.message);
110
+ if (!outcome && !lastResponse.trim() && manager.isActive()) {
111
+ manager.pause('empty response (nothing to evaluate)');
112
+ emit('⏸ Goal paused — the agent produced no judgeable response.');
113
+ break;
114
+ }
46
115
  if (!outcome?.continuationPrompt)
47
116
  break;
48
117
  prompt = outcome.continuationPrompt;
@@ -54,37 +123,151 @@ export async function runGoalLoop(agent, goalText, options = {}) {
54
123
  ...(final?.lastReason ? { lastReason: final.lastReason } : {}),
55
124
  };
56
125
  }
126
+ async function createStandaloneJudgeClient(judgeModel, agentProvider, currentClient) {
127
+ const resolved = await resolveGoalJudgeClient(currentClient, judgeModel, agentProvider);
128
+ return resolved === currentClient ? undefined : resolved;
129
+ }
130
+ export async function runGoalCommandWithAgent(agent, goal, options) {
131
+ try {
132
+ const createJudgeClient = options.createJudgeClient ?? createStandaloneJudgeClient;
133
+ const runLoop = options.runLoop ?? runGoalLoop;
134
+ const judgeClient = await createJudgeClient(options.judgeModel, options.provider, agent.getClient());
135
+ await agent.systemPromptReady;
136
+ if (shouldUseLocalGoalActorPrompt(options.provider)) {
137
+ agent.setSystemPrompt?.(buildLocalGoalActorSystemPrompt(options.workingDirectory));
138
+ }
139
+ return await runLoop(agent, goal, {
140
+ ...(options.maxTurns !== undefined ? { maxTurns: options.maxTurns } : {}),
141
+ ...(judgeClient ? { judgeClient } : {}),
142
+ ...(options.onMessage ? { onMessage: options.onMessage } : {}),
143
+ });
144
+ }
145
+ finally {
146
+ await Promise.resolve(agent.dispose?.());
147
+ }
148
+ }
149
+ export function resolveGoalCliJudgeModel(explicitJudgeModel) {
150
+ const explicit = explicitJudgeModel?.trim();
151
+ if (explicit)
152
+ return explicit;
153
+ const configured = resolveGoalsConfig().judgeModel.trim();
154
+ return configured || undefined;
155
+ }
156
+ export function resolveGoalCliMaxToolRounds(commandValue, command) {
157
+ const commandSource = command?.getOptionValueSource?.('maxToolRounds');
158
+ if (commandSource && commandSource !== 'default') {
159
+ return commandValue ?? 50;
160
+ }
161
+ const parentSource = command?.parent?.getOptionValueSource?.('maxToolRounds');
162
+ const parentValue = command?.optsWithGlobals?.()?.maxToolRounds;
163
+ if (parentSource && parentSource !== 'default' && parentValue !== undefined) {
164
+ return parsePositiveIntegerOption(String(parentValue), '--max-tool-rounds');
165
+ }
166
+ return commandValue ?? 50;
167
+ }
168
+ export function resolveGoalCliWorkingDirectory(command) {
169
+ const value = command?.optsWithGlobals?.()?.directory;
170
+ if (typeof value !== 'string')
171
+ return undefined;
172
+ const directory = value.trim();
173
+ if (!directory)
174
+ return undefined;
175
+ return path.resolve(process.cwd(), directory);
176
+ }
177
+ export function applyGoalCliWorkingDirectory(command) {
178
+ const directory = resolveGoalCliWorkingDirectory(command);
179
+ if (directory) {
180
+ process.chdir(directory);
181
+ }
182
+ return process.cwd();
183
+ }
184
+ async function loadGoalCliEnv(directory) {
185
+ const dotenv = await import('dotenv');
186
+ dotenv.config({ path: path.join(directory, '.env') });
187
+ }
188
+ async function prepareGoalCliWorkspace(command) {
189
+ const launchDirectory = process.cwd();
190
+ await loadGoalCliEnv(launchDirectory);
191
+ const cwd = applyGoalCliWorkingDirectory(command);
192
+ if (cwd !== launchDirectory) {
193
+ await loadGoalCliEnv(cwd);
194
+ }
195
+ const opts = command?.optsWithGlobals?.();
196
+ const { initializeWorkspaceIsolation } = await import('../workspace/workspace-isolation.js');
197
+ initializeWorkspaceIsolation({
198
+ allowOutside: opts?.allowOutside,
199
+ directory: cwd,
200
+ additionalPaths: opts?.addDir,
201
+ });
202
+ return cwd;
203
+ }
204
+ export function parsePositiveIntegerOption(value, optionName) {
205
+ const trimmed = value.trim();
206
+ const n = Number(trimmed);
207
+ if (!/^[1-9]\d*$/.test(trimmed) || !Number.isSafeInteger(n)) {
208
+ throw new InvalidArgumentError(`${optionName} must be a positive integer`);
209
+ }
210
+ return n;
211
+ }
212
+ export function validateGoalCommandNumericOptions(argv) {
213
+ for (let i = 2; i < argv.length; i++) {
214
+ const arg = argv[i];
215
+ if (arg === undefined)
216
+ continue;
217
+ if (arg === '--')
218
+ break;
219
+ if (arg === '--max-turns' || arg === '--max-tool-rounds') {
220
+ const value = argv[i + 1];
221
+ if (value === undefined)
222
+ continue;
223
+ parsePositiveIntegerOption(value, arg);
224
+ i++;
225
+ continue;
226
+ }
227
+ const eqIndex = arg?.indexOf('=') ?? -1;
228
+ if (eqIndex <= 0)
229
+ continue;
230
+ const optionName = arg.slice(0, eqIndex);
231
+ if (optionName === '--max-turns' || optionName === '--max-tool-rounds') {
232
+ parsePositiveIntegerOption(arg.slice(eqIndex + 1), optionName);
233
+ }
234
+ }
235
+ }
57
236
  export function createGoalCommand() {
58
237
  const cmd = new Command('goal')
59
238
  .description('Run the agent toward a standing goal until a judge model confirms it is done (Ralph loop)')
60
239
  .argument('<goal>', 'The goal to pursue')
61
- .option('--max-turns <n>', 'Turn budget (default 20, or goals.maxTurns from settings)')
240
+ .option('--max-turns <n>', 'Turn budget (default 20, or goals.maxTurns from settings)', value => parsePositiveIntegerOption(value, '--max-turns'))
62
241
  .option('--judge-model <model>', 'Model for the goal judge (default: session model)')
63
242
  .option('-m, --model <model>', 'Override the agent model for this run')
64
- .option('--max-tool-rounds <n>', 'Max tool rounds per turn', '50')
243
+ .option('--max-tool-rounds <n>', 'Max tool rounds per turn', value => parsePositiveIntegerOption(value, '--max-tool-rounds'), 50)
65
244
  .action(async (goal, options, command) => {
66
- const modelOverride = options.model ?? command?.optsWithGlobals?.()?.model;
67
- const resolved = resolveCommandProvider({ explicitModel: modelOverride });
68
- if (!resolved) {
69
- console.error('Error: No provider available — set an API key, run `buddy onboard`, or point CODEBUDDY_PROVIDER=ollama at a local Ollama.');
70
- process.exit(1);
71
- }
72
- if (options.judgeModel) {
73
- process.env.CODEBUDDY_GOAL_JUDGE_MODEL = options.judgeModel;
74
- }
75
- process.env.CODEBUDDY_DISABLE_MCP = process.env.CODEBUDDY_DISABLE_MCP ?? 'true';
76
- process.env.CODEBUDDY_HEADLESS = 'true';
77
245
  try {
246
+ const cwd = await prepareGoalCliWorkspace(command);
247
+ const modelOverride = options.model ?? command?.optsWithGlobals?.()?.model;
248
+ const resolved = resolveCommandProvider({ explicitModel: modelOverride });
249
+ if (!resolved) {
250
+ console.error('Error: No provider available — set an API key, run `buddy onboard`, or point CODEBUDDY_PROVIDER=ollama at a local Ollama.');
251
+ process.exit(1);
252
+ }
253
+ if (options.judgeModel) {
254
+ process.env.CODEBUDDY_GOAL_JUDGE_MODEL = options.judgeModel;
255
+ }
256
+ const judgeModel = resolveGoalCliJudgeModel(options.judgeModel);
257
+ const maxToolRounds = resolveGoalCliMaxToolRounds(options.maxToolRounds, command);
258
+ process.env.CODEBUDDY_DISABLE_MCP = process.env.CODEBUDDY_DISABLE_MCP ?? 'true';
259
+ process.env.CODEBUDDY_HEADLESS = 'true';
78
260
  const { CodeBuddyAgent } = await import('../agent/codebuddy-agent.js');
79
261
  const { ConfirmationService } = await import('../utils/confirmation-service.js');
80
262
  ConfirmationService.getInstance().setSessionFlag('allOperations', true);
81
- const agent = new CodeBuddyAgent(resolved.apiKey, resolved.baseURL, resolved.model, parseInt(options.maxToolRounds, 10));
82
- await agent.systemPromptReady;
83
- const result = await runGoalLoop(agent, goal, {
84
- ...(options.maxTurns ? { maxTurns: parseInt(options.maxTurns, 10) } : {}),
263
+ const agent = new CodeBuddyAgent(resolved.apiKey, resolved.baseURL, resolved.model, maxToolRounds, true, undefined, cwd, undefined, resolveLocalGoalActorSystemPrompt(resolved, cwd));
264
+ const result = await runGoalCommandWithAgent(agent, goal, {
265
+ provider: resolved,
266
+ judgeModel,
267
+ workingDirectory: cwd,
268
+ ...(options.maxTurns !== undefined ? { maxTurns: options.maxTurns } : {}),
85
269
  onMessage: text => console.log(`\n${text}`),
86
270
  });
87
- agent.dispose?.();
88
271
  process.exit(result.status === 'done' ? 0 : 1);
89
272
  }
90
273
  catch (err) {
@@ -1,4 +1,11 @@
1
+ import type { CodeBuddyClient } from '../../codebuddy/client.js';
2
+ import { type GoalPlan } from '../../goals/goal-decomposer.js';
1
3
  import { CommandHandlerResult } from './branch-handlers.js';
4
+ export interface GoalHandlerOptions {
5
+ sessionKey?: string;
6
+ client?: CodeBuddyClient | null;
7
+ planner?: (goal: string, client: CodeBuddyClient) => Promise<GoalPlan | null>;
8
+ }
2
9
  /**
3
10
  * /goal — standing goal with judge + auto-continue loop (the Ralph loop,
4
11
  * ported from Hermes Agent).
@@ -10,7 +17,7 @@ import { CommandHandlerResult } from './branch-handlers.js';
10
17
  * /goal resume restart the loop (resets the turn budget)
11
18
  * /goal clear discard the goal (aliases: stop, done)
12
19
  */
13
- export declare function handleGoal(args: string[]): Promise<CommandHandlerResult>;
20
+ export declare function handleGoal(args: string[], options?: GoalHandlerOptions): Promise<CommandHandlerResult>;
14
21
  /**
15
22
  * /subgoal — extra acceptance criteria added mid-loop.
16
23
  *
@@ -24,4 +31,4 @@ export declare function handleGoal(args: string[]): Promise<CommandHandlerResult
24
31
  * them) and the continuation prompt (agent sees them) on the next turn
25
32
  * boundary — no special kick needed.
26
33
  */
27
- export declare function handleSubgoal(args: string[]): Promise<CommandHandlerResult>;
34
+ export declare function handleSubgoal(args: string[], options?: GoalHandlerOptions): Promise<CommandHandlerResult>;