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,488 @@
1
+ import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test"
2
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs"
3
+ import { join } from "node:path"
4
+ import { createPrometheusMdOnlyHook } from "./index"
5
+ import { MESSAGE_STORAGE } from "../../features/hook-message-injector"
6
+ import { SYSTEM_DIRECTIVE_PREFIX, createSystemDirective, SystemDirectiveTypes } from "../../shared/system-directive"
7
+ import { clearSessionAgent } from "../../features/claude-code-session-state"
8
+
9
+ describe("prometheus-md-only", () => {
10
+ const TEST_SESSION_ID = "test-session-prometheus"
11
+ let testMessageDir: string
12
+
13
+ function createMockPluginInput() {
14
+ return {
15
+ client: {},
16
+ directory: "/tmp/test",
17
+ } as never
18
+ }
19
+
20
+ function setupMessageStorage(sessionID: string, agent: string): void {
21
+ testMessageDir = join(MESSAGE_STORAGE, sessionID)
22
+ mkdirSync(testMessageDir, { recursive: true })
23
+ const messageContent = {
24
+ agent,
25
+ model: { providerID: "test", modelID: "test-model" },
26
+ }
27
+ writeFileSync(
28
+ join(testMessageDir, "msg_001.json"),
29
+ JSON.stringify(messageContent)
30
+ )
31
+ }
32
+
33
+ afterEach(() => {
34
+ clearSessionAgent(TEST_SESSION_ID)
35
+ if (testMessageDir) {
36
+ try {
37
+ rmSync(testMessageDir, { recursive: true, force: true })
38
+ } catch {
39
+ // ignore
40
+ }
41
+ }
42
+ })
43
+
44
+ describe("with Prometheus agent in message storage", () => {
45
+ beforeEach(() => {
46
+ setupMessageStorage(TEST_SESSION_ID, "Prometheus (Planner)")
47
+ })
48
+
49
+ test("should block Prometheus from writing non-.md files", async () => {
50
+ // #given
51
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
52
+ const input = {
53
+ tool: "Write",
54
+ sessionID: TEST_SESSION_ID,
55
+ callID: "call-1",
56
+ }
57
+ const output = {
58
+ args: { filePath: "/path/to/file.ts" },
59
+ }
60
+
61
+ // #when / #then
62
+ await expect(
63
+ hook["tool.execute.before"](input, output)
64
+ ).rejects.toThrow("can only write/edit .md files")
65
+ })
66
+
67
+ test("should allow Prometheus to write .md files inside .sisyphus/", async () => {
68
+ // #given
69
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
70
+ const input = {
71
+ tool: "Write",
72
+ sessionID: TEST_SESSION_ID,
73
+ callID: "call-1",
74
+ }
75
+ const output = {
76
+ args: { filePath: "/tmp/test/.sisyphus/plans/work-plan.md" },
77
+ }
78
+
79
+ // #when / #then
80
+ await expect(
81
+ hook["tool.execute.before"](input, output)
82
+ ).resolves.toBeUndefined()
83
+ })
84
+
85
+ test("should block Prometheus from writing .md files outside .sisyphus/", async () => {
86
+ // #given
87
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
88
+ const input = {
89
+ tool: "Write",
90
+ sessionID: TEST_SESSION_ID,
91
+ callID: "call-1",
92
+ }
93
+ const output = {
94
+ args: { filePath: "/path/to/README.md" },
95
+ }
96
+
97
+ // #when / #then
98
+ await expect(
99
+ hook["tool.execute.before"](input, output)
100
+ ).rejects.toThrow("can only write/edit .md files inside .sisyphus/")
101
+ })
102
+
103
+ test("should block Edit tool for non-.md files", async () => {
104
+ // #given
105
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
106
+ const input = {
107
+ tool: "Edit",
108
+ sessionID: TEST_SESSION_ID,
109
+ callID: "call-1",
110
+ }
111
+ const output = {
112
+ args: { filePath: "/path/to/code.py" },
113
+ }
114
+
115
+ // #when / #then
116
+ await expect(
117
+ hook["tool.execute.before"](input, output)
118
+ ).rejects.toThrow("can only write/edit .md files")
119
+ })
120
+
121
+ test("should not affect non-Write/Edit tools", async () => {
122
+ // #given
123
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
124
+ const input = {
125
+ tool: "Read",
126
+ sessionID: TEST_SESSION_ID,
127
+ callID: "call-1",
128
+ }
129
+ const output = {
130
+ args: { filePath: "/path/to/file.ts" },
131
+ }
132
+
133
+ // #when / #then
134
+ await expect(
135
+ hook["tool.execute.before"](input, output)
136
+ ).resolves.toBeUndefined()
137
+ })
138
+
139
+ test("should handle missing filePath gracefully", async () => {
140
+ // #given
141
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
142
+ const input = {
143
+ tool: "Write",
144
+ sessionID: TEST_SESSION_ID,
145
+ callID: "call-1",
146
+ }
147
+ const output = {
148
+ args: {},
149
+ }
150
+
151
+ // #when / #then
152
+ await expect(
153
+ hook["tool.execute.before"](input, output)
154
+ ).resolves.toBeUndefined()
155
+ })
156
+
157
+ test("should inject read-only warning when Prometheus calls delegate_task", async () => {
158
+ // #given
159
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
160
+ const input = {
161
+ tool: "delegate_task",
162
+ sessionID: TEST_SESSION_ID,
163
+ callID: "call-1",
164
+ }
165
+ const output = {
166
+ args: { prompt: "Analyze this codebase" },
167
+ }
168
+
169
+ // #when
170
+ await hook["tool.execute.before"](input, output)
171
+
172
+ // #then
173
+ expect(output.args.prompt).toContain(SYSTEM_DIRECTIVE_PREFIX)
174
+ expect(output.args.prompt).toContain("DO NOT modify any files")
175
+ })
176
+
177
+ test("should inject read-only warning when Prometheus calls task", async () => {
178
+ // #given
179
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
180
+ const input = {
181
+ tool: "task",
182
+ sessionID: TEST_SESSION_ID,
183
+ callID: "call-1",
184
+ }
185
+ const output = {
186
+ args: { prompt: "Research this library" },
187
+ }
188
+
189
+ // #when
190
+ await hook["tool.execute.before"](input, output)
191
+
192
+ // #then
193
+ expect(output.args.prompt).toContain(SYSTEM_DIRECTIVE_PREFIX)
194
+ })
195
+
196
+ test("should inject read-only warning when Prometheus calls call_omo_agent", async () => {
197
+ // #given
198
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
199
+ const input = {
200
+ tool: "call_omo_agent",
201
+ sessionID: TEST_SESSION_ID,
202
+ callID: "call-1",
203
+ }
204
+ const output = {
205
+ args: { prompt: "Find implementation examples" },
206
+ }
207
+
208
+ // #when
209
+ await hook["tool.execute.before"](input, output)
210
+
211
+ // #then
212
+ expect(output.args.prompt).toContain(SYSTEM_DIRECTIVE_PREFIX)
213
+ })
214
+
215
+ test("should not double-inject warning if already present", async () => {
216
+ // #given
217
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
218
+ const input = {
219
+ tool: "delegate_task",
220
+ sessionID: TEST_SESSION_ID,
221
+ callID: "call-1",
222
+ }
223
+ const promptWithWarning = `Some prompt ${SYSTEM_DIRECTIVE_PREFIX} already here`
224
+ const output = {
225
+ args: { prompt: promptWithWarning },
226
+ }
227
+
228
+ // #when
229
+ await hook["tool.execute.before"](input, output)
230
+
231
+ // #then
232
+ const occurrences = (output.args.prompt as string).split(SYSTEM_DIRECTIVE_PREFIX).length - 1
233
+ expect(occurrences).toBe(1)
234
+ })
235
+ })
236
+
237
+ describe("with non-Prometheus agent in message storage", () => {
238
+ beforeEach(() => {
239
+ setupMessageStorage(TEST_SESSION_ID, "Sisyphus")
240
+ })
241
+
242
+ test("should not affect non-Prometheus agents", async () => {
243
+ // #given
244
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
245
+ const input = {
246
+ tool: "Write",
247
+ sessionID: TEST_SESSION_ID,
248
+ callID: "call-1",
249
+ }
250
+ const output = {
251
+ args: { filePath: "/path/to/file.ts" },
252
+ }
253
+
254
+ // #when / #then
255
+ await expect(
256
+ hook["tool.execute.before"](input, output)
257
+ ).resolves.toBeUndefined()
258
+ })
259
+
260
+ test("should not inject warning for non-Prometheus agents calling delegate_task", async () => {
261
+ // #given
262
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
263
+ const input = {
264
+ tool: "delegate_task",
265
+ sessionID: TEST_SESSION_ID,
266
+ callID: "call-1",
267
+ }
268
+ const originalPrompt = "Implement this feature"
269
+ const output = {
270
+ args: { prompt: originalPrompt },
271
+ }
272
+
273
+ // #when
274
+ await hook["tool.execute.before"](input, output)
275
+
276
+ // #then
277
+ expect(output.args.prompt).toBe(originalPrompt)
278
+ expect(output.args.prompt).not.toContain(SYSTEM_DIRECTIVE_PREFIX)
279
+ })
280
+ })
281
+
282
+ describe("without message storage", () => {
283
+ test("should handle missing session gracefully (no agent found)", async () => {
284
+ // #given
285
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
286
+ const input = {
287
+ tool: "Write",
288
+ sessionID: "non-existent-session",
289
+ callID: "call-1",
290
+ }
291
+ const output = {
292
+ args: { filePath: "/path/to/file.ts" },
293
+ }
294
+
295
+ // #when / #then
296
+ await expect(
297
+ hook["tool.execute.before"](input, output)
298
+ ).resolves.toBeUndefined()
299
+ })
300
+ })
301
+
302
+ describe("cross-platform path validation", () => {
303
+ beforeEach(() => {
304
+ setupMessageStorage(TEST_SESSION_ID, "Prometheus (Planner)")
305
+ })
306
+
307
+ test("should allow Windows-style backslash paths under .sisyphus/", async () => {
308
+ // #given
309
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
310
+ const input = {
311
+ tool: "Write",
312
+ sessionID: TEST_SESSION_ID,
313
+ callID: "call-1",
314
+ }
315
+ const output = {
316
+ args: { filePath: ".sisyphus\\plans\\work-plan.md" },
317
+ }
318
+
319
+ // #when / #then
320
+ await expect(
321
+ hook["tool.execute.before"](input, output)
322
+ ).resolves.toBeUndefined()
323
+ })
324
+
325
+ test("should allow mixed separator paths under .sisyphus/", async () => {
326
+ // #given
327
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
328
+ const input = {
329
+ tool: "Write",
330
+ sessionID: TEST_SESSION_ID,
331
+ callID: "call-1",
332
+ }
333
+ const output = {
334
+ args: { filePath: ".sisyphus\\plans/work-plan.MD" },
335
+ }
336
+
337
+ // #when / #then
338
+ await expect(
339
+ hook["tool.execute.before"](input, output)
340
+ ).resolves.toBeUndefined()
341
+ })
342
+
343
+ test("should allow uppercase .MD extension", async () => {
344
+ // #given
345
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
346
+ const input = {
347
+ tool: "Write",
348
+ sessionID: TEST_SESSION_ID,
349
+ callID: "call-1",
350
+ }
351
+ const output = {
352
+ args: { filePath: ".sisyphus/plans/work-plan.MD" },
353
+ }
354
+
355
+ // #when / #then
356
+ await expect(
357
+ hook["tool.execute.before"](input, output)
358
+ ).resolves.toBeUndefined()
359
+ })
360
+
361
+ test("should block paths outside workspace root even if containing .sisyphus", async () => {
362
+ // #given
363
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
364
+ const input = {
365
+ tool: "Write",
366
+ sessionID: TEST_SESSION_ID,
367
+ callID: "call-1",
368
+ }
369
+ const output = {
370
+ args: { filePath: "/other/project/.sisyphus/plans/x.md" },
371
+ }
372
+
373
+ // #when / #then
374
+ await expect(
375
+ hook["tool.execute.before"](input, output)
376
+ ).rejects.toThrow("can only write/edit .md files inside .sisyphus/")
377
+ })
378
+
379
+ test("should allow nested .sisyphus directories (ctx.directory may be parent)", async () => {
380
+ // #given - when ctx.directory is parent of actual project, path includes project name
381
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
382
+ const input = {
383
+ tool: "Write",
384
+ sessionID: TEST_SESSION_ID,
385
+ callID: "call-1",
386
+ }
387
+ const output = {
388
+ args: { filePath: "src/.sisyphus/plans/x.md" },
389
+ }
390
+
391
+ // #when / #then - should allow because .sisyphus is in path
392
+ await expect(
393
+ hook["tool.execute.before"](input, output)
394
+ ).resolves.toBeUndefined()
395
+ })
396
+
397
+ test("should block path traversal attempts", async () => {
398
+ // #given
399
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
400
+ const input = {
401
+ tool: "Write",
402
+ sessionID: TEST_SESSION_ID,
403
+ callID: "call-1",
404
+ }
405
+ const output = {
406
+ args: { filePath: ".sisyphus/../secrets.md" },
407
+ }
408
+
409
+ // #when / #then
410
+ await expect(
411
+ hook["tool.execute.before"](input, output)
412
+ ).rejects.toThrow("can only write/edit .md files inside .sisyphus/")
413
+ })
414
+
415
+ test("should allow case-insensitive .SISYPHUS directory", async () => {
416
+ // #given
417
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
418
+ const input = {
419
+ tool: "Write",
420
+ sessionID: TEST_SESSION_ID,
421
+ callID: "call-1",
422
+ }
423
+ const output = {
424
+ args: { filePath: ".SISYPHUS/plans/work-plan.md" },
425
+ }
426
+
427
+ // #when / #then
428
+ await expect(
429
+ hook["tool.execute.before"](input, output)
430
+ ).resolves.toBeUndefined()
431
+ })
432
+
433
+ test("should allow nested project path with .sisyphus (Windows real-world case)", async () => {
434
+ // #given - simulates when ctx.directory is parent of actual project
435
+ // User reported: xauusd-dxy-plan\.sisyphus\drafts\supabase-email-templates.md
436
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
437
+ const input = {
438
+ tool: "Write",
439
+ sessionID: TEST_SESSION_ID,
440
+ callID: "call-1",
441
+ }
442
+ const output = {
443
+ args: { filePath: "xauusd-dxy-plan\\.sisyphus\\drafts\\supabase-email-templates.md" },
444
+ }
445
+
446
+ // #when / #then
447
+ await expect(
448
+ hook["tool.execute.before"](input, output)
449
+ ).resolves.toBeUndefined()
450
+ })
451
+
452
+ test("should allow nested project path with mixed separators", async () => {
453
+ // #given
454
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
455
+ const input = {
456
+ tool: "Write",
457
+ sessionID: TEST_SESSION_ID,
458
+ callID: "call-1",
459
+ }
460
+ const output = {
461
+ args: { filePath: "my-project/.sisyphus\\plans/task.md" },
462
+ }
463
+
464
+ // #when / #then
465
+ await expect(
466
+ hook["tool.execute.before"](input, output)
467
+ ).resolves.toBeUndefined()
468
+ })
469
+
470
+ test("should block nested project path without .sisyphus", async () => {
471
+ // #given
472
+ const hook = createPrometheusMdOnlyHook(createMockPluginInput())
473
+ const input = {
474
+ tool: "Write",
475
+ sessionID: TEST_SESSION_ID,
476
+ callID: "call-1",
477
+ }
478
+ const output = {
479
+ args: { filePath: "my-project\\src\\code.ts" },
480
+ }
481
+
482
+ // #when / #then
483
+ await expect(
484
+ hook["tool.execute.before"](input, output)
485
+ ).rejects.toThrow("can only write/edit .md files")
486
+ })
487
+ })
488
+ })
@@ -0,0 +1,136 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin"
2
+ import { existsSync, readdirSync } from "node:fs"
3
+ import { join, resolve, relative, isAbsolute } from "node:path"
4
+ import { HOOK_NAME, PROMETHEUS_AGENTS, ALLOWED_EXTENSIONS, ALLOWED_PATH_PREFIX, BLOCKED_TOOLS, PLANNING_CONSULT_WARNING } from "./constants"
5
+ import { findNearestMessageWithFields, findFirstMessageWithAgent, MESSAGE_STORAGE } from "../../features/hook-message-injector"
6
+ import { getSessionAgent } from "../../features/claude-code-session-state"
7
+ import { log } from "../../shared/logger"
8
+ import { SYSTEM_DIRECTIVE_PREFIX } from "../../shared/system-directive"
9
+
10
+ export * from "./constants"
11
+
12
+ /**
13
+ * Cross-platform path validator for Prometheus file writes.
14
+ * Uses path.resolve/relative instead of string matching to handle:
15
+ * - Windows backslashes (e.g., .sisyphus\\plans\\x.md)
16
+ * - Mixed separators (e.g., .sisyphus\\plans/x.md)
17
+ * - Case-insensitive directory/extension matching
18
+ * - Workspace confinement (blocks paths outside root or via traversal)
19
+ * - Nested project paths (e.g., parent/.sisyphus/... when ctx.directory is parent)
20
+ */
21
+ function isAllowedFile(filePath: string, workspaceRoot: string): boolean {
22
+ // 1. Resolve to absolute path
23
+ const resolved = resolve(workspaceRoot, filePath)
24
+
25
+ // 2. Get relative path from workspace root
26
+ const rel = relative(workspaceRoot, resolved)
27
+
28
+ // 3. Reject if escapes root (starts with ".." or is absolute)
29
+ if (rel.startsWith("..") || isAbsolute(rel)) {
30
+ return false
31
+ }
32
+
33
+ // 4. Check if .sisyphus/ or .sisyphus\ exists anywhere in the path (case-insensitive)
34
+ // This handles both direct paths (.sisyphus/x.md) and nested paths (project/.sisyphus/x.md)
35
+ if (!/\.sisyphus[/\\]/i.test(rel)) {
36
+ return false
37
+ }
38
+
39
+ // 5. Check extension matches one of ALLOWED_EXTENSIONS (case-insensitive)
40
+ const hasAllowedExtension = ALLOWED_EXTENSIONS.some(
41
+ ext => resolved.toLowerCase().endsWith(ext.toLowerCase())
42
+ )
43
+ if (!hasAllowedExtension) {
44
+ return false
45
+ }
46
+
47
+ return true
48
+ }
49
+
50
+ function getMessageDir(sessionID: string): string | null {
51
+ if (!existsSync(MESSAGE_STORAGE)) return null
52
+
53
+ const directPath = join(MESSAGE_STORAGE, sessionID)
54
+ if (existsSync(directPath)) return directPath
55
+
56
+ for (const dir of readdirSync(MESSAGE_STORAGE)) {
57
+ const sessionPath = join(MESSAGE_STORAGE, dir, sessionID)
58
+ if (existsSync(sessionPath)) return sessionPath
59
+ }
60
+
61
+ return null
62
+ }
63
+
64
+ const TASK_TOOLS = ["delegate_task", "task", "call_omo_agent"]
65
+
66
+ function getAgentFromMessageFiles(sessionID: string): string | undefined {
67
+ const messageDir = getMessageDir(sessionID)
68
+ if (!messageDir) return undefined
69
+ return findFirstMessageWithAgent(messageDir) ?? findNearestMessageWithFields(messageDir)?.agent
70
+ }
71
+
72
+ function getAgentFromSession(sessionID: string): string | undefined {
73
+ return getSessionAgent(sessionID) ?? getAgentFromMessageFiles(sessionID)
74
+ }
75
+
76
+ export function createPrometheusMdOnlyHook(ctx: PluginInput) {
77
+ return {
78
+ "tool.execute.before": async (
79
+ input: { tool: string; sessionID: string; callID: string },
80
+ output: { args: Record<string, unknown>; message?: string }
81
+ ): Promise<void> => {
82
+ const agentName = getAgentFromSession(input.sessionID)
83
+
84
+ if (!agentName || !PROMETHEUS_AGENTS.includes(agentName)) {
85
+ return
86
+ }
87
+
88
+ const toolName = input.tool
89
+
90
+ // Inject read-only warning for task tools called by Prometheus
91
+ if (TASK_TOOLS.includes(toolName)) {
92
+ const prompt = output.args.prompt as string | undefined
93
+ if (prompt && !prompt.includes(SYSTEM_DIRECTIVE_PREFIX)) {
94
+ output.args.prompt = prompt + PLANNING_CONSULT_WARNING
95
+ log(`[${HOOK_NAME}] Injected read-only planning warning to ${toolName}`, {
96
+ sessionID: input.sessionID,
97
+ tool: toolName,
98
+ agent: agentName,
99
+ })
100
+ }
101
+ return
102
+ }
103
+
104
+ if (!BLOCKED_TOOLS.includes(toolName)) {
105
+ return
106
+ }
107
+
108
+ const filePath = (output.args.filePath ?? output.args.path ?? output.args.file) as string | undefined
109
+ if (!filePath) {
110
+ return
111
+ }
112
+
113
+ if (!isAllowedFile(filePath, ctx.directory)) {
114
+ log(`[${HOOK_NAME}] Blocked: Prometheus can only write to .sisyphus/*.md`, {
115
+ sessionID: input.sessionID,
116
+ tool: toolName,
117
+ filePath,
118
+ agent: agentName,
119
+ })
120
+ throw new Error(
121
+ `[${HOOK_NAME}] Prometheus (Planner) can only write/edit .md files inside .sisyphus/ directory. ` +
122
+ `Attempted to modify: ${filePath}. ` +
123
+ `Prometheus is a READ-ONLY planner. Use /start-work to execute the plan. ` +
124
+ `APOLOGIZE TO THE USER, REMIND OF YOUR PLAN WRITING PROCESSES, TELL USER WHAT YOU WILL GOING TO DO AS THE PROCESS, WRITE THE PLAN`
125
+ )
126
+ }
127
+
128
+ log(`[${HOOK_NAME}] Allowed: .sisyphus/*.md write permitted`, {
129
+ sessionID: input.sessionID,
130
+ tool: toolName,
131
+ filePath,
132
+ agent: agentName,
133
+ })
134
+ },
135
+ }
136
+ }
@@ -0,0 +1,5 @@
1
+ export const HOOK_NAME = "ralph-loop"
2
+ export const DEFAULT_STATE_FILE = ".sisyphus/ralph-loop.local.md"
3
+ export const COMPLETION_TAG_PATTERN = /<promise>(.*?)<\/promise>/is
4
+ export const DEFAULT_MAX_ITERATIONS = 100
5
+ export const DEFAULT_COMPLETION_PROMISE = "DONE"