@phuetz/code-buddy 2.0.0 → 2.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 (403) hide show
  1. package/README.fr.md +56 -0
  2. package/README.md +73 -16
  3. package/codebuddy-runtime.json +5 -5
  4. package/dist/agent/codebuddy-agent.d.ts +18 -0
  5. package/dist/agent/codebuddy-agent.js +45 -8
  6. package/dist/agent/execution/agent-executor.d.ts +0 -9
  7. package/dist/agent/execution/agent-executor.js +165 -12
  8. package/dist/agent/execution/context-pipeline.d.ts +9 -0
  9. package/dist/agent/execution/context-pipeline.js +64 -2
  10. package/dist/agent/execution/tool-loop-guard.d.ts +76 -0
  11. package/dist/agent/execution/tool-loop-guard.js +165 -0
  12. package/dist/agent/execution/tool-selection-strategy.js +19 -2
  13. package/dist/agent/facades/session-facade.d.ts +0 -6
  14. package/dist/agent/facades/session-facade.js +0 -45
  15. package/dist/agent/hermes-claw-migrate.d.ts +0 -21
  16. package/dist/agent/hermes-claw-migrate.js +9 -4
  17. package/dist/agent/hermes-protocol-gateways.js +8 -1
  18. package/dist/agent/repo-profiler.js +17 -4
  19. package/dist/agent/self-improvement/authored-tool-runtime.js +10 -2
  20. package/dist/agent/self-improvement/evolution/ast-novelty.d.ts +5 -0
  21. package/dist/agent/self-improvement/evolution/ast-novelty.js +58 -23
  22. package/dist/agent/self-improvement/evolution/code-variant-store.d.ts +3 -0
  23. package/dist/agent/self-improvement/evolution/evolution-engine.js +2 -0
  24. package/dist/agent/self-improvement/evolution/protected-paths.js +6 -0
  25. package/dist/agent/self-improvement/evolution/variant-fitness.d.ts +13 -2
  26. package/dist/agent/self-improvement/evolution/variant-fitness.js +184 -58
  27. package/dist/agent/self-improvement/evolution/worktree-scorer.d.ts +2 -0
  28. package/dist/agent/self-improvement/evolution/worktree-scorer.js +15 -0
  29. package/dist/agent/self-improvement/evolutionary-archive.d.ts +2 -1
  30. package/dist/agent/self-improvement/evolutionary-archive.js +34 -5
  31. package/dist/agent/self-improvement/proposal-store.d.ts +1 -0
  32. package/dist/agent/self-improvement/proposal-store.js +79 -17
  33. package/dist/agent/self-improvement/skill-apply-journal.d.ts +44 -0
  34. package/dist/agent/self-improvement/skill-apply-journal.js +178 -0
  35. package/dist/agent/self-improvement/skill-behavior-benchmark.d.ts +3 -0
  36. package/dist/agent/self-improvement/skill-behavior-benchmark.js +19 -0
  37. package/dist/agent/self-improvement/skill-behavior-gate.d.ts +34 -0
  38. package/dist/agent/self-improvement/skill-behavior-gate.js +96 -0
  39. package/dist/agent/self-improvement/skill-engine.d.ts +22 -0
  40. package/dist/agent/self-improvement/skill-engine.js +385 -13
  41. package/dist/agent/self-improvement/skill-gate.d.ts +2 -0
  42. package/dist/agent/self-improvement/skill-gate.js +17 -2
  43. package/dist/agent/self-improvement/skill-mutator.d.ts +6 -1
  44. package/dist/agent/self-improvement/skill-mutator.js +51 -15
  45. package/dist/agent/self-improvement/skill-types.d.ts +2 -1
  46. package/dist/agent/self-improvement/types.d.ts +11 -0
  47. package/dist/agent/tool-handler.d.ts +1 -1
  48. package/dist/agent/tool-handler.js +28 -9
  49. package/dist/auth/profile-manager.js +1 -1
  50. package/dist/channels/companion-channel-turn.d.ts +53 -4
  51. package/dist/channels/companion-channel-turn.js +218 -16
  52. package/dist/channels/slash-parity.d.ts +4 -6
  53. package/dist/channels/slash-parity.js +9 -44
  54. package/dist/cli/session-commands.d.ts +9 -0
  55. package/dist/cli/session-commands.js +45 -5
  56. package/dist/cli/session-picker.d.ts +43 -0
  57. package/dist/cli/session-picker.js +128 -0
  58. package/dist/codebuddy/a2a-call-tool-defs.d.ts +26 -0
  59. package/dist/codebuddy/a2a-call-tool-defs.js +12 -0
  60. package/dist/codebuddy/fleet-tool-defs.d.ts +1 -0
  61. package/dist/codebuddy/fleet-tool-defs.js +24 -0
  62. package/dist/codebuddy/providers/chatgpt-headers.d.ts +18 -0
  63. package/dist/codebuddy/providers/chatgpt-headers.js +37 -0
  64. package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +2 -1
  65. package/dist/codebuddy/providers/provider-chatgpt-responses.js +10 -23
  66. package/dist/codebuddy/providers/provider-openai-compat.js +1 -1
  67. package/dist/codebuddy/ragchat-tool-defs.d.ts +31 -0
  68. package/dist/codebuddy/ragchat-tool-defs.js +19 -0
  69. package/dist/codebuddy/resource-catalog-tool-defs.d.ts +28 -0
  70. package/dist/codebuddy/resource-catalog-tool-defs.js +18 -0
  71. package/dist/codebuddy/tool-definitions/agent-tools.js +2 -2
  72. package/dist/codebuddy/tool-definitions/code-exec-tools.js +5 -1
  73. package/dist/codebuddy/tool-definitions/cron-tools.js +6 -0
  74. package/dist/codebuddy/tool-definitions/self-describe-tools.js +6 -1
  75. package/dist/codebuddy/tools.d.ts +0 -6
  76. package/dist/codebuddy/tools.js +25 -3
  77. package/dist/commands/cli/daemon-commands.js +3 -1
  78. package/dist/commands/cli/evolve-command.js +21 -5
  79. package/dist/commands/cli/fleet-collaboration-commands.d.ts +2 -0
  80. package/dist/commands/cli/fleet-collaboration-commands.js +57 -0
  81. package/dist/commands/cli/fleet-commands.js +29 -0
  82. package/dist/commands/cli/fleet-mission-commands.d.ts +3 -0
  83. package/dist/commands/cli/fleet-mission-commands.js +72 -0
  84. package/dist/commands/cli/fleet-rooms-commands.d.ts +10 -0
  85. package/dist/commands/cli/fleet-rooms-commands.js +289 -0
  86. package/dist/commands/cli/tools-commands.js +2 -1
  87. package/dist/commands/cli/triage-command.d.ts +8 -0
  88. package/dist/commands/cli/triage-command.js +72 -0
  89. package/dist/commands/cli/utility-commands.js +25 -1
  90. package/dist/commands/client-dispatcher.d.ts +7 -0
  91. package/dist/commands/client-dispatcher.js +73 -21
  92. package/dist/commands/cron-cli/index.d.ts +3 -1
  93. package/dist/commands/cron-cli/index.js +25 -0
  94. package/dist/commands/device-auth.d.ts +10 -0
  95. package/dist/commands/device-auth.js +91 -0
  96. package/dist/commands/enhanced-command-handler.d.ts +9 -0
  97. package/dist/commands/enhanced-command-handler.js +28 -6
  98. package/dist/commands/handlers/agent-handlers.d.ts +1 -1
  99. package/dist/commands/handlers/agent-handlers.js +4 -4
  100. package/dist/commands/handlers/channel-handlers.js +53 -2
  101. package/dist/commands/handlers/export-handlers.d.ts +3 -2
  102. package/dist/commands/handlers/export-handlers.js +55 -28
  103. package/dist/commands/handlers/extra-handlers.js +5 -1
  104. package/dist/commands/handlers/grill-me-handler.js +26 -18
  105. package/dist/commands/handlers/memory-handlers.d.ts +6 -2
  106. package/dist/commands/handlers/memory-handlers.js +35 -15
  107. package/dist/commands/handlers/missing-handlers.d.ts +2 -2
  108. package/dist/commands/handlers/missing-handlers.js +4 -4
  109. package/dist/commands/handlers/resources-handler.d.ts +12 -0
  110. package/dist/commands/handlers/resources-handler.js +52 -0
  111. package/dist/commands/handlers/starter-handlers.d.ts +1 -1
  112. package/dist/commands/handlers/starter-handlers.js +32 -2
  113. package/dist/commands/handlers/switch-handler.d.ts +1 -1
  114. package/dist/commands/handlers/switch-handler.js +15 -0
  115. package/dist/commands/handlers/test-handlers.js +4 -86
  116. package/dist/commands/handlers/ui-handlers.js +11 -5
  117. package/dist/commands/login-chatgpt.d.ts +8 -0
  118. package/dist/commands/login-chatgpt.js +17 -0
  119. package/dist/commands/mcp-import.d.ts +11 -0
  120. package/dist/commands/mcp-import.js +67 -0
  121. package/dist/commands/mcp-invoke.d.ts +2 -0
  122. package/dist/commands/mcp-invoke.js +68 -0
  123. package/dist/commands/mcp.js +4 -0
  124. package/dist/commands/pipeline.d.ts +7 -1
  125. package/dist/commands/pipeline.js +104 -7
  126. package/dist/commands/resources.d.ts +2 -0
  127. package/dist/commands/resources.js +64 -0
  128. package/dist/commands/skills-cli/index.js +35 -12
  129. package/dist/commands/slash/builtin-commands.js +16 -3
  130. package/dist/commands/slash/index.d.ts +3 -1
  131. package/dist/commands/slash/index.js +2 -0
  132. package/dist/commands/slash/surfaces.d.ts +53 -0
  133. package/dist/commands/slash/surfaces.js +177 -0
  134. package/dist/commands/slash/types.d.ts +17 -0
  135. package/dist/commands/try.d.ts +1 -1
  136. package/dist/commands/try.js +1 -1
  137. package/dist/companion/companion-identity.d.ts +43 -0
  138. package/dist/companion/companion-identity.js +136 -0
  139. package/dist/companion/companion-toolset.d.ts +66 -0
  140. package/dist/companion/companion-toolset.js +368 -0
  141. package/dist/companion/companion-turn.d.ts +10 -2
  142. package/dist/companion/companion-turn.js +38 -1
  143. package/dist/config/code-exec-policy.d.ts +25 -0
  144. package/dist/config/code-exec-policy.js +39 -0
  145. package/dist/config/model-tools.d.ts +9 -0
  146. package/dist/config/model-tools.js +4 -0
  147. package/dist/context/context-manager-v2.js +2 -2
  148. package/dist/context/lm-resizer-compressor.js +3 -2
  149. package/dist/context/lm-resizer-diagnostics.d.ts +9 -0
  150. package/dist/context/lm-resizer-diagnostics.js +30 -0
  151. package/dist/context/tool-output-masking.js +3 -4
  152. package/dist/curator/curator.d.ts +6 -0
  153. package/dist/curator/curator.js +53 -3
  154. package/dist/daemon/cron-agent-bridge.d.ts +6 -0
  155. package/dist/daemon/cron-agent-bridge.js +31 -4
  156. package/dist/doctor/index.d.ts +11 -2
  157. package/dist/doctor/index.js +84 -21
  158. package/dist/doctor/integrations.d.ts +179 -0
  159. package/dist/doctor/integrations.js +227 -0
  160. package/dist/doctor/triage.d.ts +108 -0
  161. package/dist/doctor/triage.js +304 -0
  162. package/dist/fleet/collaboration.d.ts +44 -0
  163. package/dist/fleet/collaboration.js +170 -0
  164. package/dist/fleet/fleet-listener.d.ts +1 -0
  165. package/dist/fleet/fleet-listener.js +87 -46
  166. package/dist/fleet/fleet-registry.d.ts +2 -0
  167. package/dist/fleet/resource-catalog.d.ts +245 -0
  168. package/dist/fleet/resource-catalog.js +211 -0
  169. package/dist/fleet/rooms/room-access.d.ts +138 -0
  170. package/dist/fleet/rooms/room-access.js +196 -0
  171. package/dist/fleet/rooms/room-client.d.ts +158 -0
  172. package/dist/fleet/rooms/room-client.js +685 -0
  173. package/dist/fleet/rooms/room-event.d.ts +139 -0
  174. package/dist/fleet/rooms/room-event.js +330 -0
  175. package/dist/fleet/rooms/room-filter.d.ts +48 -0
  176. package/dist/fleet/rooms/room-filter.js +152 -0
  177. package/dist/fleet/rooms/room-hub.d.ts +154 -0
  178. package/dist/fleet/rooms/room-hub.js +363 -0
  179. package/dist/fleet/rooms/room-identity.d.ts +26 -0
  180. package/dist/fleet/rooms/room-identity.js +82 -0
  181. package/dist/fleet/rooms/room-lock.d.ts +34 -0
  182. package/dist/fleet/rooms/room-lock.js +180 -0
  183. package/dist/fleet/rooms/room-observations.d.ts +7 -0
  184. package/dist/fleet/rooms/room-observations.js +61 -0
  185. package/dist/fleet/rooms/room-server.d.ts +28 -0
  186. package/dist/fleet/rooms/room-server.js +75 -0
  187. package/dist/fleet/rooms/room-store.d.ts +119 -0
  188. package/dist/fleet/rooms/room-store.js +448 -0
  189. package/dist/fleet/rooms/room-ws-bridge.d.ts +27 -0
  190. package/dist/fleet/rooms/room-ws-bridge.js +137 -0
  191. package/dist/fleet/sensory-mission-bridge.d.ts +42 -0
  192. package/dist/fleet/sensory-mission-bridge.js +136 -0
  193. package/dist/git/worktree-sessions.js +1 -0
  194. package/dist/harness/contract.d.ts +4 -4
  195. package/dist/harness/fleet-supervisor.d.ts +16 -0
  196. package/dist/harness/fleet-supervisor.js +64 -0
  197. package/dist/harness/index.d.ts +4 -0
  198. package/dist/harness/index.js +4 -0
  199. package/dist/harness/mission-runner.d.ts +3 -0
  200. package/dist/harness/mission-runner.js +53 -0
  201. package/dist/harness/mission-store.d.ts +93 -0
  202. package/dist/harness/mission-store.js +297 -0
  203. package/dist/harness/tool-harness.d.ts +56 -0
  204. package/dist/harness/tool-harness.js +153 -0
  205. package/dist/hooks/use-enhanced-input.js +48 -4
  206. package/dist/hooks/use-input-handler.d.ts +1 -0
  207. package/dist/hooks/use-input-handler.js +82 -22
  208. package/dist/hooks/use-input-history.js +1 -0
  209. package/dist/identity/lisa-introspection.js +5 -2
  210. package/dist/identity/operational-self-model.d.ts +2 -0
  211. package/dist/identity/operational-self-model.js +4 -0
  212. package/dist/index.js +52 -6
  213. package/dist/mcp/client.js +6 -0
  214. package/dist/mcp/config.d.ts +20 -0
  215. package/dist/mcp/config.js +121 -8
  216. package/dist/mcp/import-normalize.d.ts +12 -0
  217. package/dist/mcp/import-normalize.js +114 -0
  218. package/dist/mcp/transports.d.ts +10 -5
  219. package/dist/mcp/transports.js +61 -99
  220. package/dist/mcp/types.d.ts +5 -0
  221. package/dist/media/comfyui-recipe-contract.d.ts +7 -7
  222. package/dist/memory/facts-memory.d.ts +11 -1
  223. package/dist/memory/facts-memory.js +47 -8
  224. package/dist/memory/memory-candidate-queue.d.ts +2 -2
  225. package/dist/memory/memory-candidate-queue.js +6 -6
  226. package/dist/memory/persistent-memory.d.ts +13 -8
  227. package/dist/memory/persistent-memory.js +77 -30
  228. package/dist/nodes/device-node.js +5 -9
  229. package/dist/observability/mobile-supervision-gateway-contract.js +20 -7
  230. package/dist/observability/run-event-writer.d.ts +21 -0
  231. package/dist/observability/run-event-writer.js +81 -0
  232. package/dist/observability/run-store.d.ts +5 -0
  233. package/dist/observability/run-store.js +18 -13
  234. package/dist/observability/run-trajectory.js +1 -1
  235. package/dist/optimization/prompt-cache.d.ts +19 -2
  236. package/dist/optimization/prompt-cache.js +41 -6
  237. package/dist/orchestration/orchestrator.d.ts +21 -0
  238. package/dist/orchestration/orchestrator.js +122 -15
  239. package/dist/persistence/session-content.d.ts +8 -0
  240. package/dist/persistence/session-content.js +76 -0
  241. package/dist/persistence/session-handoff.d.ts +64 -0
  242. package/dist/persistence/session-handoff.js +80 -0
  243. package/dist/persistence/session-history.d.ts +4 -0
  244. package/dist/persistence/session-history.js +38 -0
  245. package/dist/persistence/session-store.d.ts +9 -0
  246. package/dist/persistence/session-store.js +48 -20
  247. package/dist/personas/persona-manager.js +17 -1
  248. package/dist/plugins/code-explorer/CodeExplorerManager.d.ts +10 -4
  249. package/dist/plugins/code-explorer/CodeExplorerManager.js +69 -16
  250. package/dist/plugins/plugin-manager.js +10 -1
  251. package/dist/prompts/prompt-manager.js +11 -4
  252. package/dist/prompts/system-base.js +6 -2
  253. package/dist/protocols/a2a/codebuddy-executor.d.ts +11 -2
  254. package/dist/protocols/a2a/codebuddy-executor.js +9 -8
  255. package/dist/protocols/a2a/index.d.ts +1 -1
  256. package/dist/protocols/a2a/jsonrpc-v1.d.ts +41 -0
  257. package/dist/protocols/a2a/jsonrpc-v1.js +145 -0
  258. package/dist/protocols/a2a/peer-config.d.ts +20 -0
  259. package/dist/protocols/a2a/peer-config.js +55 -0
  260. package/dist/providers/codex-oauth.js +30 -6
  261. package/dist/providers/model-provider-compat.js +2 -1
  262. package/dist/sandbox/os-sandbox.d.ts +1 -1
  263. package/dist/scheduler/cron-scheduler.d.ts +15 -2
  264. package/dist/scheduler/cron-scheduler.js +35 -4
  265. package/dist/scheduler/job-notepad.d.ts +69 -0
  266. package/dist/scheduler/job-notepad.js +339 -0
  267. package/dist/security/audit-logger.d.ts +1 -1
  268. package/dist/security/compute-confinement.d.ts +4 -0
  269. package/dist/security/compute-confinement.js +89 -0
  270. package/dist/security/session-encryption.d.ts +4 -0
  271. package/dist/security/session-encryption.js +31 -0
  272. package/dist/sensory/voice-loop.d.ts +17 -0
  273. package/dist/sensory/voice-loop.js +72 -15
  274. package/dist/server/auth/device-session-context.d.ts +9 -0
  275. package/dist/server/auth/device-session-context.js +13 -0
  276. package/dist/server/auth/device-store.d.ts +89 -0
  277. package/dist/server/auth/device-store.js +261 -0
  278. package/dist/server/auth/device-token.d.ts +3 -0
  279. package/dist/server/auth/device-token.js +6 -0
  280. package/dist/server/auth/jwt.js +4 -0
  281. package/dist/server/index.d.ts +0 -10
  282. package/dist/server/index.js +28 -0
  283. package/dist/server/middleware/auth.js +3 -2
  284. package/dist/server/routes/a2a-jsonrpc.d.ts +9 -0
  285. package/dist/server/routes/a2a-jsonrpc.js +66 -0
  286. package/dist/server/routes/a2a-protocol.d.ts +12 -0
  287. package/dist/server/routes/a2a-protocol.js +1 -1
  288. package/dist/server/routes/device-auth.d.ts +4 -0
  289. package/dist/server/routes/device-auth.js +40 -0
  290. package/dist/server/types.d.ts +4 -0
  291. package/dist/server/websocket/handler.d.ts +13 -0
  292. package/dist/server/websocket/handler.js +102 -17
  293. package/dist/services/prompt-builder.d.ts +2 -0
  294. package/dist/services/prompt-builder.js +13 -5
  295. package/dist/services/runtime-settings-context.d.ts +73 -0
  296. package/dist/services/runtime-settings-context.js +69 -0
  297. package/dist/skills/executor.js +2 -0
  298. package/dist/skills/hub.js +16 -14
  299. package/dist/skills/local-inventory.d.ts +21 -0
  300. package/dist/skills/local-inventory.js +72 -0
  301. package/dist/skills/skill-loader.js +2 -2
  302. package/dist/skills/skill-usage-store.d.ts +49 -0
  303. package/dist/skills/skill-usage-store.js +121 -0
  304. package/dist/testing/ai-integration-tests.js +8 -5
  305. package/dist/themes/theme-manager.d.ts +12 -0
  306. package/dist/themes/theme-manager.js +29 -0
  307. package/dist/themes/theme-schema.d.ts +24 -24
  308. package/dist/tools/a2a-call-tool.d.ts +34 -0
  309. package/dist/tools/a2a-call-tool.js +75 -0
  310. package/dist/tools/bash/streaming-executor.d.ts +1 -0
  311. package/dist/tools/bash/streaming-executor.js +78 -90
  312. package/dist/tools/code-exec-preflight-runner.d.ts +41 -0
  313. package/dist/tools/code-exec-preflight-runner.js +254 -0
  314. package/dist/tools/code-exec-preflight.d.ts +44 -0
  315. package/dist/tools/code-exec-preflight.js +501 -0
  316. package/dist/tools/code-exec-tool.d.ts +48 -1
  317. package/dist/tools/code-exec-tool.js +191 -47
  318. package/dist/tools/comment-watcher.js +29 -12
  319. package/dist/tools/core-code-inspection.d.ts +56 -0
  320. package/dist/tools/core-code-inspection.js +86 -0
  321. package/dist/tools/create-skill-tool.d.ts +2 -3
  322. package/dist/tools/create-skill-tool.js +16 -75
  323. package/dist/tools/cronjob-tool.d.ts +1 -0
  324. package/dist/tools/cronjob-tool.js +1 -0
  325. package/dist/tools/env-doctor-tool.d.ts +2 -0
  326. package/dist/tools/env-doctor-tool.js +4 -3
  327. package/dist/tools/execute-code-rpc-invoker.d.ts +1 -1
  328. package/dist/tools/execute-code-rpc-invoker.js +35 -19
  329. package/dist/tools/execute-code-runner.d.ts +2 -0
  330. package/dist/tools/execute-code-runner.js +52 -17
  331. package/dist/tools/fleet-room-tool.d.ts +104 -0
  332. package/dist/tools/fleet-room-tool.js +124 -0
  333. package/dist/tools/integration-tool-hints.d.ts +4 -0
  334. package/dist/tools/integration-tool-hints.js +19 -0
  335. package/dist/tools/list-peers-tool.d.ts +3 -0
  336. package/dist/tools/list-peers-tool.js +11 -4
  337. package/dist/tools/media-generation-tool.d.ts +16 -1
  338. package/dist/tools/media-generation-tool.js +341 -9
  339. package/dist/tools/merge-conflict-tool.js +53 -25
  340. package/dist/tools/metadata.d.ts +1 -5
  341. package/dist/tools/metadata.js +21 -28
  342. package/dist/tools/ragchat-tool.d.ts +39 -0
  343. package/dist/tools/ragchat-tool.js +105 -0
  344. package/dist/tools/registry/fleet-tools.d.ts +9 -0
  345. package/dist/tools/registry/fleet-tools.js +22 -1
  346. package/dist/tools/registry/knowledge-tools.d.ts +2 -2
  347. package/dist/tools/registry/knowledge-tools.js +2 -2
  348. package/dist/tools/registry/lessons-tools.d.ts +7 -0
  349. package/dist/tools/registry/lessons-tools.js +43 -3
  350. package/dist/tools/registry/memory-tools.d.ts +1 -1
  351. package/dist/tools/registry/memory-tools.js +8 -8
  352. package/dist/tools/registry/research-tools.d.ts +1 -0
  353. package/dist/tools/registry/research-tools.js +4 -13
  354. package/dist/tools/registry/self-describe-tools.d.ts +1 -1
  355. package/dist/tools/registry/self-describe-tools.js +43 -30
  356. package/dist/tools/resource-catalog-tool.d.ts +38 -0
  357. package/dist/tools/resource-catalog-tool.js +46 -0
  358. package/dist/tools/skills-inspection-tool.js +30 -1
  359. package/dist/tools/stream-tool-output.d.ts +3 -0
  360. package/dist/tools/stream-tool-output.js +33 -0
  361. package/dist/tools/tool-call-scheduler.d.ts +10 -0
  362. package/dist/tools/tool-call-scheduler.js +58 -0
  363. package/dist/tools/tool-effect.d.ts +5 -0
  364. package/dist/tools/tool-effect.js +26 -0
  365. package/dist/tools/tool-search.d.ts +12 -11
  366. package/dist/tools/tool-search.js +67 -26
  367. package/dist/tools/types.d.ts +6 -0
  368. package/dist/ui/components/ChatHistory.d.ts +1 -1
  369. package/dist/ui/components/ChatHistory.js +2 -2
  370. package/dist/ui/components/ChatInput.d.ts +2 -1
  371. package/dist/ui/components/ChatInput.js +42 -88
  372. package/dist/ui/components/ChatInterface.d.ts +4 -2
  373. package/dist/ui/components/ChatInterface.js +27 -63
  374. package/dist/ui/components/CommandSuggestions.d.ts +0 -1
  375. package/dist/ui/components/CommandSuggestions.js +4 -6
  376. package/dist/ui/components/FileAutocomplete.js +1 -1
  377. package/dist/ui/components/KeyboardHelp.js +8 -6
  378. package/dist/ui/components/ModelSelection.js +6 -5
  379. package/dist/ui/components/StatusBar.d.ts +2 -1
  380. package/dist/ui/components/StatusBar.js +8 -7
  381. package/dist/ui/context/theme-context.js +3 -2
  382. package/dist/ui/index.d.ts +1 -1
  383. package/dist/ui/index.js +1 -1
  384. package/dist/ui/utils/markdown-renderer.js +4 -13
  385. package/dist/utils/atomic-write.d.ts +22 -0
  386. package/dist/utils/atomic-write.js +29 -0
  387. package/dist/utils/bounded-output.d.ts +16 -0
  388. package/dist/utils/bounded-output.js +63 -0
  389. package/dist/utils/device-store-file.d.ts +4 -0
  390. package/dist/utils/device-store-file.js +40 -0
  391. package/dist/utils/export-manager.d.ts +9 -0
  392. package/dist/utils/export-manager.js +19 -0
  393. package/dist/utils/first-use-hints.d.ts +25 -0
  394. package/dist/utils/first-use-hints.js +58 -0
  395. package/dist/utils/history-manager.js +1 -1
  396. package/dist/utils/update-notifier.js +16 -18
  397. package/dist/wizard/environment-detection.d.ts +5 -1
  398. package/dist/wizard/environment-detection.js +49 -7
  399. package/dist/wizard/onboarding.d.ts +5 -1
  400. package/dist/wizard/onboarding.js +90 -30
  401. package/dist/wizard/provider-onboarding.d.ts +2 -0
  402. package/dist/wizard/provider-onboarding.js +55 -17
  403. package/package.json +7 -3
