@zhijiewang/openharness 0.12.0 → 1.0.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 (523) hide show
  1. package/README.md +56 -0
  2. package/dist/agents/roles.js +42 -0
  3. package/dist/commands/index.js +127 -8
  4. package/dist/components/InitWizard.js +84 -5
  5. package/dist/harness/session.d.ts +19 -0
  6. package/dist/harness/session.js +53 -0
  7. package/dist/mcp/registry.d.ts +31 -0
  8. package/dist/mcp/registry.js +180 -0
  9. package/dist/renderer/index.d.ts +7 -0
  10. package/dist/renderer/index.js +33 -5
  11. package/dist/repl.js +13 -0
  12. package/dist/tools/AgentTool/index.js +1 -1
  13. package/package.json +5 -2
  14. package/dist/DeferredTool.d.ts.map +0 -1
  15. package/dist/DeferredTool.js.map +0 -1
  16. package/dist/DeferredTool.test.d.ts +0 -2
  17. package/dist/DeferredTool.test.d.ts.map +0 -1
  18. package/dist/DeferredTool.test.js +0 -79
  19. package/dist/DeferredTool.test.js.map +0 -1
  20. package/dist/Tool.d.ts.map +0 -1
  21. package/dist/Tool.js.map +0 -1
  22. package/dist/Tool.test.d.ts +0 -2
  23. package/dist/Tool.test.d.ts.map +0 -1
  24. package/dist/Tool.test.js +0 -48
  25. package/dist/Tool.test.js.map +0 -1
  26. package/dist/agents/roles.d.ts.map +0 -1
  27. package/dist/agents/roles.js.map +0 -1
  28. package/dist/agents/roles.test.d.ts +0 -2
  29. package/dist/agents/roles.test.d.ts.map +0 -1
  30. package/dist/agents/roles.test.js +0 -62
  31. package/dist/agents/roles.test.js.map +0 -1
  32. package/dist/commands/commands-new.test.d.ts +0 -5
  33. package/dist/commands/commands-new.test.d.ts.map +0 -1
  34. package/dist/commands/commands-new.test.js +0 -132
  35. package/dist/commands/commands-new.test.js.map +0 -1
  36. package/dist/commands/commands.test.d.ts +0 -2
  37. package/dist/commands/commands.test.d.ts.map +0 -1
  38. package/dist/commands/commands.test.js +0 -98
  39. package/dist/commands/commands.test.js.map +0 -1
  40. package/dist/commands/cybergotchi.d.ts.map +0 -1
  41. package/dist/commands/cybergotchi.js.map +0 -1
  42. package/dist/commands/index.d.ts.map +0 -1
  43. package/dist/commands/index.js.map +0 -1
  44. package/dist/components/App.d.ts.map +0 -1
  45. package/dist/components/App.js.map +0 -1
  46. package/dist/components/CompanionFooter.d.ts.map +0 -1
  47. package/dist/components/CompanionFooter.js.map +0 -1
  48. package/dist/components/CybergotchiBubble.d.ts.map +0 -1
  49. package/dist/components/CybergotchiBubble.js.map +0 -1
  50. package/dist/components/CybergotchiPanel.d.ts.map +0 -1
  51. package/dist/components/CybergotchiPanel.js.map +0 -1
  52. package/dist/components/CybergotchiPanelConnected.d.ts.map +0 -1
  53. package/dist/components/CybergotchiPanelConnected.js.map +0 -1
  54. package/dist/components/CybergotchiSetup.d.ts.map +0 -1
  55. package/dist/components/CybergotchiSetup.js.map +0 -1
  56. package/dist/components/CybergotchiSprite.d.ts.map +0 -1
  57. package/dist/components/CybergotchiSprite.js.map +0 -1
  58. package/dist/components/DiffView.d.ts.map +0 -1
  59. package/dist/components/DiffView.js.map +0 -1
  60. package/dist/components/ErrorBoundary.d.ts.map +0 -1
  61. package/dist/components/ErrorBoundary.js.map +0 -1
  62. package/dist/components/InitWizard.d.ts.map +0 -1
  63. package/dist/components/InitWizard.js.map +0 -1
  64. package/dist/components/Markdown.d.ts.map +0 -1
  65. package/dist/components/Markdown.js.map +0 -1
  66. package/dist/components/Messages.d.ts.map +0 -1
  67. package/dist/components/Messages.js.map +0 -1
  68. package/dist/components/PermissionPrompt.d.ts.map +0 -1
  69. package/dist/components/PermissionPrompt.js.map +0 -1
  70. package/dist/components/REPL.d.ts.map +0 -1
  71. package/dist/components/REPL.js.map +0 -1
  72. package/dist/components/Spinner.d.ts.map +0 -1
  73. package/dist/components/Spinner.js.map +0 -1
  74. package/dist/components/TextInput.d.ts.map +0 -1
  75. package/dist/components/TextInput.js.map +0 -1
  76. package/dist/components/ToolCallDisplay.d.ts.map +0 -1
  77. package/dist/components/ToolCallDisplay.js.map +0 -1
  78. package/dist/cybergotchi/bones.d.ts.map +0 -1
  79. package/dist/cybergotchi/bones.js.map +0 -1
  80. package/dist/cybergotchi/bones.test.d.ts +0 -2
  81. package/dist/cybergotchi/bones.test.d.ts.map +0 -1
  82. package/dist/cybergotchi/bones.test.js +0 -100
  83. package/dist/cybergotchi/bones.test.js.map +0 -1
  84. package/dist/cybergotchi/config.d.ts.map +0 -1
  85. package/dist/cybergotchi/config.js.map +0 -1
  86. package/dist/cybergotchi/events.d.ts.map +0 -1
  87. package/dist/cybergotchi/events.js.map +0 -1
  88. package/dist/cybergotchi/needs.d.ts.map +0 -1
  89. package/dist/cybergotchi/needs.js.map +0 -1
  90. package/dist/cybergotchi/needs.test.d.ts +0 -2
  91. package/dist/cybergotchi/needs.test.d.ts.map +0 -1
  92. package/dist/cybergotchi/needs.test.js +0 -150
  93. package/dist/cybergotchi/needs.test.js.map +0 -1
  94. package/dist/cybergotchi/personality.d.ts.map +0 -1
  95. package/dist/cybergotchi/personality.js.map +0 -1
  96. package/dist/cybergotchi/species.d.ts.map +0 -1
  97. package/dist/cybergotchi/species.js.map +0 -1
  98. package/dist/cybergotchi/speech.d.ts.map +0 -1
  99. package/dist/cybergotchi/speech.js.map +0 -1
  100. package/dist/cybergotchi/types.d.ts.map +0 -1
  101. package/dist/cybergotchi/types.js.map +0 -1
  102. package/dist/cybergotchi/useCybergotchi.d.ts.map +0 -1
  103. package/dist/cybergotchi/useCybergotchi.js.map +0 -1
  104. package/dist/git/git.test.d.ts +0 -2
  105. package/dist/git/git.test.d.ts.map +0 -1
  106. package/dist/git/git.test.js +0 -141
  107. package/dist/git/git.test.js.map +0 -1
  108. package/dist/git/index.d.ts.map +0 -1
  109. package/dist/git/index.js.map +0 -1
  110. package/dist/harness/checkpoints.d.ts.map +0 -1
  111. package/dist/harness/checkpoints.js.map +0 -1
  112. package/dist/harness/config.d.ts.map +0 -1
  113. package/dist/harness/config.js.map +0 -1
  114. package/dist/harness/config.test.d.ts +0 -2
  115. package/dist/harness/config.test.d.ts.map +0 -1
  116. package/dist/harness/config.test.js +0 -107
  117. package/dist/harness/config.test.js.map +0 -1
  118. package/dist/harness/context-warning.d.ts.map +0 -1
  119. package/dist/harness/context-warning.js.map +0 -1
  120. package/dist/harness/context-warning.test.d.ts +0 -2
  121. package/dist/harness/context-warning.test.d.ts.map +0 -1
  122. package/dist/harness/context-warning.test.js +0 -72
  123. package/dist/harness/context-warning.test.js.map +0 -1
  124. package/dist/harness/cost.d.ts.map +0 -1
  125. package/dist/harness/cost.js.map +0 -1
  126. package/dist/harness/cost.test.d.ts +0 -2
  127. package/dist/harness/cost.test.d.ts.map +0 -1
  128. package/dist/harness/cost.test.js +0 -43
  129. package/dist/harness/cost.test.js.map +0 -1
  130. package/dist/harness/credentials.d.ts.map +0 -1
  131. package/dist/harness/credentials.js.map +0 -1
  132. package/dist/harness/credentials.test.d.ts +0 -2
  133. package/dist/harness/credentials.test.d.ts.map +0 -1
  134. package/dist/harness/credentials.test.js +0 -113
  135. package/dist/harness/credentials.test.js.map +0 -1
  136. package/dist/harness/hooks-env.test.d.ts +0 -5
  137. package/dist/harness/hooks-env.test.d.ts.map +0 -1
  138. package/dist/harness/hooks-env.test.js +0 -41
  139. package/dist/harness/hooks-env.test.js.map +0 -1
  140. package/dist/harness/hooks.d.ts.map +0 -1
  141. package/dist/harness/hooks.js.map +0 -1
  142. package/dist/harness/hooks.test.d.ts +0 -2
  143. package/dist/harness/hooks.test.d.ts.map +0 -1
  144. package/dist/harness/hooks.test.js +0 -24
  145. package/dist/harness/hooks.test.js.map +0 -1
  146. package/dist/harness/keybindings.d.ts.map +0 -1
  147. package/dist/harness/keybindings.js.map +0 -1
  148. package/dist/harness/keybindings.test.d.ts +0 -2
  149. package/dist/harness/keybindings.test.d.ts.map +0 -1
  150. package/dist/harness/keybindings.test.js +0 -38
  151. package/dist/harness/keybindings.test.js.map +0 -1
  152. package/dist/harness/memory-consolidation.test.d.ts +0 -2
  153. package/dist/harness/memory-consolidation.test.d.ts.map +0 -1
  154. package/dist/harness/memory-consolidation.test.js +0 -152
  155. package/dist/harness/memory-consolidation.test.js.map +0 -1
  156. package/dist/harness/memory.d.ts.map +0 -1
  157. package/dist/harness/memory.js.map +0 -1
  158. package/dist/harness/memory.test.d.ts +0 -2
  159. package/dist/harness/memory.test.d.ts.map +0 -1
  160. package/dist/harness/memory.test.js +0 -107
  161. package/dist/harness/memory.test.js.map +0 -1
  162. package/dist/harness/onboarding.d.ts.map +0 -1
  163. package/dist/harness/onboarding.js.map +0 -1
  164. package/dist/harness/onboarding.test.d.ts +0 -5
  165. package/dist/harness/onboarding.test.d.ts.map +0 -1
  166. package/dist/harness/onboarding.test.js +0 -93
  167. package/dist/harness/onboarding.test.js.map +0 -1
  168. package/dist/harness/plugins.d.ts.map +0 -1
  169. package/dist/harness/plugins.js.map +0 -1
  170. package/dist/harness/plugins.test.d.ts +0 -2
  171. package/dist/harness/plugins.test.d.ts.map +0 -1
  172. package/dist/harness/plugins.test.js +0 -97
  173. package/dist/harness/plugins.test.js.map +0 -1
  174. package/dist/harness/rules.d.ts.map +0 -1
  175. package/dist/harness/rules.js.map +0 -1
  176. package/dist/harness/rules.test.d.ts +0 -2
  177. package/dist/harness/rules.test.d.ts.map +0 -1
  178. package/dist/harness/rules.test.js +0 -54
  179. package/dist/harness/rules.test.js.map +0 -1
  180. package/dist/harness/session.d.ts.map +0 -1
  181. package/dist/harness/session.js.map +0 -1
  182. package/dist/harness/session.test.d.ts +0 -2
  183. package/dist/harness/session.test.d.ts.map +0 -1
  184. package/dist/harness/session.test.js +0 -73
  185. package/dist/harness/session.test.js.map +0 -1
  186. package/dist/harness/store.d.ts.map +0 -1
  187. package/dist/harness/store.js.map +0 -1
  188. package/dist/harness/store.test.d.ts +0 -2
  189. package/dist/harness/store.test.d.ts.map +0 -1
  190. package/dist/harness/store.test.js +0 -71
  191. package/dist/harness/store.test.js.map +0 -1
  192. package/dist/harness/submit-handler.d.ts.map +0 -1
  193. package/dist/harness/submit-handler.js.map +0 -1
  194. package/dist/harness/submit-handler.test.d.ts +0 -2
  195. package/dist/harness/submit-handler.test.d.ts.map +0 -1
  196. package/dist/harness/submit-handler.test.js +0 -62
  197. package/dist/harness/submit-handler.test.js.map +0 -1
  198. package/dist/harness/verification.d.ts.map +0 -1
  199. package/dist/harness/verification.js.map +0 -1
  200. package/dist/harness/verification.test.d.ts +0 -2
  201. package/dist/harness/verification.test.d.ts.map +0 -1
  202. package/dist/harness/verification.test.js +0 -181
  203. package/dist/harness/verification.test.js.map +0 -1
  204. package/dist/lsp/client.d.ts.map +0 -1
  205. package/dist/lsp/client.js.map +0 -1
  206. package/dist/lsp/client.test.d.ts +0 -2
  207. package/dist/lsp/client.test.d.ts.map +0 -1
  208. package/dist/lsp/client.test.js +0 -44
  209. package/dist/lsp/client.test.js.map +0 -1
  210. package/dist/main.d.ts.map +0 -1
  211. package/dist/main.js.map +0 -1
  212. package/dist/mcp/DeferredMcpTool.d.ts.map +0 -1
  213. package/dist/mcp/DeferredMcpTool.js.map +0 -1
  214. package/dist/mcp/McpTool.d.ts.map +0 -1
  215. package/dist/mcp/McpTool.js.map +0 -1
  216. package/dist/mcp/McpTool.test.d.ts +0 -2
  217. package/dist/mcp/McpTool.test.d.ts.map +0 -1
  218. package/dist/mcp/McpTool.test.js +0 -53
  219. package/dist/mcp/McpTool.test.js.map +0 -1
  220. package/dist/mcp/client.d.ts.map +0 -1
  221. package/dist/mcp/client.js.map +0 -1
  222. package/dist/mcp/loader.d.ts.map +0 -1
  223. package/dist/mcp/loader.js.map +0 -1
  224. package/dist/mcp/loader.test.d.ts +0 -7
  225. package/dist/mcp/loader.test.d.ts.map +0 -1
  226. package/dist/mcp/loader.test.js +0 -25
  227. package/dist/mcp/loader.test.js.map +0 -1
  228. package/dist/mcp/schema.d.ts.map +0 -1
  229. package/dist/mcp/schema.js.map +0 -1
  230. package/dist/mcp/schema.test.d.ts +0 -2
  231. package/dist/mcp/schema.test.d.ts.map +0 -1
  232. package/dist/mcp/schema.test.js +0 -33
  233. package/dist/mcp/schema.test.js.map +0 -1
  234. package/dist/mcp/server.d.ts.map +0 -1
  235. package/dist/mcp/server.js.map +0 -1
  236. package/dist/mcp/server.test.d.ts +0 -2
  237. package/dist/mcp/server.test.d.ts.map +0 -1
  238. package/dist/mcp/server.test.js +0 -54
  239. package/dist/mcp/server.test.js.map +0 -1
  240. package/dist/mcp/types.d.ts.map +0 -1
  241. package/dist/mcp/types.js.map +0 -1
  242. package/dist/providers/anthropic-convert.test.d.ts +0 -5
  243. package/dist/providers/anthropic-convert.test.d.ts.map +0 -1
  244. package/dist/providers/anthropic-convert.test.js +0 -98
  245. package/dist/providers/anthropic-convert.test.js.map +0 -1
  246. package/dist/providers/anthropic.d.ts.map +0 -1
  247. package/dist/providers/anthropic.js.map +0 -1
  248. package/dist/providers/anthropic.test.d.ts +0 -2
  249. package/dist/providers/anthropic.test.d.ts.map +0 -1
  250. package/dist/providers/anthropic.test.js +0 -18
  251. package/dist/providers/anthropic.test.js.map +0 -1
  252. package/dist/providers/base.d.ts.map +0 -1
  253. package/dist/providers/base.js.map +0 -1
  254. package/dist/providers/index.d.ts.map +0 -1
  255. package/dist/providers/index.js.map +0 -1
  256. package/dist/providers/llamacpp.d.ts.map +0 -1
  257. package/dist/providers/llamacpp.js.map +0 -1
  258. package/dist/providers/llamacpp.test.d.ts +0 -2
  259. package/dist/providers/llamacpp.test.d.ts.map +0 -1
  260. package/dist/providers/llamacpp.test.js +0 -33
  261. package/dist/providers/llamacpp.test.js.map +0 -1
  262. package/dist/providers/message-format.test.d.ts +0 -2
  263. package/dist/providers/message-format.test.d.ts.map +0 -1
  264. package/dist/providers/message-format.test.js +0 -31
  265. package/dist/providers/message-format.test.js.map +0 -1
  266. package/dist/providers/ollama.d.ts.map +0 -1
  267. package/dist/providers/ollama.js.map +0 -1
  268. package/dist/providers/ollama.test.d.ts +0 -2
  269. package/dist/providers/ollama.test.d.ts.map +0 -1
  270. package/dist/providers/ollama.test.js +0 -40
  271. package/dist/providers/ollama.test.js.map +0 -1
  272. package/dist/providers/openai.d.ts.map +0 -1
  273. package/dist/providers/openai.js.map +0 -1
  274. package/dist/providers/openai.test.d.ts +0 -2
  275. package/dist/providers/openai.test.d.ts.map +0 -1
  276. package/dist/providers/openai.test.js +0 -23
  277. package/dist/providers/openai.test.js.map +0 -1
  278. package/dist/providers/openrouter.d.ts.map +0 -1
  279. package/dist/providers/openrouter.js.map +0 -1
  280. package/dist/providers/stream-parsing.test.d.ts +0 -6
  281. package/dist/providers/stream-parsing.test.d.ts.map +0 -1
  282. package/dist/providers/stream-parsing.test.js +0 -174
  283. package/dist/providers/stream-parsing.test.js.map +0 -1
  284. package/dist/query/compress.d.ts.map +0 -1
  285. package/dist/query/compress.js.map +0 -1
  286. package/dist/query/errors.d.ts.map +0 -1
  287. package/dist/query/errors.js.map +0 -1
  288. package/dist/query/index.d.ts.map +0 -1
  289. package/dist/query/index.js.map +0 -1
  290. package/dist/query/tools.d.ts.map +0 -1
  291. package/dist/query/tools.js.map +0 -1
  292. package/dist/query/types.d.ts.map +0 -1
  293. package/dist/query/types.js.map +0 -1
  294. package/dist/query.d.ts.map +0 -1
  295. package/dist/query.js.map +0 -1
  296. package/dist/query.test.d.ts +0 -2
  297. package/dist/query.test.d.ts.map +0 -1
  298. package/dist/query.test.js +0 -121
  299. package/dist/query.test.js.map +0 -1
  300. package/dist/remote/server.d.ts.map +0 -1
  301. package/dist/remote/server.js.map +0 -1
  302. package/dist/remote/server.test.d.ts +0 -2
  303. package/dist/remote/server.test.d.ts.map +0 -1
  304. package/dist/remote/server.test.js +0 -120
  305. package/dist/remote/server.test.js.map +0 -1
  306. package/dist/renderer/cells.d.ts.map +0 -1
  307. package/dist/renderer/cells.js.map +0 -1
  308. package/dist/renderer/cells.test.d.ts +0 -2
  309. package/dist/renderer/cells.test.d.ts.map +0 -1
  310. package/dist/renderer/cells.test.js +0 -69
  311. package/dist/renderer/cells.test.js.map +0 -1
  312. package/dist/renderer/colors.d.ts.map +0 -1
  313. package/dist/renderer/colors.js.map +0 -1
  314. package/dist/renderer/diff.d.ts.map +0 -1
  315. package/dist/renderer/diff.js.map +0 -1
  316. package/dist/renderer/diff.test.d.ts +0 -5
  317. package/dist/renderer/diff.test.d.ts.map +0 -1
  318. package/dist/renderer/diff.test.js +0 -140
  319. package/dist/renderer/diff.test.js.map +0 -1
  320. package/dist/renderer/differ.d.ts.map +0 -1
  321. package/dist/renderer/differ.js.map +0 -1
  322. package/dist/renderer/e2e.test.d.ts +0 -6
  323. package/dist/renderer/e2e.test.d.ts.map +0 -1
  324. package/dist/renderer/e2e.test.js +0 -702
  325. package/dist/renderer/e2e.test.js.map +0 -1
  326. package/dist/renderer/image.d.ts.map +0 -1
  327. package/dist/renderer/image.js.map +0 -1
  328. package/dist/renderer/image.test.d.ts +0 -5
  329. package/dist/renderer/image.test.d.ts.map +0 -1
  330. package/dist/renderer/image.test.js +0 -66
  331. package/dist/renderer/image.test.js.map +0 -1
  332. package/dist/renderer/index.d.ts.map +0 -1
  333. package/dist/renderer/index.js.map +0 -1
  334. package/dist/renderer/input.d.ts.map +0 -1
  335. package/dist/renderer/input.js.map +0 -1
  336. package/dist/renderer/input.test.d.ts +0 -5
  337. package/dist/renderer/input.test.d.ts.map +0 -1
  338. package/dist/renderer/input.test.js +0 -129
  339. package/dist/renderer/input.test.js.map +0 -1
  340. package/dist/renderer/layout.d.ts.map +0 -1
  341. package/dist/renderer/layout.js.map +0 -1
  342. package/dist/renderer/markdown.d.ts.map +0 -1
  343. package/dist/renderer/markdown.js.map +0 -1
  344. package/dist/renderer/markdown.test.d.ts +0 -2
  345. package/dist/renderer/markdown.test.d.ts.map +0 -1
  346. package/dist/renderer/markdown.test.js +0 -81
  347. package/dist/renderer/markdown.test.js.map +0 -1
  348. package/dist/renderer/perf.test.d.ts +0 -2
  349. package/dist/renderer/perf.test.d.ts.map +0 -1
  350. package/dist/renderer/perf.test.js +0 -128
  351. package/dist/renderer/perf.test.js.map +0 -1
  352. package/dist/renderer/session-browser.d.ts.map +0 -1
  353. package/dist/renderer/session-browser.js.map +0 -1
  354. package/dist/renderer/session-browser.test.d.ts +0 -6
  355. package/dist/renderer/session-browser.test.d.ts.map +0 -1
  356. package/dist/renderer/session-browser.test.js +0 -95
  357. package/dist/renderer/session-browser.test.js.map +0 -1
  358. package/dist/renderer/ui-ux.test.d.ts +0 -15
  359. package/dist/renderer/ui-ux.test.d.ts.map +0 -1
  360. package/dist/renderer/ui-ux.test.js +0 -470
  361. package/dist/renderer/ui-ux.test.js.map +0 -1
  362. package/dist/repl.d.ts.map +0 -1
  363. package/dist/repl.js.map +0 -1
  364. package/dist/services/AgentDispatcher.d.ts.map +0 -1
  365. package/dist/services/AgentDispatcher.js.map +0 -1
  366. package/dist/services/AgentDispatcher.test.d.ts +0 -2
  367. package/dist/services/AgentDispatcher.test.d.ts.map +0 -1
  368. package/dist/services/AgentDispatcher.test.js +0 -66
  369. package/dist/services/AgentDispatcher.test.js.map +0 -1
  370. package/dist/services/CronExecutor.d.ts.map +0 -1
  371. package/dist/services/CronExecutor.js.map +0 -1
  372. package/dist/services/CronExecutor.test.d.ts +0 -2
  373. package/dist/services/CronExecutor.test.d.ts.map +0 -1
  374. package/dist/services/CronExecutor.test.js +0 -128
  375. package/dist/services/CronExecutor.test.js.map +0 -1
  376. package/dist/services/StreamingToolExecutor.d.ts.map +0 -1
  377. package/dist/services/StreamingToolExecutor.js.map +0 -1
  378. package/dist/services/StreamingToolExecutor.test.d.ts +0 -2
  379. package/dist/services/StreamingToolExecutor.test.d.ts.map +0 -1
  380. package/dist/services/StreamingToolExecutor.test.js +0 -103
  381. package/dist/services/StreamingToolExecutor.test.js.map +0 -1
  382. package/dist/services/agent-messaging.d.ts.map +0 -1
  383. package/dist/services/agent-messaging.js.map +0 -1
  384. package/dist/services/agent-messaging.test.d.ts +0 -2
  385. package/dist/services/agent-messaging.test.d.ts.map +0 -1
  386. package/dist/services/agent-messaging.test.js +0 -88
  387. package/dist/services/agent-messaging.test.js.map +0 -1
  388. package/dist/services/cron.d.ts.map +0 -1
  389. package/dist/services/cron.js.map +0 -1
  390. package/dist/services/cron.test.d.ts +0 -2
  391. package/dist/services/cron.test.d.ts.map +0 -1
  392. package/dist/services/cron.test.js +0 -49
  393. package/dist/services/cron.test.js.map +0 -1
  394. package/dist/test-helpers.d.ts +0 -31
  395. package/dist/test-helpers.d.ts.map +0 -1
  396. package/dist/test-helpers.js +0 -106
  397. package/dist/test-helpers.js.map +0 -1
  398. package/dist/tools/AgentTool/index.d.ts.map +0 -1
  399. package/dist/tools/AgentTool/index.js.map +0 -1
  400. package/dist/tools/AskUserTool/index.d.ts.map +0 -1
  401. package/dist/tools/AskUserTool/index.js.map +0 -1
  402. package/dist/tools/BashTool/index.d.ts.map +0 -1
  403. package/dist/tools/BashTool/index.js.map +0 -1
  404. package/dist/tools/CronTool/index.d.ts.map +0 -1
  405. package/dist/tools/CronTool/index.js.map +0 -1
  406. package/dist/tools/DiagnosticsTool/index.d.ts.map +0 -1
  407. package/dist/tools/DiagnosticsTool/index.js.map +0 -1
  408. package/dist/tools/EnterPlanModeTool/index.d.ts.map +0 -1
  409. package/dist/tools/EnterPlanModeTool/index.js.map +0 -1
  410. package/dist/tools/EnterWorktreeTool/index.d.ts.map +0 -1
  411. package/dist/tools/EnterWorktreeTool/index.js.map +0 -1
  412. package/dist/tools/ExitPlanModeTool/index.d.ts.map +0 -1
  413. package/dist/tools/ExitPlanModeTool/index.js.map +0 -1
  414. package/dist/tools/ExitWorktreeTool/index.d.ts.map +0 -1
  415. package/dist/tools/ExitWorktreeTool/index.js.map +0 -1
  416. package/dist/tools/FileEditTool/index.d.ts.map +0 -1
  417. package/dist/tools/FileEditTool/index.js.map +0 -1
  418. package/dist/tools/FileReadTool/index.d.ts.map +0 -1
  419. package/dist/tools/FileReadTool/index.js.map +0 -1
  420. package/dist/tools/FileWriteTool/index.d.ts.map +0 -1
  421. package/dist/tools/FileWriteTool/index.js.map +0 -1
  422. package/dist/tools/GlobTool/index.d.ts.map +0 -1
  423. package/dist/tools/GlobTool/index.js.map +0 -1
  424. package/dist/tools/GrepTool/index.d.ts.map +0 -1
  425. package/dist/tools/GrepTool/index.js.map +0 -1
  426. package/dist/tools/ImageReadTool/index.d.ts.map +0 -1
  427. package/dist/tools/ImageReadTool/index.js.map +0 -1
  428. package/dist/tools/KillProcessTool/index.d.ts.map +0 -1
  429. package/dist/tools/KillProcessTool/index.js.map +0 -1
  430. package/dist/tools/LSTool/index.d.ts.map +0 -1
  431. package/dist/tools/LSTool/index.js.map +0 -1
  432. package/dist/tools/MemoryTool/index.d.ts.map +0 -1
  433. package/dist/tools/MemoryTool/index.js.map +0 -1
  434. package/dist/tools/MultiEditTool/index.d.ts.map +0 -1
  435. package/dist/tools/MultiEditTool/index.js.map +0 -1
  436. package/dist/tools/NotebookEditTool/index.d.ts.map +0 -1
  437. package/dist/tools/NotebookEditTool/index.js.map +0 -1
  438. package/dist/tools/ParallelAgentTool/index.d.ts.map +0 -1
  439. package/dist/tools/ParallelAgentTool/index.js.map +0 -1
  440. package/dist/tools/RemoteTriggerTool/index.d.ts.map +0 -1
  441. package/dist/tools/RemoteTriggerTool/index.js.map +0 -1
  442. package/dist/tools/SendMessageTool/index.d.ts.map +0 -1
  443. package/dist/tools/SendMessageTool/index.js.map +0 -1
  444. package/dist/tools/SkillTool/index.d.ts.map +0 -1
  445. package/dist/tools/SkillTool/index.js.map +0 -1
  446. package/dist/tools/TaskCreateTool/index.d.ts.map +0 -1
  447. package/dist/tools/TaskCreateTool/index.js.map +0 -1
  448. package/dist/tools/TaskGetTool/index.d.ts.map +0 -1
  449. package/dist/tools/TaskGetTool/index.js.map +0 -1
  450. package/dist/tools/TaskListTool/index.d.ts.map +0 -1
  451. package/dist/tools/TaskListTool/index.js.map +0 -1
  452. package/dist/tools/TaskOutputTool/index.d.ts.map +0 -1
  453. package/dist/tools/TaskOutputTool/index.js.map +0 -1
  454. package/dist/tools/TaskStopTool/index.d.ts.map +0 -1
  455. package/dist/tools/TaskStopTool/index.js.map +0 -1
  456. package/dist/tools/TaskUpdateTool/index.d.ts.map +0 -1
  457. package/dist/tools/TaskUpdateTool/index.js.map +0 -1
  458. package/dist/tools/ToolSearchTool/index.d.ts.map +0 -1
  459. package/dist/tools/ToolSearchTool/index.js.map +0 -1
  460. package/dist/tools/WebFetchTool/index.d.ts.map +0 -1
  461. package/dist/tools/WebFetchTool/index.js.map +0 -1
  462. package/dist/tools/WebSearchTool/index.d.ts.map +0 -1
  463. package/dist/tools/WebSearchTool/index.js.map +0 -1
  464. package/dist/tools/file-edit.test.d.ts +0 -2
  465. package/dist/tools/file-edit.test.d.ts.map +0 -1
  466. package/dist/tools/file-edit.test.js +0 -35
  467. package/dist/tools/file-edit.test.js.map +0 -1
  468. package/dist/tools/tools-basic.test.d.ts +0 -6
  469. package/dist/tools/tools-basic.test.d.ts.map +0 -1
  470. package/dist/tools/tools-basic.test.js +0 -379
  471. package/dist/tools/tools-basic.test.js.map +0 -1
  472. package/dist/tools/web-fetch.test.d.ts +0 -2
  473. package/dist/tools/web-fetch.test.d.ts.map +0 -1
  474. package/dist/tools/web-fetch.test.js +0 -27
  475. package/dist/tools/web-fetch.test.js.map +0 -1
  476. package/dist/tools.d.ts.map +0 -1
  477. package/dist/tools.js.map +0 -1
  478. package/dist/types/events.d.ts.map +0 -1
  479. package/dist/types/events.js.map +0 -1
  480. package/dist/types/message.d.ts.map +0 -1
  481. package/dist/types/message.js.map +0 -1
  482. package/dist/types/message.test.d.ts +0 -2
  483. package/dist/types/message.test.d.ts.map +0 -1
  484. package/dist/types/message.test.js +0 -46
  485. package/dist/types/message.test.js.map +0 -1
  486. package/dist/types/permissions.d.ts.map +0 -1
  487. package/dist/types/permissions.js.map +0 -1
  488. package/dist/types/permissions.test.d.ts +0 -2
  489. package/dist/types/permissions.test.d.ts.map +0 -1
  490. package/dist/types/permissions.test.js +0 -127
  491. package/dist/types/permissions.test.js.map +0 -1
  492. package/dist/utils/bash-safety.d.ts.map +0 -1
  493. package/dist/utils/bash-safety.js.map +0 -1
  494. package/dist/utils/bash-safety.test.d.ts +0 -2
  495. package/dist/utils/bash-safety.test.d.ts.map +0 -1
  496. package/dist/utils/bash-safety.test.js +0 -112
  497. package/dist/utils/bash-safety.test.js.map +0 -1
  498. package/dist/utils/diff-algorithm.d.ts.map +0 -1
  499. package/dist/utils/diff-algorithm.js.map +0 -1
  500. package/dist/utils/diff-algorithm.test.d.ts +0 -2
  501. package/dist/utils/diff-algorithm.test.d.ts.map +0 -1
  502. package/dist/utils/diff-algorithm.test.js +0 -49
  503. package/dist/utils/diff-algorithm.test.js.map +0 -1
  504. package/dist/utils/format.d.ts.map +0 -1
  505. package/dist/utils/format.js.map +0 -1
  506. package/dist/utils/fs.d.ts.map +0 -1
  507. package/dist/utils/fs.js.map +0 -1
  508. package/dist/utils/fs.test.d.ts +0 -5
  509. package/dist/utils/fs.test.d.ts.map +0 -1
  510. package/dist/utils/fs.test.js +0 -82
  511. package/dist/utils/fs.test.js.map +0 -1
  512. package/dist/utils/safe-env.d.ts.map +0 -1
  513. package/dist/utils/safe-env.js.map +0 -1
  514. package/dist/utils/theme-data.d.ts.map +0 -1
  515. package/dist/utils/theme-data.js.map +0 -1
  516. package/dist/utils/theme.d.ts.map +0 -1
  517. package/dist/utils/theme.js.map +0 -1
  518. package/dist/utils/tool-summary.d.ts.map +0 -1
  519. package/dist/utils/tool-summary.js.map +0 -1
  520. package/dist/utils/tool-summary.test.d.ts +0 -2
  521. package/dist/utils/tool-summary.test.d.ts.map +0 -1
  522. package/dist/utils/tool-summary.test.js +0 -93
  523. package/dist/utils/tool-summary.test.js.map +0 -1
