oh-my-opencode 5.0.0-beta.2 → 5.0.0-beta.21

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 (460) hide show
  1. package/.agents/command/publish.md +44 -16
  2. package/.agents/skills/publish/SKILL.md +44 -16
  3. package/.agents/skills/senpi-qa/SKILL.md +80 -0
  4. package/.agents/skills/senpi-qa/scripts/resolve-evidence-dir.mjs +65 -0
  5. package/.agents/skills/senpi-qa/scripts/resolve-evidence-dir.test.mjs +171 -0
  6. package/.agents/skills/work-with-pr/SKILL.md +37 -23
  7. package/.opencode/command/publish.md +44 -16
  8. package/.opencode/skills/work-with-pr/SKILL.md +37 -23
  9. package/README.ja.md +25 -21
  10. package/README.ko.md +25 -21
  11. package/README.md +38 -23
  12. package/README.ru.md +24 -20
  13. package/README.zh-cn.md +25 -21
  14. package/dist/agents/atlas/agent.d.ts +0 -1
  15. package/dist/agents/sisyphus/grok-4.d.ts +20 -0
  16. package/dist/agents/sisyphus/index.d.ts +2 -0
  17. package/dist/agents/sisyphus-agent-config.d.ts +6 -0
  18. package/dist/agents/sisyphus-agent-factory.d.ts +1 -1
  19. package/dist/agents/sisyphus-runtime-prompt-reconciler.d.ts +15 -4
  20. package/dist/agents/types.d.ts +2 -2
  21. package/dist/cli/fallback-lane-policy.d.ts +1 -0
  22. package/dist/cli/index.js +23057 -20893
  23. package/dist/cli/install-codex/install-codex-test-fixtures.d.ts +1 -1
  24. package/dist/cli/run/on-complete-hook.d.ts +2 -0
  25. package/dist/cli/runtime-commands.d.ts +1 -1
  26. package/dist/cli/worktree-sweep/classify.d.ts +16 -0
  27. package/dist/cli/worktree-sweep/format.d.ts +8 -0
  28. package/dist/cli/worktree-sweep/git.d.ts +19 -0
  29. package/dist/cli/worktree-sweep/index.d.ts +7 -0
  30. package/dist/cli/worktree-sweep/options.d.ts +2 -0
  31. package/dist/cli/worktree-sweep/parse-worktree-list.d.ts +8 -0
  32. package/dist/cli/worktree-sweep/sweep.d.ts +2 -0
  33. package/dist/cli/worktree-sweep/types.d.ts +65 -0
  34. package/dist/cli/worktree-sweep/worktree-sweep.d.ts +2 -0
  35. package/dist/cli-node/index.js +23093 -20929
  36. package/dist/config/schema/commands.d.ts +1 -1
  37. package/dist/config/schema/hooks.d.ts +1 -1
  38. package/dist/config/schema/oh-my-opencode-config.d.ts +4 -1
  39. package/dist/config/schema/ulw-execute.d.ts +5 -0
  40. package/dist/config/validate.d.ts +2 -0
  41. package/dist/create-hooks.d.ts +3 -1
  42. package/dist/features/btw-side/btw-command-draft.d.ts +7 -0
  43. package/dist/features/btw-side/context-injector.d.ts +15 -0
  44. package/dist/features/btw-side/index.d.ts +8 -0
  45. package/dist/features/btw-side/metadata.d.ts +15 -0
  46. package/dist/features/btw-side/parent-context-budget.d.ts +9 -0
  47. package/dist/features/btw-side/server-session-registry.d.ts +8 -0
  48. package/dist/features/btw-side/tui-adoption-cache.d.ts +13 -0
  49. package/dist/features/btw-side/tui-adoption-guard.d.ts +5 -0
  50. package/dist/features/btw-side/tui-controller-types.d.ts +68 -0
  51. package/dist/features/btw-side/tui-controller.d.ts +19 -0
  52. package/dist/features/btw-side/tui-escape-return.d.ts +12 -0
  53. package/dist/features/btw-side/tui-keymap.d.ts +17 -0
  54. package/dist/features/btw-side/tui-parent-validator.d.ts +6 -0
  55. package/dist/features/btw-side/tui-picker-options.d.ts +20 -0
  56. package/dist/features/btw-side/tui-picker.d.ts +9 -0
  57. package/dist/features/btw-side/tui-prompt-queue.d.ts +8 -0
  58. package/dist/features/btw-side/tui-session-bridge.d.ts +11 -0
  59. package/dist/features/btw-side/tui-session-catalog.d.ts +35 -0
  60. package/dist/features/btw-side/tui-side-removal.d.ts +11 -0
  61. package/dist/features/btw-side/tui-side-start.d.ts +9 -0
  62. package/dist/features/btw-side/tui-wiring.d.ts +8 -0
  63. package/dist/features/builtin-commands/templates/ulw-execute.d.ts +1 -0
  64. package/dist/features/builtin-commands/types.d.ts +1 -1
  65. package/dist/features/opengateway-provider/index.d.ts +13 -0
  66. package/dist/hooks/atlas/atlas-hook.d.ts +1 -0
  67. package/dist/hooks/atlas/atlas-lifecycle-store.d.ts +50 -0
  68. package/dist/hooks/atlas/event-handler.d.ts +1 -0
  69. package/dist/hooks/atlas/final-wave-approval-gate.test-support.d.ts +50 -0
  70. package/dist/hooks/atlas/system-reminder-templates.d.ts +0 -1
  71. package/dist/hooks/atlas/tool-execute-before.d.ts +3 -0
  72. package/dist/hooks/atlas/types.d.ts +1 -0
  73. package/dist/hooks/index.d.ts +1 -1
  74. package/dist/hooks/prometheus-md-only/agent-resolution.d.ts +2 -2
  75. package/dist/hooks/runtime-fallback/chat-message-handler.d.ts +2 -0
  76. package/dist/hooks/runtime-fallback/retry-model-payload.d.ts +1 -0
  77. package/dist/hooks/runtime-fallback/runtime-model-settings.d.ts +6 -0
  78. package/dist/hooks/runtime-fallback/session-status-handler.d.ts +1 -1
  79. package/dist/hooks/runtime-fallback/types.d.ts +3 -1
  80. package/dist/hooks/sisyphus-junior-notepad/constants.d.ts +1 -1
  81. package/dist/hooks/{start-work → ulw-execute}/context-info-builder.d.ts +1 -1
  82. package/dist/hooks/{start-work → ulw-execute}/index.d.ts +1 -1
  83. package/dist/hooks/ulw-execute/ulw-execute-hook.d.ts +23 -0
  84. package/dist/index.js +11724 -6355
  85. package/dist/oh-my-opencode.schema.json +15 -2
  86. package/dist/plugin/chat-message/types.d.ts +3 -3
  87. package/dist/plugin/chat-message/ulw-execute-message.d.ts +5 -0
  88. package/dist/plugin/event-session-lifecycle.d.ts +1 -0
  89. package/dist/plugin/hooks/create-core-hooks.d.ts +2 -1
  90. package/dist/plugin/hooks/create-session-hooks.d.ts +2 -2
  91. package/dist/plugin/hooks/create-transform-hooks.d.ts +2 -0
  92. package/dist/plugin/messages-transform.d.ts +1 -0
  93. package/dist/shared/index.d.ts +1 -0
  94. package/dist/shared/normalize-sdk-response.d.ts +1 -0
  95. package/dist/shared/runtime-fallback-retry-marker.d.ts +16 -0
  96. package/dist/shared/shell-env.d.ts +1 -1
  97. package/dist/skills/ast-grep/AGENTS.md +51 -0
  98. package/dist/skills/coding-agent-sessions/AGENTS.md +62 -0
  99. package/dist/skills/coding-agent-sessions/SKILL.md +3 -2
  100. package/dist/skills/coding-agent-sessions/references/all-platforms.md +1 -1
  101. package/dist/skills/coding-agent-sessions/references/senpi.md +4 -4
  102. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +1 -1
  103. package/dist/skills/frontend/SKILL.md +11 -7
  104. package/dist/skills/frontend/references/design/_INDEX.md +1 -0
  105. package/dist/skills/frontend/references/design/print-paged-media.md +76 -0
  106. package/dist/skills/frontend/references/design/stylegallery.md +80 -0
  107. package/dist/skills/frontend/references/designpowers/README.md +1 -1
  108. package/dist/skills/frontend/references/designpowers/lane-a-direction.md +1 -1
  109. package/dist/skills/frontend/references/designpowers/lane-b-execution.md +11 -11
  110. package/dist/skills/frontend/references/designpowers/lane-d-memory.md +3 -3
  111. package/dist/skills/frontend/references/designpowers/orchestration.md +1 -1
  112. package/dist/skills/frontend/references/designpowers/routing.md +5 -5
  113. package/dist/skills/review-work/SKILL.md +3 -3
  114. package/dist/skills/ultimate-browsing/ATTRIBUTION.md +37 -10
  115. package/dist/skills/ultimate-browsing/SKILL.md +2 -0
  116. package/dist/skills/ultimate-browsing/engine/AGENTS.md +179 -0
  117. package/dist/skills/ultimate-browsing/engine/templates/package.json +1 -1
  118. package/dist/skills/ultimate-browsing/references/chrome-stealth.md +13 -11
  119. package/dist/skills/{start-work → ulw-execute}/SKILL.md +9 -8
  120. package/dist/skills/ulw-plan/SKILL.md +6 -6
  121. package/dist/skills/ulw-plan/references/full-workflow.md +31 -7
  122. package/dist/skills/ulw-plan/references/intent-clear.md +2 -1
  123. package/dist/skills/ulw-plan/references/intent-unclear.md +5 -5
  124. package/dist/skills/ulw-research/SKILL.md +1 -1
  125. package/dist/skills/visual-qa/AGENTS.md +58 -0
  126. package/dist/skills/visual-qa/SKILL.md +3 -2
  127. package/dist/tools/delegate-task/builtin-categories.d.ts +1 -0
  128. package/dist/tools/delegate-task/builtin-category-definition.d.ts +1 -0
  129. package/dist/tools/delegate-task/tool-description.d.ts +3 -1
  130. package/dist/tui.js +8824 -8906
  131. package/package.json +38 -35
  132. package/packages/git-bash-mcp/dist/cli.js +5 -1
  133. package/packages/git-bash-mcp/package.json +1 -1
  134. package/packages/lsp-core/src/lsp/client-diagnostics-concurrency.integration.test.ts +44 -0
  135. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +0 -28
  136. package/packages/lsp-core/src/lsp/client-wrapper-outside-cwd.test.ts +78 -0
  137. package/packages/lsp-core/src/lsp/client-wrapper.test.ts +66 -13
  138. package/packages/lsp-core/src/lsp/client-wrapper.ts +70 -18
  139. package/packages/lsp-core/src/lsp/outside-context-workspace.ts +33 -0
  140. package/packages/lsp-core/src/lsp/workspace-edit-adversarial.test.ts +20 -1
  141. package/packages/lsp-core/src/lsp/workspace-markers.ts +1 -0
  142. package/packages/lsp-core/src/tools/diagnostics.ts +3 -3
  143. package/packages/lsp-core/src/tools/navigation.ts +4 -2
  144. package/packages/lsp-core/src/tools/rename.ts +4 -2
  145. package/packages/lsp-core/src/tools/symbols.ts +1 -1
  146. package/packages/lsp-daemon/dist/cli.js +246 -185
  147. package/packages/lsp-daemon/dist/client.js +222 -164
  148. package/packages/lsp-daemon/dist/ensure-daemon.js +1 -0
  149. package/packages/lsp-daemon/dist/index.js +236 -177
  150. package/packages/lsp-daemon/dist/version-reap.js +1 -1
  151. package/packages/lsp-tools-mcp/dist/cli.js +198 -136
  152. package/packages/lsp-tools-mcp/dist/lsp/manager.js +62 -92
  153. package/packages/lsp-tools-mcp/dist/mcp.js +200 -138
  154. package/packages/lsp-tools-mcp/dist/request-context.js +11 -12
  155. package/packages/lsp-tools-mcp/dist/tools.js +204 -146
  156. package/packages/omo-codex/THIRD-PARTY-NOTICES.md +4 -4
  157. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +4 -4
  158. package/packages/omo-codex/plugin/AGENTS.md +47 -0
  159. package/packages/omo-codex/plugin/README.md +1 -1
  160. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +32 -31
  161. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  162. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  163. package/packages/omo-codex/plugin/components/bootstrap/src/hook.ts +1 -0
  164. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +224 -81
  165. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +224 -82
  166. package/packages/omo-codex/plugin/components/codegraph/package.json +4 -4
  167. package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +1 -0
  168. package/packages/omo-codex/plugin/components/codegraph/src/session-start-hook-runtime.ts +1 -0
  169. package/packages/omo-codex/plugin/components/comment-checker/AGENTS.md +9 -0
  170. package/packages/omo-codex/plugin/components/comment-checker/biome.json +2 -2
  171. package/packages/omo-codex/plugin/components/comment-checker/dist/cli.js +2 -1
  172. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  173. package/packages/omo-codex/plugin/components/comment-checker/package.json +5 -5
  174. package/packages/omo-codex/plugin/components/comment-checker/src/apply-patch.ts +2 -8
  175. package/packages/omo-codex/plugin/components/comment-checker/src/core.ts +1 -2
  176. package/packages/omo-codex/plugin/components/comment-checker/src/request-extractor.ts +1 -1
  177. package/packages/omo-codex/plugin/components/comment-checker/src/runner.ts +1 -0
  178. package/packages/omo-codex/plugin/components/git-bash/AGENTS.md +1 -1
  179. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  180. package/packages/omo-codex/plugin/components/git-bash/package.json +3 -4
  181. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/biome.json +2 -2
  182. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/cli.js +1 -5
  183. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/codex-hook.js +1 -5
  184. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  185. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +5 -5
  186. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/src/codex-hook.ts +1 -5
  187. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/codex-hook.test.ts +4 -21
  188. package/packages/omo-codex/plugin/components/lsp/AGENTS.md +30 -15
  189. package/packages/omo-codex/plugin/components/lsp/biome.json +2 -2
  190. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +4 -4
  191. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +217 -171
  192. package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.js +1 -1
  193. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  194. package/packages/omo-codex/plugin/components/lsp/package.json +5 -5
  195. package/packages/omo-codex/plugin/components/lsp/src/cli.ts +1 -1
  196. package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +1 -1
  197. package/packages/omo-codex/plugin/components/lsp/src/daemon-cli-path.ts +1 -5
  198. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-unavailable.test.ts +6 -7
  199. package/packages/omo-codex/plugin/components/lsp/test/codex-hook.test.ts +1 -2
  200. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +11 -1
  201. package/packages/omo-codex/plugin/components/rules/AGENTS.md +12 -0
  202. package/packages/omo-codex/plugin/components/rules/biome.json +2 -2
  203. package/packages/omo-codex/plugin/components/rules/dist/cli.js +57 -42
  204. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  205. package/packages/omo-codex/plugin/components/rules/package.json +7 -7
  206. package/packages/omo-codex/plugin/components/rules/src/config.ts +1 -2
  207. package/packages/omo-codex/plugin/components/rules/src/persistent-cache.ts +1 -2
  208. package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +6 -2
  209. package/packages/omo-codex/plugin/components/rules/src/rules-engine-factory.ts +1 -4
  210. package/packages/omo-codex/plugin/components/rules/src/static-injection.ts +4 -9
  211. package/packages/omo-codex/plugin/components/rules/test/bundled-rules-priority.test.ts +11 -16
  212. package/packages/omo-codex/plugin/components/rules/test/bundled-rules.test.ts +17 -25
  213. package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-budget.test.ts +9 -7
  214. package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-context.test.ts +0 -6
  215. package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-dedup.test.ts +6 -4
  216. package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-directive.test.ts +12 -9
  217. package/packages/omo-codex/plugin/components/rules/test/codex-hook.test.ts +28 -37
  218. package/packages/omo-codex/plugin/components/rules/test/dynamic-target-fingerprints.test.ts +1 -2
  219. package/packages/omo-codex/plugin/components/rules/test/engine.test.ts +7 -4
  220. package/packages/omo-codex/plugin/components/rules/test/finder.test.ts +2 -3
  221. package/packages/omo-codex/plugin/components/rules/test/formatter.test.ts +39 -72
  222. package/packages/omo-codex/plugin/components/rules/test/hephaestus-model-variant.test.ts +1 -2
  223. package/packages/omo-codex/plugin/components/rules/test/hook-output.test.ts +2 -3
  224. package/packages/omo-codex/plugin/components/rules/test/matcher.test.ts +2 -3
  225. package/packages/omo-codex/plugin/components/rules/test/package-smoke.test.ts +1 -1
  226. package/packages/omo-codex/plugin/components/rules/test/parser.test.ts +1 -2
  227. package/packages/omo-codex/plugin/components/rules/test/post-compact-budget.test.ts +1 -2
  228. package/packages/omo-codex/plugin/components/rules/test/rules-engine-consumption.test.ts +3 -6
  229. package/packages/omo-codex/plugin/components/rules/test/scanner.test.ts +1 -2
  230. package/packages/omo-codex/plugin/components/rules/test/sources.test.ts +2 -5
  231. package/packages/omo-codex/plugin/components/rules/test/windows-git-bash-bundled-rule.test.ts +2 -17
  232. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  233. package/packages/omo-codex/plugin/components/teammode/package.json +5 -5
  234. package/packages/omo-codex/plugin/components/teammode/test/thread-title-hook.test.ts +3 -9
  235. package/packages/omo-codex/plugin/components/telemetry/AGENTS.md +8 -0
  236. package/packages/omo-codex/plugin/components/telemetry/biome.json +2 -2
  237. package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +2433 -422
  238. package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +2437 -426
  239. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  240. package/packages/omo-codex/plugin/components/telemetry/package.json +5 -5
  241. package/packages/omo-codex/plugin/components/telemetry/src/codex-hook.ts +1 -4
  242. package/packages/omo-codex/plugin/components/telemetry/src/posthog.ts +4 -14
  243. package/packages/omo-codex/plugin/components/telemetry/src/product-identity.ts +1 -1
  244. package/packages/omo-codex/plugin/components/telemetry/test/diagnostics.test.ts +2 -5
  245. package/packages/omo-codex/plugin/components/ultrawork/AGENTS.md +5 -0
  246. package/packages/omo-codex/plugin/components/ultrawork/biome.json +2 -2
  247. package/packages/omo-codex/plugin/components/ultrawork/directive.md +13 -1
  248. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  249. package/packages/omo-codex/plugin/components/ultrawork/package.json +5 -5
  250. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +13 -1
  251. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +6 -6
  252. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +31 -7
  253. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-clear.md +2 -1
  254. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-unclear.md +5 -5
  255. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook-trigger-policy.test.ts +9 -1
  256. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +0 -136
  257. package/packages/omo-codex/plugin/components/ultrawork/test/skill-pointer.test.ts +0 -2
  258. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/AGENTS.md +5 -4
  259. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/NOTICE +1 -1
  260. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/README.md +5 -5
  261. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/biome.json +2 -2
  262. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/directive.md +6 -6
  263. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/dist/cli.js +12 -12
  264. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +28 -0
  265. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/package.json +12 -12
  266. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/src/boulder-reader.ts +1 -1
  267. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/src/cli.ts +1 -1
  268. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/src/codex-hook.ts +3 -3
  269. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/src/directive.ts +1 -1
  270. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/src/index.ts +1 -1
  271. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/test/boulder-reader.test.ts +3 -3
  272. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/test/cli.test.ts +1 -4
  273. package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/test/codex-hook.test.ts +8 -22
  274. package/packages/omo-codex/plugin/components/ulw-loop/AGENTS.md +16 -0
  275. package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +1 -1
  276. package/packages/omo-codex/plugin/components/ulw-loop/biome.json +2 -2
  277. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +13 -1
  278. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +2 -4
  279. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.js +1 -1
  280. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.js +1 -1
  281. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +3 -3
  282. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  283. package/packages/omo-codex/plugin/components/ulw-loop/package.json +5 -5
  284. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +3 -2
  285. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/define-goal.md +108 -0
  286. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +1 -0
  287. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-snapshot.ts +3 -1
  288. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-hook.ts +3 -1
  289. package/packages/omo-codex/plugin/components/ulw-loop/src/paths.ts +1 -1
  290. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +3 -3
  291. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-continuation.test.ts +0 -1
  292. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-create-goals.test.ts +25 -0
  293. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-instruction.test.ts +2 -2
  294. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-snapshot.test.ts +18 -0
  295. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-hook.test.ts +0 -3
  296. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +5 -37
  297. package/packages/omo-codex/plugin/components/ulw-loop/test/paths.test.ts +9 -0
  298. package/packages/omo-codex/plugin/components/ulw-loop/test/steering.test.ts +8 -5
  299. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +1 -1
  300. package/packages/omo-codex/plugin/components/ulw-loop/test/ultrawork-directive.test.ts +4 -5
  301. package/packages/omo-codex/plugin/components/ulw-loop/vitest.config.ts +1 -0
  302. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  303. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  304. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  305. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  306. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  307. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  308. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  309. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  310. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  311. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  312. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  313. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  314. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  315. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  316. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  317. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  318. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +17 -0
  319. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  320. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-ulw-execute-continuation.json +17 -0
  321. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  322. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  323. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  324. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  325. package/packages/omo-codex/plugin/package-lock.json +681 -395
  326. package/packages/omo-codex/plugin/package.json +2 -2
  327. package/packages/omo-codex/plugin/scripts/AGENTS.md +40 -0
  328. package/packages/omo-codex/plugin/scripts/auto-update-plan.mjs +1 -1
  329. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +11 -11
  330. package/packages/omo-codex/plugin/shared/package.json +3 -4
  331. package/packages/omo-codex/plugin/skills/ast-grep/AGENTS.md +51 -0
  332. package/packages/omo-codex/plugin/skills/coding-agent-sessions/AGENTS.md +62 -0
  333. package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +3 -2
  334. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +1 -1
  335. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/senpi.md +4 -4
  336. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +1 -1
  337. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +11 -7
  338. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +1 -0
  339. package/packages/omo-codex/plugin/skills/frontend/references/design/print-paged-media.md +76 -0
  340. package/packages/omo-codex/plugin/skills/frontend/references/design/stylegallery.md +80 -0
  341. package/packages/omo-codex/plugin/skills/frontend/references/designpowers/README.md +1 -1
  342. package/packages/omo-codex/plugin/skills/frontend/references/designpowers/lane-a-direction.md +1 -1
  343. package/packages/omo-codex/plugin/skills/frontend/references/designpowers/lane-b-execution.md +11 -11
  344. package/packages/omo-codex/plugin/skills/frontend/references/designpowers/lane-d-memory.md +3 -3
  345. package/packages/omo-codex/plugin/skills/frontend/references/designpowers/orchestration.md +1 -1
  346. package/packages/omo-codex/plugin/skills/frontend/references/designpowers/routing.md +5 -5
  347. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +4 -4
  348. package/packages/omo-codex/plugin/skills/ultimate-browsing/ATTRIBUTION.md +37 -10
  349. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +2 -0
  350. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +179 -0
  351. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/package.json +1 -1
  352. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/chrome-stealth.md +13 -11
  353. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +13 -1
  354. package/packages/omo-codex/plugin/skills/{start-work → ulw-execute}/SKILL.md +12 -11
  355. package/packages/omo-codex/plugin/skills/ulw-execute/agents/openai.yaml +2 -0
  356. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +3 -2
  357. package/packages/omo-codex/plugin/skills/ulw-loop/references/define-goal.md +108 -0
  358. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +1 -0
  359. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +6 -6
  360. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +31 -7
  361. package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-clear.md +2 -1
  362. package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-unclear.md +5 -5
  363. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +1 -1
  364. package/packages/omo-codex/plugin/skills/visual-qa/AGENTS.md +58 -0
  365. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +3 -2
  366. package/packages/omo-codex/plugin/test/AGENTS.md +47 -0
  367. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +19 -173
  368. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +8 -28
  369. package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +1 -1
  370. package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +175 -13
  371. package/packages/omo-codex/plugin/test/aggregate.test.mjs +78 -2
  372. package/packages/omo-codex/plugin/test/auto-update-release-notes.test.mjs +19 -33
  373. package/packages/omo-codex/plugin/test/component-bin-names.test.mjs +2 -2
  374. package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +1 -1
  375. package/packages/omo-codex/plugin/test/component-hook-contract-cases.mjs +2 -2
  376. package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +4 -4
  377. package/packages/omo-codex/plugin/test/lcx-contribute-bug-fix-template.test.mjs +21 -27
  378. package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +0 -36
  379. package/packages/omo-codex/plugin/test/sync-skills-codex-compatibility.test.mjs +101 -0
  380. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +12 -12
  381. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +1 -119
  382. package/packages/omo-codex/plugin/test/teammode-archive-ambiguity.test.mjs +0 -40
  383. package/packages/omo-codex/plugin/test/teammode-communication.test.mjs +6 -62
  384. package/packages/omo-codex/plugin/test/teammode-thread-links.test.mjs +3 -36
  385. package/packages/omo-codex/plugin/test/teammode-transport.test.mjs +0 -44
  386. package/packages/omo-codex/plugin/test/teammode-worktree.test.mjs +2 -6
  387. package/packages/omo-codex/plugin/test/ultrawork-skill-pointer.test.mjs +0 -3
  388. package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +0 -3
  389. package/packages/omo-codex/scripts/AGENTS.md +36 -0
  390. package/packages/omo-codex/scripts/install-dist/install-local.mjs +2601 -528
  391. package/packages/omo-codex/scripts/install-lazycodex-version-stamp.test.mjs +7 -2
  392. package/packages/shared-skills/skills/ast-grep/AGENTS.md +51 -0
  393. package/packages/shared-skills/skills/coding-agent-sessions/AGENTS.md +62 -0
  394. package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +3 -2
  395. package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +1 -1
  396. package/packages/shared-skills/skills/coding-agent-sessions/references/senpi.md +4 -4
  397. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/pi_family.py +1 -1
  398. package/packages/shared-skills/skills/frontend/SKILL.md +11 -7
  399. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +1 -0
  400. package/packages/shared-skills/skills/frontend/references/design/print-paged-media.md +76 -0
  401. package/packages/shared-skills/skills/frontend/references/design/stylegallery.md +80 -0
  402. package/packages/shared-skills/skills/frontend/references/designpowers/README.md +1 -1
  403. package/packages/shared-skills/skills/frontend/references/designpowers/lane-a-direction.md +1 -1
  404. package/packages/shared-skills/skills/frontend/references/designpowers/lane-b-execution.md +11 -11
  405. package/packages/shared-skills/skills/frontend/references/designpowers/lane-d-memory.md +3 -3
  406. package/packages/shared-skills/skills/frontend/references/designpowers/orchestration.md +1 -1
  407. package/packages/shared-skills/skills/frontend/references/designpowers/routing.md +5 -5
  408. package/packages/shared-skills/skills/review-work/SKILL.md +3 -3
  409. package/packages/shared-skills/skills/ultimate-browsing/ATTRIBUTION.md +37 -10
  410. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +2 -0
  411. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +179 -0
  412. package/packages/shared-skills/skills/ultimate-browsing/engine/templates/package.json +1 -1
  413. package/packages/shared-skills/skills/ultimate-browsing/references/chrome-stealth.md +13 -11
  414. package/packages/shared-skills/skills/{start-work → ulw-execute}/SKILL.md +9 -8
  415. package/packages/shared-skills/skills/ulw-plan/SKILL.md +6 -6
  416. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +31 -7
  417. package/packages/shared-skills/skills/ulw-plan/references/intent-clear.md +2 -1
  418. package/packages/shared-skills/skills/ulw-plan/references/intent-unclear.md +5 -5
  419. package/packages/shared-skills/skills/ulw-research/SKILL.md +1 -1
  420. package/packages/shared-skills/skills/visual-qa/AGENTS.md +58 -0
  421. package/packages/shared-skills/skills/visual-qa/SKILL.md +3 -2
  422. package/script/qa/web-terminal-visual-qa.mjs +2 -0
  423. package/script/qa/xterm-live-terminal.mjs +18 -2
  424. package/dist/config/schema/start-work.d.ts +0 -5
  425. package/dist/features/builtin-commands/templates/start-work.d.ts +0 -1
  426. package/dist/hooks/start-work/start-work-hook.d.ts +0 -23
  427. package/dist/plugin/chat-message/start-work-message.d.ts +0 -5
  428. package/dist/tools/call-omo-agent/background-agent-executor.d.ts +0 -5
  429. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +0 -28
  430. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +0 -17
  431. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +0 -17
  432. package/packages/omo-codex/plugin/skills/start-work/agents/openai.yaml +0 -2
  433. package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +0 -92
  434. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +0 -314
  435. package/packages/omo-codex/plugin/test/ulw-plan-scope-contract.test.mjs +0 -24
  436. /package/dist/hooks/{start-work → ulw-execute}/context-info-formatters.d.ts +0 -0
  437. /package/dist/hooks/{start-work → ulw-execute}/explicit-plan-context.d.ts +0 -0
  438. /package/dist/hooks/{start-work → ulw-execute}/notepad-scaffold.d.ts +0 -0
  439. /package/dist/hooks/{start-work → ulw-execute}/parse-user-request.d.ts +0 -0
  440. /package/dist/hooks/{start-work → ulw-execute}/plan-discovery-context.d.ts +0 -0
  441. /package/dist/hooks/{start-work → ulw-execute}/plan-selection.d.ts +0 -0
  442. /package/dist/hooks/{start-work → ulw-execute}/session-plan-affinity.d.ts +0 -0
  443. /package/dist/hooks/{start-work → ulw-execute}/work-initializer.d.ts +0 -0
  444. /package/dist/hooks/{start-work → ulw-execute}/worktree-block.d.ts +0 -0
  445. /package/dist/hooks/{start-work → ulw-execute}/worktree-detector.d.ts +0 -0
  446. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/.gitattributes +0 -0
  447. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/CHANGELOG.md +0 -0
  448. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/LICENSE +0 -0
  449. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/src/plan-checklist.ts +0 -0
  450. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/src/types.ts +0 -0
  451. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/test/fixtures/boulder-completed.json +0 -0
  452. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/test/fixtures/boulder-mixed-platforms.json +0 -0
  453. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/test/fixtures/boulder-single-codex-work.json +0 -0
  454. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/test/fixtures/plan-all-done.md +0 -0
  455. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/test/fixtures/plan-scaffold.md +0 -0
  456. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/test/fixtures/plan-with-nested-checkboxes.md +0 -0
  457. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/test/fixtures/plan-with-unchecked.md +0 -0
  458. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/tsconfig.build.json +0 -0
  459. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/tsconfig.json +0 -0
  460. /package/packages/omo-codex/plugin/components/{start-work-continuation → ulw-execute-continuation}/vitest.config.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode",
