opencode-repos 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (475) hide show
  1. package/AGENTS.md +180 -0
  2. package/README.md +103 -3
  3. package/TODO.md +3 -0
  4. package/index.ts +1590 -158
  5. package/oh-my-opencode/.github/FUNDING.yml +15 -0
  6. package/oh-my-opencode/.github/ISSUE_TEMPLATE/bug_report.yml +129 -0
  7. package/oh-my-opencode/.github/ISSUE_TEMPLATE/config.yml +8 -0
  8. package/oh-my-opencode/.github/ISSUE_TEMPLATE/feature_request.yml +100 -0
  9. package/oh-my-opencode/.github/ISSUE_TEMPLATE/general.yml +83 -0
  10. package/oh-my-opencode/.github/assets/google.jpg +0 -0
  11. package/oh-my-opencode/.github/assets/hero.jpg +0 -0
  12. package/oh-my-opencode/.github/assets/indent.jpg +0 -0
  13. package/oh-my-opencode/.github/assets/microsoft.jpg +0 -0
  14. package/oh-my-opencode/.github/assets/omo.png +0 -0
  15. package/oh-my-opencode/.github/assets/orchestrator-atlas.png +0 -0
  16. package/oh-my-opencode/.github/assets/sisyphus.png +0 -0
  17. package/oh-my-opencode/.github/assets/sisyphuslabs.png +0 -0
  18. package/oh-my-opencode/.github/pull_request_template.md +34 -0
  19. package/oh-my-opencode/.github/workflows/ci.yml +138 -0
  20. package/oh-my-opencode/.github/workflows/cla.yml +41 -0
  21. package/oh-my-opencode/.github/workflows/lint-workflows.yml +22 -0
  22. package/oh-my-opencode/.github/workflows/publish.yml +165 -0
  23. package/oh-my-opencode/.github/workflows/sisyphus-agent.yml +500 -0
  24. package/oh-my-opencode/.opencode/background-tasks.json +27 -0
  25. package/oh-my-opencode/.opencode/command/get-unpublished-changes.md +84 -0
  26. package/oh-my-opencode/.opencode/command/omomomo.md +37 -0
  27. package/oh-my-opencode/.opencode/command/publish.md +257 -0
  28. package/oh-my-opencode/AGENTS.md +179 -0
  29. package/oh-my-opencode/CLA.md +58 -0
  30. package/oh-my-opencode/CONTRIBUTING.md +268 -0
  31. package/oh-my-opencode/LICENSE.md +82 -0
  32. package/oh-my-opencode/README.ja.md +370 -0
  33. package/oh-my-opencode/README.md +376 -0
  34. package/oh-my-opencode/README.zh-cn.md +380 -0
  35. package/oh-my-opencode/assets/oh-my-opencode.schema.json +2171 -0
  36. package/oh-my-opencode/bin/oh-my-opencode.js +80 -0
  37. package/oh-my-opencode/bin/platform.js +38 -0
  38. package/oh-my-opencode/bin/platform.test.ts +148 -0
  39. package/oh-my-opencode/bun.lock +314 -0
  40. package/oh-my-opencode/bunfig.toml +2 -0
  41. package/oh-my-opencode/docs/category-skill-guide.md +200 -0
  42. package/oh-my-opencode/docs/cli-guide.md +272 -0
  43. package/oh-my-opencode/docs/configurations.md +654 -0
  44. package/oh-my-opencode/docs/features.md +550 -0
  45. package/oh-my-opencode/docs/guide/installation.md +288 -0
  46. package/oh-my-opencode/docs/guide/overview.md +97 -0
  47. package/oh-my-opencode/docs/guide/understanding-orchestration-system.md +445 -0
  48. package/oh-my-opencode/docs/orchestration-guide.md +152 -0
  49. package/oh-my-opencode/docs/ultrawork-manifesto.md +197 -0
  50. package/oh-my-opencode/package.json +89 -0
  51. package/oh-my-opencode/packages/darwin-arm64/bin/.gitkeep +0 -0
  52. package/oh-my-opencode/packages/darwin-arm64/package.json +22 -0
  53. package/oh-my-opencode/packages/darwin-x64/bin/.gitkeep +0 -0
  54. package/oh-my-opencode/packages/darwin-x64/package.json +22 -0
  55. package/oh-my-opencode/packages/linux-arm64/bin/.gitkeep +0 -0
  56. package/oh-my-opencode/packages/linux-arm64/package.json +25 -0
  57. package/oh-my-opencode/packages/linux-arm64-musl/bin/.gitkeep +0 -0
  58. package/oh-my-opencode/packages/linux-arm64-musl/package.json +25 -0
  59. package/oh-my-opencode/packages/linux-x64/bin/.gitkeep +0 -0
  60. package/oh-my-opencode/packages/linux-x64/package.json +25 -0
  61. package/oh-my-opencode/packages/linux-x64-musl/bin/.gitkeep +0 -0
  62. package/oh-my-opencode/packages/linux-x64-musl/package.json +25 -0
  63. package/oh-my-opencode/packages/windows-x64/bin/.gitkeep +0 -0
  64. package/oh-my-opencode/packages/windows-x64/package.json +22 -0
  65. package/oh-my-opencode/postinstall.mjs +43 -0
  66. package/oh-my-opencode/script/build-binaries.ts +103 -0
  67. package/oh-my-opencode/script/build-schema.ts +28 -0
  68. package/oh-my-opencode/script/generate-changelog.ts +92 -0
  69. package/oh-my-opencode/script/publish.ts +344 -0
  70. package/oh-my-opencode/signatures/cla.json +676 -0
  71. package/oh-my-opencode/src/agents/AGENTS.md +67 -0
  72. package/oh-my-opencode/src/agents/atlas.ts +1383 -0
  73. package/oh-my-opencode/src/agents/dynamic-agent-prompt-builder.ts +400 -0
  74. package/oh-my-opencode/src/agents/explore.ts +122 -0
  75. package/oh-my-opencode/src/agents/index.ts +13 -0
  76. package/oh-my-opencode/src/agents/librarian.ts +326 -0
  77. package/oh-my-opencode/src/agents/metis.ts +315 -0
  78. package/oh-my-opencode/src/agents/momus.test.ts +57 -0
  79. package/oh-my-opencode/src/agents/momus.ts +444 -0
  80. package/oh-my-opencode/src/agents/multimodal-looker.ts +56 -0
  81. package/oh-my-opencode/src/agents/oracle.ts +122 -0
  82. package/oh-my-opencode/src/agents/prometheus-prompt.test.ts +22 -0
  83. package/oh-my-opencode/src/agents/prometheus-prompt.ts +1196 -0
  84. package/oh-my-opencode/src/agents/sisyphus-junior.test.ts +232 -0
  85. package/oh-my-opencode/src/agents/sisyphus-junior.ts +134 -0
  86. package/oh-my-opencode/src/agents/sisyphus.ts +633 -0
  87. package/oh-my-opencode/src/agents/types.ts +80 -0
  88. package/oh-my-opencode/src/agents/utils.test.ts +311 -0
  89. package/oh-my-opencode/src/agents/utils.ts +240 -0
  90. package/oh-my-opencode/src/cli/AGENTS.md +91 -0
  91. package/oh-my-opencode/src/cli/config-manager.test.ts +364 -0
  92. package/oh-my-opencode/src/cli/config-manager.ts +641 -0
  93. package/oh-my-opencode/src/cli/doctor/checks/auth.test.ts +114 -0
  94. package/oh-my-opencode/src/cli/doctor/checks/auth.ts +115 -0
  95. package/oh-my-opencode/src/cli/doctor/checks/config.test.ts +103 -0
  96. package/oh-my-opencode/src/cli/doctor/checks/config.ts +123 -0
  97. package/oh-my-opencode/src/cli/doctor/checks/dependencies.test.ts +152 -0
  98. package/oh-my-opencode/src/cli/doctor/checks/dependencies.ts +163 -0
  99. package/oh-my-opencode/src/cli/doctor/checks/gh.test.ts +151 -0
  100. package/oh-my-opencode/src/cli/doctor/checks/gh.ts +171 -0
  101. package/oh-my-opencode/src/cli/doctor/checks/index.ts +34 -0
  102. package/oh-my-opencode/src/cli/doctor/checks/lsp.test.ts +134 -0
  103. package/oh-my-opencode/src/cli/doctor/checks/lsp.ts +77 -0
  104. package/oh-my-opencode/src/cli/doctor/checks/mcp.test.ts +115 -0
  105. package/oh-my-opencode/src/cli/doctor/checks/mcp.ts +128 -0
  106. package/oh-my-opencode/src/cli/doctor/checks/opencode.test.ts +227 -0
  107. package/oh-my-opencode/src/cli/doctor/checks/opencode.ts +178 -0
  108. package/oh-my-opencode/src/cli/doctor/checks/plugin.test.ts +109 -0
  109. package/oh-my-opencode/src/cli/doctor/checks/plugin.ts +124 -0
  110. package/oh-my-opencode/src/cli/doctor/checks/version.test.ts +148 -0
  111. package/oh-my-opencode/src/cli/doctor/checks/version.ts +135 -0
  112. package/oh-my-opencode/src/cli/doctor/constants.ts +72 -0
  113. package/oh-my-opencode/src/cli/doctor/formatter.test.ts +218 -0
  114. package/oh-my-opencode/src/cli/doctor/formatter.ts +140 -0
  115. package/oh-my-opencode/src/cli/doctor/index.ts +11 -0
  116. package/oh-my-opencode/src/cli/doctor/runner.test.ts +153 -0
  117. package/oh-my-opencode/src/cli/doctor/runner.ts +132 -0
  118. package/oh-my-opencode/src/cli/doctor/types.ts +113 -0
  119. package/oh-my-opencode/src/cli/get-local-version/formatter.ts +66 -0
  120. package/oh-my-opencode/src/cli/get-local-version/index.ts +106 -0
  121. package/oh-my-opencode/src/cli/get-local-version/types.ts +14 -0
  122. package/oh-my-opencode/src/cli/index.ts +153 -0
  123. package/oh-my-opencode/src/cli/install.ts +523 -0
  124. package/oh-my-opencode/src/cli/model-fallback.ts +246 -0
  125. package/oh-my-opencode/src/cli/run/completion.test.ts +170 -0
  126. package/oh-my-opencode/src/cli/run/completion.ts +79 -0
  127. package/oh-my-opencode/src/cli/run/events.test.ts +155 -0
  128. package/oh-my-opencode/src/cli/run/events.ts +325 -0
  129. package/oh-my-opencode/src/cli/run/index.ts +2 -0
  130. package/oh-my-opencode/src/cli/run/runner.ts +159 -0
  131. package/oh-my-opencode/src/cli/run/types.ts +76 -0
  132. package/oh-my-opencode/src/cli/types.ts +40 -0
  133. package/oh-my-opencode/src/config/index.ts +26 -0
  134. package/oh-my-opencode/src/config/schema.test.ts +444 -0
  135. package/oh-my-opencode/src/config/schema.ts +339 -0
  136. package/oh-my-opencode/src/features/AGENTS.md +77 -0
  137. package/oh-my-opencode/src/features/background-agent/concurrency.test.ts +418 -0
  138. package/oh-my-opencode/src/features/background-agent/concurrency.ts +137 -0
  139. package/oh-my-opencode/src/features/background-agent/index.ts +3 -0
  140. package/oh-my-opencode/src/features/background-agent/manager.test.ts +1928 -0
  141. package/oh-my-opencode/src/features/background-agent/manager.ts +1335 -0
  142. package/oh-my-opencode/src/features/background-agent/types.ts +66 -0
  143. package/oh-my-opencode/src/features/boulder-state/constants.ts +13 -0
  144. package/oh-my-opencode/src/features/boulder-state/index.ts +3 -0
  145. package/oh-my-opencode/src/features/boulder-state/storage.test.ts +250 -0
  146. package/oh-my-opencode/src/features/boulder-state/storage.ts +150 -0
  147. package/oh-my-opencode/src/features/boulder-state/types.ts +26 -0
  148. package/oh-my-opencode/src/features/builtin-commands/commands.ts +89 -0
  149. package/oh-my-opencode/src/features/builtin-commands/index.ts +2 -0
  150. package/oh-my-opencode/src/features/builtin-commands/templates/init-deep.ts +300 -0
  151. package/oh-my-opencode/src/features/builtin-commands/templates/ralph-loop.ts +38 -0
  152. package/oh-my-opencode/src/features/builtin-commands/templates/refactor.ts +619 -0
  153. package/oh-my-opencode/src/features/builtin-commands/templates/start-work.ts +72 -0
  154. package/oh-my-opencode/src/features/builtin-commands/types.ts +9 -0
  155. package/oh-my-opencode/src/features/builtin-skills/frontend-ui-ux/SKILL.md +78 -0
  156. package/oh-my-opencode/src/features/builtin-skills/git-master/SKILL.md +1105 -0
  157. package/oh-my-opencode/src/features/builtin-skills/index.ts +2 -0
  158. package/oh-my-opencode/src/features/builtin-skills/skills.ts +1203 -0
  159. package/oh-my-opencode/src/features/builtin-skills/types.ts +16 -0
  160. package/oh-my-opencode/src/features/claude-code-agent-loader/index.ts +2 -0
  161. package/oh-my-opencode/src/features/claude-code-agent-loader/loader.ts +90 -0
  162. package/oh-my-opencode/src/features/claude-code-agent-loader/types.ts +17 -0
  163. package/oh-my-opencode/src/features/claude-code-command-loader/index.ts +2 -0
  164. package/oh-my-opencode/src/features/claude-code-command-loader/loader.ts +144 -0
  165. package/oh-my-opencode/src/features/claude-code-command-loader/types.ts +46 -0
  166. package/oh-my-opencode/src/features/claude-code-mcp-loader/env-expander.ts +27 -0
  167. package/oh-my-opencode/src/features/claude-code-mcp-loader/index.ts +11 -0
  168. package/oh-my-opencode/src/features/claude-code-mcp-loader/loader.test.ts +162 -0
  169. package/oh-my-opencode/src/features/claude-code-mcp-loader/loader.ts +113 -0
  170. package/oh-my-opencode/src/features/claude-code-mcp-loader/transformer.ts +53 -0
  171. package/oh-my-opencode/src/features/claude-code-mcp-loader/types.ts +42 -0
  172. package/oh-my-opencode/src/features/claude-code-plugin-loader/index.ts +3 -0
  173. package/oh-my-opencode/src/features/claude-code-plugin-loader/loader.ts +486 -0
  174. package/oh-my-opencode/src/features/claude-code-plugin-loader/types.ts +210 -0
  175. package/oh-my-opencode/src/features/claude-code-session-state/index.ts +1 -0
  176. package/oh-my-opencode/src/features/claude-code-session-state/state.test.ts +126 -0
  177. package/oh-my-opencode/src/features/claude-code-session-state/state.ts +37 -0
  178. package/oh-my-opencode/src/features/context-injector/collector.test.ts +330 -0
  179. package/oh-my-opencode/src/features/context-injector/collector.ts +85 -0
  180. package/oh-my-opencode/src/features/context-injector/index.ts +14 -0
  181. package/oh-my-opencode/src/features/context-injector/injector.test.ts +122 -0
  182. package/oh-my-opencode/src/features/context-injector/injector.ts +167 -0
  183. package/oh-my-opencode/src/features/context-injector/types.ts +91 -0
  184. package/oh-my-opencode/src/features/hook-message-injector/constants.ts +6 -0
  185. package/oh-my-opencode/src/features/hook-message-injector/index.ts +4 -0
  186. package/oh-my-opencode/src/features/hook-message-injector/injector.ts +195 -0
  187. package/oh-my-opencode/src/features/hook-message-injector/types.ts +47 -0
  188. package/oh-my-opencode/src/features/opencode-skill-loader/async-loader.test.ts +448 -0
  189. package/oh-my-opencode/src/features/opencode-skill-loader/async-loader.ts +180 -0
  190. package/oh-my-opencode/src/features/opencode-skill-loader/blocking.test.ts +210 -0
  191. package/oh-my-opencode/src/features/opencode-skill-loader/blocking.ts +62 -0
  192. package/oh-my-opencode/src/features/opencode-skill-loader/discover-worker.ts +59 -0
  193. package/oh-my-opencode/src/features/opencode-skill-loader/index.ts +4 -0
  194. package/oh-my-opencode/src/features/opencode-skill-loader/loader.test.ts +273 -0
  195. package/oh-my-opencode/src/features/opencode-skill-loader/loader.ts +259 -0
  196. package/oh-my-opencode/src/features/opencode-skill-loader/merger.ts +267 -0
  197. package/oh-my-opencode/src/features/opencode-skill-loader/skill-content.test.ts +267 -0
  198. package/oh-my-opencode/src/features/opencode-skill-loader/skill-content.ts +206 -0
  199. package/oh-my-opencode/src/features/opencode-skill-loader/types.ts +38 -0
  200. package/oh-my-opencode/src/features/skill-mcp-manager/env-cleaner.test.ts +201 -0
  201. package/oh-my-opencode/src/features/skill-mcp-manager/env-cleaner.ts +27 -0
  202. package/oh-my-opencode/src/features/skill-mcp-manager/index.ts +2 -0
  203. package/oh-my-opencode/src/features/skill-mcp-manager/manager.test.ts +611 -0
  204. package/oh-my-opencode/src/features/skill-mcp-manager/manager.ts +520 -0
  205. package/oh-my-opencode/src/features/skill-mcp-manager/types.ts +14 -0
  206. package/oh-my-opencode/src/features/task-toast-manager/index.ts +2 -0
  207. package/oh-my-opencode/src/features/task-toast-manager/manager.test.ts +249 -0
  208. package/oh-my-opencode/src/features/task-toast-manager/manager.ts +215 -0
  209. package/oh-my-opencode/src/features/task-toast-manager/types.ts +24 -0
  210. package/oh-my-opencode/src/hooks/AGENTS.md +73 -0
  211. package/oh-my-opencode/src/hooks/agent-usage-reminder/constants.ts +54 -0
  212. package/oh-my-opencode/src/hooks/agent-usage-reminder/index.ts +109 -0
  213. package/oh-my-opencode/src/hooks/agent-usage-reminder/storage.ts +42 -0
  214. package/oh-my-opencode/src/hooks/agent-usage-reminder/types.ts +6 -0
  215. package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/executor.test.ts +307 -0
  216. package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/executor.ts +485 -0
  217. package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/index.ts +151 -0
  218. package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/parser.ts +201 -0
  219. package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/pruning-deduplication.test.ts +33 -0
  220. package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/pruning-deduplication.ts +184 -0
  221. package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/pruning-types.ts +44 -0
  222. package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/storage.test.ts +77 -0
  223. package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/storage.ts +250 -0
  224. package/oh-my-opencode/src/hooks/anthropic-context-window-limit-recovery/types.ts +42 -0
  225. package/oh-my-opencode/src/hooks/atlas/index.test.ts +953 -0
  226. package/oh-my-opencode/src/hooks/atlas/index.ts +771 -0
  227. package/oh-my-opencode/src/hooks/auto-slash-command/constants.ts +12 -0
  228. package/oh-my-opencode/src/hooks/auto-slash-command/detector.test.ts +296 -0
  229. package/oh-my-opencode/src/hooks/auto-slash-command/detector.ts +65 -0
  230. package/oh-my-opencode/src/hooks/auto-slash-command/executor.ts +205 -0
  231. package/oh-my-opencode/src/hooks/auto-slash-command/index.test.ts +254 -0
  232. package/oh-my-opencode/src/hooks/auto-slash-command/index.ts +89 -0
  233. package/oh-my-opencode/src/hooks/auto-slash-command/types.ts +23 -0
  234. package/oh-my-opencode/src/hooks/auto-update-checker/cache.ts +93 -0
  235. package/oh-my-opencode/src/hooks/auto-update-checker/checker.test.ts +24 -0
  236. package/oh-my-opencode/src/hooks/auto-update-checker/checker.ts +284 -0
  237. package/oh-my-opencode/src/hooks/auto-update-checker/constants.ts +64 -0
  238. package/oh-my-opencode/src/hooks/auto-update-checker/index.test.ts +254 -0
  239. package/oh-my-opencode/src/hooks/auto-update-checker/index.ts +260 -0
  240. package/oh-my-opencode/src/hooks/auto-update-checker/types.ts +29 -0
  241. package/oh-my-opencode/src/hooks/background-compaction/index.ts +87 -0
  242. package/oh-my-opencode/src/hooks/background-notification/index.ts +28 -0
  243. package/oh-my-opencode/src/hooks/background-notification/types.ts +5 -0
  244. package/oh-my-opencode/src/hooks/claude-code-hooks/AGENTS.md +70 -0
  245. package/oh-my-opencode/src/hooks/claude-code-hooks/config-loader.ts +107 -0
  246. package/oh-my-opencode/src/hooks/claude-code-hooks/config.ts +103 -0
  247. package/oh-my-opencode/src/hooks/claude-code-hooks/index.ts +401 -0
  248. package/oh-my-opencode/src/hooks/claude-code-hooks/plugin-config.ts +12 -0
  249. package/oh-my-opencode/src/hooks/claude-code-hooks/post-tool-use.ts +199 -0
  250. package/oh-my-opencode/src/hooks/claude-code-hooks/pre-compact.ts +109 -0
  251. package/oh-my-opencode/src/hooks/claude-code-hooks/pre-tool-use.ts +172 -0
  252. package/oh-my-opencode/src/hooks/claude-code-hooks/stop.ts +118 -0
  253. package/oh-my-opencode/src/hooks/claude-code-hooks/todo.ts +76 -0
  254. package/oh-my-opencode/src/hooks/claude-code-hooks/tool-input-cache.ts +47 -0
  255. package/oh-my-opencode/src/hooks/claude-code-hooks/transcript.ts +252 -0
  256. package/oh-my-opencode/src/hooks/claude-code-hooks/types.ts +204 -0
  257. package/oh-my-opencode/src/hooks/claude-code-hooks/user-prompt-submit.ts +117 -0
  258. package/oh-my-opencode/src/hooks/comment-checker/cli.test.ts +68 -0
  259. package/oh-my-opencode/src/hooks/comment-checker/cli.ts +221 -0
  260. package/oh-my-opencode/src/hooks/comment-checker/downloader.ts +196 -0
  261. package/oh-my-opencode/src/hooks/comment-checker/index.ts +171 -0
  262. package/oh-my-opencode/src/hooks/comment-checker/types.ts +33 -0
  263. package/oh-my-opencode/src/hooks/compaction-context-injector/index.ts +61 -0
  264. package/oh-my-opencode/src/hooks/context-window-monitor.ts +99 -0
  265. package/oh-my-opencode/src/hooks/delegate-task-retry/index.test.ts +119 -0
  266. package/oh-my-opencode/src/hooks/delegate-task-retry/index.ts +136 -0
  267. package/oh-my-opencode/src/hooks/directory-agents-injector/constants.ts +9 -0
  268. package/oh-my-opencode/src/hooks/directory-agents-injector/index.ts +182 -0
  269. package/oh-my-opencode/src/hooks/directory-agents-injector/storage.ts +48 -0
  270. package/oh-my-opencode/src/hooks/directory-agents-injector/types.ts +5 -0
  271. package/oh-my-opencode/src/hooks/directory-readme-injector/constants.ts +9 -0
  272. package/oh-my-opencode/src/hooks/directory-readme-injector/index.ts +177 -0
  273. package/oh-my-opencode/src/hooks/directory-readme-injector/storage.ts +48 -0
  274. package/oh-my-opencode/src/hooks/directory-readme-injector/types.ts +5 -0
  275. package/oh-my-opencode/src/hooks/edit-error-recovery/index.test.ts +126 -0
  276. package/oh-my-opencode/src/hooks/edit-error-recovery/index.ts +57 -0
  277. package/oh-my-opencode/src/hooks/empty-task-response-detector.ts +27 -0
  278. package/oh-my-opencode/src/hooks/index.ts +32 -0
  279. package/oh-my-opencode/src/hooks/interactive-bash-session/constants.ts +15 -0
  280. package/oh-my-opencode/src/hooks/interactive-bash-session/index.ts +262 -0
  281. package/oh-my-opencode/src/hooks/interactive-bash-session/storage.ts +59 -0
  282. package/oh-my-opencode/src/hooks/interactive-bash-session/types.ts +11 -0
  283. package/oh-my-opencode/src/hooks/keyword-detector/constants.ts +300 -0
  284. package/oh-my-opencode/src/hooks/keyword-detector/detector.ts +52 -0
  285. package/oh-my-opencode/src/hooks/keyword-detector/index.test.ts +529 -0
  286. package/oh-my-opencode/src/hooks/keyword-detector/index.ts +100 -0
  287. package/oh-my-opencode/src/hooks/keyword-detector/types.ts +4 -0
  288. package/oh-my-opencode/src/hooks/non-interactive-env/constants.ts +70 -0
  289. package/oh-my-opencode/src/hooks/non-interactive-env/detector.ts +19 -0
  290. package/oh-my-opencode/src/hooks/non-interactive-env/index.test.ts +323 -0
  291. package/oh-my-opencode/src/hooks/non-interactive-env/index.ts +63 -0
  292. package/oh-my-opencode/src/hooks/non-interactive-env/types.ts +3 -0
  293. package/oh-my-opencode/src/hooks/prometheus-md-only/constants.ts +32 -0
  294. package/oh-my-opencode/src/hooks/prometheus-md-only/index.test.ts +488 -0
  295. package/oh-my-opencode/src/hooks/prometheus-md-only/index.ts +136 -0
  296. package/oh-my-opencode/src/hooks/ralph-loop/constants.ts +5 -0
  297. package/oh-my-opencode/src/hooks/ralph-loop/index.test.ts +835 -0
  298. package/oh-my-opencode/src/hooks/ralph-loop/index.ts +417 -0
  299. package/oh-my-opencode/src/hooks/ralph-loop/storage.ts +115 -0
  300. package/oh-my-opencode/src/hooks/ralph-loop/types.ts +19 -0
  301. package/oh-my-opencode/src/hooks/rules-injector/constants.ts +30 -0
  302. package/oh-my-opencode/src/hooks/rules-injector/finder.test.ts +381 -0
  303. package/oh-my-opencode/src/hooks/rules-injector/finder.ts +263 -0
  304. package/oh-my-opencode/src/hooks/rules-injector/index.ts +223 -0
  305. package/oh-my-opencode/src/hooks/rules-injector/matcher.ts +63 -0
  306. package/oh-my-opencode/src/hooks/rules-injector/parser.test.ts +226 -0
  307. package/oh-my-opencode/src/hooks/rules-injector/parser.ts +211 -0
  308. package/oh-my-opencode/src/hooks/rules-injector/storage.ts +59 -0
  309. package/oh-my-opencode/src/hooks/rules-injector/types.ts +57 -0
  310. package/oh-my-opencode/src/hooks/session-notification-utils.ts +140 -0
  311. package/oh-my-opencode/src/hooks/session-notification.test.ts +361 -0
  312. package/oh-my-opencode/src/hooks/session-notification.ts +330 -0
  313. package/oh-my-opencode/src/hooks/session-recovery/constants.ts +10 -0
  314. package/oh-my-opencode/src/hooks/session-recovery/index.test.ts +223 -0
  315. package/oh-my-opencode/src/hooks/session-recovery/index.ts +435 -0
  316. package/oh-my-opencode/src/hooks/session-recovery/storage.ts +390 -0
  317. package/oh-my-opencode/src/hooks/session-recovery/types.ts +98 -0
  318. package/oh-my-opencode/src/hooks/start-work/index.test.ts +402 -0
  319. package/oh-my-opencode/src/hooks/start-work/index.ts +242 -0
  320. package/oh-my-opencode/src/hooks/task-resume-info/index.ts +36 -0
  321. package/oh-my-opencode/src/hooks/think-mode/detector.ts +57 -0
  322. package/oh-my-opencode/src/hooks/think-mode/index.test.ts +353 -0
  323. package/oh-my-opencode/src/hooks/think-mode/index.ts +89 -0
  324. package/oh-my-opencode/src/hooks/think-mode/switcher.test.ts +461 -0
  325. package/oh-my-opencode/src/hooks/think-mode/switcher.ts +222 -0
  326. package/oh-my-opencode/src/hooks/think-mode/types.ts +21 -0
  327. package/oh-my-opencode/src/hooks/thinking-block-validator/index.ts +171 -0
  328. package/oh-my-opencode/src/hooks/todo-continuation-enforcer.test.ts +876 -0
  329. package/oh-my-opencode/src/hooks/todo-continuation-enforcer.ts +480 -0
  330. package/oh-my-opencode/src/hooks/tool-output-truncator.test.ts +168 -0
  331. package/oh-my-opencode/src/hooks/tool-output-truncator.ts +61 -0
  332. package/oh-my-opencode/src/index.ts +589 -0
  333. package/oh-my-opencode/src/mcp/AGENTS.md +70 -0
  334. package/oh-my-opencode/src/mcp/context7.ts +6 -0
  335. package/oh-my-opencode/src/mcp/grep-app.ts +6 -0
  336. package/oh-my-opencode/src/mcp/index.test.ts +86 -0
  337. package/oh-my-opencode/src/mcp/index.ts +32 -0
  338. package/oh-my-opencode/src/mcp/types.ts +9 -0
  339. package/oh-my-opencode/src/mcp/websearch.ts +10 -0
  340. package/oh-my-opencode/src/plugin-config.test.ts +119 -0
  341. package/oh-my-opencode/src/plugin-config.ts +135 -0
  342. package/oh-my-opencode/src/plugin-handlers/config-handler.test.ts +103 -0
  343. package/oh-my-opencode/src/plugin-handlers/config-handler.ts +399 -0
  344. package/oh-my-opencode/src/plugin-handlers/index.ts +1 -0
  345. package/oh-my-opencode/src/plugin-state.ts +30 -0
  346. package/oh-my-opencode/src/shared/AGENTS.md +63 -0
  347. package/oh-my-opencode/src/shared/agent-tool-restrictions.ts +44 -0
  348. package/oh-my-opencode/src/shared/agent-variant.test.ts +83 -0
  349. package/oh-my-opencode/src/shared/agent-variant.ts +40 -0
  350. package/oh-my-opencode/src/shared/claude-config-dir.test.ts +60 -0
  351. package/oh-my-opencode/src/shared/claude-config-dir.ts +11 -0
  352. package/oh-my-opencode/src/shared/command-executor.ts +225 -0
  353. package/oh-my-opencode/src/shared/config-errors.ts +18 -0
  354. package/oh-my-opencode/src/shared/config-path.ts +47 -0
  355. package/oh-my-opencode/src/shared/data-path.ts +22 -0
  356. package/oh-my-opencode/src/shared/deep-merge.test.ts +336 -0
  357. package/oh-my-opencode/src/shared/deep-merge.ts +53 -0
  358. package/oh-my-opencode/src/shared/dynamic-truncator.ts +193 -0
  359. package/oh-my-opencode/src/shared/external-plugin-detector.test.ts +133 -0
  360. package/oh-my-opencode/src/shared/external-plugin-detector.ts +132 -0
  361. package/oh-my-opencode/src/shared/file-reference-resolver.ts +85 -0
  362. package/oh-my-opencode/src/shared/file-utils.ts +40 -0
  363. package/oh-my-opencode/src/shared/first-message-variant.test.ts +32 -0
  364. package/oh-my-opencode/src/shared/first-message-variant.ts +28 -0
  365. package/oh-my-opencode/src/shared/frontmatter.test.ts +262 -0
  366. package/oh-my-opencode/src/shared/frontmatter.ts +31 -0
  367. package/oh-my-opencode/src/shared/hook-disabled.ts +22 -0
  368. package/oh-my-opencode/src/shared/index.ts +29 -0
  369. package/oh-my-opencode/src/shared/jsonc-parser.test.ts +266 -0
  370. package/oh-my-opencode/src/shared/jsonc-parser.ts +66 -0
  371. package/oh-my-opencode/src/shared/logger.ts +20 -0
  372. package/oh-my-opencode/src/shared/migration.test.ts +602 -0
  373. package/oh-my-opencode/src/shared/migration.ts +191 -0
  374. package/oh-my-opencode/src/shared/model-resolver.test.ts +101 -0
  375. package/oh-my-opencode/src/shared/model-resolver.ts +35 -0
  376. package/oh-my-opencode/src/shared/model-sanitizer.ts +12 -0
  377. package/oh-my-opencode/src/shared/opencode-config-dir.test.ts +318 -0
  378. package/oh-my-opencode/src/shared/opencode-config-dir.ts +142 -0
  379. package/oh-my-opencode/src/shared/opencode-version.test.ts +223 -0
  380. package/oh-my-opencode/src/shared/opencode-version.ts +72 -0
  381. package/oh-my-opencode/src/shared/pattern-matcher.ts +29 -0
  382. package/oh-my-opencode/src/shared/permission-compat.test.ts +134 -0
  383. package/oh-my-opencode/src/shared/permission-compat.ts +77 -0
  384. package/oh-my-opencode/src/shared/session-cursor.test.ts +66 -0
  385. package/oh-my-opencode/src/shared/session-cursor.ts +85 -0
  386. package/oh-my-opencode/src/shared/shell-env.test.ts +278 -0
  387. package/oh-my-opencode/src/shared/shell-env.ts +111 -0
  388. package/oh-my-opencode/src/shared/snake-case.ts +49 -0
  389. package/oh-my-opencode/src/shared/system-directive.ts +40 -0
  390. package/oh-my-opencode/src/shared/tool-name.ts +26 -0
  391. package/oh-my-opencode/src/shared/zip-extractor.ts +83 -0
  392. package/oh-my-opencode/src/tools/AGENTS.md +74 -0
  393. package/oh-my-opencode/src/tools/ast-grep/cli.ts +230 -0
  394. package/oh-my-opencode/src/tools/ast-grep/constants.ts +261 -0
  395. package/oh-my-opencode/src/tools/ast-grep/downloader.ts +128 -0
  396. package/oh-my-opencode/src/tools/ast-grep/index.ts +13 -0
  397. package/oh-my-opencode/src/tools/ast-grep/tools.ts +112 -0
  398. package/oh-my-opencode/src/tools/ast-grep/types.ts +61 -0
  399. package/oh-my-opencode/src/tools/ast-grep/utils.ts +102 -0
  400. package/oh-my-opencode/src/tools/background-task/constants.ts +7 -0
  401. package/oh-my-opencode/src/tools/background-task/index.ts +7 -0
  402. package/oh-my-opencode/src/tools/background-task/tools.ts +479 -0
  403. package/oh-my-opencode/src/tools/background-task/types.ts +16 -0
  404. package/oh-my-opencode/src/tools/call-omo-agent/constants.ts +7 -0
  405. package/oh-my-opencode/src/tools/call-omo-agent/index.ts +3 -0
  406. package/oh-my-opencode/src/tools/call-omo-agent/tools.ts +338 -0
  407. package/oh-my-opencode/src/tools/call-omo-agent/types.ts +27 -0
  408. package/oh-my-opencode/src/tools/delegate-task/constants.ts +205 -0
  409. package/oh-my-opencode/src/tools/delegate-task/index.ts +3 -0
  410. package/oh-my-opencode/src/tools/delegate-task/tools.test.ts +1575 -0
  411. package/oh-my-opencode/src/tools/delegate-task/tools.ts +885 -0
  412. package/oh-my-opencode/src/tools/delegate-task/types.ts +9 -0
  413. package/oh-my-opencode/src/tools/glob/cli.test.ts +158 -0
  414. package/oh-my-opencode/src/tools/glob/cli.ts +191 -0
  415. package/oh-my-opencode/src/tools/glob/constants.ts +12 -0
  416. package/oh-my-opencode/src/tools/glob/index.ts +3 -0
  417. package/oh-my-opencode/src/tools/glob/tools.ts +41 -0
  418. package/oh-my-opencode/src/tools/glob/types.ts +22 -0
  419. package/oh-my-opencode/src/tools/glob/utils.ts +26 -0
  420. package/oh-my-opencode/src/tools/grep/cli.ts +229 -0
  421. package/oh-my-opencode/src/tools/grep/constants.ts +127 -0
  422. package/oh-my-opencode/src/tools/grep/downloader.test.ts +103 -0
  423. package/oh-my-opencode/src/tools/grep/downloader.ts +145 -0
  424. package/oh-my-opencode/src/tools/grep/index.ts +3 -0
  425. package/oh-my-opencode/src/tools/grep/tools.ts +40 -0
  426. package/oh-my-opencode/src/tools/grep/types.ts +39 -0
  427. package/oh-my-opencode/src/tools/grep/utils.ts +53 -0
  428. package/oh-my-opencode/src/tools/index.ts +72 -0
  429. package/oh-my-opencode/src/tools/interactive-bash/constants.ts +18 -0
  430. package/oh-my-opencode/src/tools/interactive-bash/index.ts +4 -0
  431. package/oh-my-opencode/src/tools/interactive-bash/tools.ts +126 -0
  432. package/oh-my-opencode/src/tools/interactive-bash/utils.ts +71 -0
  433. package/oh-my-opencode/src/tools/look-at/constants.ts +3 -0
  434. package/oh-my-opencode/src/tools/look-at/index.ts +3 -0
  435. package/oh-my-opencode/src/tools/look-at/tools.test.ts +73 -0
  436. package/oh-my-opencode/src/tools/look-at/tools.ts +173 -0
  437. package/oh-my-opencode/src/tools/look-at/types.ts +4 -0
  438. package/oh-my-opencode/src/tools/lsp/client.ts +596 -0
  439. package/oh-my-opencode/src/tools/lsp/config.test.ts +130 -0
  440. package/oh-my-opencode/src/tools/lsp/config.ts +285 -0
  441. package/oh-my-opencode/src/tools/lsp/constants.ts +390 -0
  442. package/oh-my-opencode/src/tools/lsp/index.ts +7 -0
  443. package/oh-my-opencode/src/tools/lsp/tools.ts +261 -0
  444. package/oh-my-opencode/src/tools/lsp/types.ts +124 -0
  445. package/oh-my-opencode/src/tools/lsp/utils.ts +406 -0
  446. package/oh-my-opencode/src/tools/session-manager/constants.ts +97 -0
  447. package/oh-my-opencode/src/tools/session-manager/index.ts +3 -0
  448. package/oh-my-opencode/src/tools/session-manager/storage.test.ts +315 -0
  449. package/oh-my-opencode/src/tools/session-manager/storage.ts +238 -0
  450. package/oh-my-opencode/src/tools/session-manager/tools.test.ts +124 -0
  451. package/oh-my-opencode/src/tools/session-manager/tools.ts +146 -0
  452. package/oh-my-opencode/src/tools/session-manager/types.ts +99 -0
  453. package/oh-my-opencode/src/tools/session-manager/utils.test.ts +160 -0
  454. package/oh-my-opencode/src/tools/session-manager/utils.ts +199 -0
  455. package/oh-my-opencode/src/tools/skill/constants.ts +8 -0
  456. package/oh-my-opencode/src/tools/skill/index.ts +3 -0
  457. package/oh-my-opencode/src/tools/skill/tools.test.ts +239 -0
  458. package/oh-my-opencode/src/tools/skill/tools.ts +200 -0
  459. package/oh-my-opencode/src/tools/skill/types.ts +31 -0
  460. package/oh-my-opencode/src/tools/skill-mcp/constants.ts +3 -0
  461. package/oh-my-opencode/src/tools/skill-mcp/index.ts +3 -0
  462. package/oh-my-opencode/src/tools/skill-mcp/tools.test.ts +215 -0
  463. package/oh-my-opencode/src/tools/skill-mcp/tools.ts +172 -0
  464. package/oh-my-opencode/src/tools/skill-mcp/types.ts +8 -0
  465. package/oh-my-opencode/src/tools/slashcommand/index.ts +2 -0
  466. package/oh-my-opencode/src/tools/slashcommand/tools.ts +252 -0
  467. package/oh-my-opencode/src/tools/slashcommand/types.ts +28 -0
  468. package/oh-my-opencode/test-setup.ts +6 -0
  469. package/oh-my-opencode/tsconfig.json +20 -0
  470. package/package.json +1 -1
  471. package/src/__tests__/git.test.ts +7 -2
  472. package/src/__tests__/manifest.test.ts +5 -5
  473. package/src/agents/repo-explorer.ts +2 -1
  474. package/src/git.ts +18 -3
  475. package/src/manifest.ts +22 -15