package/README.md CHANGED
@@ -366,6 +366,16 @@ mcpServers:
366
366
 
367
367
  MCP tools appear alongside built-in tools. `/status` shows connected servers.
368
368
 
369
+ **MCP Server Registry** — browse and install from a curated catalog:
370
+
371
+ ```
372
+ /mcp-registry # browse all available servers
373
+ /mcp-registry github # show install config for a specific server
374
+ /mcp-registry database # search by category
375
+ ```
376
+
377
+ Categories: filesystem, git, database, api, search, productivity, dev-tools, ai.
378
+
369
379
  ## Git Integration
370
380
 
371
381
  OpenHarness auto-commits AI edits in git repos:
@@ -460,6 +470,9 @@ Dispatch specialized sub-agents for focused tasks:
460
470
  | `refactorer` | Simplify code without changing behavior | All file tools + Bash |
461
471
  | `security-auditor` | OWASP, injection, secrets, CVE scanning | Read-only + Bash |
462
472
  | `evaluator` | Evaluate code quality and run tests (read-only) | Read-only + Bash + Diagnostics |
473
+ | `planner` | Design step-by-step implementation plans | Read-only + Bash |
474
+ | `architect` | Analyze architecture and design structural changes | Read-only |
475
+ | `migrator` | Systematic codebase migrations and upgrades | All file tools + Bash |
463
476
 