3
- "version": "5.0.0-beta.2",
3
+ "version": "5.0.0-beta.21",
4
4
  "description": "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
5
5
  "main": "./dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -85,7 +85,7 @@
85
85
  "packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/cli.js",
86
86
  "packages/omo-codex/plugin/components/lsp/dist/cli.js",
87
87
  "packages/omo-codex/plugin/components/rules/dist/cli.js",
88
- "packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js",
88
+ "packages/omo-codex/plugin/components/ulw-execute-continuation/dist/cli.js",
89
89
  "packages/omo-codex/plugin/components/teammode/dist/cli.js",
90
90
  "packages/omo-codex/plugin/components/telemetry/dist/cli.js",
91
91
  "packages/omo-codex/plugin/components/ultrawork/dist/cli.js",
@@ -136,7 +136,8 @@
136
136
  "typecheck:script": "tsgo --noEmit -p script/tsconfig.json",
137
137
  "test": "bun test",
138
138
  "test:codex": "bun run build:codex-install && bun run build:git-bash-mcp && bun run build:lsp-tools-mcp && bun run build:lsp-daemon && npm --prefix packages/lsp-tools-mcp test && npm --prefix packages/omo-codex/plugin ci && npm --prefix packages/omo-codex/plugin/components/ulw-loop test && bun run --cwd packages/omo-codex/plugin build && npm --prefix packages/omo-codex/plugin/components/codegraph run typecheck && npm --prefix packages/omo-codex/plugin/components/codegraph test && node scripts/check-third-party-notices.mjs --ship && bun test packages/omo-opencode/src/cli/cli-installer.platform.test.ts packages/omo-codex/src/install/codex-cache.test.ts packages/omo-codex/src/install/codex-cleanup.test.ts packages/omo-codex/src/install/codex-config-agent-cleanup.test.ts packages/omo-codex/src/install/codex-config-autonomous-features.test.ts packages/omo-codex/src/install/codex-config-reasoning.test.ts packages/omo-codex/src/install/codex-config-toml.test.ts packages/omo-codex/src/install/codex-project-local-cleanup.test.ts packages/omo-codex/src/install/install-codex-project-local-cleanup.test.ts packages/omo-codex/src/install/install-codex.test.ts packages/omo-codex/src/install/install-codex-packaged.test.ts packages/omo-codex/src/install/link-cached-plugin-agents.test.ts packages/omo-codex/src/**/*.test.ts packages/utils/src/jsonc-parser.test.ts packages/utils/src/frontmatter.test.ts packages/hashline-core/src/hash-computation.test.ts packages/hashline-core/src/smoke-untested-modules.test.ts packages/rules-engine/src/index.test.ts packages/rules-engine/src/security-boundary.test.ts packages/agents-md-core/src/injector.test.ts packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts && node --test packages/omo-codex/plugin/test/*.test.mjs packages/omo-codex/scripts/install-cache-copy.test.mjs packages/omo-codex/scripts/install-cli-args.test.mjs packages/omo-codex/scripts/install-delegated-command.test.mjs packages/omo-codex/scripts/install-config-autonomous-features.test.mjs packages/omo-codex/scripts/install-config-autonomous.test.mjs packages/omo-codex/scripts/install-config-reasoning.test.mjs packages/omo-codex/scripts/install-config.test.mjs packages/omo-codex/scripts/install-hook-targets.test.mjs packages/omo-codex/scripts/install-project-local-cleanup.test.mjs packages/omo-codex/scripts/install-lazycodex-version-stamp.test.mjs packages/omo-codex/scripts/install-local-entrypoint.test.mjs packages/omo-codex/scripts/install-local-git-bash-preflight.test.mjs packages/omo-codex/scripts/install-local.test.mjs packages/omo-codex/scripts/install-marketplace-cache.test.mjs packages/omo-codex/scripts/install-mcp-context7-runtime.test.mjs packages/omo-codex/scripts/install-mcp-runtime.test.mjs packages/omo-codex/scripts/install-packaged-local.test.mjs packages/omo-codex/scripts/install-generated-bundle.test.mjs packages/omo-codex/scripts/install-agent-links.test.mjs packages/omo-codex/scripts/install-bin-links.test.mjs",