@@ -0,0 +1,249 @@
1
+ import { describe, test, expect, beforeEach, mock } from "bun:test"
2
+ import { TaskToastManager } from "./manager"
3
+ import type { ConcurrencyManager } from "../background-agent/concurrency"
4
+
5
+ describe("TaskToastManager", () => {
6
+ let mockClient: {
7
+ tui: {
8
+ showToast: ReturnType<typeof mock>
9
+ }
10
+ }
11
+ let toastManager: TaskToastManager
12
+ let mockConcurrencyManager: ConcurrencyManager
13
+
14
+ beforeEach(() => {
15
+ mockClient = {
16
+ tui: {
17
+ showToast: mock(() => Promise.resolve()),
18
+ },
19
+ }
20
+ mockConcurrencyManager = {
21
+ getConcurrencyLimit: mock(() => 5),
22
+ } as unknown as ConcurrencyManager
23
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
+ toastManager = new TaskToastManager(mockClient as any, mockConcurrencyManager)
25
+ })
26
+
27
+ describe("skills in toast message", () => {
28
+ test("should display skills when provided", () => {
29
+ // #given - a task with skills
30
+ const task = {
31
+ id: "task_1",
32
+ description: "Test task",
33
+ agent: "Sisyphus-Junior",
34
+ isBackground: true,
35
+ skills: ["playwright", "git-master"],
36
+ }
37
+
38
+ // #when - addTask is called
39
+ toastManager.addTask(task)
40
+
41
+ // #then - toast message should include skills
42
+ expect(mockClient.tui.showToast).toHaveBeenCalled()
43
+ const call = mockClient.tui.showToast.mock.calls[0][0]
44
+ expect(call.body.message).toContain("playwright")
45
+ expect(call.body.message).toContain("git-master")
46
+ })
47
+
48
+ test("should not display skills section when no skills provided", () => {
49
+ // #given - a task without skills
50
+ const task = {
51
+ id: "task_2",
52
+ description: "Test task without skills",
53
+ agent: "explore",
54
+ isBackground: true,
55
+ }
56
+
57
+ // #when - addTask is called
58
+ toastManager.addTask(task)
59
+
60
+ // #then - toast message should not include skills prefix
61
+ expect(mockClient.tui.showToast).toHaveBeenCalled()
62
+ const call = mockClient.tui.showToast.mock.calls[0][0]
63
+ expect(call.body.message).not.toContain("Skills:")
64
+ })
65
+ })
66
+
67
+ describe("concurrency info in toast message", () => {
68
+ test("should display concurrency status in toast", () => {
69
+ // #given - multiple running tasks
70
+ toastManager.addTask({
71
+ id: "task_1",
72
+ description: "First task",
73
+ agent: "explore",
74
+ isBackground: true,
75
+ })
76
+ toastManager.addTask({
77
+ id: "task_2",
78
+ description: "Second task",
79
+ agent: "librarian",
80
+ isBackground: true,
81
+ })
82
+
83
+ // #when - third task is added
84
+ toastManager.addTask({
85
+ id: "task_3",
86
+ description: "Third task",
87
+ agent: "explore",
88
+ isBackground: true,
89
+ })
90
+
91
+ // #then - toast should show concurrency info
92
+ expect(mockClient.tui.showToast).toHaveBeenCalledTimes(3)
93
+ const lastCall = mockClient.tui.showToast.mock.calls[2][0]
94
+ // Should show "Running (3):" header
95
+ expect(lastCall.body.message).toContain("Running (3):")
96
+ })
97
+
98
+ test("should display concurrency limit info when available", () => {
99
+ // #given - a concurrency manager with known limit
100
+ const mockConcurrencyWithCounts = {
101
+ getConcurrencyLimit: mock(() => 5),
102
+ getRunningCount: mock(() => 2),
103
+ getQueuedCount: mock(() => 1),
104
+ } as unknown as ConcurrencyManager
105
+
106
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
+ const managerWithConcurrency = new TaskToastManager(mockClient as any, mockConcurrencyWithCounts)
108
+
109
+ // #when - a task is added
110
+ managerWithConcurrency.addTask({
111
+ id: "task_1",
112
+ description: "Test task",
113
+ agent: "explore",
114
+ isBackground: true,
115
+ })
116
+
117
+ // #then - toast should show concurrency status like "2/5 slots"
118
+ expect(mockClient.tui.showToast).toHaveBeenCalled()
119
+ const call = mockClient.tui.showToast.mock.calls[0][0]
120
+ expect(call.body.message).toMatch(/\d+\/\d+/)
121
+ })
122
+ })
123
+
124
+ describe("combined skills and concurrency display", () => {
125
+ test("should display both skills and concurrency info together", () => {
126
+ // #given - a task with skills and concurrency manager
127
+ const task = {
128
+ id: "task_1",
129
+ description: "Full info task",
130
+ agent: "Sisyphus-Junior",
131
+ isBackground: true,
132
+ skills: ["frontend-ui-ux"],
133
+ }
134
+
135
+ // #when - addTask is called
136
+ toastManager.addTask(task)
137
+
138
+ // #then - toast should include both skills and task count
139
+ expect(mockClient.tui.showToast).toHaveBeenCalled()
140
+ const call = mockClient.tui.showToast.mock.calls[0][0]
141
+ expect(call.body.message).toContain("frontend-ui-ux")
142
+ expect(call.body.message).toContain("Running (1):")
143
+ })
144
+ })
145
+
146
+ describe("model fallback info in toast message", () => {
147
+ test("should NOT display warning when model is category-default (normal behavior)", () => {
148
+ // #given - category-default is the intended behavior, not a fallback
149
+ const task = {
150
+ id: "task_1",
151
+ description: "Task with category default model",
152
+ agent: "Sisyphus-Junior",
153
+ isBackground: false,
154
+ modelInfo: { model: "google/gemini-3-pro-preview", type: "category-default" as const },
155
+ }
156
+
157
+ // #when - addTask is called
158
+ toastManager.addTask(task)
159
+
160
+ // #then - toast should NOT show warning - category default is expected
161
+ expect(mockClient.tui.showToast).toHaveBeenCalled()
162
+ const call = mockClient.tui.showToast.mock.calls[0][0]
163
+ expect(call.body.message).not.toContain("⚠️")
164
+ expect(call.body.message).not.toContain("(category default)")
165
+ })
166
+
167
+ test("should display warning when model falls back to system-default", () => {
168
+ // #given - system-default is a fallback (no category default, no user config)
169
+ const task = {
170
+ id: "task_1b",
171
+ description: "Task with system default model",
172
+ agent: "Sisyphus-Junior",
173
+ isBackground: false,
174
+ modelInfo: { model: "anthropic/claude-sonnet-4-5", type: "system-default" as const },
175
+ }
176
+
177
+ // #when - addTask is called
178
+ toastManager.addTask(task)
179
+
180
+ // #then - toast should show fallback warning
181
+ expect(mockClient.tui.showToast).toHaveBeenCalled()
182
+ const call = mockClient.tui.showToast.mock.calls[0][0]
183
+ expect(call.body.message).toContain("⚠️")
184
+ expect(call.body.message).toContain("anthropic/claude-sonnet-4-5")
185
+ expect(call.body.message).toContain("(system default fallback)")
186
+ })
187
+
188
+ test("should display warning when model is inherited from parent", () => {
189
+ // #given - inherited is a fallback (custom category without model definition)
190
+ const task = {
191
+ id: "task_2",
192
+ description: "Task with inherited model",
193
+ agent: "Sisyphus-Junior",
194
+ isBackground: false,
195
+ modelInfo: { model: "cliproxy/claude-opus-4-5", type: "inherited" as const },
196
+ }
197
+
198
+ // #when - addTask is called
199
+ toastManager.addTask(task)
200
+
201
+ // #then - toast should show fallback warning
202
+ expect(mockClient.tui.showToast).toHaveBeenCalled()
203
+ const call = mockClient.tui.showToast.mock.calls[0][0]
204
+ expect(call.body.message).toContain("⚠️")
205
+ expect(call.body.message).toContain("cliproxy/claude-opus-4-5")
206
+ expect(call.body.message).toContain("(inherited from parent)")
207
+ })
208
+
209
+ test("should not display model info when user-defined", () => {
210
+ // #given - a task with user-defined model
211
+ const task = {
212
+ id: "task_3",
213
+ description: "Task with user model",
214
+ agent: "Sisyphus-Junior",
215
+ isBackground: false,
216
+ modelInfo: { model: "my-provider/my-model", type: "user-defined" as const },
217
+ }
218
+
219
+ // #when - addTask is called
220
+ toastManager.addTask(task)
221
+
222
+ // #then - toast should NOT show model warning
223
+ expect(mockClient.tui.showToast).toHaveBeenCalled()
224
+ const call = mockClient.tui.showToast.mock.calls[0][0]
225
+ expect(call.body.message).not.toContain("⚠️ Model:")
226
+ expect(call.body.message).not.toContain("(inherited)")
227
+ expect(call.body.message).not.toContain("(category default)")
228
+ expect(call.body.message).not.toContain("(system default)")
229
+ })
230
+
231
+ test("should not display model info when not provided", () => {
232
+ // #given - a task without model info
233
+ const task = {
234
+ id: "task_4",
235
+ description: "Task without model info",
236
+ agent: "explore",
237
+ isBackground: true,
238
+ }
239
+
240
+ // #when - addTask is called
241
+ toastManager.addTask(task)
242
+
243
+ // #then - toast should NOT show model warning
244
+ expect(mockClient.tui.showToast).toHaveBeenCalled()
245
+ const call = mockClient.tui.showToast.mock.calls[0][0]
246
+ expect(call.body.message).not.toContain("⚠️ Model:")
247
+ })
248
+ })
249
+ })
@@ -0,0 +1,215 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin"
2
+ import type { TrackedTask, TaskStatus, ModelFallbackInfo } from "./types"
3
+ import type { ConcurrencyManager } from "../background-agent/concurrency"
4
+
5
+ type OpencodeClient = PluginInput["client"]
6
+
7
+ export class TaskToastManager {
8
+ private tasks: Map<string, TrackedTask> = new Map()
9
+ private client: OpencodeClient
10
+ private concurrencyManager?: ConcurrencyManager
11
+
12
+ constructor(client: OpencodeClient, concurrencyManager?: ConcurrencyManager) {
13
+ this.client = client
14
+ this.concurrencyManager = concurrencyManager
15
+ }
16
+
17
+ setConcurrencyManager(manager: ConcurrencyManager): void {
18
+ this.concurrencyManager = manager
19
+ }
20
+
21
+ addTask(task: {
22
+ id: string
23
+ description: string
24
+ agent: string
25
+ isBackground: boolean
26
+ status?: TaskStatus
27
+ skills?: string[]
28
+ modelInfo?: ModelFallbackInfo
29
+ }): void {
30
+ const trackedTask: TrackedTask = {
31
+ id: task.id,
32
+ description: task.description,
33
+ agent: task.agent,
34
+ status: task.status ?? "running",
35
+ startedAt: new Date(),
36
+ isBackground: task.isBackground,
37
+ skills: task.skills,
38
+ modelInfo: task.modelInfo,
39
+ }
40
+
41
+ this.tasks.set(task.id, trackedTask)
42
+ this.showTaskListToast(trackedTask)
43
+ }
44
+
45
+ /**
46
+ * Update task status
47
+ */
48
+ updateTask(id: string, status: TaskStatus): void {
49
+ const task = this.tasks.get(id)
50
+ if (task) {
51
+ task.status = status
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Remove completed/error task
57
+ */
58
+ removeTask(id: string): void {
59
+ this.tasks.delete(id)
60
+ }
61
+
62
+ /**
63
+ * Get all running tasks (newest first)
64
+ */
65
+ getRunningTasks(): TrackedTask[] {
66
+ const running = Array.from(this.tasks.values())
67
+ .filter((t) => t.status === "running")
68
+ .sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime())
69
+ return running
70
+ }
71
+
72
+ /**
73
+ * Get all queued tasks
74
+ */
75
+ getQueuedTasks(): TrackedTask[] {
76
+ return Array.from(this.tasks.values())
77
+ .filter((t) => t.status === "queued")
78
+ .sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime())
79
+ }
80
+
81
+ /**
82
+ * Format duration since task started
83
+ */
84
+ private formatDuration(startedAt: Date): string {
85
+ const seconds = Math.floor((Date.now() - startedAt.getTime()) / 1000)
86
+ if (seconds < 60) return `${seconds}s`
87
+ const minutes = Math.floor(seconds / 60)
88
+ if (minutes < 60) return `${minutes}m ${seconds % 60}s`
89
+ const hours = Math.floor(minutes / 60)
90
+ return `${hours}h ${minutes % 60}m`
91
+ }
92
+
93
+ private getConcurrencyInfo(): string {
94
+ if (!this.concurrencyManager) return ""
95
+ const running = this.getRunningTasks()
96
+ const queued = this.getQueuedTasks()
97
+ const total = running.length + queued.length
98
+ const limit = this.concurrencyManager.getConcurrencyLimit("default")
99
+ if (limit === Infinity) return ""
100
+ return ` [${total}/${limit}]`
101
+ }
102
+
103
+ private buildTaskListMessage(newTask: TrackedTask): string {
104
+ const running = this.getRunningTasks()
105
+ const queued = this.getQueuedTasks()
106
+ const concurrencyInfo = this.getConcurrencyInfo()
107
+
108
+ const lines: string[] = []
109
+
110
+ const isFallback = newTask.modelInfo && (
111
+ newTask.modelInfo.type === "inherited" || newTask.modelInfo.type === "system-default"
112
+ )
113
+ if (isFallback) {
114
+ const suffixMap: Record<"inherited" | "system-default", string> = {
115
+ inherited: " (inherited from parent)",
116
+ "system-default": " (system default fallback)",
117
+ }
118
+ const suffix = suffixMap[newTask.modelInfo!.type as "inherited" | "system-default"]
119
+ lines.push(`⚠️ Model fallback: ${newTask.modelInfo!.model}${suffix}`)
120
+ lines.push("")
121
+ }
122
+
123
+ if (running.length > 0) {
124
+ lines.push(`Running (${running.length}):${concurrencyInfo}`)
125
+ for (const task of running) {
126
+ const duration = this.formatDuration(task.startedAt)
127
+ const bgIcon = task.isBackground ? "⚡" : "🔄"
128
+ const isNew = task.id === newTask.id ? " ← NEW" : ""
129
+ const skillsInfo = task.skills?.length ? ` [${task.skills.join(", ")}]` : ""
130
+ lines.push(`${bgIcon} ${task.description} (${task.agent})${skillsInfo} - ${duration}${isNew}`)
131
+ }
132
+ }
133
+
134
+ if (queued.length > 0) {
135
+ if (lines.length > 0) lines.push("")
136
+ lines.push(`Queued (${queued.length}):`)
137
+ for (const task of queued) {
138
+ const bgIcon = task.isBackground ? "⏳" : "⏸️"
139
+ const skillsInfo = task.skills?.length ? ` [${task.skills.join(", ")}]` : ""
140
+ const isNew = task.id === newTask.id ? " ← NEW" : ""
141
+ lines.push(`${bgIcon} ${task.description} (${task.agent})${skillsInfo} - Queued${isNew}`)
142
+ }
143
+ }
144
+
145
+ return lines.join("\n")
146
+ }
147
+
148
+ /**
149
+ * Show consolidated toast with all running/queued tasks
150
+ */
151
+ private showTaskListToast(newTask: TrackedTask): void {
152
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
153
+ const tuiClient = this.client as any
154
+ if (!tuiClient.tui?.showToast) return
155
+
156
+ const message = this.buildTaskListMessage(newTask)
157
+ const running = this.getRunningTasks()
158
+ const queued = this.getQueuedTasks()
159
+
160
+ const title = newTask.isBackground
161
+ ? `⚡ New Background Task`
162
+ : `🔄 New Task Executed`
163
+
164
+ tuiClient.tui.showToast({
165
+ body: {
166
+ title,
167
+ message: message || `${newTask.description} (${newTask.agent})`,
168
+ variant: "info",
169
+ duration: running.length + queued.length > 2 ? 5000 : 3000,
170
+ },
171
+ }).catch(() => {})
172
+ }
173
+
174
+ /**
175
+ * Show task completion toast
176
+ */
177
+ showCompletionToast(task: { id: string; description: string; duration: string }): void {
178
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
+ const tuiClient = this.client as any
180
+ if (!tuiClient.tui?.showToast) return
181
+
182
+ this.removeTask(task.id)
183
+
184
+ const remaining = this.getRunningTasks()
185
+ const queued = this.getQueuedTasks()
186
+
187
+ let message = `✅ "${task.description}" finished in ${task.duration}`
188
+ if (remaining.length > 0 || queued.length > 0) {
189
+ message += `\n\nStill running: ${remaining.length} | Queued: ${queued.length}`
190
+ }
191
+
192
+ tuiClient.tui.showToast({
193
+ body: {
194
+ title: "Task Completed",
195
+ message,
196
+ variant: "success",
197
+ duration: 5000,
198
+ },
199
+ }).catch(() => {})
200
+ }
201
+ }
202
+
203
+ let instance: TaskToastManager | null = null
204
+
205
+ export function getTaskToastManager(): TaskToastManager | null {
206
+ return instance
207
+ }
208
+
209
+ export function initTaskToastManager(
210
+ client: OpencodeClient,
211
+ concurrencyManager?: ConcurrencyManager
212
+ ): TaskToastManager {
213
+ instance = new TaskToastManager(client, concurrencyManager)
214
+ return instance
215
+ }
@@ -0,0 +1,24 @@
1
+ export type TaskStatus = "running" | "queued" | "completed" | "error"
2
+
3
+ export interface ModelFallbackInfo {
4
+ model: string
5
+ type: "user-defined" | "inherited" | "category-default" | "system-default"
6
+ }
7
+
8
+ export interface TrackedTask {
9
+ id: string
10
+ description: string
11
+ agent: string
12
+ status: TaskStatus
13
+ startedAt: Date
14
+ isBackground: boolean
15
+ skills?: string[]
16
+ modelInfo?: ModelFallbackInfo
17
+ }
18
+
19
+ export interface TaskToastOptions {
20
+ title: string
21
+ message: string
22
+ variant: "info" | "success" | "warning" | "error"
23
+ duration?: number
24
+ }
@@ -0,0 +1,73 @@
1
+ # HOOKS KNOWLEDGE BASE
2
+
3
+ ## OVERVIEW
4
+
5
+ 31 lifecycle hooks intercepting/modifying agent behavior. Events: PreToolUse, PostToolUse, UserPromptSubmit, Stop, onSummarize.
6
+
7
+ ## STRUCTURE
8
+
9
+ ```
10
+ hooks/
11
+ ├── atlas/ # Main orchestration & delegation (771 lines)
12
+ ├── anthropic-context-window-limit-recovery/ # Auto-summarize at token limit
13
+ ├── todo-continuation-enforcer.ts # Force TODO completion
14
+ ├── ralph-loop/ # Self-referential dev loop until done
15
+ ├── claude-code-hooks/ # settings.json hook compat layer (13 files)
16
+ ├── comment-checker/ # Prevents AI slop/excessive comments
17
+ ├── auto-slash-command/ # Detects /command patterns
18
+ ├── rules-injector/ # Conditional rules from .claude/rules/
19
+ ├── directory-agents-injector/ # Auto-injects AGENTS.md files
20
+ ├── directory-readme-injector/ # Auto-injects README.md files
21
+ ├── preemptive-compaction/ # Triggers summary at 85% context
22
+ ├── edit-error-recovery/ # Recovers from tool failures
23
+ ├── thinking-block-validator/ # Ensures valid <thinking> format
24
+ ├── context-window-monitor.ts # Reminds agents of remaining headroom
25
+ ├── session-recovery/ # Auto-recovers from crashes
26
+ ├── think-mode/ # Dynamic thinking budget
27
+ ├── keyword-detector/ # ultrawork/search/analyze modes
28
+ ├── background-notification/ # OS notification on task completion
29
+ └── tool-output-truncator.ts # Prevents context bloat
30
+ ```
31
+
32
+ ## HOOK EVENTS
33
+
34
+ | Event | Timing | Can Block | Use Case |
35
+ |-------|--------|-----------|----------|
36
+ | PreToolUse | Before tool | Yes | Validate/modify inputs, inject context |
37
+ | PostToolUse | After tool | No | Append warnings, truncate output |
38
+ | UserPromptSubmit | On prompt | Yes | Keyword detection, mode switching |
39
+ | Stop | Session idle | No | Auto-continue (todo-continuation, ralph-loop) |
40
+ | onSummarize | Compaction | No | Preserve critical state |
41
+
42
+ ## EXECUTION ORDER
43
+
44
+ **chat.message**: keywordDetector → claudeCodeHooks → autoSlashCommand → startWork → ralphLoop
45
+
46
+ **tool.execute.before**: claudeCodeHooks → nonInteractiveEnv → commentChecker → directoryAgentsInjector → directoryReadmeInjector → rulesInjector
47
+
48
+ **tool.execute.after**: editErrorRecovery → delegateTaskRetry → commentChecker → toolOutputTruncator → emptyTaskResponseDetector → claudeCodeHooks
49
+
50
+ ## HOW TO ADD
51
+
52
+ 1. Create `src/hooks/name/` with `index.ts` exporting `createMyHook(ctx)`
53
+ 2. Implement event handlers: `"tool.execute.before"`, `"tool.execute.after"`, etc.
54
+ 3. Add hook name to `HookNameSchema` in `src/config/schema.ts`
55
+ 4. Register in `src/index.ts`:
56
+ ```typescript
57
+ const myHook = isHookEnabled("my-hook") ? createMyHook(ctx) : null
58
+ // Add to event handlers
59
+ ```
60
+
61
+ ## PATTERNS
62
+
63
+ - **Session-scoped state**: `Map<sessionID, Set<string>>` for tracking per-session
64
+ - **Conditional execution**: Check `input.tool` before processing
65
+ - **Output modification**: `output.output += "\n${REMINDER}"` to append context
66
+ - **Async state**: Use promises for CLI path resolution, cache results
67
+
68
+ ## ANTI-PATTERNS
69
+
70
+ - **Blocking non-critical**: Use PostToolUse warnings instead of PreToolUse blocks
71
+ - **Heavy computation**: Keep PreToolUse light - slows every tool call
72
+ - **Redundant injection**: Track injected files to prevent duplicates
73
+ - **Verbose output**: Keep hook messages technical, brief
@@ -0,0 +1,54 @@
1
+ import { join } from "node:path";
2
+ import { getOpenCodeStorageDir } from "../../shared/data-path";
3
+
4
+ export const OPENCODE_STORAGE = getOpenCodeStorageDir();
5
+ export const AGENT_USAGE_REMINDER_STORAGE = join(
6
+ OPENCODE_STORAGE,
7
+ "agent-usage-reminder",
8
+ );
9
+
10
+ // All tool names normalized to lowercase for case-insensitive matching
11
+ export const TARGET_TOOLS = new Set([
12
+ "grep",
13
+ "safe_grep",
14
+ "glob",
15
+ "safe_glob",
16
+ "webfetch",
17
+ "context7_resolve-library-id",
18
+ "context7_query-docs",
19
+ "websearch_web_search_exa",
20
+ "context7_get-library-docs",
21
+ "grep_app_searchgithub",
22
+ ]);
23
+
24
+ export const AGENT_TOOLS = new Set([
25
+ "task",
26
+ "call_omo_agent",
27
+ "delegate_task",
28
+ ]);
29
+
30
+ export const REMINDER_MESSAGE = `
31
+ [Agent Usage Reminder]
32
+
33
+ You called a search/fetch tool directly without leveraging specialized agents.
34
+
35
+ RECOMMENDED: Use delegate_task with explore/librarian agents for better results:
36
+
37
+ \`\`\`
38
+ // Parallel exploration - fire multiple agents simultaneously
39
+ delegate_task(agent="explore", prompt="Find all files matching pattern X")
40
+ delegate_task(agent="explore", prompt="Search for implementation of Y")
41
+ delegate_task(agent="librarian", prompt="Lookup documentation for Z")
42
+
43
+ // Then continue your work while they run in background
44
+ // System will notify you when each completes
45
+ \`\`\`
46
+
47
+ WHY:
48
+ - Agents can perform deeper, more thorough searches
49
+ - Background tasks run in parallel, saving time
50
+ - Specialized agents have domain expertise
51
+ - Reduces context window usage in main session
52
+
53
+ ALWAYS prefer: Multiple parallel delegate_task calls > Direct tool calls
54
+ `;