464
477
  Each role restricts the sub-agent to only its suggested tools. You can also pass `allowed_tools` explicitly:
465
478
 
@@ -598,6 +611,49 @@ Create `.oh/RULES.md` in any repo (or run `oh init`):
598
611
 
599
612
  Rules load automatically into every session.
600
613
 
614
+ ## Skills & Plugins
615
+
616
+ ### Skills
617
+
618
+ Skills are markdown files with YAML frontmatter that add reusable behaviors:
619
+
620
+ ```markdown
621
+ ---
622
+ name: deploy
623
+ description: Deploy the application to production
624
+ trigger: deploy
625
+ tools: [Bash, Read]
626
+ ---
627
+
628
+ Run the deploy script with health checks...
629
+ ```
630
+
631
+ **Locations** (searched in order):
632
+ 1. `.oh/skills/` — project-level skills
633
+ 2. `~/.oh/skills/` — global skills (available in all projects)
634
+
635
+ Skills auto-trigger when the user's message contains the trigger keyword, or can be invoked explicitly with `/skill deploy`.
636
+
637
+ ### Plugins
638
+
639
+ Plugins are npm packages that bundle skills, hooks, and MCP servers:
640
+
641
+ ```json
642
+ {
643
+ "name": "my-openharness-plugin",
644
+ "version": "1.0.0",
645
+ "skills": ["skills/deploy.md", "skills/review.md"],
646
+ "hooks": {
647
+ "sessionStart": "scripts/setup.sh"
648
+ },
649
+ "mcpServers": [
650
+ { "name": "my-api", "command": "npx", "args": ["-y", "@my-org/mcp-server"] }
651
+ ]
652
+ }
653
+ ```
654
+
655
+ Save as `openharness-plugin.json` in your npm package root. Install with `npm install`, and openHarness discovers it automatically from `node_modules/`.
656
+
601
657
  ## How It Works