139
- "test:senpi": "bun run build:senpi-plugin && tsgo --noEmit -p packages/omo-senpi/tsconfig.json && bun test packages/omo-senpi",
139
+ "test:fast": "bun run script/test-fast.ts",
140
+ "test:senpi": "bun run build:senpi-plugin && tsgo --noEmit -p packages/omo-senpi/tsconfig.json && bun test packages/omo-senpi && bun test ./.agents/skills/senpi-qa/scripts/resolve-evidence-dir.test.mjs",
140
141
  "test:windows-codex": "bun run test:codex",
141
142
  "build:git-bash-mcp": "bun run --cwd packages/git-bash-mcp build",
142
143
  "build:ast-grep-mcp": "bun run --cwd packages/ast-grep-mcp build"
@@ -161,26 +162,26 @@
161
162
  },
162
163
  "homepage": "https://github.com/code-yeongyu/oh-my-openagent#readme",
163
164
  "dependencies": {
164
- "@clack/prompts": "^1.4.0",
165
+ "@clack/prompts": "^1.7.0",
165
166
  "@code-yeongyu/comment-checker": "^0.8.0",
166
- "@modelcontextprotocol/sdk": "^1.29.0",
167
- "@opencode-ai/plugin": "1.15.13",
168
- "@opencode-ai/sdk": "1.15.13",
169
- "@opentui/core": "^0.2.16",
170
- "@opentui/keymap": "^0.2.16",
171
- "@opentui/solid": "^0.2.16",
172
- "commander": "^14.0.3",
167
+ "@modelcontextprotocol/sdk": "^1.30.0",
168
+ "@opencode-ai/plugin": "1.18.22",
169
+ "@opencode-ai/sdk": "1.18.22",
170
+ "@opentui/core": "^0.5.8",
171
+ "@opentui/keymap": "^0.5.8",
172
+ "@opentui/solid": "^0.5.8",
173
+ "commander": "^15.0.0",
173
174
  "detect-libc": "^2.1.2",
174
175
  "diff": "^9.0.0",
175
- "js-yaml": "^4.1.1",
176
+ "js-yaml": "^5.0.0",
176
177
  "jsonc-parser": "^3.3.1",
177
178
  "picocolors": "^1.1.1",
178
- "picomatch": "^4.0.4",
179
- "posthog-node": "^5.34.3",
179
+ "picomatch": "^4.0.7",
180
+ "posthog-node": "^5.51.1",
180
181
  "zod": "^4.4.3"
181
182
  },