@@ -0,0 +1,227 @@
1
+ /**
2
+ * `buddy doctor` — Integrations section (comparatif plan P3).
3
+ *
4
+ * Aggregates EXISTING diagnostics without any network probe:
5
+ * - LM Resizer protocol (`diagnoseLmResizer`: bounded local `--help` runs only);
6
+ * - Code Explorer index freshness vs git HEAD (`getFreshness`, autoIndex off);
7
+ * - MCP configuration structure (`loadMCPConfig`, no server is started);
8
+ * - resource catalog observations (`ResourceCatalog.list`, never `probe`);
9
+ * - Skills Hub integrity (`listWithIntegrity`, same data as `buddy skills doctor`).
10
+ *
11
+ * Every probe is fail-soft: a broken source becomes a `warn` check, never a crash.
12
+ */
13
+ import { z } from 'zod';
14
+ const KNOWN_MCP_TRANSPORTS = new Set(['stdio', 'http', 'sse', 'streamable_http']);
15
+ function check(id, name, status, message, optional) {
16
+ return { id, section: 'integrations', name, status, message, ...(optional ? { optional } : {}) };
17
+ }
18
+ function errorText(error) {
19
+ return error instanceof Error ? error.message : String(error);
20
+ }
21
+ async function lmResizerCheck(deps) {
22
+ try {
23
+ if (deps.noSubprocess && !deps.lmResizer) {
24
+ const { isLmResizerEnabled, resolveLmResizerBin } = await import('../context/lm-resizer-compressor.js');
25
+ if (!isLmResizerEnabled()) {
26
+ return check('lm-resizer', 'LM Resizer', 'ok', 'disabled (CODEBUDDY_LM_RESIZER unset): tool observations stay raw', true);
27
+ }
28
+ const { commandExistsOnPath } = await import('./index.js');
29
+ const binary = resolveLmResizerBin();
30
+ return commandExistsOnPath(binary)
31
+ ? check('lm-resizer', 'LM Resizer', 'ok', `enabled, ${binary} found; protocol not probed (no child process in this mode — run buddy doctor --integrations)`, true)
32
+ : check('lm-resizer', 'LM Resizer', 'warn', `enabled but binary unavailable (${binary}); observations stay raw`);
33
+ }
34
+ const diagnose = deps.lmResizer ?? (async () => (await import('../context/lm-resizer-diagnostics.js')).diagnoseLmResizer());
35
+ const d = await diagnose();
36
+ if (!d.enabled) {
37
+ return check('lm-resizer', 'LM Resizer', 'ok', 'disabled (CODEBUDDY_LM_RESIZER unset): tool observations stay raw', true);
38
+ }
39
+ if (!d.available) {
40
+ return check('lm-resizer', 'LM Resizer', 'warn', `enabled but binary unavailable (${d.binary}); observations stay raw`);
41
+ }
42
+ if (!d.toolOutputSupported) {
43
+ return check('lm-resizer', 'LM Resizer', 'warn', `enabled but ${d.binary} (${d.version}) lacks the tool-output protocol; compression is inactive — install a compatible release`);
44
+ }
45
+ return check('lm-resizer', 'LM Resizer', 'ok', `tool-output protocol supported (${d.version})`);
46
+ }
47
+ catch (error) {
48
+ return check('lm-resizer', 'LM Resizer', 'warn', `diagnostic failed: ${errorText(error)}`);
49
+ }
50
+ }
51
+ async function codeExplorerCheck(cwd, deps) {
52
+ try {
53
+ const freshness = deps.codeExplorerFreshness ?? (async (dir) => {
54
+ const { CodeExplorerManager } = await import('../plugins/code-explorer/CodeExplorerManager.js');
55
+ return new CodeExplorerManager(dir).getFreshness(undefined, { autoIndex: false });
56
+ });
57
+ const f = await freshness(cwd);
58
+ if (!f.indexed)
59
+ return check('code-explorer', 'Code Explorer index', 'ok', 'repository not indexed (optional)', true);
60
+ if (f.unverified)
61
+ return check('code-explorer', 'Code Explorer index', 'warn', 'index present but freshness could not be verified against git HEAD');
62
+ if (f.stale) {
63
+ const behind = typeof f.commitsBehind === 'number' ? `${f.commitsBehind} commit(s) behind HEAD` : 'does not match HEAD';
64
+ return check('code-explorer', 'Code Explorer index', 'warn', `stale: ${behind} — re-run the Code Explorer analyze step`);
65
+ }
66
+ return check('code-explorer', 'Code Explorer index', 'ok', 'index matches git HEAD');
67
+ }
68
+ catch (error) {
69
+ return check('code-explorer', 'Code Explorer index', 'warn', `freshness check failed: ${errorText(error)}`);
70
+ }
71
+ }
72
+ async function mcpCheck(cwd, deps) {
73
+ try {
74
+ // Explicit project directory: never process.chdir() (global state shared across awaits).
75
+ // Diagnostics use the strictly read-only loader: no restore/rename/chmod/create of
76
+ // config or backups/temporaries, and corrupt sources surface as honest warnings.
77
+ let servers;
78
+ let warnings = [];
79
+ if (deps.mcpServers) {
80
+ servers = await deps.mcpServers(cwd);
81
+ }
82
+ else {
83
+ const { loadMCPConfigReadOnly } = await import('../mcp/config.js');
84
+ const result = loadMCPConfigReadOnly({ includeDisabled: true, cwd });
85
+ servers = result.servers;
86
+ warnings = result.warnings;
87
+ }
88
+ const disabled = servers.filter((s) => s.enabled === false).length;
89
+ const unusable = servers.filter((s) => {
90
+ if (s.enabled === false)
91
+ return false;
92
+ const type = s.transport && typeof s.transport === 'object' ? s.transport.type : undefined;
93
+ if (typeof type === 'string')
94
+ return !KNOWN_MCP_TRANSPORTS.has(type);
95
+ return typeof s.command !== 'string' || s.command.trim() === '';
96
+ }).map((s) => s.name);
97
+ const base = servers.length === 0
98
+ ? 'no MCP server configured'
99
+ : `${servers.length} configured (${disabled} disabled); no server started`;
100
+ const parts = [base];
101
+ if (warnings.length > 0)
102
+ parts.push(`configuration warnings: ${warnings.join('; ')}`);
103
+ if (unusable.length > 0)
104
+ parts.push(`unusable transport configuration: ${unusable.join(', ')}`);
105
+ const message = parts.join('; ');
106
+ if (warnings.length > 0 || unusable.length > 0) {
107
+ return check('mcp', 'MCP servers', 'warn', message);
108
+ }
109
+ if (servers.length === 0)
110
+ return check('mcp', 'MCP servers', 'ok', message, true);
111
+ return check('mcp', 'MCP servers', 'ok', message);
112
+ }
113
+ catch (error) {
114
+ return check('mcp', 'MCP servers', 'warn', `configuration unreadable: ${errorText(error)}`);
115
+ }
116
+ }
117
+ async function resourcesCheck(deps) {
118
+ try {
119
+ const list = deps.resources ?? (async () => new (await import('../fleet/resource-catalog.js')).ResourceCatalog().list());
120
+ const entries = await list();
121
+ if (entries.length === 0)
122
+ return check('resources', 'Resource catalog', 'ok', 'no resource declared (buddy resources add)', true);
123
+ const count = (state) => entries.filter((e) => e.state === state).length;
124
+ const summary = `${entries.length} declared: ${count('online')} online, ${count('stale')} stale, ${count('offline')} offline, ${count('unknown')} unknown (read-only, no probe)`;
125
+ const attention = entries.filter((e) => e.state === 'stale' || e.state === 'offline').map((e) => `${e.resource.id}=${e.state}`);
126
+ return attention.length > 0
127
+ ? check('resources', 'Resource catalog', 'warn', `${summary}; refresh explicitly with buddy resources probe <id>: ${attention.join(', ')}`)
128
+ : check('resources', 'Resource catalog', 'ok', summary);
129
+ }
130
+ catch (error) {
131
+ return check('resources', 'Resource catalog', 'warn', `catalog unreadable: ${errorText(error)}`);
132
+ }
133
+ }
134
+ async function skillsCheck(deps) {
135
+ try {
136
+ const list = deps.skillsIntegrity ?? (async () => (await import('../skills/hub.js')).getSkillsHub().listWithIntegrity());
137
+ const skills = await list();
138
+ const missing = skills.filter((s) => !s.exists).length;
139
+ const modified = skills.filter((s) => s.exists && !s.integrityOk).length;
140
+ if (missing + modified > 0) {
141
+ return check('skills', 'Skills Hub integrity', 'warn', `${skills.length} installed: ${missing} missing SKILL.md, ${modified} modified — run buddy skills doctor`);
142
+ }
143
+ return check('skills', 'Skills Hub integrity', 'ok', `${skills.length} installed, all intact`, skills.length === 0);
144
+ }
145
+ catch (error) {
146
+ return check('skills', 'Skills Hub integrity', 'warn', `lockfile unreadable: ${errorText(error)}`);
147
+ }
148
+ }
149
+ async function codeExecPolicyCheck(deps) {
150
+ try {
151
+ const model = deps.configuredModel
152
+ ? await deps.configuredModel()
153
+ : await (async () => {
154
+ const { getSettingsManager } = await import('../utils/settings-manager.js');
155
+ const settings = getSettingsManager().readUserSettingsIfPresent();
156
+ return settings?.defaultModel ?? settings?.model ?? process.env.GROK_MODEL;
157
+ })();
158
+ const { resolveCodeExecPolicy } = await import('../config/code-exec-policy.js');
159
+ const resolved = resolveCodeExecPolicy(model);
160
+ const evidence = resolved.evidence ? `, evidence ${resolved.evidence}` : '';
161
+ return check('code-exec-policy', 'Programmatic tool calling', 'ok', `code_exec policy ${resolved.policy} for ${model ?? 'unknown model'} (source ${resolved.source}${evidence})`, true);
162
+ }
163
+ catch (error) {
164
+ return check('code-exec-policy', 'Programmatic tool calling', 'warn', `policy unreadable: ${errorText(error)}`);
165
+ }
166
+ }
167
+ export async function runIntegrationChecks(cwd, deps = {}) {
168
+ return Promise.all([
169
+ lmResizerCheck(deps),
170
+ codeExplorerCheck(cwd, deps),
171
+ mcpCheck(cwd, deps),
172
+ resourcesCheck(deps),
173
+ skillsCheck(deps),
174
+ codeExecPolicyCheck(deps),
175
+ ]);
176
+ }
177
+ // ── JSON report ─────────────────────────────────────────────────────────────
178
+ export const doctorJsonReportSchema = z.object({
179
+ version: z.literal(1),
180
+ generatedAt: z.string(),
181
+ offline: z.boolean(),
182
+ summary: z.object({
183
+ passed: z.number().int().nonnegative(),
184
+ warnings: z.number().int().nonnegative(),
185
+ errors: z.number().int().nonnegative(),
186
+ optionalNotInstalled: z.number().int().nonnegative(),
187
+ }).strict(),
188
+ checks: z.array(z.object({
189
+ id: z.string().regex(/^[a-z0-9][a-z0-9-]*$/),
190
+ section: z.enum(['core', 'integrations']),
191
+ name: z.string(),
192
+ status: z.enum(['ok', 'warn', 'error']),
193
+ message: z.string(),
194
+ fixable: z.boolean(),
195
+ optional: z.boolean(),
196
+ }).strict()),
197
+ fixes: z.array(z.object({ success: z.boolean(), message: z.string(), action: z.string() }).strict()).optional(),
198
+ }).strict();
199
+ export function slugifyCheckName(name) {
200
+ return name.toLowerCase().normalize('NFKD').replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'check';
201
+ }
202
+ export function buildDoctorJsonReport(checks, summary, options) {
203
+ const seen = new Map();
204
+ const report = {
205
+ version: 1,
206
+ generatedAt: (options.now ?? new Date()).toISOString(),
207
+ offline: options.offline,
208
+ summary,
209
+ checks: checks.map((c) => {
210
+ const baseId = c.id ?? slugifyCheckName(c.name);
211
+ const n = (seen.get(baseId) ?? 0) + 1;
212
+ seen.set(baseId, n);
213
+ return {
214
+ id: n === 1 ? baseId : `${baseId}-${n}`,
215
+ section: c.section ?? 'core',
216
+ name: c.name,
217
+ status: c.status,
218
+ message: c.message,
219
+ fixable: c.fixable === true,
220
+ optional: c.optional === true,
221
+ };
222
+ }),
223
+ ...(options.fixes ? { fixes: options.fixes } : {}),
224
+ };
225
+ return doctorJsonReportSchema.parse(report);
226
+ }
227
+ //# sourceMappingURL=integrations.js.map
@@ -0,0 +1,108 @@
1
+ /**
2
+ * `buddy triage` — local, redacted support bundle (P7).
3
+ *
4
+ * Collects the offline doctor report, versions, recent failed runs, a bounded
5
+ * log tail and configuration KEY NAMES, then writes `triage.json` and a
6
+ * prompt (≤ 8 KiB) into a fresh 0700 directory. Nothing is sent anywhere and
7
+ * no agent is launched: the suggested command is printed for the user.
8
+ *
9
+ * Fail closed: every string goes through literal masking of known secret
10
+ * values, then the redaction engine; the serialized bundle and prompt are
11
+ * scanned again and any section still flagged is withheld. If the scan itself
12
+ * fails, nothing is written.
13
+ */
14
+ import type { DoctorCheck } from './index.js';
15
+ import type { DoctorJsonReport } from './integrations.js';
16
+ export declare const TRIAGE_PROMPT_MAX_BYTES = 8192;
17
+ export interface TriageFailedRun {
18
+ runId: string;
19
+ status: string;
20
+ startedAt: string;
21
+ eventCount: number;
22
+ objective: string;
23
+ }
24
+ export interface TriageBundle {
25
+ version: 1;
26
+ generatedAt: string;
27
+ networkUsed: false;
28
+ versions: {
29
+ node: string;
30
+ buddy: string;
31
+ os: string;
32
+ };
33
+ doctor: DoctorJsonReport | {
34
+ withheld: true;
35
+ reason: string;
36
+ };
37
+ failedRuns: TriageFailedRun[] | {
38
+ withheld: true;
39
+ reason: string;
40
+ };
41
+ logs: {
42
+ file: string | null;
43
+ lines: string[];
44
+ } | {
45
+ withheld: true;
46
+ reason: string;
47
+ };
48
+ config: {
49
+ files: Array<{
50
+ file: string;
51
+ keys: string[];
52
+ }>;
53
+ } | {
54
+ withheld: true;
55
+ reason: string;
56
+ };
57
+ redactions: number;
58
+ withheld: string[];
59
+ }
60
+ export interface TriageDeps {
61
+ runDoctor?: () => Promise<{
62
+ checks: DoctorCheck[];
63
+ report: DoctorJsonReport;
64
+ }>;
65
+ listRuns?: (limit: number) => Array<{
66
+ runId: string;
67
+ status: string;
68
+ startedAt: number;
69
+ eventCount: number;
70
+ objective: string;
71
+ }>;
72
+ redact?: (text: string) => {
73
+ redacted: string;
74
+ count: number;
75
+ };
76
+ }
77
+ export interface TriageOptions {
78
+ cwd: string;
79
+ homeDir?: string;
80
+ outParent?: string;
81
+ logFile?: string | null;
82
+ logLines?: number;
83
+ env?: NodeJS.ProcessEnv;
84
+ now?: Date;
85
+ buddyVersion?: string;
86
+ deps?: TriageDeps;
87
+ }
88
+ export interface TriageResult {
89
+ dir: string;
90
+ jsonPath: string;
91
+ promptPath: string;
92
+ promptBytes: number;
93
+ redactions: number;
94
+ withheld: string[];
95
+ suggestedCommand: string;
96
+ bundle: TriageBundle;
97
+ }
98
+ /** Literal values that must never leave the machine, longest first. */
99
+ export declare function collectSecretValues(cwd: string, home: string, env: NodeJS.ProcessEnv): string[];
100
+ export declare function createMasker(secretValues: string[], redact: NonNullable<TriageDeps['redact']>, home?: string): {
101
+ mask: (input: string) => string;
102
+ leaks: (text: string) => boolean;
103
+ count: () => number;
104
+ };
105
+ /** Last `maxLines` lines of a file, reading at most 256 KiB from its end. */
106
+ export declare function tailFile(file: string, maxLines: number): string[];
107
+ export declare function buildTriagePrompt(bundle: TriageBundle, maxBytes?: number): string;
108
+ export declare function writeTriageBundle(options: TriageOptions): Promise<TriageResult>;
@@ -0,0 +1,304 @@
1
+ /**
2
+ * `buddy triage` — local, redacted support bundle (P7).
3
+ *
4
+ * Collects the offline doctor report, versions, recent failed runs, a bounded
5
+ * log tail and configuration KEY NAMES, then writes `triage.json` and a
6
+ * prompt (≤ 8 KiB) into a fresh 0700 directory. Nothing is sent anywhere and
7
+ * no agent is launched: the suggested command is printed for the user.
8
+ *
9
+ * Fail closed: every string goes through literal masking of known secret
10
+ * values, then the redaction engine; the serialized bundle and prompt are
11
+ * scanned again and any section still flagged is withheld. If the scan itself
12
+ * fails, nothing is written.
13
+ */
14
+ import * as fs from 'node:fs';
15
+ import * as os from 'node:os';
16
+ import * as path from 'node:path';
17
+ export const TRIAGE_PROMPT_MAX_BYTES = 8192;
18
+ const DEFAULT_LOG_LINES = 200;
19
+ const LOG_TAIL_READ_BYTES = 256 * 1024;
20
+ const LOG_LINE_MAX_CHARS = 400;
21
+ const SECRET_NAME = /(KEY|TOKEN|SECRET|PASSWORD|PASSWD|PWD|AUTH|CREDENTIAL|COOKIE|SESSION|PRIVATE)/i;
22
+ const PLACEHOLDER = /\[REDACTED(?::[A-Z0-9_]+)?\]/g;
23
+ // ── Masking ─────────────────────────────────────────────────────────────────
24
+ function parseEnvFile(file) {
25
+ const entries = new Map();
26
+ let text;
27
+ try {
28
+ text = fs.readFileSync(file, 'utf8');
29
+ }
30
+ catch {
31
+ return entries;
32
+ }
33
+ for (const raw of text.split(/\r?\n/)) {
34
+ const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_.-]*)\s*=\s*(.*)$/.exec(raw);
35
+ if (!match)
36
+ continue;
37
+ const value = match[2].trim().replace(/^(['"])(.*)\1$/, '$2');
38
+ entries.set(match[1], value);
39
+ }
40
+ return entries;
41
+ }
42
+ function envFiles(cwd, home) {
43
+ return [path.join(cwd, '.env'), path.join(cwd, '.env.local'), path.join(home, '.codebuddy', '.env')];
44
+ }
45
+ /** Literal values that must never leave the machine, longest first. */
46
+ export function collectSecretValues(cwd, home, env) {
47
+ const values = new Set();
48
+ const consider = (name, value) => {
49
+ if (!value)
50
+ return;
51
+ const trimmed = value.trim();
52
+ if (trimmed.length < 6)
53
+ return;
54
+ if (SECRET_NAME.test(name) || /:\/\/[^\s/@]+:[^\s/@]+@/.test(trimmed))
55
+ values.add(trimmed);
56
+ };
57
+ for (const file of envFiles(cwd, home)) {
58
+ for (const [name, value] of parseEnvFile(file))
59
+ consider(name, value);
60
+ }
61
+ for (const [name, value] of Object.entries(env))
62
+ consider(name, value);
63
+ return [...values].sort((a, b) => b.length - a.length);
64
+ }
65
+ export function createMasker(secretValues, redact, home) {
66
+ let total = 0;
67
+ const mask = (input) => {
68
+ let text = input;
69
+ for (const value of secretValues) {
70
+ if (text.includes(value)) {
71
+ const parts = text.split(value);
72
+ total += parts.length - 1;
73
+ text = parts.join('[REDACTED:VALUE]');
74
+ }
75
+ }
76
+ // The home path carries the account name; keep paths readable as `~`.
77
+ if (home && home.length > 1)
78
+ text = text.split(home).join('~');
79
+ text = text.replace(/([a-z][a-z0-9+.-]*:\/\/)[^\s:@/]+:[^\s@/]+@/gi, (_m, scheme) => {
80
+ total += 1;
81
+ return `${scheme}[REDACTED:USERINFO]@`;
82
+ });
83
+ // Fixed point: replacements can expose a new match (`token: <jwt>`).
84
+ for (let i = 0; i < 3; i++) {
85
+ const result = redact(text);
86
+ total += result.count;
87
+ if (result.redacted === text)
88
+ break;
89
+ text = result.redacted;
90
+ }
91
+ return text;
92
+ };
93
+ const leaks = (text) => {
94
+ const stripped = text.replace(PLACEHOLDER, '');
95
+ if (secretValues.some((value) => stripped.includes(value)))
96
+ return true;
97
+ if (/[a-z][a-z0-9+.-]*:\/\/[^\s:@/]+:[^\s@/]+@/i.test(stripped))
98
+ return true;
99
+ return redact(stripped).count > 0;
100
+ };
101
+ return { mask, leaks, count: () => total };
102
+ }
103
+ async function defaultRedact() {
104
+ const { getDataRedactionEngine } = await import('../security/data-redaction.js');
105
+ const engine = getDataRedactionEngine();
106
+ return (text) => {
107
+ const result = engine.redact(text);
108
+ return { redacted: result.redacted, count: result.redactions.length };
109
+ };
110
+ }
111
+ // ── Collectors ──────────────────────────────────────────────────────────────
112
+ async function defaultRunDoctor(cwd) {
113
+ const { runDoctorChecks, summarizeDoctorChecks } = await import('./index.js');
114
+ const { runIntegrationChecks, buildDoctorJsonReport } = await import('./integrations.js');
115
+ const checks = [...(await runDoctorChecks(cwd, { offline: true, noSubprocess: true })), ...(await runIntegrationChecks(cwd, { noSubprocess: true }))];
116
+ return { checks, report: buildDoctorJsonReport(checks, summarizeDoctorChecks(checks), { offline: true }) };
117
+ }
118
+ async function defaultListRuns(limit) {
119
+ const { RunStore } = await import('../observability/run-store.js');
120
+ return RunStore.getInstance().listRuns(limit);
121
+ }
122
+ /** Last `maxLines` lines of a file, reading at most 256 KiB from its end. */
123
+ export function tailFile(file, maxLines) {
124
+ let fd;
125
+ try {
126
+ fd = fs.openSync(file, 'r');
127
+ const size = fs.fstatSync(fd).size;
128
+ const length = Math.min(size, LOG_TAIL_READ_BYTES);
129
+ const buffer = Buffer.alloc(length);
130
+ fs.readSync(fd, buffer, 0, length, size - length);
131
+ const lines = buffer.toString('utf8').split(/\r?\n/);
132
+ if (length < size)
133
+ lines.shift(); // first line is probably partial
134
+ return lines.filter((line) => line.trim() !== '').slice(-maxLines)
135
+ .map((line) => (line.length > LOG_LINE_MAX_CHARS ? `${line.slice(0, LOG_LINE_MAX_CHARS)}…` : line));
136
+ }
137
+ catch {
138
+ return [];
139
+ }
140
+ finally {
141
+ if (fd !== undefined)
142
+ fs.closeSync(fd);
143
+ }
144
+ }
145
+ function jsonKeys(file) {
146
+ try {
147
+ const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
148
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? Object.keys(parsed).sort() : [];
149
+ }
150
+ catch {
151
+ return null;
152
+ }
153
+ }
154
+ function configKeyNames(cwd, home) {
155
+ const out = [];
156
+ const label = (file) => (file.startsWith(home) ? `~${file.slice(home.length)}` : path.relative(cwd, file) || file);
157
+ for (const file of [path.join(home, '.codebuddy', 'settings.json'), path.join(home, '.codebuddy', 'user-settings.json'), path.join(cwd, '.codebuddy', 'settings.json')]) {
158
+ const keys = jsonKeys(file);
159
+ if (keys)
160
+ out.push({ file: label(file), keys });
161
+ }
162
+ for (const file of envFiles(cwd, home)) {
163
+ if (!fs.existsSync(file))
164
+ continue;
165
+ out.push({ file: label(file), keys: [...parseEnvFile(file).keys()].sort() });
166
+ }
167
+ return out;
168
+ }
169
+ // ── Prompt ──────────────────────────────────────────────────────────────────
170
+ function byteLength(text) {
171
+ return Buffer.byteLength(text, 'utf8');
172
+ }
173
+ function truncateBytes(text, max) {
174
+ if (byteLength(text) <= max)
175
+ return text;
176
+ const marker = '\n…[truncated]\n';
177
+ let slice = Buffer.from(text, 'utf8').subarray(0, max - byteLength(marker)).toString('utf8');
178
+ slice = slice.replace(/�$/, '');
179
+ return slice + marker;
180
+ }
181
+ export function buildTriagePrompt(bundle, maxBytes = TRIAGE_PROMPT_MAX_BYTES) {
182
+ const head = [
183
+ '# Code Buddy triage',
184
+ '',
185
+ 'Diagnose this Code Buddy installation from the local bundle below. Secret values are masked as [REDACTED:…];',
186
+ 'do not ask the user to paste keys, tokens or passwords. Propose concrete fixes and the command to verify each one.',
187
+ '',
188
+ `Versions: Buddy ${bundle.versions.buddy}, Node ${bundle.versions.node}, ${bundle.versions.os}`,
189
+ '',
190
+ ].join('\n');
191
+ const doctor = 'withheld' in bundle.doctor
192
+ ? `## Doctor (offline)\nwithheld: ${bundle.doctor.reason}\n`
193
+ : `## Doctor (offline): ${bundle.doctor.summary.errors} error(s), ${bundle.doctor.summary.warnings} warning(s)\n${bundle.doctor.checks
194
+ .filter((c) => c.status !== 'ok')
195
+ .map((c) => `- [${c.status}] ${c.name}: ${c.message}`)
196
+ .join('\n') || '- all checks ok'}\n`;
197
+ const runs = Array.isArray(bundle.failedRuns)
198
+ ? `## Recent failed or stuck runs\n${bundle.failedRuns.map((r) => `- ${r.runId} ${r.status} ${r.startedAt} events=${r.eventCount}: ${r.objective}`).join('\n') || '- none'}\n`
199
+ : `## Recent failed or stuck runs\nwithheld: ${bundle.failedRuns.reason}\n`;
200
+ const config = 'withheld' in bundle.config
201
+ ? `## Configuration keys\nwithheld: ${bundle.config.reason}\n`
202
+ : `## Configuration keys (names only)\n${bundle.config.files.map((f) => `- ${f.file}: ${f.keys.join(', ') || '(empty)'}`).join('\n') || '- none found'}\n`;
203
+ const withheld = bundle.withheld.length > 0 ? `Withheld sections: ${bundle.withheld.join(', ')}\n` : '';
204
+ const fixed = [head, doctor, runs, config, withheld].join('\n');
205
+ const logLines = 'withheld' in bundle.logs ? [] : [...bundle.logs.lines];
206
+ const logHeader = 'withheld' in bundle.logs ? `## Log tail\nwithheld: ${bundle.logs.reason}\n` : '## Log tail (most recent last)\n```\n';
207
+ const logFooter = 'withheld' in bundle.logs ? '' : '```\n';
208
+ const budget = maxBytes - byteLength(fixed) - byteLength(logHeader) - byteLength(logFooter) - 2;
209
+ // Keep the most recent lines that fit.
210
+ const kept = [];
211
+ let used = 0;
212
+ for (let i = logLines.length - 1; i >= 0; i--) {
213
+ const size = byteLength(logLines[i]) + 1;
214
+ if (used + size > budget)
215
+ break;
216
+ kept.unshift(logLines[i]);
217
+ used += size;
218
+ }
219
+ const logs = budget > 0 ? `${logHeader}${kept.map((l) => `${l}\n`).join('')}${logFooter}` : '';
220
+ return truncateBytes(`${fixed}\n${logs}`, maxBytes);
221
+ }
222
+ // ── Main ────────────────────────────────────────────────────────────────────
223
+ function stamp(now) {
224
+ return now.toISOString().replace(/[:.]/g, '-');
225
+ }
226
+ function shellQuote(value) {
227
+ return `'${value.replace(/'/g, `'\\''`)}'`;
228
+ }
229
+ export async function writeTriageBundle(options) {
230
+ const env = options.env ?? process.env;
231
+ const home = options.homeDir ?? os.homedir();
232
+ const now = options.now ?? new Date();
233
+ const deps = options.deps ?? {};
234
+ const redact = deps.redact ?? (await defaultRedact());
235
+ const masker = createMasker(collectSecretValues(options.cwd, home, env), redact, home);
236
+ const withheld = [];
237
+ const doctorRaw = await (deps.runDoctor ?? (() => defaultRunDoctor(options.cwd)))();
238
+ const doctor = {
239
+ ...doctorRaw.report,
240
+ checks: doctorRaw.report.checks.map((c) => ({ ...c, name: masker.mask(c.name), message: masker.mask(c.message) })),
241
+ };
242
+ const runs = deps.listRuns ? deps.listRuns(50) : await defaultListRuns(50);
243
+ const failedRuns = runs
244
+ .filter((r) => r.status === 'failed' || r.status === 'running')
245
+ .slice(0, 10)
246
+ .map((r) => ({
247
+ runId: masker.mask(r.runId),
248
+ status: r.status,
249
+ startedAt: new Date(r.startedAt).toISOString(),
250
+ eventCount: r.eventCount,
251
+ objective: masker.mask((r.objective ?? '').slice(0, 160)),
252
+ }));
253
+ const logFile = options.logFile === undefined
254
+ ? (env.LOG_FILE !== undefined ? env.LOG_FILE || null : path.join(home, '.codebuddy', 'logs', 'codebuddy.log'))
255
+ : options.logFile;
256
+ const logLines = logFile ? tailFile(logFile, options.logLines ?? DEFAULT_LOG_LINES).map(masker.mask) : [];
257
+ const displayLog = logFile ? (logFile.startsWith(home) ? `~${logFile.slice(home.length)}` : logFile) : null;
258
+ const bundle = {
259
+ version: 1,
260
+ generatedAt: now.toISOString(),
261
+ networkUsed: false,
262
+ versions: { node: process.version, buddy: options.buddyVersion ?? 'unknown', os: `${os.platform()} ${os.release()} ${os.arch()}` },
263
+ doctor,
264
+ failedRuns,
265
+ logs: { file: displayLog, lines: logLines },
266
+ config: { files: configKeyNames(options.cwd, home).map((f) => ({ file: masker.mask(f.file), keys: f.keys.map(masker.mask) })) },
267
+ redactions: 0,
268
+ withheld,
269
+ };
270
+ // Fail closed: withhold any section still flagged after masking.
271
+ for (const section of ['doctor', 'failedRuns', 'logs', 'config']) {
272
+ if (masker.leaks(JSON.stringify(bundle[section]))) {
273
+ bundle[section] = { withheld: true, reason: 'secret scan still matched after redaction' };
274
+ withheld.push(section);
275
+ }
276
+ }
277
+ bundle.redactions = masker.count();
278
+ const prompt = buildTriagePrompt(bundle);
279
+ const json = JSON.stringify(bundle, null, 2);
280
+ if (masker.leaks(json) || masker.leaks(prompt)) {
281
+ throw new Error('TRIAGE_SECRET_SCAN_FAILED: refusing to write a bundle that still matches the secret scanner');
282
+ }
283
+ const parent = options.outParent ?? path.join(home, '.codebuddy', 'triage');
284
+ fs.mkdirSync(parent, { recursive: true, mode: 0o700 });
285
+ const dir = fs.mkdtempSync(path.join(parent, `triage-${stamp(now)}-`));
286
+ fs.chmodSync(dir, 0o700);
287
+ const jsonPath = path.join(dir, 'triage.json');
288
+ const promptPath = path.join(dir, 'prompt.md');
289
+ fs.writeFileSync(jsonPath, `${json}\n`, { mode: 0o600, flag: 'wx' });
290
+ fs.writeFileSync(promptPath, prompt, { mode: 0o600, flag: 'wx' });
291
+ fs.chmodSync(jsonPath, 0o600);
292
+ fs.chmodSync(promptPath, 0o600);
293
+ return {
294
+ dir,
295
+ jsonPath,
296
+ promptPath,
297
+ promptBytes: byteLength(prompt),
298
+ redactions: bundle.redactions,
299
+ withheld,
300
+ suggestedCommand: `buddy -p "$(cat ${shellQuote(promptPath)})"`,
301
+ bundle,
302
+ };
303
+ }
304
+ //# sourceMappingURL=triage.js.map
@@ -0,0 +1,44 @@
1
+ /** Explicit, process-independent connections for a bounded fleet review. */
2
+ import { FleetListener } from './fleet-listener.js';
3
+ export interface CollaborationPeer {
4
+ id: string;
5
+ url: string;
6
+ tokenEnv: string;
7
+ role: string;
8
+ }
9
+ export interface CollaborationConfig {
10
+ version: 1;
11
+ peers: CollaborationPeer[];
12
+ }
13
+ export interface PeerResult {
14
+ id: string;
15
+ status: 'ready' | 'completed' | 'failed';
16
+ hostname?: string;
17
+ pid?: number;
18
+ model?: string;
19
+ text?: string;
20
+ error?: string;
21
+ }
22
+ export interface CollaborationReport {
23
+ status: 'complete' | 'partial' | 'failed';
24
+ peers: PeerResult[];
25
+ synthesis?: {
26
+ peer: string;
27
+ text: string;
28
+ };
29
+ synthesisError?: string;
30
+ }
31
+ /** Configuration stores environment variable names, never bearer tokens. */
32
+ export declare function parseCollaborationConfig(value: unknown): CollaborationConfig;
33
+ type Connection = Pick<FleetListener, 'connect' | 'disconnect' | 'request'>;
34
+ export interface CollaborationOptions {
35
+ goal?: string;
36
+ timeoutMs?: number;
37
+ env?: NodeJS.ProcessEnv;
38
+ signal?: AbortSignal;
39
+ onProgress?: (message: string) => void;
40
+ createListener?: (options: ConstructorParameters<typeof FleetListener>[0]) => Connection;
41
+ }
42
+ /** Parallel reviews followed by a synthesis. Peer chat does not execute remote tools. */
43
+ export declare function runCollaboration(config: CollaborationConfig, options?: CollaborationOptions): Promise<CollaborationReport>;
44
+ export {};