602
658
 
603
659
  ```mermaid
@@ -114,6 +114,48 @@ Report findings with severity (Critical/High/Medium/Low), affected file:line, an
114
114
  You CANNOT modify files. Only read, search, and run test/lint commands to evaluate.`,
115
115
  suggestedTools: ['Read', 'Glob', 'Grep', 'LS', 'Bash', 'Diagnostics'],
116
116
  },
117
+ {
118
+ id: 'planner',
119
+ name: 'Planner',
120
+ description: 'Designs step-by-step implementation plans from requirements',
121
+ systemPromptSupplement: `You are a planning agent. Your job is to:
122
+ - Read the codebase to understand architecture, patterns, and conventions
123
+ - Design a detailed step-by-step implementation plan for the given task
124
+ - Identify files to create, modify, or delete with specific change descriptions
125
+ - Flag risks, dependencies, and the recommended implementation order
126
+ - Estimate scope (number of files, complexity)
127
+
128
+ Do NOT implement anything. Your output is a plan document, not code. Read widely before planning.`,
129
+ suggestedTools: ['Read', 'Glob', 'Grep', 'LS', 'Bash'],
130
+ },
131
+ {
132
+ id: 'architect',
133
+ name: 'Architect',
134
+ description: 'Analyzes system architecture and designs structural changes',
135
+ systemPromptSupplement: `You are an architecture agent. Your job is to:
136
+ - Map the current system architecture (modules, dependencies, data flow)
137
+ - Identify architectural patterns and conventions in use
138
+ - Design structural changes that preserve existing patterns
139
+ - Evaluate trade-offs between approaches (performance, maintainability, complexity)
140
+ - Document interfaces, contracts, and integration points
141
+
142
+ Focus on the big picture: module boundaries, data flow, dependency graphs. Leave implementation details to other agents.`,
143
+ suggestedTools: ['Read', 'Glob', 'Grep', 'LS'],
144
+ },
145
+ {
146
+ id: 'migrator',
147
+ name: 'Migrator',
148
+ description: 'Performs codebase migrations (API upgrades, framework changes, renames)',
149
+ systemPromptSupplement: `You are a migration agent. Your job is to:
150
+ - Identify all occurrences of the pattern/API/convention being migrated
151
+ - Apply changes systematically across all affected files
152
+ - Preserve behavior while updating the implementation
153
+ - Run tests after each batch of changes to catch regressions
154
+ - Handle edge cases and conditional patterns that need manual review
155
+
156
+ Work methodically: search exhaustively, change incrementally, test after each batch. Never leave a migration half-done.`,
157
+ suggestedTools: ['Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash'],
158
+ },
117
159
  ];