182
183
  "devDependencies": {
183
- "@code-yeongyu/senpi": "2026.8.9-2",
184
+ "@code-yeongyu/senpi": "2026.8.26",
184
185
  "@oh-my-opencode/agents-md-core": "workspace:*",
185
186
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
186
187
  "@oh-my-opencode/boulder-state": "workspace:*",
@@ -196,6 +197,7 @@
196
197
  "@oh-my-opencode/model-core": "workspace:*",
197
198
  "@oh-my-opencode/omo-codex": "workspace:*",
198
199
  "@oh-my-opencode/omo-config-core": "workspace:*",
200
+ "@oh-my-opencode/omo-opencode": "workspace:*",
199
201
  "@oh-my-opencode/omo-senpi": "workspace:*",
200
202
  "@oh-my-opencode/openclaw-core": "workspace:*",
201
203
  "@oh-my-opencode/pi-goal": "workspace:*",
@@ -211,38 +213,39 @@
211
213
  "@oh-my-opencode/utils": "workspace:*",
212
214
  "@types/js-yaml": "^4.0.9",
213
215
  "@types/picomatch": "^4.0.3",
214
- "@typescript/native-preview": "7.0.0-dev.20260518.1",
216
+ "@typescript/native-preview": "7.0.0-dev.20260707.2",
215
217
  "@xterm/addon-unicode11": "^0.9.0",
216
218
  "@xterm/xterm": "^6.0.0",
217
- "bun-types": "1.3.14",
219
+ "bun-types": "1.4.0",
218
220
  "node-pty": "^1.1.0",
219
- "puppeteer-core": "^25.3.0",
221
+ "puppeteer-core": "^25.8.0",
220
222
  "typescript": "^7.0.2"
221
223
  },