118
160
  /** Get a role by ID */
119
161
  export function getRole(id) {
@@ -27,7 +27,7 @@ register("help", "Show available commands", () => {
27
27
  const categories = {
28
28
  'Session': ['clear', 'compact', 'export', 'history', 'browse', 'resume', 'fork', 'pin', 'unpin'],
29
29
  'Git': ['diff', 'undo', 'rewind', 'commit', 'log'],
30
- 'Info': ['help', 'cost', 'status', 'config', 'files', 'model', 'memory', 'doctor', 'context', 'mcp'],
30
+ 'Info': ['help', 'cost', 'status', 'config', 'files', 'model', 'memory', 'doctor', 'context', 'mcp', 'mcp-registry'],
31
31
  'Settings': ['theme', 'vim', 'companion', 'fast', 'keys'],
32
32
  'AI': ['plan', 'review', 'roles'],
33
33
  'Pet': ['cybergotchi'],
@@ -473,18 +473,61 @@ register("doctor", "Run diagnostic health checks", (_args, ctx) => {
473
473
  lines.push(` Session: ${ctx.sessionId}`);
474
474
  lines.push(` Messages: ${ctx.messages.length}`);
475
475
  lines.push(` Cost: $${ctx.totalCost.toFixed(4)}`);
476
- // Disk space (basic check)
476
+ // Disk space & storage
477
477
  try {
478
- const { statSync } = require("node:fs");
479
478
  const ohDir = join(homedir(), ".oh");
480
479
  if (existsSync(ohDir)) {
481
- const sessions = readdirSync(join(ohDir, "sessions")).length;
482
- lines.push(` Sessions: ${sessions} saved`);
483
- if (sessions > 80)
484
- issues.push(`${sessions} saved sessions. Consider cleaning old ones.`);
480
+ const sessionsDir = join(ohDir, "sessions");
481
+ const sessCount = existsSync(sessionsDir) ? readdirSync(sessionsDir).filter(f => f.endsWith('.json')).length : 0;
482
+ lines.push(` Sessions: ${sessCount} saved`);
483
+ if (sessCount > 80)
484
+ issues.push(`${sessCount} saved sessions. Consider cleaning old ones.`);
485
+ // Memory stats
486
+ const memDir = join(ohDir, "memory");
487
+ const memCount = existsSync(memDir) ? readdirSync(memDir).filter(f => f.endsWith('.md')).length : 0;
488
+ lines.push(` Memories: ${memCount} global`);
489
+ // Cron stats
490
+ const cronDir = join(ohDir, "crons");
491
+ const cronCount = existsSync(cronDir) ? readdirSync(cronDir).filter(f => f.endsWith('.json')).length : 0;
492
+ lines.push(` Cron tasks: ${cronCount}`);
485
493
  }
486
494
  }
487
495
  catch { /* ignore */ }
496
+ // Project-level stats
497
+ try {
498
+ const projMemDir = join(".oh", "memory");
499
+ const projMemCount = existsSync(projMemDir) ? readdirSync(projMemDir).filter(f => f.endsWith('.md')).length : 0;
500
+ if (projMemCount > 0)
501
+ lines.push(` Project mems: ${projMemCount}`);
502
+ const skillsDir = join(".oh", "skills");
503
+ const skillCount = existsSync(skillsDir) ? readdirSync(skillsDir).filter(f => f.endsWith('.md')).length : 0;
504
+ if (skillCount > 0)
505
+ lines.push(` Skills: ${skillCount}`);
506
+ }
507
+ catch { /* ignore */ }
508
+ // Global config
509
+ const globalCfg = existsSync(join(homedir(), ".oh", "config.yaml"));
510
+ lines.push(` Global config: ${globalCfg ? "~/.oh/config.yaml ✓" : "not set (optional)"}`);
511
+ // Verification config
512
+ try {
513
+ const { getVerificationConfig } = require('../harness/verification.js');
514
+ const vCfg = getVerificationConfig();
515
+ if (vCfg?.enabled) {
516
+ lines.push(` Verification: ✓ (${vCfg.rules.length} rules, mode: ${vCfg.mode})`);
517
+ }
518
+ else {
519
+ lines.push(` Verification: off (no rules detected)`);
520
+ }
521
+ }
522
+ catch { /* ignore */ }
523
+ // Tools
524
+ lines.push("");
525
+ lines.push(` Tools: ${ctx.messages.length > 0 ? 'ready' : 'loaded'}`);
526
+ // Node.js version
527
+ lines.push(` Node.js: ${process.version}`);
528
+ const [major] = process.version.slice(1).split('.').map(Number);
529
+ if (major && major < 18)
530
+ issues.push(`Node.js ${process.version} is below minimum (18+). Upgrade Node.js.`);
488
531
  // Issues summary
489
532
  if (issues.length > 0) {
490
533
  lines.push("");
@@ -519,14 +562,43 @@ register("context", "Show context window usage breakdown", (_args, ctx) => {
519
562
  register("mcp", "Show MCP server status", () => {
520
563
  const mcp = connectedMcpServers();
521
564
  if (mcp.length === 0) {
522
- return { output: "No MCP servers connected.\nConfigure in .oh/config.yaml under mcpServers.", handled: true };
565
+ return { output: "No MCP servers connected.\nConfigure in .oh/config.yaml under mcpServers.\nRun /mcp-registry to browse available servers.", handled: true };
523
566
  }
524
567
  const lines = [`MCP Servers (${mcp.length} connected):\n`];
525
568
  for (const name of mcp) {
526
569
  lines.push(` ✓ ${name}`);
527
570
  }
571
+ lines.push("\nRun /mcp-registry to browse and add more servers.");
528
572
  return { output: lines.join("\n"), handled: true };
529
573
  });
574
+ register("mcp-registry", "Browse and add MCP servers from the curated registry", (args) => {
575
+ const { searchRegistry, formatRegistry, generateConfigBlock, MCP_REGISTRY } = require('../mcp/registry.js');
576
+ const query = args.trim();
577
+ if (!query) {
578
+ // Show full registry
579
+ const output = `MCP Server Registry (${MCP_REGISTRY.length} servers)\n${'─'.repeat(50)}\n\n${formatRegistry()}\n\nUsage:\n /mcp-registry <name> Show install config for a server\n /mcp-registry <keyword> Search by name, description, or category`;
580
+ return { output, handled: true };
581
+ }
582
+ // Search or show specific server
583
+ const results = searchRegistry(query);
584
+ if (results.length === 0) {
585
+ return { output: `No MCP servers found matching "${query}".`, handled: true };
586
+ }
587
+ if (results.length === 1) {
588
+ // Show install instructions
589
+ const entry = results[0];
590
+ const config = generateConfigBlock(entry);
591
+ const envNote = entry.envVars?.length
592
+ ? `\n\nRequired environment variables:\n${entry.envVars.map((v) => ` - ${v}`).join('\n')}`
593
+ : '';
594
+ return {
595
+ output: `${entry.name} — ${entry.description}\nPackage: ${entry.package}\nRisk: ${entry.riskLevel ?? 'medium'}${envNote}\n\nAdd to .oh/config.yaml under mcpServers:\n\n${config}`,
596
+ handled: true,
597
+ };
598
+ }
599
+ // Multiple results
600
+ return { output: `Found ${results.length} servers:\n\n${formatRegistry(results)}`, handled: true };
601
+ });
530
602
  function setPinned(args, ctx, pinned) {
531
603
  const idx = parseInt(args.trim(), 10);
532
604
  if (isNaN(idx) || idx < 1 || idx > ctx.messages.length) {
@@ -542,6 +614,53 @@ function setPinned(args, ctx, pinned) {
542
614
  }
543
615
  register("pin", "Pin a message (survives /compact)", (args, ctx) => setPinned(args, ctx, true));
544
616
  register("unpin", "Unpin a message", (args, ctx) => setPinned(args, ctx, false));
617
+ register("plugins", "List installed plugins and discover new ones", (args) => {
618
+ const { discoverPlugins, discoverSkills } = require('../harness/plugins.js');
619
+ const query = args.trim();
620
+ if (query === 'search' || query.startsWith('search ')) {
621
+ // npm registry search
622
+ const keyword = query.replace(/^search\s*/, '').trim() || 'openharness-plugin';
623
+ return {
624
+ output: `To discover plugins, search npm:\n\n npm search openharness-plugin${keyword !== 'openharness-plugin' ? ' ' + keyword : ''}\n\nInstall with:\n npm install <package-name>\n\nPlugins are auto-discovered from node_modules/ if they contain openharness-plugin.json.`,
625
+ handled: true,
626
+ };
627
+ }
628
+ // List installed
629
+ const plugins = discoverPlugins();
630
+ const skills = discoverSkills();
631
+ const lines = [];
632
+ if (plugins.length > 0) {
633
+ lines.push(`Installed Plugins (${plugins.length}):`);
634
+ for (const p of plugins) {
635
+ lines.push(` ${p.name}@${p.version} — ${p.description || 'no description'}`);
636
+ if (p.skills?.length)
637
+ lines.push(` Skills: ${p.skills.length}`);
638
+ if (p.mcpServers?.length)
639
+ lines.push(` MCP servers: ${p.mcpServers.map((s) => s.name).join(', ')}`);
640
+ }
641
+ lines.push('');
642
+ }
643
+ if (skills.length > 0) {
644
+ lines.push(`Available Skills (${skills.length}):`);
645
+ const bySource = {};
646
+ for (const s of skills) {
647
+ (bySource[s.source] ??= []).push(s);
648
+ }
649
+ for (const [source, sourceSkills] of Object.entries(bySource)) {
650
+ lines.push(` ${source}:`);
651
+ for (const s of sourceSkills) {
652
+ lines.push(` ${s.name} — ${s.description}${s.trigger ? ` (trigger: "${s.trigger}")` : ''}`);
653
+ }
654
+ }
655
+ }
656
+ else if (plugins.length === 0) {
657
+ lines.push('No plugins or skills installed.');
658
+ lines.push('');
659
+ lines.push('Create skills in .oh/skills/ or ~/.oh/skills/');
660
+ lines.push('Run /plugins search to find npm packages.');
661
+ }
662
+ return { output: lines.join('\n'), handled: true };
663
+ });
545
664
  // ── Command Parser ──
546
665
  /**
547
666
  * Check if input is a slash command. If so, execute it.
@@ -30,9 +30,30 @@ const PERMISSION_MODES = [
30
30
  { key: "trust", label: "trust — auto-approve everything" },
31
31
  { key: "deny", label: "deny — read-only, block write/run tools" },
32
32
  ];
33
+ /** Auto-detect provider from environment variables */
34
+ function detectProviderFromEnv() {
35
+ if (process.env.ANTHROPIC_API_KEY)
36
+ return PROVIDERS.findIndex(p => p.key === "anthropic");
37
+ if (process.env.OPENAI_API_KEY)
38
+ return PROVIDERS.findIndex(p => p.key === "openai");
39
+ if (process.env.OPENROUTER_API_KEY)
40
+ return PROVIDERS.findIndex(p => p.key === "openrouter");
41
+ return 0; // Default to Ollama
42
+ }
43
+ /** Get the detected API key for a provider */
44
+ function getEnvApiKey(providerKey) {
45
+ const envMap = {
46
+ anthropic: 'ANTHROPIC_API_KEY',
47
+ openai: 'OPENAI_API_KEY',
48
+ openrouter: 'OPENROUTER_API_KEY',
49
+ };
50
+ const envVar = envMap[providerKey];
51
+ return envVar ? (process.env[envVar] ?? '') : '';
52
+ }
33
53
  export default function InitWizard({ onDone }) {
54
+ const detectedIdx = detectProviderFromEnv();
34
55
  const [step, setStep] = useState("provider");
35
- const [providerIdx, setProviderIdx] = useState(0);
56
+ const [providerIdx, setProviderIdx] = useState(detectedIdx);
36
57
  const [apiKey, setApiKey] = useState("");
37
58
  const [baseUrl, setBaseUrl] = useState("");
38
59
  const [model, setModel] = useState("");
@@ -43,6 +64,9 @@ export default function InitWizard({ onDone }) {
43
64
  const [permIdx, setPermIdx] = useState(0);
44
65
  const [hatchGotchi, setHatchGotchi] = useState(false);
45
66
  const [showSetup, setShowSetup] = useState(false);
67
+ const [suggestedMcp, setSuggestedMcp] = useState([]);
68
+ const [selectedMcp, setSelectedMcp] = useState(new Set());
69
+ const [mcpIdx, setMcpIdx] = useState(0);
46
70
  const provider = PROVIDERS[providerIdx];
47
71
  // ── Keyboard navigation ──
48
72
  useInput(useCallback((input, key) => {
@@ -54,11 +78,19 @@ export default function InitWizard({ onDone }) {
54
78
  if (key.return) {
55
79
  setBaseUrl(provider.defaultBaseUrl ?? "");
56
80
  setModel(provider.defaultModel);
81
+ // Auto-fill API key from environment if available
82
+ const envKey = getEnvApiKey(provider.key);
83
+ if (envKey)
84
+ setApiKey(envKey);
57
85
  if (!provider.needsApiKey) {
58
- // Skip API key, go straight to testing
59
86
  runTest(provider, "", provider.defaultBaseUrl ?? "");
60
87
  setStep("testing");
61
88
  }
89
+ else if (envKey) {
90
+ // Have env API key — skip manual entry, go to testing
91
+ runTest(provider, envKey, provider.defaultBaseUrl ?? "");
92
+ setStep("testing");
93
+ }
62
94
  else {
63
95
  setStep("apikey");
64
96
  }
@@ -69,8 +101,37 @@ export default function InitWizard({ onDone }) {
69
101
  setPermIdx(i => Math.max(0, i - 1));
70
102
  if (key.downArrow)
71
103
  setPermIdx(i => Math.min(PERMISSION_MODES.length - 1, i + 1));
104
+ if (key.return) {
105
+ // Suggest popular MCP servers
106
+ setSuggestedMcp(['github', 'memory', 'fetch', 'sequential-thinking', 'brave-search']);
107
+ setMcpIdx(0);
108
+ setSelectedMcp(new Set());
109
+ setStep("mcp");
110
+ }
111
+ }
112
+ if (step === "mcp") {
113
+ if (key.upArrow)
114
+ setMcpIdx(i => Math.max(0, i - 1));
115
+ if (key.downArrow)
116
+ setMcpIdx(i => Math.min(suggestedMcp.length - 1, i + 1));
117
+ if (input === " ") {
118
+ // Toggle selection
119
+ const name = suggestedMcp[mcpIdx];
120
+ if (name) {
121
+ setSelectedMcp(prev => {
122
+ const next = new Set(prev);
123
+ if (next.has(name))
124
+ next.delete(name);
125
+ else
126
+ next.add(name);
127
+ return next;
128
+ });
129
+ }
130
+ }
72
131
  if (key.return)
73
132
  setStep("gotchi");
133
+ if (input === "s" || input === "S")
134
+ setStep("gotchi"); // Skip
74
135
  }
75
136
  if (step === "model" && availableModels.length > 0) {
76
137
  if (key.upArrow)
@@ -120,16 +181,34 @@ export default function InitWizard({ onDone }) {
120
181
  // ── Write final config ──
121
182
  const writeFinal = useCallback(() => {
122
183
  const selectedModel = availableModels.length > 0 ? (availableModels[modelIdx] ?? model) : model;
184
+ // Build MCP server configs from selected registry entries
185
+ let mcpServers;
186
+ if (selectedMcp.size > 0) {
187
+ try {
188
+ const { MCP_REGISTRY } = require('../mcp/registry.js');
189
+ mcpServers = [...selectedMcp]
190
+ .map(name => MCP_REGISTRY.find((e) => e.name === name))
191
+ .filter(Boolean)
192
+ .map((e) => ({
193
+ name: e.name,
194
+ command: 'npx',
195
+ args: ['-y', e.package, ...(e.args ?? [])],
196
+ ...(e.envVars?.length ? { env: Object.fromEntries(e.envVars.map((v) => [v, `YOUR_${v}`])) } : {}),
197
+ }));
198
+ }
199
+ catch { /* ignore */ }
200
+ }
123
201
  writeOhConfig({
124
202
  provider: provider.key,
125
203
  model: selectedModel || provider.defaultModel,
126
204
  permissionMode: PERMISSION_MODES[permIdx].key,
127
205
  ...(apiKey ? { apiKey } : {}),
128
206
  ...(baseUrl ? { baseUrl } : {}),
207
+ ...(mcpServers?.length ? { mcpServers } : {}),
129
208
  });
130
209
  setStep("done");
131
210
  setTimeout(() => onDone?.(), 1500);
132
- }, [provider, model, availableModels, modelIdx, permIdx, apiKey, baseUrl]);
211
+ }, [provider, model, availableModels, modelIdx, permIdx, apiKey, baseUrl, selectedMcp]);
133
212
  // ── Render ──
134
213
  if (showSetup) {
135
214
  return _jsx(CybergotchiSetup, { onComplete: () => { setShowSetup(false); writeFinal(); }, onSkip: () => { setShowSetup(false); writeFinal(); } });
@@ -137,7 +216,7 @@ export default function InitWizard({ onDone }) {
137
216
  if (step === "done") {
138
217
  return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { color: "green", children: "\u2713 OpenHarness configured!" }), _jsx(Text, { dimColor: true, children: "Config saved to .oh/config.yaml" }), _jsx(Text, { dimColor: true, children: "Run: oh" })] }));
139
218
  }
140
- return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "OpenHarness Setup" }), _jsx(Text, { children: " " }), step === "provider" && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Select provider:" }), PROVIDERS.map((p, i) => (_jsxs(Text, { color: i === providerIdx ? "cyan" : undefined, children: [i === providerIdx ? "▶ " : " ", p.label] }, p.key))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Enter select" })] })), step === "apikey" && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["API key for ", _jsx(Text, { color: "cyan", children: provider.label }), ":"] }), _jsx(TextInput, { value: apiKey, onChange: setApiKey, mask: "*", onSubmit: (val) => {
219
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "OpenHarness Setup" }), _jsx(Text, { children: " " }), step === "provider" && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["Select provider:", detectedIdx > 0 ? _jsx(Text, { dimColor: true, children: " (auto-detected from env)" }) : ''] }), PROVIDERS.map((p, i) => (_jsxs(Text, { color: i === providerIdx ? "cyan" : undefined, children: [i === providerIdx ? "▶ " : " ", p.label] }, p.key))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Enter select" })] })), step === "apikey" && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["API key for ", _jsx(Text, { color: "cyan", children: provider.label }), ":"] }), _jsx(TextInput, { value: apiKey, onChange: setApiKey, mask: "*", onSubmit: (val) => {
141
220
  if (!val.trim())
142
221
  return;
143
222
  if (provider.key === "custom") {
@@ -158,6 +237,6 @@ export default function InitWizard({ onDone }) {
158
237
  const gi = start + vi;
159
238
  return (_jsxs(Text, { color: gi === modelIdx ? "cyan" : undefined, children: [gi === modelIdx ? "▶ " : " ", m] }, m));
160
239
  }), start + WINDOW < availableModels.length && (_jsxs(Text, { dimColor: true, children: [" \u2193 ", availableModels.length - start - WINDOW, " more"] }))] }));
161
- })() : (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "Could not fetch model list. Enter model name:" }), _jsx(TextInput, { value: model, onChange: setModel, onSubmit: () => setStep("permission") })] })), availableModels.length > 0 && _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Enter select" })] })), step === "permission" && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Permission mode:" }), PERMISSION_MODES.map((p, i) => (_jsxs(Text, { color: i === permIdx ? "cyan" : undefined, children: [i === permIdx ? "▶ " : " ", p.label] }, p.key))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Enter select" })] })), step === "gotchi" && (_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { children: ["Hatch a cybergotchi companion? ", _jsx(Text, { dimColor: true, children: "(Y/n)" })] }) }))] }));
240
+ })() : (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "Could not fetch model list. Enter model name:" }), _jsx(TextInput, { value: model, onChange: setModel, onSubmit: () => setStep("permission") })] })), availableModels.length > 0 && _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Enter select" })] })), step === "permission" && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Permission mode:" }), PERMISSION_MODES.map((p, i) => (_jsxs(Text, { color: i === permIdx ? "cyan" : undefined, children: [i === permIdx ? "▶ " : " ", p.label] }, p.key))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Enter select" })] })), step === "mcp" && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["Add MCP servers? ", _jsx(Text, { dimColor: true, children: "(Space to toggle, Enter to confirm, S to skip)" })] }), _jsx(Text, { children: " " }), suggestedMcp.map((name, i) => (_jsxs(Text, { color: i === mcpIdx ? "cyan" : undefined, children: [i === mcpIdx ? "▶ " : " ", selectedMcp.has(name) ? "[✓] " : "[ ] ", name] }, name))), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate Space toggle Enter confirm S skip" })] })), step === "gotchi" && (_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { children: ["Hatch a cybergotchi companion? ", _jsx(Text, { dimColor: true, children: "(Y/n)" })] }) }))] }));
162
241
  }