222
224
  "optionalDependencies": {
223
- "oh-my-opencode-darwin-arm64": "5.0.0-beta.2",
224
- "oh-my-opencode-darwin-x64": "5.0.0-beta.2",
225
- "oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.2",
226
- "oh-my-opencode-linux-arm64": "5.0.0-beta.2",
227
- "oh-my-opencode-linux-arm64-musl": "5.0.0-beta.2",
228
- "oh-my-opencode-linux-x64": "5.0.0-beta.2",
229
- "oh-my-opencode-linux-x64-baseline": "5.0.0-beta.2",
230
- "oh-my-opencode-linux-x64-musl": "5.0.0-beta.2",
231
- "oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.2",
232
- "oh-my-opencode-windows-arm64": "5.0.0-beta.2",
233
- "oh-my-opencode-windows-x64": "5.0.0-beta.2",
234
- "oh-my-opencode-windows-x64-baseline": "5.0.0-beta.2"
225
+ "oh-my-opencode-darwin-arm64": "5.0.0-beta.21",
226
+ "oh-my-opencode-darwin-x64": "5.0.0-beta.21",
227
+ "oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.21",
228
+ "oh-my-opencode-linux-arm64": "5.0.0-beta.21",
229
+ "oh-my-opencode-linux-arm64-musl": "5.0.0-beta.21",
230
+ "oh-my-opencode-linux-x64": "5.0.0-beta.21",
231
+ "oh-my-opencode-linux-x64-baseline": "5.0.0-beta.21",
232
+ "oh-my-opencode-linux-x64-musl": "5.0.0-beta.21",
233
+ "oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.21",
234
+ "oh-my-opencode-windows-arm64": "5.0.0-beta.21",
235
+ "oh-my-opencode-windows-x64": "5.0.0-beta.21",
236
+ "oh-my-opencode-windows-x64-baseline": "5.0.0-beta.21"
235
237
  },
236
238
  "overrides": {
237
- "@earendil-works/pi-agent-core": "0.80.3",
238
- "@earendil-works/pi-ai": "0.80.3",
239
- "@earendil-works/pi-coding-agent": "0.80.3",
240
- "@earendil-works/pi-tui": "0.80.3",
239
+ "@earendil-works/pi-agent-core": "0.84.2",
240
+ "@earendil-works/pi-ai": "0.84.2",
241
+ "@earendil-works/pi-coding-agent": "0.84.2",
242
+ "@earendil-works/pi-tui": "0.84.2",
241
243
  "hono": "^4.12.18",
242
244
  "@hono/node-server": "^1.19.13",
243
245
  "express-rate-limit": "^8.5.1",
244
246
  "fast-uri": "^3.1.2",
245
- "path-to-regexp": "^8.4.2"
247
+ "path-to-regexp": "^8.4.2",
248
+ "@types/node": "26.2.0"
246
249
  },
247
250
  "trustedDependencies": [
248
251
  "@code-yeongyu/comment-checker"
@@ -276,7 +276,11 @@ function createParentWatchdog(config, onDeadParent) {
276
276
  const probeAlive = config.probeAlive ?? isProcessAlive;
277
277
  let fired = false;
278
278
  const timer = setInterval(() => {
279
- if (fired || probeAlive(parentPid))
279
+ if (fired)
280
+ return;
281
+ const alive = probeAlive(parentPid);
282
+ config.onPoll?.(alive);
283
+ if (alive)
280
284
  return;
281
285
  fired = true;
282
286
  onDeadParent(parentPid, pollIntervalMs);
@@ -22,6 +22,6 @@
22
22
  "@oh-my-opencode/utils": "workspace:*"
23
23
  },
24
24
  "devDependencies": {
25
- "bun-types": "1.3.12"
25
+ "bun-types": "1.4.0"
26
26
  }
27
27
  }
@@ -0,0 +1,44 @@
1
+ import { afterEach, describe, expect, it } from "bun:test";
2
+
3
+ import {
4
+ createWorkspaceEditTestHarness,
5
+ diagnostic,
6
+ readEvents,
7
+ } from "./workspace-apply-edit-test-support.js";
8
+
9
+ const harness = createWorkspaceEditTestHarness();
10
+
11
+ afterEach(async () => {
12
+ await harness.cleanup();
13
+ });
14
+
15
+ describe("LspClient diagnostics concurrency", () => {
16
+ it("#given concurrent diagnostics on a cold file and an exact didOpen publish #when pull is not advertised #then one didOpen opens the file and both requests receive the current diagnostics", async () => {
17
+ const context = await harness.makeClient(
18
+ {
19
+ publishDiagnostics: [
20
+ {
21
+ trigger: "didOpen",
22
+ version: 1,
23
+ diagnostics: [diagnostic("exact-current")],
24
+ awaitClientDelivery: true,
25
+ },
26
+ ],
27
+ },
28
+ { diagnosticsFreshnessTimeoutMs: 500, versionlessPublishQuiescenceMs: 5 },
29
+ );
30
+
31
+ const [first, second] = await Promise.all([
32
+ context.client.diagnostics(context.source),
33
+ context.client.diagnostics(context.source),
34
+ ]);
35
+
36
+ expect(first.items).toEqual([diagnostic("exact-current")]);
37
+ expect(second.items).toEqual([diagnostic("exact-current")]);
38
+ expect(
39
+ readEvents(context.events).filter(
40
+ (event) => event.type === "clientNotification" && event.method === "textDocument/didOpen",
41
+ ),
42
+ ).toHaveLength(1);
43
+ });
44
+ });
@@ -16,34 +16,6 @@ afterEach(async () => {
16
16
  });
17
17
 
18
18
  describe("LspClient diagnostics freshness", () => {
19
- it("#given concurrent diagnostics on a cold file and an exact didOpen publish #when pull is not advertised #then one didOpen opens the file and both requests receive the current diagnostics", async () => {
20
- const context = await harness.makeClient(
21
- {
22
- publishDiagnostics: [
23
- {
24
- trigger: "didOpen",
25
- version: 1,
26
- diagnostics: [diagnostic("exact-current")],
27
- },
28
- ],
29
- },
30
- { diagnosticsFreshnessTimeoutMs: 50, versionlessPublishQuiescenceMs: 5 },
31
- );
32
-
33
- const [first, second] = await Promise.all([
34
- context.client.diagnostics(context.source),
35
- context.client.diagnostics(context.source),
36
- ]);
37
-
38
- expect(first.items).toEqual([diagnostic("exact-current")]);
39
- expect(second.items).toEqual([diagnostic("exact-current")]);
40
- expect(
41
- readEvents(context.events).filter(
42
- (event) => event.type === "clientNotification" && event.method === "textDocument/didOpen",
43
- ),
44
- ).toHaveLength(1);
45
- });
46
-
47
19
  it("#given a versionless publish that arrives after the current change #when no newer eligible publish arrives before quiescence #then diagnostics wait for that quiescence window and return the versionless payload", async () => {
48
20
  const context = await harness.makeClient(
49
21
  {
@@ -0,0 +1,78 @@
1
+ import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, describe, expect, it } from "bun:test";
5
+
6
+ import { createStandaloneMcpRequestContext, runWithRequestContext } from "../request-context.js";
7
+ import { findWorkspaceRoot, resolveReadablePathInsideContext, resolvePathInsideContext } from "./client-wrapper.js";
8
+ import { LspInvalidPathError } from "./errors.js";
9
+
10
+ const tempDirectories: string[] = [];
11
+
12
+ afterEach(() => {
13
+ for (const directory of tempDirectories.splice(0)) {
14
+ rmSync(directory, { recursive: true, force: true });
15
+ }
16
+ });
17
+
18
+ function tempRoot(prefix: string): string {
19
+ const root = mkdtempSync(join(tmpdir(), prefix));
20
+ tempDirectories.push(root);
21
+ return root;
22
+ }
23
+
24
+ describe("LSP read-only access outside request cwd", () => {
25
+ it("#given an absolute file outside cwd #when resolving a read path #then returns the canonical path", () => {
26
+ const root = tempRoot("lsp-outcwd-root-");
27
+ const outside = tempRoot("lsp-outcwd-outside-");
28
+ const outsideFile = join(outside, "probe.ts");
29
+ writeFileSync(outsideFile, "export const value = 1;\n");
30
+
31
+ const resolved = runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
32
+ resolveReadablePathInsideContext(outsideFile),
33
+ );
34
+
35
+ expect(resolved).toBe(realpathSync(outsideFile));
36
+ });
37
+
38
+ it("#given an outside file inside its own marked project #when inferring workspace #then uses that project root", () => {
39
+ const root = tempRoot("lsp-outcwd-marker-root-");
40
+ const outside = tempRoot("lsp-outcwd-marker-outside-");
41
+ mkdirSync(join(outside, ".git"), { recursive: true });
42
+ mkdirSync(join(outside, "src"), { recursive: true });
43
+ const outsideFile = join(outside, "src", "probe.ts");
44
+ writeFileSync(outsideFile, "export const value = 1;\n");
45
+
46
+ const workspace = runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
47
+ findWorkspaceRoot(outsideFile),
48
+ );
49
+
50
+ expect(workspace).toBe(realpathSync(outside));
51
+ });
52
+
53
+ it("#given an outside file without any marker #when inferring workspace #then falls back to its own directory", () => {
54
+ const root = tempRoot("lsp-outcwd-nomarker-root-");
55
+ const outside = tempRoot("lsp-outcwd-nomarker-outside-");
56
+ const outsideFile = join(outside, "probe.ts");
57
+ writeFileSync(outsideFile, "export const value = 1;\n");
58
+
59
+ const workspace = runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
60
+ findWorkspaceRoot(outsideFile),
61
+ );
62
+
63
+ expect(workspace).toBe(realpathSync(outside));
64
+ });
65
+
66
+ it("#given an outside file #when resolving a mutating path #then still rejects to keep writes confined", () => {
67
+ const root = tempRoot("lsp-outcwd-write-root-");
68
+ const outside = tempRoot("lsp-outcwd-write-outside-");
69
+ const outsideFile = join(outside, "probe.ts");
70
+ writeFileSync(outsideFile, "export const value = 1;\n");
71
+
72
+ expect(() =>
73
+ runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
74
+ resolvePathInsideContext(outsideFile),
75
+ ),
76
+ ).toThrow(LspInvalidPathError);
77
+ });
78
+ });
@@ -4,8 +4,9 @@ import { join } from "node:path";
4
4
  import { afterEach, describe, expect, it } from "bun:test";
5
5
 
6
6
  import { createStandaloneMcpRequestContext, runWithRequestContext } from "../request-context.js";
7
- import { findWorkspaceRoot, resolvePathInsideContext } from "./client-wrapper.js";
7
+ import { findWorkspaceRoot, resolveReadablePathInsideContext, withLspClient } from "./client-wrapper.js";
8
8
  import { LspInvalidPathError } from "./errors.js";
9
+ import { LspManager } from "./manager.js";
9
10
 
10
11
  const tempDirectories: string[] = [];
11
12
 
@@ -35,29 +36,81 @@ describe("LSP client path confinement", () => {
35
36
  expect(workspace).toBe(realpathSync(root));
36
37
  });
37
38
 
38
- it("#given an absolute file outside context cwd #when resolving #then rejects before workspace inference", () => {
39
+ it("#given an absolute file outside context cwd #when resolving #then infers the file's own project root", () => {
39
40
  const root = tempRoot("lsp-client-wrapper-cwd-");
40
41
  const outside = tempRoot("lsp-client-wrapper-outside-");
41
42
  mkdirSync(join(outside, ".git"), { recursive: true });
42
43
  writeFileSync(join(outside, "file.ts"), "export const outside = true;\n");
43
44
 
44
- expect(() =>
45
- runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
46
- findWorkspaceRoot(join(outside, "file.ts")),
47
- ),
48
- ).toThrow(LspInvalidPathError);
45
+ const workspace = runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
46
+ findWorkspaceRoot(join(outside, "file.ts")),
47
+ );
48
+
49
+ expect(workspace).toBe(realpathSync(outside));
49
50
  });
50
51
 
51
- it("#given a symlink inside cwd that points outside #when resolving #then rejects the escape", () => {
52
+ it("#given a symlink inside cwd that points outside #when resolving read path #then keeps lexical path and cwd root", () => {
52
53
  const root = tempRoot("lsp-client-wrapper-symlink-root-");
53
54
  const outside = tempRoot("lsp-client-wrapper-symlink-outside-");
55
+ mkdirSync(join(root, ".git"), { recursive: true });
56
+ mkdirSync(join(outside, ".git"), { recursive: true });
57
+ writeFileSync(join(outside, "file.ts"), "export const outside = true;\n");
58
+ symlinkSync(outside, join(root, "linked"));
59
+
60
+ const result = runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () => ({
61
+ filePath: resolveReadablePathInsideContext("linked/file.ts"),
62
+ workspaceRoot: findWorkspaceRoot("linked/file.ts"),
63
+ }));
64
+
65
+ expect(result.filePath).toBe(join(realpathSync(root), "linked", "file.ts"));
66
+ expect(result.workspaceRoot).toBe(realpathSync(root));
67
+ });
68
+
69
+ it("#given an absolute macOS alias path through an outside symlink #when resolving read path #then preserves the lexical suffix", () => {
70
+ const root = tempRoot("lsp-client-wrapper-alias-root-");
71
+ const outside = tempRoot("lsp-client-wrapper-alias-outside-");
72
+ mkdirSync(join(root, ".git"), { recursive: true });
73
+ writeFileSync(join(outside, "file.ts"), "export const outside = true;\n");
74
+ symlinkSync(outside, join(root, "linked"));
75
+
76
+ const result = runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
77
+ resolveReadablePathInsideContext(join(root, "linked", "file.ts")),
78
+ );
79
+
80
+ expect(result).toBe(join(realpathSync(root), "linked", "file.ts"));
81
+ });
82
+
83
+ it("#given missing parent directories without markers #when resolving workspace #then uses an existing directory", () => {
84
+ const root = tempRoot("lsp-client-wrapper-missing-root-");
85
+ const workspace = runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
86
+ findWorkspaceRoot("missing/deep/file.ts"),
87
+ );
88
+
89
+ expect(workspace).toBe(realpathSync(root));
90
+ });
91
+
92
+ it("#given a symlink inside cwd that points outside #when starting rename #then rejects before client acquisition", async () => {
93
+ const root = tempRoot("lsp-client-wrapper-rename-root-");
94
+ const outside = tempRoot("lsp-client-wrapper-rename-outside-");
54
95
  writeFileSync(join(outside, "file.ts"), "export const outside = true;\n");
55
96
  symlinkSync(outside, join(root, "linked"));
97
+ let clientFactoryCalls = 0;
98
+ const manager = new LspManager({
99
+ clientFactory: () => {
100
+ clientFactoryCalls += 1;
101
+ throw new Error("client acquisition must not start");
102
+ },
103
+ });
56
104
 
57
- expect(() =>
58
- runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
59
- resolvePathInsideContext("linked/file.ts"),
60
- ),
61
- ).toThrow(LspInvalidPathError);
105
+ try {
106
+ await expect(
107
+ runWithRequestContext(createStandaloneMcpRequestContext({ cwd: root }), () =>
108
+ withLspClient("linked/file.ts", async () => undefined, "rename", { manager }),
109
+ ),
110
+ ).rejects.toThrow(LspInvalidPathError);
111
+ expect(clientFactoryCalls).toBe(0);
112
+ } finally {
113
+ await manager.stopAll();
114
+ }
62
115
  });
63
116
  });