163
242
  //# sourceMappingURL=InitWizard.js.map
@@ -13,6 +13,14 @@ export type Session = {
13
13
  gitBranch?: string;
14
14
  workingDir?: string;
15
15
  tools?: string[];
16
+ /** Hibernate state — saved on exit for wake reconstruction */
17
+ hibernate?: {
18
+ summary?: string;
19
+ lastUserMessage?: string;
20
+ pendingTask?: string;
21
+ totalInputTokens?: number;
22
+ totalOutputTokens?: number;
23
+ };
16
24
  };
17
25
  export declare function createSession(provider: string, model: string, extras?: {
18
26
  gitBranch?: string;
@@ -30,6 +38,17 @@ export declare function listSessions(dir?: string): Array<{
30
38
  }>;
31
39
  /** Returns the ID of the most recently updated session, or null if none exist. */
32
40
  export declare function getLastSessionId(dir?: string): string | null;
41
+ /**
42
+ * Build hibernate state from the current session.
43
+ * Captures the last user message, recent assistant activity,
44
+ * and a brief summary for context reconstruction on wake.
45
+ */
46
+ export declare function buildHibernateState(messages: Message[]): Session['hibernate'];
47
+ /**
48
+ * Generate a wake-up context message for a resumed session.
49
+ * Tells the LLM what happened in the previous session.
50
+ */
51
+ export declare function buildWakeContext(session: Session): string;
33
52
  /**
34
53
  * Evict oldest sessions when count exceeds MAX_SESSIONS.
35
54
  * Called automatically by saveSession.
@@ -72,6 +72,59 @@ export function getLastSessionId(dir) {
72
72
  const sessions = listSessions(dir);
73
73
  return sessions.length > 0 ? sessions[0].id : null;
74
74
  }
75
+ /**
76
+ * Build hibernate state from the current session.
77
+ * Captures the last user message, recent assistant activity,
78
+ * and a brief summary for context reconstruction on wake.
79
+ */
80
+ export function buildHibernateState(messages) {
81
+ if (messages.length === 0)
82
+ return undefined;
83
+ // Find last user message
84
+ const lastUser = [...messages].reverse().find(m => m.role === 'user');
85
+ const lastAssistant = [...messages].reverse().find(m => m.role === 'assistant');
86
+ // Build a brief summary from the last few exchanges
87
+ const recentMsgs = messages.slice(-6);
88
+ const summaryParts = [];
89
+ for (const m of recentMsgs) {
90
+ if (m.role === 'user') {
91
+ summaryParts.push(`User: ${m.content.slice(0, 100)}`);
92
+ }
93
+ else if (m.role === 'assistant' && m.content) {
94
+ summaryParts.push(`Assistant: ${m.content.slice(0, 100)}`);
95
+ }
96
+ }
97
+ return {
98
+ lastUserMessage: lastUser?.content.slice(0, 200),
99
+ pendingTask: lastAssistant?.content.slice(0, 200),
100
+ summary: summaryParts.join('\n'),
101
+ };
102
+ }
103
+ /**
104
+ * Generate a wake-up context message for a resumed session.
105
+ * Tells the LLM what happened in the previous session.
106
+ */
107
+ export function buildWakeContext(session) {
108
+ const parts = ['[Session Resumed]'];
109
+ if (session.workingDir) {
110
+ parts.push(`Previous working directory: ${session.workingDir}`);
111
+ if (session.workingDir !== process.cwd()) {
112
+ parts.push(`WARNING: Working directory changed! Was: ${session.workingDir}, Now: ${process.cwd()}`);
113
+ }
114
+ }
115
+ if (session.gitBranch) {
116
+ parts.push(`Previous git branch: ${session.gitBranch}`);
117
+ }
118
+ if (session.hibernate?.summary) {
119
+ parts.push(`\nPrevious session context:\n${session.hibernate.summary}`);
120
+ }
121
+ if (session.hibernate?.lastUserMessage) {
122
+ parts.push(`\nLast user request: ${session.hibernate.lastUserMessage}`);
123
+ }
124
+ parts.push(`\nSession has ${session.messages.length} messages and cost $${session.totalCost.toFixed(4)} so far.`);
125
+ parts.push('Continue where you left off. If the user\'s last request was incomplete, acknowledge that and ask how to proceed.');
126
+ return parts.join('\n');
127
+ }
75
128
  /** Maximum number of sessions to keep on disk. */
76
129
  const MAX_SESSIONS = 100;
77
130
  /**
@@ -0,0 +1,31 @@
1
+ /**
2
+ * MCP Server Registry — curated list of compatible MCP servers.
3
+ *
4
+ * Provides a browsable catalog of MCP servers that users can add
5
+ * to their .oh/config.yaml with a single command.
6
+ */
7
+ export type McpRegistryEntry = {
8
+ name: string;
9
+ package: string;
10
+ description: string;
11
+ category: 'filesystem' | 'git' | 'database' | 'api' | 'search' | 'productivity' | 'dev-tools' | 'ai';
12
+ args?: string[];
13
+ envVars?: string[];
14
+ riskLevel?: 'low' | 'medium' | 'high';
15
+ };
16
+ /**
17
+ * Curated registry of popular MCP servers.
18
+ * Each entry has enough info to generate the config.yaml block.
19
+ */
20
+ export declare const MCP_REGISTRY: McpRegistryEntry[];
21
+ /** Search the registry by name or keyword */
22
+ export declare function searchRegistry(query: string): McpRegistryEntry[];
23
+ /** Get all entries in a category */
24
+ export declare function getByCategory(category: McpRegistryEntry['category']): McpRegistryEntry[];
25
+ /** Get all unique categories */
26
+ export declare function getCategories(): string[];
27
+ /** Generate the config.yaml block for a registry entry */
28
+ export declare function generateConfigBlock(entry: McpRegistryEntry): string;
29
+ /** Format registry as a browsable list */
30
+ export declare function formatRegistry(entries?: McpRegistryEntry[]): string;
31
+ //# sourceMappingURL=registry.d.ts.map