@@ -1,5 +1,5 @@
1
- import { existsSync, statSync } from "node:fs";
2
- import { dirname, join, resolve } from "node:path";
1
+ import { existsSync, realpathSync, statSync } from "node:fs";
2
+ import { basename, dirname, join, resolve } from "node:path";
3
3
  import {
4
4
  canonicalizeExistingOrNearestAncestor,
5
5
  contextCwd,
@@ -16,11 +16,11 @@ import {
16
16
  LspServerLookupError,
17
17
  } from "./errors.js";
18
18
  import { getLspManager, type LspManager } from "./manager.js";
19
+ import { findWorkspaceRootOutsideContext } from "./outside-context-workspace.js";
19
20
  import { loadInstallDecision } from "./server-install-state.js";
20
21
  import { findServerForExtension } from "./server-resolution.js";
21
22
  import type { ServerLookupResult } from "./types.js";
22
-
23
- const WORKSPACE_MARKERS = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
23
+ import { WORKSPACE_MARKERS } from "./workspace-markers.js";
24
24
 
25
25
  export function isDirectoryPath(filePath: string): boolean {
26
26
  try {
@@ -31,30 +31,47 @@ export function isDirectoryPath(filePath: string): boolean {
31
31
  }
32
32
 
33
33
  export function findWorkspaceRoot(filePath: string): string {
34
- const abs = resolvePathInsideContext(filePath);
34
+ const cwd = contextCwd();
35
+ const abs = resolveReadablePathInsideContext(filePath);
35
36
  let dir = abs;
36
37
 
37
38
  if (!isDirectoryPath(dir)) {
38
39
  dir = dirname(dir);
39
40
  }
40
41
 
41
- let prevDir = "";
42
- while (dir !== prevDir) {
43
- for (const marker of WORKSPACE_MARKERS) {
44
- if (existsSync(join(dir, marker))) {
45
- return dir;
42
+ if (!isPathInside(cwd, abs)) return findWorkspaceRootOutsideContext(dir);
43
+
44
+ const fallbackRoot = nearestExistingDirectoryInsideContext(dir, cwd) ?? cwd;
45
+ while (isPathInside(cwd, dir)) {
46
+ const canonicalDir = existingDirectoryInsideContext(dir, cwd);
47
+ if (canonicalDir !== undefined) {
48
+ for (const marker of WORKSPACE_MARKERS) {
49
+ if (existsSync(join(dir, marker))) {
50
+ return canonicalDir;
51
+ }
46
52
  }
47
53
  }
48
- prevDir = dir;
54
+ if (dir === cwd) break;
49
55
  dir = dirname(dir);
50
56
  }
51
57
 
52
- return dirname(abs);
58
+ return fallbackRoot;
53
59
  }
54
60
 
55
- export function resolvePathInsideContext(filePath: string): string {
61
+ export function resolveReadablePathInsideContext(filePath: string): string {
56
62
  const cwd = contextCwd();
57
63
  const abs = resolve(cwd, filePath);
64
+ if (isPathInside(cwd, abs)) return abs;
65
+
66
+ const rebased = rebaseThroughCanonicalAncestor(abs, cwd);
67
+ if (rebased !== undefined) return rebased;
68
+
69
+ return canonicalizeExistingOrNearestAncestor(abs);
70
+ }
71
+
72
+ export function resolvePathInsideContext(filePath: string): string {
73
+ const cwd = contextCwd();
74
+ const abs = resolveReadablePathInsideContext(filePath);
58
75
  const canonical = canonicalizeExistingOrNearestAncestor(abs);
59
76
  if (!isPathInside(cwd, canonical)) {
60
77
  throw new LspInvalidPathError(`LSP file path must be inside request cwd: ${filePath}`);
@@ -62,6 +79,39 @@ export function resolvePathInsideContext(filePath: string): string {
62
79
  return canonical;
63
80
  }
64
81
 
82
+ function rebaseThroughCanonicalAncestor(path: string, cwd: string): string | undefined {
83
+ let current = path;
84
+ const suffix: string[] = [];
85
+ while (true) {
86
+ if (existsSync(current)) {
87
+ const canonical = realpathSync(current);
88
+ if (isPathInside(cwd, canonical)) return suffix.length === 0 ? canonical : join(canonical, ...suffix);
89
+ }
90
+ const parent = dirname(current);
91
+ if (parent === current) return undefined;
92
+ suffix.unshift(basename(current));
93
+ current = parent;
94
+ }
95
+ }
96
+
97
+ function existingDirectoryInsideContext(directory: string, cwd: string): string | undefined {
98
+ if (!existsSync(directory)) return undefined;
99
+ const canonical = realpathSync(directory);
100
+ if (!statSync(canonical).isDirectory()) return undefined;
101
+ return isPathInside(cwd, canonical) ? canonical : undefined;
102
+ }
103
+
104
+ function nearestExistingDirectoryInsideContext(directory: string, cwd: string): string | undefined {
105
+ let current = directory;
106
+ while (isPathInside(cwd, current)) {
107
+ const canonical = existingDirectoryInsideContext(current, cwd);
108
+ if (canonical !== undefined) return canonical;
109
+ if (current === cwd) return undefined;
110
+ current = dirname(current);
111
+ }
112
+ return undefined;
113
+ }
114
+
65
115
  export function formatServerLookupError(result: Exclude<ServerLookupResult, { status: "found" }>): string {
66
116
  if (result.status === "not_installed") {
67
117
  return formatNotInstalled(result);
@@ -143,7 +193,7 @@ export interface WithLspClientOptions {
143
193
  manager?: LspManager;
144
194
  }
145
195
 
146
- const READ_ONLY_RETRY_TOOLS = new Set([
196
+ const READ_ONLY_TOOLS = new Set([
147
197
  "diagnostics",
148
198
  "definition",
149
199
  "references",
@@ -154,11 +204,13 @@ const READ_ONLY_RETRY_TOOLS = new Set([
154
204
 
155
205
  export async function withLspClient<T>(
156
206
  filePath: string,
157
- fn: (client: LspClient, workspaceRoot: string) => Promise<T>,
207
+ fn: (client: LspClient, workspaceRoot: string, resolvedFilePath: string) => Promise<T>,
158
208
  toolName: string,
159
209
  options: WithLspClientOptions = {},
160
210
  ): Promise<T> {
161
- const absPath = resolvePathInsideContext(filePath);
211
+ const absPath = READ_ONLY_TOOLS.has(toolName)
212
+ ? resolveReadablePathInsideContext(filePath)
213
+ : resolvePathInsideContext(filePath);
162
214
 
163
215
  if (isDirectoryPath(absPath)) {
164
216
  throw new LspInvalidPathError(
@@ -181,9 +233,9 @@ export async function withLspClient<T>(
181
233
  const client = await manager.getClient(root, server, options.signal);
182
234
 
183
235
  try {
184
- return await fn(client, root);
236
+ return await fn(client, root, absPath);
185
237
  } catch (err) {
186
- if (allowRetry && READ_ONLY_RETRY_TOOLS.has(toolName) && isLspDeadConnectionError(err)) {
238
+ if (allowRetry && READ_ONLY_TOOLS.has(toolName) && isLspDeadConnectionError(err)) {
187
239
  manager.invalidateClient(root, server.id, client);
188
240
  return acquireAndCall(false);
189
241
  }
@@ -0,0 +1,33 @@
1
+ import { existsSync, realpathSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+
4
+ import { WORKSPACE_MARKERS } from "./workspace-markers.js";
5
+
6
+ /** Read-only tools accept out-of-cwd files, so cwd cannot bound this search; the fallback keeps the server scoped to the file's own directory. */
7
+ export function findWorkspaceRootOutsideContext(directory: string): string {
8
+ const marked = nearestMarkedAncestor(directory);
9
+ if (marked !== undefined) return marked;
10
+ return realpathSync(nearestExistingAncestor(directory));
11
+ }
12
+
13
+ function nearestMarkedAncestor(directory: string): string | undefined {
14
+ let current = directory;
15
+ for (;;) {
16
+ if (existsSync(current) && WORKSPACE_MARKERS.some((marker) => existsSync(join(current, marker)))) {
17
+ return realpathSync(current);
18
+ }
19
+ const parent = dirname(current);
20
+ if (parent === current) return undefined;
21
+ current = parent;
22
+ }
23
+ }
24
+
25
+ function nearestExistingAncestor(directory: string): string {
26
+ let current = directory;
27
+ while (!existsSync(current)) {
28
+ const parent = dirname(current);
29
+ if (parent === current) return current;
30
+ current = parent;
31
+ }
32
+ return current;
33
+ }
@@ -1,4 +1,4 @@
1
- import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
1
+ import { mkdtempSync, readFileSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { pathToFileURL } from "node:url";
@@ -87,6 +87,25 @@ describe("workspace edit adversarial inputs", () => {
87
87
  expect(readFileSync(outsideFile, "utf-8")).toBe("outside bytes\n");
88
88
  });
89
89
 
90
+ it("#given an in-workspace symlink to an outside file #when applying an edit #then rejects without modifying target", () => {
91
+ const workspace = makeTempDirectory("lsp-workspace-");
92
+ const outside = makeTempDirectory("lsp-outside-");
93
+ const outsideFile = join(outside, "outside.ts");
94
+ const canonicalWorkspace = realpathSync(workspace);
95
+ const linkedDirectory = join(canonicalWorkspace, "linked");
96
+ writeFileSync(outsideFile, "outside bytes\n", "utf-8");
97
+ symlinkSync(outside, linkedDirectory);
98
+
99
+ const result = applyWorkspaceEdit(
100
+ { changes: { [pathToFileURL(join(linkedDirectory, "outside.ts")).href]: [insertion()] } },
101
+ { workspaceRoot: canonicalWorkspace },
102
+ );
103
+
104
+ expect(result.success).toBe(false);
105
+ expect(result.errors.join("\n")).toContain("outside workspace");
106
+ expect(readFileSync(outsideFile, "utf-8")).toBe("outside bytes\n");
107
+ });
108
+
90
109
  it("#given annotations or mixed edit representations #when planned #then unsupported claims are rejected", () => {
91
110
  const workspace = makeTempDirectory("lsp-workspace-");
92
111
  const unsupported = [
@@ -0,0 +1 @@
1
+ export const WORKSPACE_MARKERS = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"] as const;