oh-my-codex 0.20.1 → 0.20.2

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 (654) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +10 -1
  4. package/dist/adapt/__tests__/hermes.test.js.map +1 -1
  5. package/dist/adapt/contracts.d.ts.map +1 -1
  6. package/dist/agents/native-config.js.map +1 -1
  7. package/dist/auth/__tests__/hotswap.test.d.ts +2 -0
  8. package/dist/auth/__tests__/hotswap.test.d.ts.map +1 -0
  9. package/dist/auth/__tests__/hotswap.test.js +183 -0
  10. package/dist/auth/__tests__/hotswap.test.js.map +1 -0
  11. package/dist/auth/hotswap.d.ts.map +1 -1
  12. package/dist/auth/hotswap.js +17 -4
  13. package/dist/auth/hotswap.js.map +1 -1
  14. package/dist/auth/paths.js.map +1 -1
  15. package/dist/auth/storage.js.map +1 -1
  16. package/dist/autopilot/__tests__/ralplan-gate.test.js +4 -4
  17. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  18. package/dist/autopilot/completion-gate.js.map +1 -1
  19. package/dist/autopilot/fsm.d.ts +1 -1
  20. package/dist/autopilot/fsm.d.ts.map +1 -1
  21. package/dist/autoresearch/goal.js.map +1 -1
  22. package/dist/autoresearch/runtime.js.map +1 -1
  23. package/dist/capabilities/lockfile.js.map +1 -1
  24. package/dist/catalog/reader.js.map +1 -1
  25. package/dist/catalog/skill-mirror.js.map +1 -1
  26. package/dist/cli/__tests__/adapt-help.test.js.map +1 -1
  27. package/dist/cli/__tests__/agents-init.test.js.map +1 -1
  28. package/dist/cli/__tests__/agents.test.js.map +1 -1
  29. package/dist/cli/__tests__/api.test.js.map +1 -1
  30. package/dist/cli/__tests__/ask.test.js.map +1 -1
  31. package/dist/cli/__tests__/auth.test.js.map +1 -1
  32. package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
  33. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  34. package/dist/cli/__tests__/capabilities.test.js.map +1 -1
  35. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  36. package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
  37. package/dist/cli/__tests__/doctor-invalid-config.test.js +47 -4
  38. package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
  39. package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
  40. package/dist/cli/__tests__/doctor-warning-copy.test.js +650 -15
  41. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  42. package/dist/cli/__tests__/exec.test.js.map +1 -1
  43. package/dist/cli/__tests__/imagegen-continuation.test.js.map +1 -1
  44. package/dist/cli/__tests__/index.test.js +216 -11
  45. package/dist/cli/__tests__/index.test.js.map +1 -1
  46. package/dist/cli/__tests__/launch-fallback.test.js +49 -3
  47. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  48. package/dist/cli/__tests__/mcp-parity.test.js +84 -12
  49. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  50. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts +2 -0
  51. package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts.map +1 -0
  52. package/dist/cli/__tests__/native-hook-claim-journal.test.js +128 -0
  53. package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -0
  54. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  55. package/dist/cli/__tests__/question.test.js.map +1 -1
  56. package/dist/cli/__tests__/ralph-deslop-contract.test.js.map +1 -1
  57. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +6 -0
  58. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  59. package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
  60. package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -1
  61. package/dist/cli/__tests__/ralph.test.js +8 -3
  62. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  63. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts +2 -0
  64. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts.map +1 -0
  65. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +187 -0
  66. package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -0
  67. package/dist/cli/__tests__/ralplan.test.d.ts +2 -0
  68. package/dist/cli/__tests__/ralplan.test.d.ts.map +1 -0
  69. package/dist/cli/__tests__/ralplan.test.js +129 -0
  70. package/dist/cli/__tests__/ralplan.test.js.map +1 -0
  71. package/dist/cli/__tests__/resume.test.js +6 -1
  72. package/dist/cli/__tests__/resume.test.js.map +1 -1
  73. package/dist/cli/__tests__/session-scoped-runtime.test.js +25 -0
  74. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  75. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  76. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  77. package/dist/cli/__tests__/setup-agents-overwrite.test.js +79 -0
  78. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  79. package/dist/cli/__tests__/setup-gh-star.test.js.map +1 -1
  80. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +77 -3
  81. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
  82. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts +2 -0
  83. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts.map +1 -0
  84. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js +285 -0
  85. package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js.map +1 -0
  86. package/dist/cli/__tests__/setup-install-mode.test.js +2261 -14
  87. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  88. package/dist/cli/__tests__/setup-preferences.test.d.ts +2 -0
  89. package/dist/cli/__tests__/setup-preferences.test.d.ts.map +1 -0
  90. package/dist/cli/__tests__/setup-preferences.test.js +79 -0
  91. package/dist/cli/__tests__/setup-preferences.test.js.map +1 -0
  92. package/dist/cli/__tests__/setup-refresh.test.js +33 -2
  93. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  94. package/dist/cli/__tests__/setup-scope.test.js +46 -3
  95. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  96. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  97. package/dist/cli/__tests__/state.test.js +37 -0
  98. package/dist/cli/__tests__/state.test.js.map +1 -1
  99. package/dist/cli/__tests__/team.test.js.map +1 -1
  100. package/dist/cli/__tests__/ultragoal.test.js +61 -1
  101. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  102. package/dist/cli/__tests__/uninstall.test.js +1771 -24
  103. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  104. package/dist/cli/__tests__/update.test.js +58 -0
  105. package/dist/cli/__tests__/update.test.js.map +1 -1
  106. package/dist/cli/__tests__/url.test.js.map +1 -1
  107. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
  108. package/dist/cli/adapt.js.map +1 -1
  109. package/dist/cli/agents-init.js.map +1 -1
  110. package/dist/cli/agents.js.map +1 -1
  111. package/dist/cli/api.js.map +1 -1
  112. package/dist/cli/ask.d.ts +1 -1
  113. package/dist/cli/ask.d.ts.map +1 -1
  114. package/dist/cli/ask.js.map +1 -1
  115. package/dist/cli/auth.js.map +1 -1
  116. package/dist/cli/autoresearch-guided.js.map +1 -1
  117. package/dist/cli/autoresearch-intake.js.map +1 -1
  118. package/dist/cli/capabilities.js.map +1 -1
  119. package/dist/cli/cleanup.js.map +1 -1
  120. package/dist/cli/codex-feature-probe.js.map +1 -1
  121. package/dist/cli/codex-home.js.map +1 -1
  122. package/dist/cli/doctor.d.ts +18 -0
  123. package/dist/cli/doctor.d.ts.map +1 -1
  124. package/dist/cli/doctor.js +594 -130
  125. package/dist/cli/doctor.js.map +1 -1
  126. package/dist/cli/explore.js.map +1 -1
  127. package/dist/cli/index.d.ts +11 -3
  128. package/dist/cli/index.d.ts.map +1 -1
  129. package/dist/cli/index.js +108 -20
  130. package/dist/cli/index.js.map +1 -1
  131. package/dist/cli/mcp-serve.js.map +1 -1
  132. package/dist/cli/mission.js.map +1 -1
  133. package/dist/cli/native-assets.js.map +1 -1
  134. package/dist/cli/native-hook-claim-journal.d.ts +18 -0
  135. package/dist/cli/native-hook-claim-journal.d.ts.map +1 -0
  136. package/dist/cli/native-hook-claim-journal.js +239 -0
  137. package/dist/cli/native-hook-claim-journal.js.map +1 -0
  138. package/dist/cli/omx.js.map +1 -1
  139. package/dist/cli/plugin-marketplace.js.map +1 -1
  140. package/dist/cli/project-runtime-codex-homes.js.map +1 -1
  141. package/dist/cli/question.js.map +1 -1
  142. package/dist/cli/ralph.d.ts.map +1 -1
  143. package/dist/cli/ralph.js +3 -2
  144. package/dist/cli/ralph.js.map +1 -1
  145. package/dist/cli/ralplan.d.ts +14 -0
  146. package/dist/cli/ralplan.d.ts.map +1 -0
  147. package/dist/cli/ralplan.js +203 -0
  148. package/dist/cli/ralplan.js.map +1 -0
  149. package/dist/cli/setup-preferences.d.ts +11 -0
  150. package/dist/cli/setup-preferences.d.ts.map +1 -1
  151. package/dist/cli/setup-preferences.js +32 -0
  152. package/dist/cli/setup-preferences.js.map +1 -1
  153. package/dist/cli/setup.d.ts +53 -0
  154. package/dist/cli/setup.d.ts.map +1 -1
  155. package/dist/cli/setup.js +1401 -398
  156. package/dist/cli/setup.js.map +1 -1
  157. package/dist/cli/sparkshell.js.map +1 -1
  158. package/dist/cli/star-prompt.js.map +1 -1
  159. package/dist/cli/state.d.ts.map +1 -1
  160. package/dist/cli/state.js +2 -1
  161. package/dist/cli/state.js.map +1 -1
  162. package/dist/cli/team.js.map +1 -1
  163. package/dist/cli/ultragoal.d.ts +5 -1
  164. package/dist/cli/ultragoal.d.ts.map +1 -1
  165. package/dist/cli/ultragoal.js +34 -2
  166. package/dist/cli/ultragoal.js.map +1 -1
  167. package/dist/cli/uninstall.d.ts +8 -0
  168. package/dist/cli/uninstall.d.ts.map +1 -1
  169. package/dist/cli/uninstall.js +944 -95
  170. package/dist/cli/uninstall.js.map +1 -1
  171. package/dist/cli/update.d.ts.map +1 -1
  172. package/dist/cli/update.js +8 -1
  173. package/dist/cli/update.js.map +1 -1
  174. package/dist/cli/url.js.map +1 -1
  175. package/dist/compat/__tests__/doctor-contract.test.js +5 -1
  176. package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
  177. package/dist/compat/__tests__/rust-runtime-compat.test.js.map +1 -1
  178. package/dist/config/__tests__/codex-hooks.test.js +1287 -11
  179. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  180. package/dist/config/__tests__/generator-idempotent.test.js +850 -32
  181. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  182. package/dist/config/__tests__/generator-notify.test.js +6 -1
  183. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  184. package/dist/config/codex-feature-flags.d.ts.map +1 -1
  185. package/dist/config/codex-feature-flags.js.map +1 -1
  186. package/dist/config/codex-hooks.d.ts +124 -1
  187. package/dist/config/codex-hooks.d.ts.map +1 -1
  188. package/dist/config/codex-hooks.js +2142 -265
  189. package/dist/config/codex-hooks.js.map +1 -1
  190. package/dist/config/commit-lore-guard.js.map +1 -1
  191. package/dist/config/generator.d.ts +21 -16
  192. package/dist/config/generator.d.ts.map +1 -1
  193. package/dist/config/generator.js +991 -137
  194. package/dist/config/generator.js.map +1 -1
  195. package/dist/config/mcp-registry.js.map +1 -1
  196. package/dist/config/models.d.ts +2 -2
  197. package/dist/config/models.d.ts.map +1 -1
  198. package/dist/config/models.js.map +1 -1
  199. package/dist/config/omx-first-party-mcp.js.map +1 -1
  200. package/dist/config/team-mode.d.ts.map +1 -1
  201. package/dist/document-refresh/__tests__/enforcer.test.js.map +1 -1
  202. package/dist/document-refresh/enforcer.js.map +1 -1
  203. package/dist/exec/followup.js.map +1 -1
  204. package/dist/goal-workflows/artifacts.js.map +1 -1
  205. package/dist/hooks/__tests__/agents-overlay.test.js +7 -4
  206. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  207. package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
  208. package/dist/hooks/__tests__/analyze-skill-contract.test.js.map +1 -1
  209. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  210. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +9 -3
  211. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  212. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
  213. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  214. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  215. package/dist/hooks/__tests__/design-skill.test.js.map +1 -1
  216. package/dist/hooks/__tests__/keyword-detector.test.js +1372 -2
  217. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  218. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +14 -0
  219. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  220. package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
  221. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +669 -1
  222. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  223. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +2 -0
  224. package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
  225. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +142 -1
  226. package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
  227. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  228. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +48 -2
  229. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
  230. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +61 -44
  231. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  232. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  233. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +3 -2
  234. package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -1
  235. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +243 -4
  236. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  237. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  238. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +8 -1
  239. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  240. package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
  241. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  242. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  243. package/dist/hooks/__tests__/notify-hook-visual-verdict.test.js.map +1 -1
  244. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  245. package/dist/hooks/__tests__/openclaw-setup-contract.test.js.map +1 -1
  246. package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
  247. package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -1
  248. package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
  249. package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
  250. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts +2 -0
  251. package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts.map +1 -0
  252. package/dist/hooks/__tests__/prompt-session-provenance.test.js +82 -0
  253. package/dist/hooks/__tests__/prompt-session-provenance.test.js.map +1 -0
  254. package/dist/hooks/__tests__/session.test.js +532 -11
  255. package/dist/hooks/__tests__/session.test.js.map +1 -1
  256. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -1
  257. package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js.map +1 -1
  258. package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -1
  259. package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -1
  260. package/dist/hooks/agents-overlay.js +5 -5
  261. package/dist/hooks/agents-overlay.js.map +1 -1
  262. package/dist/hooks/code-simplifier/index.js.map +1 -1
  263. package/dist/hooks/explore-routing.js.map +1 -1
  264. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  265. package/dist/hooks/extensibility/events.js.map +1 -1
  266. package/dist/hooks/extensibility/loader.js.map +1 -1
  267. package/dist/hooks/extensibility/plugin-runner-stdin.js.map +1 -1
  268. package/dist/hooks/extensibility/sdk/logging.js.map +1 -1
  269. package/dist/hooks/keyword-detector.d.ts +33 -7
  270. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  271. package/dist/hooks/keyword-detector.js +2228 -119
  272. package/dist/hooks/keyword-detector.js.map +1 -1
  273. package/dist/hooks/keyword-registry.d.ts +12 -0
  274. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  275. package/dist/hooks/keyword-registry.js +41 -0
  276. package/dist/hooks/keyword-registry.js.map +1 -1
  277. package/dist/hooks/prompt-session-provenance.d.ts +87 -0
  278. package/dist/hooks/prompt-session-provenance.d.ts.map +1 -0
  279. package/dist/hooks/prompt-session-provenance.js +180 -0
  280. package/dist/hooks/prompt-session-provenance.js.map +1 -0
  281. package/dist/hooks/session.d.ts +139 -34
  282. package/dist/hooks/session.d.ts.map +1 -1
  283. package/dist/hooks/session.js +1239 -317
  284. package/dist/hooks/session.js.map +1 -1
  285. package/dist/hooks/task-size-detector.js.map +1 -1
  286. package/dist/hud/__tests__/state.test.js.map +1 -1
  287. package/dist/hud/authority.js.map +1 -1
  288. package/dist/hud/constants.js.map +1 -1
  289. package/dist/hud/index.js.map +1 -1
  290. package/dist/hud/reconcile.js.map +1 -1
  291. package/dist/hud/render.js.map +1 -1
  292. package/dist/hud/state.js.map +1 -1
  293. package/dist/hud/tmux.js.map +1 -1
  294. package/dist/leader/__tests__/contract.test.js +103 -1
  295. package/dist/leader/__tests__/contract.test.js.map +1 -1
  296. package/dist/leader/contract.d.ts +26 -5
  297. package/dist/leader/contract.d.ts.map +1 -1
  298. package/dist/leader/contract.js +109 -3
  299. package/dist/leader/contract.js.map +1 -1
  300. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  301. package/dist/mcp/__tests__/server-lifecycle.test.js +15 -2
  302. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  303. package/dist/mcp/__tests__/state-paths.test.js +98 -1
  304. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  305. package/dist/mcp/bootstrap.js.map +1 -1
  306. package/dist/mcp/code-intel-server.d.ts +58 -58
  307. package/dist/mcp/code-intel-server.d.ts.map +1 -1
  308. package/dist/mcp/code-intel-server.js.map +1 -1
  309. package/dist/mcp/hermes-bridge.js.map +1 -1
  310. package/dist/mcp/hermes-server.d.ts +70 -70
  311. package/dist/mcp/hermes-server.d.ts.map +1 -1
  312. package/dist/mcp/lifecycle-telemetry.js.map +1 -1
  313. package/dist/mcp/memory-server.d.ts +34 -34
  314. package/dist/mcp/memory-server.d.ts.map +1 -1
  315. package/dist/mcp/memory-server.js.map +1 -1
  316. package/dist/mcp/state-paths.d.ts +14 -0
  317. package/dist/mcp/state-paths.d.ts.map +1 -1
  318. package/dist/mcp/state-paths.js +96 -20
  319. package/dist/mcp/state-paths.js.map +1 -1
  320. package/dist/mcp/state-server.d.ts +47 -47
  321. package/dist/mcp/state-server.d.ts.map +1 -1
  322. package/dist/mcp/trace-server.d.ts +4 -4
  323. package/dist/mcp/trace-server.d.ts.map +1 -1
  324. package/dist/mcp/trace-server.js.map +1 -1
  325. package/dist/mcp/wiki-server.d.ts +23 -23
  326. package/dist/mcp/wiki-server.d.ts.map +1 -1
  327. package/dist/mcp/wiki-server.js.map +1 -1
  328. package/dist/modes/__tests__/base-session-scope.test.js +53 -1
  329. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  330. package/dist/modes/base.d.ts.map +1 -1
  331. package/dist/modes/base.js +5 -5
  332. package/dist/modes/base.js.map +1 -1
  333. package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -1
  334. package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
  335. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +177 -85
  336. package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -1
  337. package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
  338. package/dist/notifications/__tests__/template-engine.test.js.map +1 -1
  339. package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
  340. package/dist/notifications/http-client.js.map +1 -1
  341. package/dist/notifications/index.d.ts +4 -1
  342. package/dist/notifications/index.d.ts.map +1 -1
  343. package/dist/notifications/index.js +3 -9
  344. package/dist/notifications/index.js.map +1 -1
  345. package/dist/notifications/lifecycle-dedupe.js.map +1 -1
  346. package/dist/notifications/reply-listener.d.ts.map +1 -1
  347. package/dist/notifications/reply-listener.js.map +1 -1
  348. package/dist/notifications/session-registry.js.map +1 -1
  349. package/dist/notifications/session-status.js.map +1 -1
  350. package/dist/notifications/temp-contract.js.map +1 -1
  351. package/dist/notifications/tmux-detector.js.map +1 -1
  352. package/dist/notifications/tmux.js.map +1 -1
  353. package/dist/openclaw/config.js.map +1 -1
  354. package/dist/openclaw/dispatcher.js.map +1 -1
  355. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  356. package/dist/pipeline/__tests__/stages.test.js +8 -5
  357. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  358. package/dist/pipeline/stages/code-review.js.map +1 -1
  359. package/dist/pipeline/stages/ralph-verify.js.map +1 -1
  360. package/dist/pipeline/stages/ralplan.js.map +1 -1
  361. package/dist/pipeline/stages/team-exec.js.map +1 -1
  362. package/dist/pipeline/stages/ultragoal.js.map +1 -1
  363. package/dist/pipeline/stages/ultraqa.js.map +1 -1
  364. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -1
  365. package/dist/planning/artifact-names.js.map +1 -1
  366. package/dist/planning/artifacts.js.map +1 -1
  367. package/dist/question/__tests__/state.test.js.map +1 -1
  368. package/dist/question/__tests__/ui.test.js.map +1 -1
  369. package/dist/question/autopilot-wait.js.map +1 -1
  370. package/dist/question/client.d.ts.map +1 -1
  371. package/dist/question/client.js.map +1 -1
  372. package/dist/question/deep-interview.js.map +1 -1
  373. package/dist/question/events.js.map +1 -1
  374. package/dist/question/renderer.js.map +1 -1
  375. package/dist/question/state.d.ts.map +1 -1
  376. package/dist/question/state.js.map +1 -1
  377. package/dist/question/ui.js.map +1 -1
  378. package/dist/ralph/contract.d.ts +1 -1
  379. package/dist/ralph/contract.d.ts.map +1 -1
  380. package/dist/ralplan/__tests__/consensus-gate.test.js +403 -8
  381. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  382. package/dist/ralplan/__tests__/runtime.test.js +112 -14
  383. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  384. package/dist/ralplan/consensus-gate.d.ts +3 -3
  385. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  386. package/dist/ralplan/consensus-gate.js +155 -60
  387. package/dist/ralplan/consensus-gate.js.map +1 -1
  388. package/dist/ralplan/runtime.d.ts +2 -2
  389. package/dist/ralplan/runtime.d.ts.map +1 -1
  390. package/dist/ralplan/runtime.js +1 -2
  391. package/dist/ralplan/runtime.js.map +1 -1
  392. package/dist/runtime/bridge.d.ts.map +1 -1
  393. package/dist/runtime/bridge.js.map +1 -1
  394. package/dist/runtime/process-tree.js.map +1 -1
  395. package/dist/runtime/run-loop.js.map +1 -1
  396. package/dist/runtime/run-outcome.d.ts +3 -3
  397. package/dist/runtime/run-outcome.d.ts.map +1 -1
  398. package/dist/runtime/run-outcome.js.map +1 -1
  399. package/dist/runtime/run-state.js.map +1 -1
  400. package/dist/scripts/__tests__/codex-native-hook.test.js +4121 -592
  401. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  402. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts +2 -0
  403. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts.map +1 -0
  404. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js +634 -0
  405. package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js.map +1 -0
  406. package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
  407. package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
  408. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  409. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts +2 -0
  410. package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts.map +1 -0
  411. package/dist/scripts/__tests__/notify-fallback-delivery.test.js +174 -0
  412. package/dist/scripts/__tests__/notify-fallback-delivery.test.js.map +1 -0
  413. package/dist/scripts/__tests__/notify-state-io.test.js +57 -1
  414. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  415. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts +2 -0
  416. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts.map +1 -0
  417. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +205 -0
  418. package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -0
  419. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts +2 -0
  420. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts.map +1 -0
  421. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +175 -0
  422. package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -0
  423. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  424. package/dist/scripts/__tests__/smoke-packed-install.test.js +848 -4
  425. package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
  426. package/dist/scripts/__tests__/test-reply-listener-live.test.js.map +1 -1
  427. package/dist/scripts/build-api.js.map +1 -1
  428. package/dist/scripts/build-explore-harness.js.map +1 -1
  429. package/dist/scripts/build-sparkshell.js.map +1 -1
  430. package/dist/scripts/cleanup-explore-harness.js.map +1 -1
  431. package/dist/scripts/codex-execution-surface.js.map +1 -1
  432. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  433. package/dist/scripts/codex-native-hook.js +689 -156
  434. package/dist/scripts/codex-native-hook.js.map +1 -1
  435. package/dist/scripts/generate-native-release-manifest.js.map +1 -1
  436. package/dist/scripts/generate-release-body.js.map +1 -1
  437. package/dist/scripts/hook-payload-guard.d.ts.map +1 -1
  438. package/dist/scripts/hook-payload-guard.js.map +1 -1
  439. package/dist/scripts/notify-fallback-delivery.d.ts +48 -0
  440. package/dist/scripts/notify-fallback-delivery.d.ts.map +1 -0
  441. package/dist/scripts/notify-fallback-delivery.js +419 -0
  442. package/dist/scripts/notify-fallback-delivery.js.map +1 -0
  443. package/dist/scripts/notify-fallback-watcher.js +136 -30
  444. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  445. package/dist/scripts/notify-hook/auto-nudge.d.ts +18 -9
  446. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  447. package/dist/scripts/notify-hook/auto-nudge.js +39 -13
  448. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  449. package/dist/scripts/notify-hook/managed-tmux.d.ts +23 -0
  450. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  451. package/dist/scripts/notify-hook/managed-tmux.js +122 -12
  452. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  453. package/dist/scripts/notify-hook/orchestration-intent.d.ts +7 -7
  454. package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -1
  455. package/dist/scripts/notify-hook/ralph-session-resume.d.ts +5 -2
  456. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  457. package/dist/scripts/notify-hook/ralph-session-resume.js +33 -37
  458. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  459. package/dist/scripts/notify-hook/state-io.d.ts +17 -0
  460. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  461. package/dist/scripts/notify-hook/state-io.js +73 -11
  462. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  463. package/dist/scripts/notify-hook/team-dispatch.d.ts +1 -1
  464. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  465. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +2 -2
  466. package/dist/scripts/notify-hook/team-tmux-guard.d.ts +4 -4
  467. package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -1
  468. package/dist/scripts/notify-hook/team-worker-stop.d.ts +1 -1
  469. package/dist/scripts/notify-hook/team-worker.d.ts +2 -2
  470. package/dist/scripts/notify-hook/tmux-injection.d.ts +8 -1
  471. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  472. package/dist/scripts/notify-hook/tmux-injection.js +25 -16
  473. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  474. package/dist/scripts/notify-hook/visual-verdict.d.ts +1 -1
  475. package/dist/scripts/notify-hook/visual-verdict.d.ts.map +1 -1
  476. package/dist/scripts/notify-hook/visual-verdict.js +15 -13
  477. package/dist/scripts/notify-hook/visual-verdict.js.map +1 -1
  478. package/dist/scripts/notify-hook.d.ts +24 -1
  479. package/dist/scripts/notify-hook.d.ts.map +1 -1
  480. package/dist/scripts/notify-hook.js +266 -90
  481. package/dist/scripts/notify-hook.js.map +1 -1
  482. package/dist/scripts/postinstall.js.map +1 -1
  483. package/dist/scripts/prompt-inventory.js.map +1 -1
  484. package/dist/scripts/smoke-packed-install.d.ts +879 -2
  485. package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
  486. package/dist/scripts/smoke-packed-install.js +1528 -7
  487. package/dist/scripts/smoke-packed-install.js.map +1 -1
  488. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  489. package/dist/scripts/test-reply-listener-live.js.map +1 -1
  490. package/dist/scripts/test-sparkshell.js.map +1 -1
  491. package/dist/scripts/verify-native-agents.js.map +1 -1
  492. package/dist/session-history/friction.js.map +1 -1
  493. package/dist/session-history/search.js.map +1 -1
  494. package/dist/sidecar/__tests__/boundary.test.js.map +1 -1
  495. package/dist/sidecar/collector.js.map +1 -1
  496. package/dist/sidecar/index.js.map +1 -1
  497. package/dist/sidecar/render.js.map +1 -1
  498. package/dist/sidecar/tmux.js.map +1 -1
  499. package/dist/state/__tests__/operations.test.js +64 -15
  500. package/dist/state/__tests__/operations.test.js.map +1 -1
  501. package/dist/state/__tests__/skill-active.test.js +19 -1
  502. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  503. package/dist/state/__tests__/workflow-transition.test.js +61 -0
  504. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  505. package/dist/state/mode-state-context.js.map +1 -1
  506. package/dist/state/operations.d.ts +1 -1
  507. package/dist/state/operations.d.ts.map +1 -1
  508. package/dist/state/operations.js +49 -55
  509. package/dist/state/operations.js.map +1 -1
  510. package/dist/state/paths.d.ts +1 -1
  511. package/dist/state/paths.d.ts.map +1 -1
  512. package/dist/state/paths.js +1 -1
  513. package/dist/state/paths.js.map +1 -1
  514. package/dist/state/skill-active.d.ts +13 -1
  515. package/dist/state/skill-active.d.ts.map +1 -1
  516. package/dist/state/skill-active.js +43 -4
  517. package/dist/state/skill-active.js.map +1 -1
  518. package/dist/state/terminal-normalization.js.map +1 -1
  519. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  520. package/dist/state/workflow-transition-reconcile.js +20 -4
  521. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  522. package/dist/state/workflow-transition.d.ts +1 -1
  523. package/dist/state/workflow-transition.d.ts.map +1 -1
  524. package/dist/state/workflow-transition.js.map +1 -1
  525. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +2 -0
  526. package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +1 -0
  527. package/dist/subagents/__tests__/adapted-role-binding.test.js +988 -0
  528. package/dist/subagents/__tests__/adapted-role-binding.test.js.map +1 -0
  529. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts +2 -0
  530. package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts.map +1 -0
  531. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +273 -0
  532. package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -0
  533. package/dist/subagents/__tests__/tracker.test.js +1550 -10
  534. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  535. package/dist/subagents/adapted-role-binding.d.ts +17 -0
  536. package/dist/subagents/adapted-role-binding.d.ts.map +1 -0
  537. package/dist/subagents/adapted-role-binding.js +75 -0
  538. package/dist/subagents/adapted-role-binding.js.map +1 -0
  539. package/dist/subagents/role-routing-marker.d.ts +10 -0
  540. package/dist/subagents/role-routing-marker.d.ts.map +1 -0
  541. package/dist/subagents/role-routing-marker.js +136 -0
  542. package/dist/subagents/role-routing-marker.js.map +1 -0
  543. package/dist/subagents/tracker.d.ts +172 -0
  544. package/dist/subagents/tracker.d.ts.map +1 -1
  545. package/dist/subagents/tracker.js +1077 -61
  546. package/dist/subagents/tracker.js.map +1 -1
  547. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  548. package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
  549. package/dist/team/__tests__/model-contract.test.js +231 -1
  550. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  551. package/dist/team/__tests__/role-router.test.js.map +1 -1
  552. package/dist/team/__tests__/runtime.test.js +401 -1
  553. package/dist/team/__tests__/runtime.test.js.map +1 -1
  554. package/dist/team/__tests__/scaling.test.js +181 -0
  555. package/dist/team/__tests__/scaling.test.js.map +1 -1
  556. package/dist/team/__tests__/state-root.test.js.map +1 -1
  557. package/dist/team/__tests__/tmux-session.test.js +434 -4
  558. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  559. package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
  560. package/dist/team/api-interop.d.ts +2 -2
  561. package/dist/team/api-interop.d.ts.map +1 -1
  562. package/dist/team/api-interop.js.map +1 -1
  563. package/dist/team/commit-hygiene.js.map +1 -1
  564. package/dist/team/contracts.d.ts +5 -5
  565. package/dist/team/contracts.d.ts.map +1 -1
  566. package/dist/team/delivery-log.js.map +1 -1
  567. package/dist/team/followup-planner.js.map +1 -1
  568. package/dist/team/goal-workflow.js.map +1 -1
  569. package/dist/team/idle-nudge.d.ts.map +1 -1
  570. package/dist/team/model-contract.d.ts +20 -1
  571. package/dist/team/model-contract.d.ts.map +1 -1
  572. package/dist/team/model-contract.js +267 -30
  573. package/dist/team/model-contract.js.map +1 -1
  574. package/dist/team/orchestrator.js.map +1 -1
  575. package/dist/team/pane-status.js.map +1 -1
  576. package/dist/team/phase-controller.js.map +1 -1
  577. package/dist/team/reminder-intents.d.ts +1 -1
  578. package/dist/team/reminder-intents.d.ts.map +1 -1
  579. package/dist/team/reminder-intents.js.map +1 -1
  580. package/dist/team/runtime-cli.js.map +1 -1
  581. package/dist/team/runtime.d.ts.map +1 -1
  582. package/dist/team/runtime.js +83 -52
  583. package/dist/team/runtime.js.map +1 -1
  584. package/dist/team/scaling.d.ts +9 -7
  585. package/dist/team/scaling.d.ts.map +1 -1
  586. package/dist/team/scaling.js +69 -28
  587. package/dist/team/scaling.js.map +1 -1
  588. package/dist/team/state/dispatch-lock.js.map +1 -1
  589. package/dist/team/state/dispatch.js.map +1 -1
  590. package/dist/team/state/events.js.map +1 -1
  591. package/dist/team/state/tasks.js.map +1 -1
  592. package/dist/team/state-root.js.map +1 -1
  593. package/dist/team/state.js.map +1 -1
  594. package/dist/team/team-identity.d.ts.map +1 -1
  595. package/dist/team/team-identity.js.map +1 -1
  596. package/dist/team/tmux-session.d.ts +2 -0
  597. package/dist/team/tmux-session.d.ts.map +1 -1
  598. package/dist/team/tmux-session.js +90 -42
  599. package/dist/team/tmux-session.js.map +1 -1
  600. package/dist/team/worker-bootstrap.js.map +1 -1
  601. package/dist/team/worktree.js.map +1 -1
  602. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  603. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  604. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  605. package/dist/ultragoal/artifacts.js +4 -1
  606. package/dist/ultragoal/artifacts.js.map +1 -1
  607. package/dist/url-reader/index.js.map +1 -1
  608. package/dist/utils/agents-model-table.js.map +1 -1
  609. package/dist/utils/package.js.map +1 -1
  610. package/dist/utils/paths.js.map +1 -1
  611. package/dist/utils/platform-command.js.map +1 -1
  612. package/dist/utils/repo-deps.js.map +1 -1
  613. package/dist/utils/version.js.map +1 -1
  614. package/dist/verification/__tests__/ci-rust-gates.test.js +1 -1
  615. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  616. package/dist/verification/verifier.js.map +1 -1
  617. package/dist/visual/constants.d.ts +1 -1
  618. package/dist/visual/constants.d.ts.map +1 -1
  619. package/dist/vscode/__tests__/index.test.js.map +1 -1
  620. package/dist/vscode/index.js.map +1 -1
  621. package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -1
  622. package/dist/wiki/__tests__/lint.test.js.map +1 -1
  623. package/dist/wiki/__tests__/query.test.js.map +1 -1
  624. package/dist/wiki/__tests__/storage.test.js.map +1 -1
  625. package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -1
  626. package/dist/wiki/lint.js.map +1 -1
  627. package/dist/wiki/query.js.map +1 -1
  628. package/dist/wiki/storage.js.map +1 -1
  629. package/package.json +2 -2
  630. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  631. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +10 -5
  632. package/plugins/oh-my-codex/skills/ralph/SKILL.md +4 -2
  633. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +5 -2
  634. package/skills/omx-setup/SKILL.md +10 -5
  635. package/skills/ralph/SKILL.md +4 -2
  636. package/skills/ralplan/SKILL.md +5 -2
  637. package/src/scripts/__tests__/codex-native-hook.test.ts +17522 -10630
  638. package/src/scripts/__tests__/fixtures/issue-3138-installed-smoke.ts +734 -0
  639. package/src/scripts/__tests__/notify-fallback-delivery.test.ts +177 -0
  640. package/src/scripts/__tests__/notify-state-io.test.ts +73 -0
  641. package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +215 -0
  642. package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +193 -0
  643. package/src/scripts/__tests__/smoke-packed-install.test.ts +991 -4
  644. package/src/scripts/codex-native-hook.ts +779 -159
  645. package/src/scripts/notify-fallback-delivery.ts +416 -0
  646. package/src/scripts/notify-fallback-watcher.ts +132 -40
  647. package/src/scripts/notify-hook/auto-nudge.ts +43 -12
  648. package/src/scripts/notify-hook/managed-tmux.ts +149 -12
  649. package/src/scripts/notify-hook/ralph-session-resume.ts +37 -36
  650. package/src/scripts/notify-hook/state-io.ts +100 -13
  651. package/src/scripts/notify-hook/tmux-injection.ts +38 -21
  652. package/src/scripts/notify-hook/visual-verdict.ts +22 -12
  653. package/src/scripts/notify-hook.ts +335 -90
  654. package/src/scripts/smoke-packed-install.ts +1829 -6
@@ -10,14 +10,14 @@
10
10
  * rather than the full keyword/state table.
11
11
  */
12
12
  import { constants as fsConstants } from 'node:fs';
13
- import { access, lstat, mkdir, readFile, realpath, writeFile } from 'node:fs/promises';
13
+ import { access, lstat, mkdir, readFile, readdir, realpath, writeFile } from 'node:fs/promises';
14
14
  import { withModeRuntimeContext } from '../state/mode-state-context.js';
15
15
  import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
16
16
  import { classifyTaskSize, isHeavyMode } from './task-size-detector.js';
17
17
  import { isApprovedExecutionFollowupShortcut } from '../team/followup-planner.js';
18
18
  import { isPlanningComplete, readPlanningArtifacts } from '../planning/artifacts.js';
19
19
  import { hasDurableRalplanConsensusEvidenceForCwd } from '../ralplan/consensus-gate.js';
20
- import { KEYWORD_TRIGGER_DEFINITIONS, compareKeywordMatches } from './keyword-registry.js';
20
+ import { getExplicitSkillDefinition, KEYWORD_TRIGGER_DEFINITIONS, compareKeywordMatches } from './keyword-registry.js';
21
21
  import { readTeamModeConfig } from '../config/team-mode.js';
22
22
  import { SKILL_ACTIVE_STATE_FILE, listActiveSkills, writeSkillActiveStateCopiesForStateDir, } from '../state/skill-active.js';
23
23
  import { buildWorkflowTransitionError, evaluateWorkflowTransition, isTrackedWorkflowMode, } from '../state/workflow-transition.js';
@@ -30,6 +30,17 @@ import { deriveAutopilotChildPhase, AUTOPILOT_CHILD_PHASES } from '../autopilot/
30
30
  import { canAdvanceAutopilotDeepInterviewToRalplan } from '../autopilot/deep-interview-gate.js';
31
31
  import { canAdvanceAutopilotRalplanToUltragoal } from '../autopilot/ralplan-gate.js';
32
32
  import { validateAutopilotCompletionTransition } from '../autopilot/completion-gate.js';
33
+ import { preflightSelectedTargetOwner, extractSelectedTargetOwnerEvidence, } from './prompt-session-provenance.js';
34
+ /** Stable diagnostic precedence for explicit candidates. */
35
+ export const KEYWORD_INERT_DIAGNOSTIC_ORDER = Object.freeze([
36
+ 'fenced-code',
37
+ 'indented-code',
38
+ 'blockquote',
39
+ 'inline-code',
40
+ 'quote',
41
+ 'escaped',
42
+ 'not-leading-region',
43
+ ]);
33
44
  const ACTIVE_SKILL_CONTINUATION_PATTERNS = [
34
45
  /^[\\/]?\s*keep going(?:\s+now)?[.!]?\s*$/i,
35
46
  /^[\\/]?\s*continue(?:\s+now)?[.!]?\s*$/i,
@@ -350,6 +361,7 @@ export async function persistDeepInterviewModeState(stateDir, nextSkill, nowIso,
350
361
  started_at: previousModeState?.active ? previousModeState.started_at || nowIso : nowIso,
351
362
  updated_at: nowIso,
352
363
  session_id: input.sessionId ?? previousModeState?.session_id,
364
+ owner_codex_session_id: nextSkill.owner_codex_session_id ?? previousModeState?.owner_codex_session_id,
353
365
  thread_id: input.threadId ?? previousModeState?.thread_id,
354
366
  turn_id: input.turnId ?? previousModeState?.turn_id,
355
367
  ...configStateFields,
@@ -376,6 +388,7 @@ export async function persistDeepInterviewModeState(stateDir, nextSkill, nowIso,
376
388
  updated_at: nowIso,
377
389
  completed_at: nowIso,
378
390
  session_id: input.sessionId ?? previousModeState?.session_id ?? previousSkill?.session_id,
391
+ owner_codex_session_id: nextSkill?.owner_codex_session_id ?? previousModeState?.owner_codex_session_id ?? previousSkill?.owner_codex_session_id,
379
392
  thread_id: input.threadId ?? previousModeState?.thread_id ?? previousSkill?.thread_id,
380
393
  turn_id: input.turnId ?? previousModeState?.turn_id ?? previousSkill?.turn_id,
381
394
  ...(releasedInputLock ? { input_lock: releasedInputLock } : {}),
@@ -420,7 +433,7 @@ async function persistStatefulSkillSeedState(stateDir, nextSkill, nowIso, previo
420
433
  const config = STATEFUL_SKILL_SEED_CONFIG[nextSkill.skill];
421
434
  if (!config)
422
435
  return nextSkill;
423
- const { absolutePath, relativePath } = resolveSeedStateFilePath(stateDir, config.mode, nextSkill.session_id, config.scope);
436
+ const { absolutePath, relativePath } = resolveSeedStateFilePath(stateDir, config.mode, nextSkill.session_id, options.forceSessionScope ? 'session' : config.scope);
424
437
  const existingModeStateResult = await readJsonStateWithStatus(absolutePath);
425
438
  const existingModeState = existingModeStateResult.state;
426
439
  const sameActiveSkill = previousSkill?.skill === nextSkill.skill && previousSkill.active;
@@ -450,6 +463,7 @@ async function persistStatefulSkillSeedState(stateDir, nextSkill, nowIso, previo
450
463
  started_at: startedAt,
451
464
  updated_at: nowIso,
452
465
  session_id: nextSkill.session_id || safeString(existingModeState?.session_id).trim() || undefined,
466
+ owner_codex_session_id: nextSkill.owner_codex_session_id || safeString(existingModeState?.owner_codex_session_id).trim() || undefined,
453
467
  thread_id: nextSkill.thread_id || safeString(existingModeState?.thread_id).trim() || undefined,
454
468
  turn_id: nextSkill.turn_id || safeString(existingModeState?.turn_id).trim() || undefined,
455
469
  }, { nowIso });
@@ -569,6 +583,7 @@ function keywordToPattern(keyword) {
569
583
  return new RegExp(`${prefix}${escaped}${suffix}`, 'i');
570
584
  }
571
585
  const KEYWORD_MAP = KEYWORD_TRIGGER_DEFINITIONS.map((entry) => ({
586
+ keyword: entry.keyword,
572
587
  pattern: keywordToPattern(entry.keyword),
573
588
  skill: entry.skill,
574
589
  priority: entry.priority,
@@ -653,8 +668,215 @@ const KEYWORD_INTENT_PATTERNS = {
653
668
  /\bautopilot\s+(?:mode|workflow|skill|loop)\b/i,
654
669
  ],
655
670
  };
656
- function hasExplicitPromptsInvocation(text) {
657
- return /(?:^|\s)\/prompts:[\w.-]+(?=[\s.,!?;:]|$)/i.test(text);
671
+ const EXPLICIT_TOKEN_START = /\$(?:(?:[Oo][Hh]-[Mm][Yy]-[Cc][Oo][Dd][Ee][Xx]):)?([A-Za-z][A-Za-z0-9_-]*)/gyu;
672
+ const TOKEN_CONTINUATION = /[\p{L}\p{N}\p{M}\p{Pc}\p{Pd}]/u;
673
+ const SAFE_TOKEN_WHITESPACE = /[\p{Zs}\t\n\r\f\v\u2028\u2029]/u;
674
+ const EXPLICIT_TOKEN_BOUNDARY_PUNCTUATION = /[,;؛!?:؟)\]}"'”’»›」』—–…。、،]/u;
675
+ const DIRECTIVE_PUNCTUATION = /[,;؛:!?؟?、،]/u;
676
+ const CLAUSE_BOUNDARY_PUNCTUATION = /[,;؛.!?؟?。!?:،、\r\n\u2013\u2014\u2028\u2029]/u;
677
+ const LOGICAL_SENTENCE_BOUNDARY_PUNCTUATION = /[;؛.!?؟?。!?]/u;
678
+ function asciiCaseWordPattern(word) {
679
+ return [...word].map((character) => `[${character.toUpperCase()}${character.toLowerCase()}]`).join('');
680
+ }
681
+ const ASCII_DIRECTIVE_VERB = `(?:${['use', 'run', 'start', 'enable', 'launch', 'invoke', 'activate', 'resume', 'continue'].map(asciiCaseWordPattern).join('|')})`;
682
+ const ASCII_PLEASE = asciiCaseWordPattern('please');
683
+ const ASCII_INSTEAD = asciiCaseWordPattern('instead');
684
+ const ASCII_AND = asciiCaseWordPattern('and');
685
+ const ASCII_BUT = asciiCaseWordPattern('but');
686
+ const ASCII_THEN = asciiCaseWordPattern('then');
687
+ const ASCII_NOW = asciiCaseWordPattern('now');
688
+ const ASCII_THE = asciiCaseWordPattern('the');
689
+ const ASCII_WITH = asciiCaseWordPattern('with');
690
+ const ASCII_JUMP = asciiCaseWordPattern('jump');
691
+ const ASCII_STRAIGHT = asciiCaseWordPattern('straight');
692
+ const ASCII_TO = asciiCaseWordPattern('to');
693
+ const ASCII_ADVANCE = asciiCaseWordPattern('advance');
694
+ const ASCII_DIRECTIVE_COMMAND = `(?:${asciiCaseWordPattern('continue')}\\s+${ASCII_WITH}|${ASCII_JUMP}\\s+${ASCII_STRAIGHT}\\s+${ASCII_TO}|${ASCII_ADVANCE}\\s+${ASCII_TO}|${ASCII_DIRECTIVE_VERB})`;
695
+ const DIRECTIVE_COMMAND_PREFIX_SOURCE = `(?:(?:${ASCII_PLEASE}\\s+)?(?:(?:${ASCII_INSTEAD}|${ASCII_THEN}|${ASCII_NOW})\\s+)?${ASCII_DIRECTIVE_COMMAND}\\s+|(?:${ASCII_PLEASE}\\s+)?(?:${ASCII_INSTEAD}|${ASCII_THEN}|${ASCII_NOW}|${ASCII_PLEASE})\\s+|(?:${ASCII_AND}|${ASCII_BUT})\\s+(?:${ASCII_PLEASE}\\s+)?(?:${ASCII_INSTEAD}\\s+)?${ASCII_DIRECTIVE_COMMAND}\\s+)(?:${ASCII_THE}\\s+)?`;
696
+ const DIRECTIVE_COMMAND_PREFIX_AT = new RegExp(DIRECTIVE_COMMAND_PREFIX_SOURCE, 'yu');
697
+ const EXCLUSION_PREFIX = `(?:${['ignore', 'discard', 'skip', 'omit', 'forget', 'disregard', 'except'].map(asciiCaseWordPattern).join('|')}|${asciiCaseWordPattern('exclude')}(?:${asciiCaseWordPattern('ing')})?)`;
698
+ const ASCII_NEGATIVE_PREFIX = `(?:${asciiCaseWordPattern('do')}\\s+${asciiCaseWordPattern('not')}|${asciiCaseWordPattern('don')}'${asciiCaseWordPattern('t')}|${asciiCaseWordPattern('without')}|${asciiCaseWordPattern('never')}|${asciiCaseWordPattern('not')}|${asciiCaseWordPattern('no')}|${asciiCaseWordPattern('neither')}|${asciiCaseWordPattern('nor')}|${asciiCaseWordPattern('avoid')}(?:${asciiCaseWordPattern('ing')})?|${asciiCaseWordPattern('cannot')}|${asciiCaseWordPattern('can')}'${asciiCaseWordPattern('t')}|${EXCLUSION_PREFIX})`;
699
+ const NEGATIVE_PREFIX_PATTERN = new RegExp(`(?:${ASCII_NEGATIVE_PREFIX}|[Нн][Ее]\\s+(?:[Зз]апускай|[Ии]спользуй)|実行しないで|使わないで)`, 'u');
700
+ const LIST_DOCUMENTATION_SUFFIX = /^(?:\s*(?::|:|[—–])\s*\S|\s+(?:is|are|means|refer(?:s)?\s+to|denote(?:s)?)\b.*\b(?:commands?|workflows?|skills?|modes?|files?|documentation)\b)/i;
701
+ const EXPLICIT_DOCUMENTATION_SUFFIX_SOURCE = String.raw `\s+(?:(?:is|are|means|refer(?:s)?\s+to|denote(?:s)?)\b.*\b(?:commands?|workflows?|skills?|modes?|files?|documentation)\b|(?:is|are)\s+(?:(?:also|still)\s+)?(?:(?:its|an?|the)\s+)?alias(?:es)?\b|(?:is|are)\s+(?:(?:also|still)\s+)?(?:documented|described)\b|(?:appears?|occurs?|is\s+(?:also\s+)?mentioned)\b.*\b(?:docs?|documentation|examples?|references?|guide|manual)\b)`;
702
+ const EXPLICIT_DOCUMENTATION_SUFFIX_AT = new RegExp(EXPLICIT_DOCUMENTATION_SUFFIX_SOURCE, 'iyu');
703
+ const IMPLICIT_WORKFLOW_NOUN = '(?:modes?|workflows?|skills?|loops?)';
704
+ const POSTPOSED_NEGATIVE_PREDICATE = /\s+(?:(?:is|are|was|were|should|must|can|could|may|will|would)\s+(?:(?:also|still)\s+)?(?:not|never)\b|(?:isn't|aren't|wasn't|weren't|cannot|can't|shouldn't|mustn't|won't|wouldn't|couldn't)\b|(?:is|are|was|were)\s+(?:(?:also|still)\s+)?(?:inert|prohibited|forbidden|disabled|disallowed|unsupported)\b|(?:should|must|can|could|may|will|would)\s+(?:(?:also|still)\s+)?(?:not\s+)?be\s+(?:inert|avoided|prohibited|forbidden|disabled|disallowed|unsupported)\b|(?:is|are|was|were)\s+(?:(?:also|still)\s+)?to\s+be\s+(?:inert|avoided|prohibited|forbidden|disabled|disallowed|unsupported)\b)/iu;
705
+ const PROMPTS_TOKEN_PATTERN = /\/[Pp][Rr][Oo][Mm][Pp][Tt][Ss]:[\w.-]+/gu;
706
+ const COORDINATED_WORKFLOW_SUBJECT = '(?:(?:the|a|an)\\s+)?(?:autopilot|deep(?:[- ]+)interview|ralph|ralplan|ultrawork|ulw|ultragoal|ultraqa|autoresearch|coordinated\\s+team|team|consensus\\s+plan|code\\s+review|wiki|prometheus(?:-strict)?)';
707
+ const COORDINATED_SUBJECT_JOINER = '(?:and|or|nor|as\\s+well\\s+as|along\\s+with|together\\s+with|&)';
708
+ const EXPLICIT_POSTPOSED_SUBJECT = '\\$(?:oh-my-codex:)?[A-Za-z][A-Za-z0-9_-]*';
709
+ const COORDINATED_POSTPOSED_SEPARATOR_SOURCE = `\\s*(?:(?:[,,،、]|/)\\s*(?:${COORDINATED_SUBJECT_JOINER}\\s+)?|${COORDINATED_SUBJECT_JOINER}\\s+)`;
710
+ const COORDINATED_EXPLICIT_POSTPOSED_SEPARATOR = new RegExp(COORDINATED_POSTPOSED_SEPARATOR_SOURCE, 'iyu');
711
+ const COORDINATED_EXPLICIT_POSTPOSED_SUBJECT = new RegExp(`(?:(?:the|a|an)\\s+)?(?:${EXPLICIT_POSTPOSED_SUBJECT}|${COORDINATED_WORKFLOW_SUBJECT})`, 'iyu');
712
+ const COORDINATED_IMPLICIT_DOCUMENTATION_SUBJECT = new RegExp(`${COORDINATED_WORKFLOW_SUBJECT}(?:\\s+${IMPLICIT_WORKFLOW_NOUN})?`, 'iyu');
713
+ const COORDINATED_SUBJECT_QUANTIFIER = new RegExp(asciiCaseWordPattern('both'), 'yu');
714
+ const POSTPOSED_SUBJECT_NOUN = new RegExp(`\\s+${IMPLICIT_WORKFLOW_NOUN}`, 'iyu');
715
+ const IMPLICIT_POSTPOSED_SUBJECT_CHAIN = new RegExp(`^\\s*(?:${asciiCaseWordPattern('both')}\\s+)?${COORDINATED_WORKFLOW_SUBJECT}(?:\\s+${IMPLICIT_WORKFLOW_NOUN})?(?:${COORDINATED_POSTPOSED_SEPARATOR_SOURCE}${COORDINATED_WORKFLOW_SUBJECT}(?:\\s+${IMPLICIT_WORKFLOW_NOUN})?)*\\s*[,,،、]?\\s*$`, 'iu');
716
+ const IMPLICIT_DOCUMENTATION_SUBJECT_PATTERN = new RegExp(`^\\s*${COORDINATED_WORKFLOW_SUBJECT}(?:\\s+${IMPLICIT_WORKFLOW_NOUN})?(?:\\s+(?:(?:is|are|means|refer(?:s)?\\s+to|denote(?:s)?)\\b.*\\b(?:commands?|workflows?|skills?|modes?|files?|documentation)\\b|(?:is|are)\\s+(?:(?:documented|described)\\b|(?:(?:also|still)\\s+)?(?:(?:its|an?|the)\\s+)?alias(?:es)?\\b)|(?:appears?|occurs?|is\\s+(?:also\\s+)?mentioned)\\b.*\\b(?:docs?|documentation|examples?|references?|guide|manual)\\b)|\\s*(?::|:|[—–-]|\\/)\\s*\\S.*\\b(?:commands?|workflows?|skills?|modes?|files?|documentation)\\b)[.!?]?`, 'iu');
717
+ const DOCUMENTATION_LEADING_PATTERN = new RegExp(`^\\s*(?:(?:the|a|an)\\s+)?(?:docs?|documentation|examples?|references?|guide|manual)\\b.*\\b(?:mention(?:s|ed|ing)?|document(?:s|ed|ing)?|describ(?:e|es|ed|ing)|explain(?:s|ed|ing)|refer(?:s|red|ring)?\\s+to)\\b.*\\b${COORDINATED_WORKFLOW_SUBJECT}(?:\\s+${IMPLICIT_WORKFLOW_NOUN})?\\b`, 'iu');
718
+ function hasImplicitDocumentationSubjectPrefix(text) {
719
+ return IMPLICIT_DOCUMENTATION_SUBJECT_PATTERN.test(text);
720
+ }
721
+ function normalizeGrammarApostrophes(text) {
722
+ return text.replace(/[’']/gu, "'");
723
+ }
724
+ function latestPositiveContrastStart(text) {
725
+ const pattern = new RegExp(`${ASCII_BUT}\\s+(?:${ASCII_PLEASE}\\s+)?(?:${ASCII_INSTEAD}\\s+)?(?=${ASCII_DIRECTIVE_COMMAND})`, 'gu');
726
+ let start = -1;
727
+ let match;
728
+ while ((match = pattern.exec(text)) !== null) {
729
+ if (hasUnicodeGrammarTokenStart(text, match.index))
730
+ start = match.index;
731
+ }
732
+ return start;
733
+ }
734
+ function hasAsciiDirectiveCommand(text) {
735
+ const scanner = new RegExp(ASCII_DIRECTIVE_COMMAND, 'gu');
736
+ let match;
737
+ while ((match = scanner.exec(text)) !== null) {
738
+ if (hasUnicodeGrammarTokenBoundaries(text, match.index, scanner.lastIndex))
739
+ return true;
740
+ }
741
+ return false;
742
+ }
743
+ function isDiscourseNegation(text, start, end) {
744
+ const suffix = text.slice(end);
745
+ if (/^\s+worries\s*[,,،、]/iu.test(suffix))
746
+ return true;
747
+ return /(?:^|\s)i'm\s+$/iu.test(text.slice(0, start))
748
+ && /^\s+sure\s*[,,،、]/iu.test(suffix);
749
+ }
750
+ function hasUnicodeBoundedNegativePrefix(text) {
751
+ const scanner = new RegExp(NEGATIVE_PREFIX_PATTERN.source, 'gu');
752
+ let match;
753
+ while ((match = scanner.exec(text)) !== null) {
754
+ if (hasUnicodeGrammarTokenBoundaries(text, match.index, scanner.lastIndex)
755
+ && !isDiscourseNegation(text, match.index, scanner.lastIndex))
756
+ return true;
757
+ }
758
+ return false;
759
+ }
760
+ function isNegativePrefixExemptImplicitKeyword(keyword) {
761
+ const normalized = keyword.toLowerCase();
762
+ return normalized === "don't stop" || normalized === "don't assume";
763
+ }
764
+ function normalizedPunctuation(character) {
765
+ return character.normalize('NFKC');
766
+ }
767
+ function isDirectivePunctuation(character) {
768
+ return DIRECTIVE_PUNCTUATION.test(normalizedPunctuation(character));
769
+ }
770
+ function isClauseBoundaryAt(text, index) {
771
+ const character = codePointAt(text, index);
772
+ const normalized = normalizedPunctuation(character);
773
+ if (!CLAUSE_BOUNDARY_PUNCTUATION.test(normalized))
774
+ return false;
775
+ if (normalized !== '.')
776
+ return true;
777
+ const next = codePointAt(text, index + character.length);
778
+ return !next || SAFE_TOKEN_WHITESPACE.test(next) || /["'”’»›」』))\]}]/u.test(next);
779
+ }
780
+ function isLogicalSentenceBoundaryAt(text, index) {
781
+ const character = codePointAt(text, index);
782
+ const normalized = normalizedPunctuation(character);
783
+ return LOGICAL_SENTENCE_BOUNDARY_PUNCTUATION.test(normalized) && isClauseBoundaryAt(text, index);
784
+ }
785
+ function documentationSuffixLimit(text, start, limit) {
786
+ for (let cursor = start; cursor < limit;) {
787
+ if (isStrongDocumentationClauseSeparatorAt(text, cursor))
788
+ return cursor;
789
+ cursor += codePointAt(text, cursor).length;
790
+ }
791
+ return limit;
792
+ }
793
+ function hasExplicitDocumentationSuffixAt(text, start, limit) {
794
+ const clauseLimit = documentationSuffixLimit(text, start, limit);
795
+ EXPLICIT_DOCUMENTATION_SUFFIX_AT.lastIndex = 0;
796
+ return EXPLICIT_DOCUMENTATION_SUFFIX_AT.exec(text.slice(start, clauseLimit)) !== null;
797
+ }
798
+ function isAbbreviationDotAt(text, index) {
799
+ const context = text.slice(Math.max(0, index - 8), index + 1);
800
+ return /(?:^|[^A-Za-z0-9_])(?:e\.g|i\.e|etc|vs|mr|mrs|ms|dr|prof|sr|jr|st|no|fig|ver|v)\.$/i.test(context);
801
+ }
802
+ function hasLogicalSentenceBoundary(text) {
803
+ for (let cursor = 0; cursor < text.length;) {
804
+ const character = codePointAt(text, cursor);
805
+ if (character === '\r' || character === '\n' || character === '\u2028' || character === '\u2029')
806
+ return true;
807
+ if (isLogicalSentenceBoundaryAt(text, cursor)) {
808
+ if (normalizedPunctuation(character) !== '.' || !isAbbreviationDotAt(text, cursor))
809
+ return true;
810
+ }
811
+ cursor += character.length;
812
+ }
813
+ return false;
814
+ }
815
+ function codePointAt(text, index) {
816
+ const value = text.codePointAt(index);
817
+ return value === undefined ? '' : String.fromCodePoint(value);
818
+ }
819
+ function codePointBefore(text, index) {
820
+ if (index <= 0)
821
+ return '';
822
+ const trailing = text.charCodeAt(index - 1);
823
+ if (trailing >= 0xDC00 && trailing <= 0xDFFF && index >= 2) {
824
+ const leading = text.charCodeAt(index - 2);
825
+ if (leading >= 0xD800 && leading <= 0xDBFF)
826
+ return text.slice(index - 2, index);
827
+ }
828
+ return text[index - 1] ?? '';
829
+ }
830
+ function hasUnicodeGrammarTokenStart(text, start) {
831
+ const previous = codePointBefore(text, start);
832
+ return !previous || !TOKEN_CONTINUATION.test(previous);
833
+ }
834
+ /** ASCII grammar words cannot begin or end inside a Unicode identifier-like token. */
835
+ function hasUnicodeGrammarTokenBoundaries(text, start, end) {
836
+ const next = codePointAt(text, end);
837
+ return hasUnicodeGrammarTokenStart(text, start)
838
+ && (!next || !TOKEN_CONTINUATION.test(next));
839
+ }
840
+ function firstGrammarTokenStart(text, start, end) {
841
+ let cursor = start;
842
+ while (cursor < end && SAFE_TOKEN_WHITESPACE.test(codePointAt(text, cursor)))
843
+ cursor += codePointAt(text, cursor).length;
844
+ return cursor;
845
+ }
846
+ function hasAsciiGrammarTokenCharacters(text, start, end) {
847
+ for (let cursor = start; cursor < end;) {
848
+ const character = codePointAt(text, cursor);
849
+ if (TOKEN_CONTINUATION.test(character) && !/^[A-Za-z0-9_-]$/u.test(character))
850
+ return false;
851
+ cursor += character.length;
852
+ }
853
+ return true;
854
+ }
855
+ function isExplicitTokenBoundary(text, index) {
856
+ if (index >= text.length)
857
+ return true;
858
+ const character = codePointAt(text, index);
859
+ if (SAFE_TOKEN_WHITESPACE.test(character))
860
+ return true;
861
+ const compatibility = character.normalize('NFKC');
862
+ if (compatibility === '.') {
863
+ const next = codePointAt(text, index + character.length);
864
+ return !next || SAFE_TOKEN_WHITESPACE.test(next) || next === '$';
865
+ }
866
+ if (character === "'" || character === '’' || compatibility === "'") {
867
+ const next = codePointAt(text, index + character.length);
868
+ return !next || !TOKEN_CONTINUATION.test(next);
869
+ }
870
+ if ([...compatibility].length === 1 && EXPLICIT_TOKEN_BOUNDARY_PUNCTUATION.test(compatibility))
871
+ return true;
872
+ return EXPLICIT_TOKEN_BOUNDARY_PUNCTUATION.test(character);
873
+ }
874
+ function maximalExplicitTokenEnd(text, initialEnd) {
875
+ let cursor = initialEnd;
876
+ while (cursor < text.length && !isExplicitTokenBoundary(text, cursor)) {
877
+ cursor += codePointAt(text, cursor).length;
878
+ }
879
+ return cursor;
658
880
  }
659
881
  /**
660
882
  * Korean 2-set keyboard typo aliases for workflow keywords.
@@ -664,51 +886,1852 @@ function hasExplicitPromptsInvocation(text) {
664
886
  * as the canonical keyword without introducing broad transliteration surprises.
665
887
  */
666
888
  function normalizeWorkflowKeyboardTypos(text) {
667
- return text.replace(/ㅕㅣㅈ/g, 'ulw');
668
- }
669
- function normalizeExplicitSkillToken(token) {
670
- if (token === 'ulw')
671
- return 'ultrawork';
672
- if (token === 'frontend-ui-ux')
673
- return 'design';
674
- return token;
675
- }
676
- function parseExplicitSkillInvocations(text) {
677
- const results = [];
678
- const regex = /(?:^|[^\w])\$(?:(?:oh-my-codex:)?([a-z][a-z0-9-]*))\b/gi;
889
+ return text.replace(/\$ㅕㅣㅈ(?=로)/g, '$ulw ').replace(/ㅕㅣㅈ/g, 'ulw');
890
+ }
891
+ const STRUCTURAL_INERT_DIAGNOSTICS = [
892
+ 'fenced-code',
893
+ 'indented-code',
894
+ 'blockquote',
895
+ 'inline-code',
896
+ 'quote',
897
+ ];
898
+ const QUOTE_CLOSERS = Object.freeze({
899
+ '“': '”',
900
+ '‘': '’',
901
+ '„': '“',
902
+ '‚': '‘',
903
+ '«': '»',
904
+ '‹': '›',
905
+ '「': '」',
906
+ '『': '』',
907
+ });
908
+ const LETTER_OR_NUMBER = /[\p{L}\p{N}]/u;
909
+ function lineEnd(text, start) {
910
+ for (let cursor = start; cursor < text.length; cursor += 1) {
911
+ const character = text[cursor];
912
+ if (character === '\r' || character === '\n' || character === '\u2028' || character === '\u2029')
913
+ return cursor;
914
+ }
915
+ return text.length;
916
+ }
917
+ function nextLineStart(text, end) {
918
+ return text[end] === '\r' && text[end + 1] === '\n' ? end + 2 : end + 1;
919
+ }
920
+ function advanceSpaces(text, start, maximum) {
921
+ let cursor = start;
922
+ while (cursor - start < maximum && text[cursor] === ' ')
923
+ cursor += 1;
924
+ return cursor;
925
+ }
926
+ function markdownContainerPrefix(line, maximumLeadingSpaces = 3) {
927
+ let cursor = advanceSpaces(line, 0, maximumLeadingSpaces);
928
+ let blockquoteDepth = 0;
929
+ let listDepth = 0;
930
+ while (cursor < line.length) {
931
+ if (line[cursor] === '>') {
932
+ blockquoteDepth += 1;
933
+ cursor += 1;
934
+ if (line[cursor] === ' ' || line[cursor] === '\t')
935
+ cursor += 1;
936
+ cursor = advanceSpaces(line, cursor, 3);
937
+ continue;
938
+ }
939
+ const listMarker = /^(?:(?:[-*+])|(?:\d{1,9}[.)]))[\t ]+/u.exec(line.slice(cursor));
940
+ if (!listMarker)
941
+ break;
942
+ listDepth += 1;
943
+ cursor += listMarker[0].length;
944
+ cursor = advanceSpaces(line, cursor, 3);
945
+ }
946
+ return { cursor, blockquoteDepth, listDepth };
947
+ }
948
+ function markdownFenceAtStart(line, maximumLeadingSpaces = 3) {
949
+ const container = markdownContainerPrefix(line, maximumLeadingSpaces);
950
+ let cursor = container.cursor;
951
+ const marker = line[cursor];
952
+ if (marker !== '`' && marker !== '~')
953
+ return null;
954
+ const start = cursor;
955
+ while (line[cursor] === marker)
956
+ cursor += 1;
957
+ const length = cursor - start;
958
+ return length >= 3
959
+ ? { marker, length, blockquoteDepth: container.blockquoteDepth, listItem: container.listDepth > 0, indent: start, closingEligible: /^[\t ]*$/.test(line.slice(cursor)) }
960
+ : null;
961
+ }
962
+ function isBlockquoteLine(line) {
963
+ return markdownContainerPrefix(line).blockquoteDepth > 0;
964
+ }
965
+ function hasMarkdownCodeIndent(line) {
966
+ let columns = 0;
967
+ for (const character of line) {
968
+ if (character === ' ')
969
+ columns += 1;
970
+ else if (character === '\t')
971
+ columns += 4 - (columns % 4);
972
+ else
973
+ break;
974
+ if (columns >= 4)
975
+ return true;
976
+ }
977
+ return false;
978
+ }
979
+ function hasListContainedCodeIndent(line) {
980
+ const leading = /^[ \t]{0,3}/u.exec(line)?.[0] ?? '';
981
+ let cursor = leading.length;
982
+ let column = 0;
983
+ for (const character of leading)
984
+ column += character === '\t' ? 4 - (column % 4) : 1;
985
+ while (cursor < line.length) {
986
+ const marker = /^(?:[-+*]|\d{1,9}[.)])/u.exec(line.slice(cursor));
987
+ if (!marker)
988
+ return false;
989
+ for (const character of marker[0])
990
+ column += character === '\t' ? 4 - (column % 4) : 1;
991
+ cursor += marker[0].length;
992
+ const gap = /^[ \t]+/u.exec(line.slice(cursor))?.[0];
993
+ if (!gap)
994
+ return false;
995
+ const markerEndColumn = column;
996
+ for (const character of gap)
997
+ column += character === '\t' ? 4 - (column % 4) : 1;
998
+ cursor += gap.length;
999
+ if (column - markerEndColumn > 4)
1000
+ return true;
1001
+ }
1002
+ return false;
1003
+ }
1004
+ function collectIndentedCodeRanges(text) {
1005
+ const ranges = [];
1006
+ let lineStart = 0;
1007
+ while (lineStart <= text.length) {
1008
+ const end = lineEnd(text, lineStart);
1009
+ const line = text.slice(lineStart, end);
1010
+ if (hasMarkdownCodeIndent(line) || hasListContainedCodeIndent(line)) {
1011
+ ranges.push({ start: lineStart, end, reason: 'indented-code', bounded: true });
1012
+ }
1013
+ if (end === text.length)
1014
+ break;
1015
+ lineStart = nextLineStart(text, end);
1016
+ }
1017
+ return ranges;
1018
+ }
1019
+ function collectFencedCodeRanges(text) {
1020
+ const ranges = [];
1021
+ let lineStart = 0;
1022
+ let fence = null;
1023
+ while (lineStart <= text.length) {
1024
+ const end = lineEnd(text, lineStart);
1025
+ const line = text.slice(lineStart, end);
1026
+ if (fence) {
1027
+ const closer = markdownFenceAtStart(line, fence.definition.listItem ? fence.definition.indent + 3 : 3);
1028
+ if (closer?.marker === fence.definition.marker
1029
+ && closer.length >= fence.definition.length
1030
+ && closer.blockquoteDepth === fence.definition.blockquoteDepth
1031
+ && !closer.listItem
1032
+ && (!fence.definition.listItem || closer.indent >= fence.definition.indent)
1033
+ && closer.closingEligible) {
1034
+ ranges.push({ start: fence.start, end, reason: 'fenced-code', bounded: true });
1035
+ fence = null;
1036
+ }
1037
+ else if (fence.definition.listItem) {
1038
+ const rootFence = markdownFenceAtStart(line);
1039
+ if (rootFence && rootFence.indent < fence.definition.indent) {
1040
+ ranges.push({ start: fence.start, end: lineStart, reason: 'fenced-code', bounded: true });
1041
+ fence = { definition: rootFence, start: lineStart };
1042
+ }
1043
+ }
1044
+ }
1045
+ else {
1046
+ const opener = markdownFenceAtStart(line);
1047
+ if (opener)
1048
+ fence = { definition: opener, start: lineStart };
1049
+ }
1050
+ if (end === text.length)
1051
+ break;
1052
+ lineStart = nextLineStart(text, end);
1053
+ }
1054
+ if (fence)
1055
+ ranges.push({ start: fence.start, end: text.length, reason: 'fenced-code', bounded: false });
1056
+ return ranges;
1057
+ }
1058
+ function collectInlineCodeRanges(text) {
1059
+ const ranges = [];
1060
+ let lineStart = 0;
1061
+ while (lineStart <= text.length) {
1062
+ const end = lineEnd(text, lineStart);
1063
+ let cursor = lineStart;
1064
+ let opener = null;
1065
+ while (cursor < end) {
1066
+ if (text[cursor] !== '`') {
1067
+ cursor += 1;
1068
+ continue;
1069
+ }
1070
+ let runEnd = cursor + 1;
1071
+ while (runEnd < end && text[runEnd] === '`')
1072
+ runEnd += 1;
1073
+ const length = runEnd - cursor;
1074
+ if (!opener)
1075
+ opener = { start: cursor, length };
1076
+ else if (opener.length === length) {
1077
+ ranges.push({ start: opener.start, end: runEnd, reason: 'inline-code', bounded: true });
1078
+ opener = null;
1079
+ }
1080
+ cursor = runEnd;
1081
+ }
1082
+ if (opener)
1083
+ ranges.push({ start: opener.start, end, reason: 'inline-code', bounded: false });
1084
+ if (end === text.length)
1085
+ break;
1086
+ lineStart = nextLineStart(text, end);
1087
+ }
1088
+ return ranges;
1089
+ }
1090
+ function collectQuoteRanges(text) {
1091
+ const ranges = [];
1092
+ let lineStart = 0;
1093
+ while (lineStart <= text.length) {
1094
+ const end = lineEnd(text, lineStart);
1095
+ let opener = null;
1096
+ for (let cursor = lineStart; cursor < end; cursor += 1) {
1097
+ const character = text[cursor];
1098
+ const previous = text[cursor - 1] ?? '';
1099
+ const next = text[cursor + 1] ?? '';
1100
+ if ((character === "'" || character === '’' || character === ''') && LETTER_OR_NUMBER.test(previous) && LETTER_OR_NUMBER.test(next))
1101
+ continue;
1102
+ if (!opener && (character === "'" || character === '’' || character === ''') && /[sS]/u.test(previous) && (!next || SAFE_TOKEN_WHITESPACE.test(next) || /[.,;:!?)}\]”’»›」』]/u.test(next)))
1103
+ continue;
1104
+ if (hasOddImmediateBackslashes(text, cursor))
1105
+ continue;
1106
+ if (opener && character === opener.closing) {
1107
+ ranges.push({ start: opener.start, end: cursor + 1, reason: 'quote', bounded: true });
1108
+ opener = null;
1109
+ continue;
1110
+ }
1111
+ if (!opener && (character === '"' || character === "'" || character === '"' || character === ''')) {
1112
+ opener = { start: cursor, closing: character };
1113
+ continue;
1114
+ }
1115
+ if (!opener && QUOTE_CLOSERS[character])
1116
+ opener = { start: cursor, closing: QUOTE_CLOSERS[character] };
1117
+ }
1118
+ if (opener)
1119
+ ranges.push({ start: opener.start, end, reason: 'quote', bounded: false });
1120
+ if (end === text.length)
1121
+ break;
1122
+ lineStart = nextLineStart(text, end);
1123
+ }
1124
+ return ranges;
1125
+ }
1126
+ function collectBlockquoteRanges(text) {
1127
+ const ranges = [];
1128
+ let lineStart = 0;
1129
+ while (lineStart <= text.length) {
1130
+ const end = lineEnd(text, lineStart);
1131
+ if (isBlockquoteLine(text.slice(lineStart, end))) {
1132
+ ranges.push({ start: lineStart, end, reason: 'blockquote', bounded: true });
1133
+ }
1134
+ if (end === text.length)
1135
+ break;
1136
+ lineStart = nextLineStart(text, end);
1137
+ }
1138
+ return ranges;
1139
+ }
1140
+ function createInertRangeIndex(ranges) {
1141
+ ranges.sort((a, b) => a.start - b.start || a.end - b.end);
1142
+ const starts = new Array(ranges.length);
1143
+ const maximumEnds = new Array(ranges.length);
1144
+ let maximumEnd = -1;
1145
+ for (const [index, range] of ranges.entries()) {
1146
+ starts[index] = range.start;
1147
+ maximumEnd = Math.max(maximumEnd, range.end);
1148
+ maximumEnds[index] = maximumEnd;
1149
+ }
1150
+ return { starts, maximumEnds };
1151
+ }
1152
+ function collectInertRangeIndexes(text) {
1153
+ return {
1154
+ 'indented-code': createInertRangeIndex(collectIndentedCodeRanges(text)),
1155
+ 'fenced-code': createInertRangeIndex(collectFencedCodeRanges(text)),
1156
+ blockquote: createInertRangeIndex(collectBlockquoteRanges(text)),
1157
+ 'inline-code': createInertRangeIndex(collectInlineCodeRanges(text)),
1158
+ quote: createInertRangeIndex(collectQuoteRanges(text)),
1159
+ };
1160
+ }
1161
+ function inertRangeEndAt(index, position) {
1162
+ let lower = 0;
1163
+ let upper = index.starts.length;
1164
+ while (lower < upper) {
1165
+ const middle = lower + Math.floor((upper - lower) / 2);
1166
+ if (index.starts[middle] <= position)
1167
+ lower = middle + 1;
1168
+ else
1169
+ upper = middle;
1170
+ }
1171
+ if (lower === 0)
1172
+ return null;
1173
+ const end = index.maximumEnds[lower - 1];
1174
+ return end > position ? end : null;
1175
+ }
1176
+ function isInInertRange(index, position) {
1177
+ return inertRangeEndAt(index, position) !== null;
1178
+ }
1179
+ function isStructurallyInert(indexes, position) {
1180
+ return STRUCTURAL_INERT_DIAGNOSTICS.some((reason) => isInInertRange(indexes[reason], position));
1181
+ }
1182
+ function structuralInertRangeEnd(indexes, position) {
1183
+ let end = -1;
1184
+ for (const reason of STRUCTURAL_INERT_DIAGNOSTICS)
1185
+ end = Math.max(end, inertRangeEndAt(indexes[reason], position) ?? -1);
1186
+ return end >= 0 ? end : null;
1187
+ }
1188
+ function lineStartForPosition(text, position) {
1189
+ for (let cursor = position - 1; cursor >= 0; cursor -= 1) {
1190
+ const character = text[cursor];
1191
+ if (character === '\n' || character === '\r' || character === '\u2028' || character === '\u2029')
1192
+ return cursor + 1;
1193
+ }
1194
+ return 0;
1195
+ }
1196
+ function leadingDirectiveCursor(text, regionStart, allowLineBreaks) {
1197
+ const whitespacePattern = allowLineBreaks ? /^\s*/u : /^[^\S\r\n\u2028\u2029]*/u;
1198
+ const cursor = regionStart + (whitespacePattern.exec(text.slice(regionStart))?.[0].length ?? 0);
1199
+ const container = markdownContainerPrefix(text.slice(cursor, lineEnd(text, cursor)));
1200
+ if (container.blockquoteDepth > 0)
1201
+ return { cursor, listItem: false };
1202
+ return { cursor: cursor + container.cursor, listItem: container.listDepth > 0 };
1203
+ }
1204
+ function directiveCommandTargetStart(text, cursor, limit = text.length) {
1205
+ DIRECTIVE_COMMAND_PREFIX_AT.lastIndex = cursor;
1206
+ const prefix = DIRECTIVE_COMMAND_PREFIX_AT.exec(text);
1207
+ const targetStart = prefix?.index === cursor ? cursor + prefix[0].length : null;
1208
+ return targetStart !== null && targetStart <= limit ? targetStart : null;
1209
+ }
1210
+ function promptLeadingExplicitCandidateStart(text) {
1211
+ const leading = leadingDirectiveCursor(text, 0, true);
1212
+ if (text[leading.cursor] === '$')
1213
+ return leading.cursor;
1214
+ const commandTarget = directiveCommandTargetStart(text, leading.cursor);
1215
+ return commandTarget !== null && text[commandTarget] === '$' ? commandTarget : leading.cursor;
1216
+ }
1217
+ function punctuationSeparatedCandidateStart(text, cursor) {
1218
+ let next = boundedHorizontalCursor(text, cursor, text.length);
1219
+ if (text[next] === '$')
1220
+ return next;
1221
+ const punctuationStart = next;
1222
+ while (next < text.length) {
1223
+ const character = codePointAt(text, next);
1224
+ if (!isDirectivePunctuation(character))
1225
+ break;
1226
+ next += character.length;
1227
+ }
1228
+ if (next === punctuationStart)
1229
+ return null;
1230
+ next = boundedHorizontalCursor(text, next, text.length);
1231
+ return text[next] === '$' ? next : null;
1232
+ }
1233
+ function coordinatedDocumentationCandidateStart(text, cursor) {
1234
+ const scanner = /(?:[^\S\r\n\u2028\u2029]+(?:[Aa][Nn][Dd]|[Oo][Rr])\s+|[^\S\r\n\u2028\u2029]*[,,،、]\s*(?:[Aa][Nn][Dd]|[Oo][Rr])\s+|[^\S\r\n\u2028\u2029]*\/[^\S\r\n\u2028\u2029]*)/uy;
1235
+ scanner.lastIndex = cursor;
1236
+ const separator = scanner.exec(text);
1237
+ if (!separator)
1238
+ return null;
1239
+ const next = cursor + separator[0].length;
1240
+ return text[next] === '$' ? next : null;
1241
+ }
1242
+ function clauseDirectiveStart(text, predecessorEnd, candidateStart) {
1243
+ if (predecessorEnd > candidateStart)
1244
+ return false;
1245
+ let clauseStart = predecessorEnd;
1246
+ for (let cursor = predecessorEnd; cursor < candidateStart;) {
1247
+ const character = codePointAt(text, cursor);
1248
+ if (isClauseBoundaryAt(text, cursor) || normalizedPunctuation(character) === ':')
1249
+ clauseStart = cursor + character.length;
1250
+ cursor += character.length;
1251
+ }
1252
+ const directiveStart = boundedHorizontalCursor(text, clauseStart, candidateStart);
1253
+ return hasAdjacentPredecessorSeparator(text, predecessorEnd, directiveStart)
1254
+ && directiveCommandTargetStart(text, directiveStart, candidateStart) === candidateStart;
1255
+ }
1256
+ function hasAdjacentPredecessorSeparator(text, predecessorEnd, candidateStart) {
1257
+ if (predecessorEnd > candidateStart)
1258
+ return false;
1259
+ for (let cursor = predecessorEnd; cursor < candidateStart;) {
1260
+ const character = codePointAt(text, cursor);
1261
+ if (!SAFE_TOKEN_WHITESPACE.test(character)
1262
+ && !isClauseBoundaryAt(text, cursor)
1263
+ && normalizedPunctuation(character) !== ':'
1264
+ && !/[—–-]/u.test(character))
1265
+ return false;
1266
+ cursor += character.length;
1267
+ }
1268
+ return true;
1269
+ }
1270
+ function isPostposedSubjectBoundaryAt(text, index) {
1271
+ if (!isClauseBoundaryAt(text, index))
1272
+ return false;
1273
+ return !/[,،、]/u.test(normalizedPunctuation(codePointAt(text, index)));
1274
+ }
1275
+ function collectLineMatchPositions(pattern, text, start, end, requireEndBoundary = true) {
1276
+ const matches = [];
1277
+ pattern.lastIndex = start;
679
1278
  let match;
680
- let captureStarted = false;
681
- let lastMatchEnd = -1;
682
- while ((match = regex.exec(text)) !== null) {
683
- const token = (match[1] ?? '').toLowerCase();
684
- if (!token)
1279
+ while ((match = pattern.exec(text)) !== null && match.index < end) {
1280
+ if (pattern.lastIndex <= end
1281
+ && hasUnicodeGrammarTokenStart(text, match.index)
1282
+ && (!requireEndBoundary || hasUnicodeGrammarTokenBoundaries(text, match.index, pattern.lastIndex))) {
1283
+ matches.push({ start: match.index, end: pattern.lastIndex });
1284
+ }
1285
+ }
1286
+ return matches;
1287
+ }
1288
+ function hasExclusionPrefixAt(text, start) {
1289
+ const pattern = new RegExp(`\\s*(?:${ASCII_PLEASE}\\s+)?${EXCLUSION_PREFIX}`, 'yu');
1290
+ pattern.lastIndex = start;
1291
+ const match = pattern.exec(text);
1292
+ return match?.index === start && hasUnicodeGrammarTokenBoundaries(text, start, pattern.lastIndex);
1293
+ }
1294
+ function collectExplicitPrefixNegationIndex(text, candidates) {
1295
+ const negatedStarts = new Set();
1296
+ const postposedSubjectStarts = new Map();
1297
+ const firstPostposedSubjectCandidates = new Set();
1298
+ const normalizedText = normalizeGrammarApostrophes(text);
1299
+ const negativeScanner = new RegExp(NEGATIVE_PREFIX_PATTERN.source, 'gu');
1300
+ const contrastScanner = new RegExp(`${ASCII_BUT}\\s+(?:${ASCII_PLEASE}\\s+)?(?:${ASCII_INSTEAD}\\s+)?(?=${ASCII_DIRECTIVE_COMMAND})`, 'gu');
1301
+ const directiveScanner = new RegExp(`(?:${ASCII_AND}|${ASCII_THEN})\\s+(?:${ASCII_PLEASE}\\s+)?(?:${ASCII_INSTEAD}\\s+)?(?=${ASCII_DIRECTIVE_COMMAND})`, 'gu');
1302
+ let firstCandidateIndex = 0;
1303
+ while (firstCandidateIndex < candidates.length) {
1304
+ const lineStart = lineStartForPosition(text, candidates[firstCandidateIndex].start);
1305
+ const lineLimit = lineEnd(text, candidates[firstCandidateIndex].end);
1306
+ let afterLineCandidateIndex = firstCandidateIndex;
1307
+ while (afterLineCandidateIndex < candidates.length && candidates[afterLineCandidateIndex].start < lineLimit)
1308
+ afterLineCandidateIndex += 1;
1309
+ const negativeMatches = collectLineMatchPositions(negativeScanner, normalizedText, lineStart, lineLimit);
1310
+ const contrastMatches = collectLineMatchPositions(contrastScanner, text, lineStart, lineLimit, false);
1311
+ const directiveMatches = collectLineMatchPositions(directiveScanner, text, lineStart, lineLimit, false);
1312
+ let negativeMatchIndex = 0;
1313
+ let contrastMatchIndex = 0;
1314
+ let directiveMatchIndex = 0;
1315
+ let latestNegativeStart = -1;
1316
+ let latestContrastStart = -1;
1317
+ let latestCoordinatedDirectiveStart = -1;
1318
+ let latestPunctuationDirectiveStart = -1;
1319
+ let cursor = lineStart;
1320
+ let clauseStart = lineStart;
1321
+ let punctuationStart = lineStart;
1322
+ let postposedSubjectStart = lineStart;
1323
+ let hasPostposedSubjectCandidate = false;
1324
+ for (let candidateIndex = firstCandidateIndex; candidateIndex < afterLineCandidateIndex; candidateIndex += 1) {
1325
+ const candidate = candidates[candidateIndex];
1326
+ while (cursor < candidate.start) {
1327
+ const character = codePointAt(text, cursor);
1328
+ if (isLogicalSentenceBoundaryAt(text, cursor))
1329
+ clauseStart = cursor + character.length;
1330
+ if (isClauseBoundaryAt(text, cursor))
1331
+ punctuationStart = cursor + character.length;
1332
+ if (isPostposedSubjectBoundaryAt(text, cursor)) {
1333
+ postposedSubjectStart = cursor + character.length;
1334
+ hasPostposedSubjectCandidate = false;
1335
+ }
1336
+ cursor += character.length;
1337
+ }
1338
+ while (negativeMatchIndex < negativeMatches.length && negativeMatches[negativeMatchIndex].end <= candidate.start) {
1339
+ latestNegativeStart = negativeMatches[negativeMatchIndex].start;
1340
+ negativeMatchIndex += 1;
1341
+ }
1342
+ while (contrastMatchIndex < contrastMatches.length && contrastMatches[contrastMatchIndex].end <= candidate.start) {
1343
+ latestContrastStart = contrastMatches[contrastMatchIndex].start;
1344
+ contrastMatchIndex += 1;
1345
+ }
1346
+ while (directiveMatchIndex < directiveMatches.length && directiveMatches[directiveMatchIndex].end <= candidate.start) {
1347
+ latestCoordinatedDirectiveStart = directiveMatches[directiveMatchIndex].start;
1348
+ directiveMatchIndex += 1;
1349
+ }
1350
+ const punctuationDirectiveStart = boundedHorizontalCursor(text, punctuationStart, candidate.start);
1351
+ if (directiveCommandTargetStart(text, punctuationDirectiveStart, candidate.start) === candidate.start) {
1352
+ latestPunctuationDirectiveStart = Math.max(latestPunctuationDirectiveStart, punctuationDirectiveStart);
1353
+ }
1354
+ let effectiveClauseStart = clauseStart;
1355
+ if (latestContrastStart >= effectiveClauseStart)
1356
+ effectiveClauseStart = latestContrastStart;
1357
+ if (latestPunctuationDirectiveStart >= effectiveClauseStart && latestPunctuationDirectiveStart > latestNegativeStart) {
1358
+ effectiveClauseStart = latestPunctuationDirectiveStart;
1359
+ }
1360
+ if (latestCoordinatedDirectiveStart >= effectiveClauseStart && hasExclusionPrefixAt(text, effectiveClauseStart)) {
1361
+ effectiveClauseStart = latestCoordinatedDirectiveStart;
1362
+ }
1363
+ if (latestNegativeStart >= effectiveClauseStart)
1364
+ negatedStarts.add(candidate.start);
1365
+ postposedSubjectStarts.set(candidate.start, postposedSubjectStart);
1366
+ if (!hasPostposedSubjectCandidate)
1367
+ firstPostposedSubjectCandidates.add(candidate.start);
1368
+ hasPostposedSubjectCandidate = true;
1369
+ }
1370
+ firstCandidateIndex = afterLineCandidateIndex;
1371
+ }
1372
+ return { negatedStarts, postposedSubjectStarts, firstPostposedSubjectCandidates };
1373
+ }
1374
+ function directivePrefixAfter(text, start, limit) {
1375
+ let cursor = boundedHorizontalCursor(text, start, limit);
1376
+ const transitionStart = cursor;
1377
+ if (cursor >= limit)
1378
+ return null;
1379
+ if (isClauseBoundaryAt(text, cursor)) {
1380
+ cursor += codePointAt(text, cursor).length;
1381
+ cursor = boundedHorizontalCursor(text, cursor, limit);
1382
+ }
1383
+ else {
1384
+ const coordinator = new RegExp(`(?:${ASCII_AND}|${ASCII_BUT}|${ASCII_INSTEAD}|${ASCII_THEN}|${ASCII_NOW})`, 'yu');
1385
+ coordinator.lastIndex = cursor;
1386
+ const match = coordinator.exec(text);
1387
+ if (match?.index !== cursor || !hasUnicodeGrammarTokenBoundaries(text, cursor, coordinator.lastIndex))
1388
+ return null;
1389
+ }
1390
+ const targetStart = directiveCommandTargetStart(text, cursor, limit);
1391
+ return targetStart === null ? null : { start: transitionStart, targetStart };
1392
+ }
1393
+ function isExclusionPrefixDirectiveTransitionAt(text, prefixStart, transitionStart, limit) {
1394
+ if (!hasExclusionPrefixAt(text, prefixStart))
1395
+ return false;
1396
+ const punctuationTransition = isClauseBoundaryAt(text, transitionStart);
1397
+ let coordinatedTransition = false;
1398
+ if (!punctuationTransition) {
1399
+ const coordinator = new RegExp(`(?:${ASCII_AND}|${ASCII_THEN})`, 'yu');
1400
+ coordinator.lastIndex = transitionStart;
1401
+ const match = coordinator.exec(text);
1402
+ coordinatedTransition = match?.index === transitionStart
1403
+ && hasUnicodeGrammarTokenBoundaries(text, transitionStart, coordinator.lastIndex);
1404
+ }
1405
+ if (!punctuationTransition && !coordinatedTransition)
1406
+ return false;
1407
+ const directive = directivePrefixAfter(text, transitionStart, limit);
1408
+ if (directive?.start !== transitionStart)
1409
+ return false;
1410
+ return !punctuationTransition || clauseDirectiveStart(text, transitionStart, directive.targetStart);
1411
+ }
1412
+ function exclusionPrefixDirectiveTransitionStart(text, prefixStart, start, limit) {
1413
+ if (!hasExclusionPrefixAt(text, prefixStart))
1414
+ return null;
1415
+ for (let cursor = start; cursor < limit;) {
1416
+ if (isExclusionPrefixDirectiveTransitionAt(text, prefixStart, cursor, limit))
1417
+ return cursor;
1418
+ cursor += codePointAt(text, cursor).length;
1419
+ }
1420
+ return null;
1421
+ }
1422
+ function documentationDirectiveTransitionStart(text, start, limit) {
1423
+ const scanner = new RegExp(`(?:${ASCII_AND}|${ASCII_BUT}|${ASCII_THEN}|${ASCII_INSTEAD}|${ASCII_NOW})`, 'gu');
1424
+ scanner.lastIndex = start;
1425
+ let match;
1426
+ while ((match = scanner.exec(text)) !== null && match.index < limit) {
1427
+ if (scanner.lastIndex <= limit
1428
+ && hasUnicodeGrammarTokenBoundaries(text, match.index, scanner.lastIndex)
1429
+ && directiveCommandTargetStart(text, match.index, limit) !== null)
1430
+ return match.index;
1431
+ }
1432
+ return null;
1433
+ }
1434
+ function postposedNegationClauseEnd(text, start, limit) {
1435
+ for (let cursor = start; cursor < limit;) {
1436
+ if (isClauseBoundaryAt(text, cursor))
1437
+ return cursor;
1438
+ cursor += codePointAt(text, cursor).length;
1439
+ }
1440
+ return limit;
1441
+ }
1442
+ function stickyMatchEnd(pattern, text, start, limit) {
1443
+ pattern.lastIndex = start;
1444
+ const match = pattern.exec(text);
1445
+ return match && match.index === start && pattern.lastIndex <= limit ? pattern.lastIndex : null;
1446
+ }
1447
+ function coordinatedSubjectAt(text, start, limit, candidates, candidateIndexByStart) {
1448
+ const articlePrefix = /(?:the|a|an)/iyu;
1449
+ articlePrefix.lastIndex = start;
1450
+ const article = articlePrefix.exec(text);
1451
+ const articleSubjectStart = article?.index === start
1452
+ ? boundedHorizontalCursor(text, articlePrefix.lastIndex, limit)
1453
+ : start;
1454
+ const candidateStart = article?.index === start
1455
+ && articlePrefix.lastIndex <= limit
1456
+ && articleSubjectStart > articlePrefix.lastIndex
1457
+ && hasUnicodeGrammarTokenBoundaries(text, start, articlePrefix.lastIndex)
1458
+ ? articleSubjectStart
1459
+ : start;
1460
+ const explicitIndex = candidateIndexByStart.get(candidateStart);
1461
+ if (explicitIndex !== undefined && candidates[explicitIndex].reasons.size === 0) {
1462
+ return { end: candidates[explicitIndex].end, explicitIndex };
1463
+ }
1464
+ const implicitEnd = stickyMatchEnd(COORDINATED_IMPLICIT_DOCUMENTATION_SUBJECT, text, start, limit);
1465
+ return implicitEnd === null ? null : { end: implicitEnd };
1466
+ }
1467
+ function collectMixedSubjectChain(text, start, limit, candidates, candidateIndexByStart) {
1468
+ const chainStart = boundedHorizontalCursor(text, start, limit);
1469
+ let cursor = chainStart;
1470
+ const quantifierEnd = stickyMatchEnd(COORDINATED_SUBJECT_QUANTIFIER, text, cursor, limit);
1471
+ const quantifiedSubjectStart = quantifierEnd === null ? cursor : boundedHorizontalCursor(text, quantifierEnd, limit);
1472
+ if (quantifierEnd !== null
1473
+ && quantifiedSubjectStart > quantifierEnd
1474
+ && hasUnicodeGrammarTokenBoundaries(text, cursor, quantifierEnd))
1475
+ cursor = quantifiedSubjectStart;
1476
+ const firstSubject = coordinatedSubjectAt(text, cursor, limit, candidates, candidateIndexByStart);
1477
+ if (!firstSubject)
1478
+ return null;
1479
+ const explicitIndexes = firstSubject.explicitIndex === undefined ? [] : [firstSubject.explicitIndex];
1480
+ cursor = firstSubject.end;
1481
+ while (cursor < limit) {
1482
+ const separatorEnd = stickyMatchEnd(COORDINATED_EXPLICIT_POSTPOSED_SEPARATOR, text, cursor, limit);
1483
+ if (separatorEnd === null)
1484
+ break;
1485
+ const subject = coordinatedSubjectAt(text, separatorEnd, limit, candidates, candidateIndexByStart);
1486
+ if (!subject)
1487
+ break;
1488
+ if (subject.explicitIndex !== undefined)
1489
+ explicitIndexes.push(subject.explicitIndex);
1490
+ cursor = subject.end;
1491
+ }
1492
+ return { start: chainStart, explicitIndexes, end: cursor };
1493
+ }
1494
+ function hasCoordinatedExplicitPostposedTail(text, start, end) {
1495
+ let cursor = stickyMatchEnd(POSTPOSED_SUBJECT_NOUN, text, start, end) ?? start;
1496
+ while (cursor < end) {
1497
+ const trailingStart = boundedHorizontalCursor(text, cursor, end);
1498
+ if (trailingStart === end)
1499
+ return true;
1500
+ if (/[,,،、]/u.test(text[trailingStart] ?? '')
1501
+ && boundedHorizontalCursor(text, trailingStart + 1, end) === end)
1502
+ return true;
1503
+ const separatorEnd = stickyMatchEnd(COORDINATED_EXPLICIT_POSTPOSED_SEPARATOR, text, cursor, end);
1504
+ if (separatorEnd === null)
1505
+ return false;
1506
+ const subjectEnd = stickyMatchEnd(COORDINATED_EXPLICIT_POSTPOSED_SUBJECT, text, separatorEnd, end);
1507
+ if (subjectEnd === null)
1508
+ return false;
1509
+ cursor = stickyMatchEnd(POSTPOSED_SUBJECT_NOUN, text, subjectEnd, end) ?? subjectEnd;
1510
+ }
1511
+ return true;
1512
+ }
1513
+ function coordinatedPostposedSubjectGroupStartIndex(text, candidates, firstIndex, lastIndex) {
1514
+ let groupStartIndex = lastIndex;
1515
+ while (groupStartIndex > firstIndex
1516
+ && hasCoordinatedExplicitPostposedTail(text, candidates[groupStartIndex - 1].end, candidates[groupStartIndex].end)) {
1517
+ groupStartIndex -= 1;
1518
+ }
1519
+ return groupStartIndex;
1520
+ }
1521
+ function mixedImplicitPostposedSubjectStart(text, candidate, prefixNegations, candidates, candidateIndexByStart, predicateStart) {
1522
+ if (!prefixNegations.firstPostposedSubjectCandidates.has(candidate.start))
1523
+ return candidate.start;
1524
+ const candidateIndex = candidateIndexByStart.get(candidate.start);
1525
+ const start = prefixNegations.postposedSubjectStarts.get(candidate.start) ?? candidate.start;
1526
+ const chain = collectMixedSubjectChain(text, start, predicateStart, candidates, candidateIndexByStart);
1527
+ return chain
1528
+ && candidateIndex !== undefined
1529
+ && chain.explicitIndexes.includes(candidateIndex)
1530
+ && boundedHorizontalCursor(text, chain.end, predicateStart) === predicateStart
1531
+ ? chain.start
1532
+ : candidate.start;
1533
+ }
1534
+ function collectPostposedExplicitNegationIndex(text, candidates) {
1535
+ const ends = new Map();
1536
+ const implicitBlocks = [];
1537
+ const prefixNegations = collectExplicitPrefixNegationIndex(text, candidates);
1538
+ const candidateIndexByStart = new Map();
1539
+ for (const [index, candidate] of candidates.entries())
1540
+ candidateIndexByStart.set(candidate.start, index);
1541
+ let firstCandidateIndex = 0;
1542
+ while (firstCandidateIndex < candidates.length) {
1543
+ const start = lineStartForPosition(text, candidates[firstCandidateIndex].start);
1544
+ const end = lineEnd(text, candidates[firstCandidateIndex].end);
1545
+ let afterLineCandidateIndex = firstCandidateIndex;
1546
+ while (afterLineCandidateIndex < candidates.length && candidates[afterLineCandidateIndex].start < end)
1547
+ afterLineCandidateIndex += 1;
1548
+ const predicateScanner = new RegExp(POSTPOSED_NEGATIVE_PREDICATE.source, 'giu');
1549
+ const normalizedLine = normalizeGrammarApostrophes(text.slice(start, end));
1550
+ let predicate;
1551
+ let lastCandidateIndex = firstCandidateIndex - 1;
1552
+ const groupStartIndexesByEndpoint = new Map();
1553
+ let lastEvaluatedCandidateIndex = -1;
1554
+ while ((predicate = predicateScanner.exec(normalizedLine)) !== null) {
1555
+ const predicateTokenStart = firstGrammarTokenStart(normalizedLine, predicate.index, predicateScanner.lastIndex);
1556
+ if (!hasUnicodeGrammarTokenBoundaries(normalizedLine, predicateTokenStart, predicateScanner.lastIndex)
1557
+ || !hasAsciiGrammarTokenCharacters(normalizedLine, predicateTokenStart, predicateScanner.lastIndex))
1558
+ continue;
1559
+ const predicateStart = start + predicate.index;
1560
+ while (lastCandidateIndex + 1 < afterLineCandidateIndex && candidates[lastCandidateIndex + 1].end <= predicateStart) {
1561
+ lastCandidateIndex += 1;
1562
+ }
1563
+ if (lastCandidateIndex < firstCandidateIndex)
1564
+ continue;
1565
+ if (lastCandidateIndex === lastEvaluatedCandidateIndex)
1566
+ continue;
1567
+ lastEvaluatedCandidateIndex = lastCandidateIndex;
1568
+ const groupEndpoint = candidates[lastCandidateIndex].end;
1569
+ let groupStartIndex = groupStartIndexesByEndpoint.get(groupEndpoint);
1570
+ if (groupStartIndex === undefined) {
1571
+ groupStartIndex = coordinatedPostposedSubjectGroupStartIndex(text, candidates, firstCandidateIndex, lastCandidateIndex);
1572
+ groupStartIndexesByEndpoint.set(groupEndpoint, groupStartIndex);
1573
+ }
1574
+ if (!hasCoordinatedExplicitPostposedTail(text, candidates[groupStartIndex].end, predicateStart))
1575
+ continue;
1576
+ const predicateEnd = start + predicateScanner.lastIndex;
1577
+ const positiveTransition = directivePrefixAfter(text, predicateEnd, end);
1578
+ const negationEnd = positiveTransition?.start ?? postposedNegationClauseEnd(text, predicateEnd, end);
1579
+ for (let index = groupStartIndex; index <= lastCandidateIndex; index += 1)
1580
+ ends.set(candidates[index].start, negationEnd);
1581
+ const subjectStart = mixedImplicitPostposedSubjectStart(text, candidates[groupStartIndex], prefixNegations, candidates, candidateIndexByStart, predicateStart);
1582
+ implicitBlocks.push({
1583
+ start: subjectStart,
1584
+ end: positiveTransition?.start ?? postposedNegationClauseEnd(text, predicateEnd, end),
1585
+ });
1586
+ }
1587
+ firstCandidateIndex = afterLineCandidateIndex;
1588
+ }
1589
+ return {
1590
+ ends,
1591
+ prefixNegatedStarts: prefixNegations.negatedStarts,
1592
+ implicitBlocks: createInertRangeIndex(implicitBlocks),
1593
+ };
1594
+ }
1595
+ function postposedExplicitNegationEnd(index, candidate) {
1596
+ return index.ends.get(candidate.start) ?? null;
1597
+ }
1598
+ function hasPostposedExplicitNegation(index, candidate) {
1599
+ return postposedExplicitNegationEnd(index, candidate) !== null;
1600
+ }
1601
+ function isNegativeExplicitMention(candidate, postposedNegations) {
1602
+ return hasPostposedExplicitNegation(postposedNegations, candidate)
1603
+ || postposedNegations.prefixNegatedStarts.has(candidate.start);
1604
+ }
1605
+ function isInertOrNegativeMention(candidate, postposedNegations) {
1606
+ return [...candidate.reasons].some((reason) => reason !== 'not-leading-region') || isNegativeExplicitMention(candidate, postposedNegations);
1607
+ }
1608
+ function listItemDocumentationTokenRange(text, candidateStart, blockEnd) {
1609
+ const lineStart = lineStartForPosition(text, candidateStart);
1610
+ const leading = leadingDirectiveCursor(text, lineStart, false);
1611
+ if (!leading.listItem)
1612
+ return null;
1613
+ const end = lineEnd(text, blockEnd);
1614
+ const directiveTarget = directiveCommandTargetStart(text, leading.cursor);
1615
+ const tokenStart = directiveTarget !== null && directiveTarget < end && text[directiveTarget] === '$'
1616
+ ? directiveTarget
1617
+ : leading.cursor;
1618
+ const tokenSequence = /^(?:(?:\$(?:oh-my-codex:)?[A-Za-z][A-Za-z0-9_-]*)|(?:\/prompts:[\w.-]+))(?:(?:\s*,\s*(?:(?:and|or)\s+)?|\s+(?:and|or)\s+|\s*\/\s*)(?:(?:\$(?:oh-my-codex:)?[A-Za-z][A-Za-z0-9_-]*)|(?:\/prompts:[\w.-]+)))*/iu.exec(text.slice(tokenStart, end));
1619
+ if (!tokenSequence || !LIST_DOCUMENTATION_SUFFIX.test(text.slice(tokenStart + tokenSequence[0].length, end)))
1620
+ return null;
1621
+ return { start: tokenStart, end: tokenStart + tokenSequence[0].length };
1622
+ }
1623
+ function isListItemDocumentation(text, candidateStart, blockEnd) {
1624
+ return listItemDocumentationTokenRange(text, candidateStart, blockEnd) !== null;
1625
+ }
1626
+ function hasActivePromptsTokenBoundary(text, start, end) {
1627
+ if (hasOddImmediateBackslashes(text, start))
1628
+ return false;
1629
+ const previous = codePointBefore(text, start);
1630
+ const validPrevious = !previous || SAFE_TOKEN_WHITESPACE.test(previous) || /[(\[{]/u.test(previous);
1631
+ return validPrevious && isExplicitTokenBoundary(text, end);
1632
+ }
1633
+ function isMarkdownTableDelimiter(line) {
1634
+ const trimmed = line.trim();
1635
+ if (!trimmed.includes('|'))
1636
+ return false;
1637
+ const cells = trimmed.replace(/^\|/u, '').replace(/\|$/u, '').split('|');
1638
+ return cells.length >= 2 && cells.every((cell) => /^\s*:?-{3,}:?\s*$/u.test(cell));
1639
+ }
1640
+ function collectMarkdownTableRanges(text) {
1641
+ const ranges = [];
1642
+ let start = 0;
1643
+ while (start < text.length) {
1644
+ const headerEnd = lineEnd(text, start);
1645
+ const delimiterStart = headerEnd < text.length ? nextLineStart(text, headerEnd) : text.length;
1646
+ const delimiterEnd = delimiterStart < text.length ? lineEnd(text, delimiterStart) : text.length;
1647
+ if (text.slice(start, headerEnd).includes('|') && isMarkdownTableDelimiter(text.slice(delimiterStart, delimiterEnd))) {
1648
+ let tableEnd = delimiterEnd;
1649
+ let rowStart = delimiterEnd < text.length ? nextLineStart(text, delimiterEnd) : text.length;
1650
+ while (rowStart < text.length) {
1651
+ const rowEnd = lineEnd(text, rowStart);
1652
+ if (!text.slice(rowStart, rowEnd).includes('|'))
1653
+ break;
1654
+ tableEnd = rowEnd;
1655
+ rowStart = rowEnd < text.length ? nextLineStart(text, rowEnd) : text.length;
1656
+ }
1657
+ ranges.push({ start, end: tableEnd });
1658
+ if (tableEnd === text.length)
1659
+ break;
1660
+ start = nextLineStart(text, tableEnd);
685
1661
  continue;
686
- const rawKeyword = match[0].slice(match[0].lastIndexOf('$')).toLowerCase();
687
- const normalizedSkill = normalizeExplicitSkillToken(token);
688
- const registryEntry = KEYWORD_TRIGGER_DEFINITIONS.find((entry) => entry.skill.toLowerCase() === normalizedSkill);
689
- const matchStart = match.index + match[0].lastIndexOf('$');
690
- if (captureStarted) {
691
- const between = text.slice(lastMatchEnd, matchStart);
692
- if (!/^\s*$/.test(between))
1662
+ }
1663
+ if (headerEnd === text.length)
1664
+ break;
1665
+ start = nextLineStart(text, headerEnd);
1666
+ }
1667
+ return ranges;
1668
+ }
1669
+ function normalizeMarkdownReferenceLabel(label) {
1670
+ return label
1671
+ .trim()
1672
+ .replace(/\s+/gu, ' ')
1673
+ .normalize('NFKC')
1674
+ .toLowerCase()
1675
+ .toUpperCase()
1676
+ .toLowerCase();
1677
+ }
1678
+ function markdownLabelClosingIndex(text, start, limit) {
1679
+ for (let cursor = start; cursor < limit; cursor += 1) {
1680
+ if (text[cursor] === ']' && !hasOddImmediateBackslashes(text, cursor))
1681
+ return cursor;
1682
+ }
1683
+ return -1;
1684
+ }
1685
+ function referenceDestinationEnd(text, start, end) {
1686
+ if (/["'(`]/u.test(text[start] ?? ''))
1687
+ return null;
1688
+ const destination = /^(?:<[^>\r\n]*>|\S+)/u.exec(text.slice(start, end));
1689
+ return destination ? start + destination[0].length : null;
1690
+ }
1691
+ function referenceTitleStartAfterDestination(text, start, end) {
1692
+ const destinationEnd = referenceDestinationEnd(text, start, end);
1693
+ if (destinationEnd === null)
1694
+ return null;
1695
+ const titlePrefix = /^[ \t]+(["'(])/u.exec(text.slice(destinationEnd, end));
1696
+ return titlePrefix ? destinationEnd + titlePrefix[0].length - 1 : null;
1697
+ }
1698
+ function referenceTitleRange(text, start) {
1699
+ const opening = text[start];
1700
+ const closing = opening === '(' ? ')' : opening;
1701
+ for (let cursor = start + 1; cursor < text.length; cursor += 1) {
1702
+ if (text[cursor] === closing && !hasOddImmediateBackslashes(text, cursor)) {
1703
+ return { start, end: cursor + 1 };
1704
+ }
1705
+ }
1706
+ return { start, end: text.length };
1707
+ }
1708
+ function collectMarkdownReferenceIndex(text) {
1709
+ const labels = new Set();
1710
+ const destinationRanges = [];
1711
+ const predecessorRanges = [];
1712
+ const codeIndex = createInertRangeIndex([...collectFencedCodeRanges(text), ...collectIndentedCodeRanges(text)]);
1713
+ const tablePredecessorRanges = collectMarkdownTableRanges(text);
1714
+ const tableRanges = createInertRangeIndex([...tablePredecessorRanges]);
1715
+ let titleMaskEnd = -1;
1716
+ let start = 0;
1717
+ while (start <= text.length) {
1718
+ if (start < titleMaskEnd) {
1719
+ const maskedLineEnd = lineEnd(text, start);
1720
+ if (maskedLineEnd === text.length)
693
1721
  break;
1722
+ start = nextLineStart(text, maskedLineEnd);
1723
+ continue;
1724
+ }
1725
+ const end = lineEnd(text, start);
1726
+ const line = text.slice(start, end);
1727
+ const containerPrefix = /^[ \t]{0,3}(?:(?:>[ \t]{0,4})|(?:(?:[-+*]|\d{1,9}[.)])[ \t]+))*/u.exec(line)?.[0] ?? '';
1728
+ const labelStart = start + containerPrefix.length;
1729
+ if (!isInInertRange(codeIndex, labelStart) && text[labelStart] === '[') {
1730
+ const closing = markdownLabelClosingIndex(text, labelStart + 1, end);
1731
+ if (closing > labelStart) {
1732
+ const colon = /^\s*:\s*/u.exec(text.slice(closing + 1, end));
1733
+ if (colon) {
1734
+ const inlineDestinationStart = closing + 1 + colon[0].length;
1735
+ let destinationStart = inlineDestinationStart;
1736
+ let destinationLineEnd = end;
1737
+ let destinationEnd = destinationStart < end && !isInInertRange(codeIndex, destinationStart)
1738
+ ? referenceDestinationEnd(text, destinationStart, destinationLineEnd)
1739
+ : null;
1740
+ if (destinationEnd === null && inlineDestinationStart === end && end < text.length) {
1741
+ const continuationStart = nextLineStart(text, end);
1742
+ destinationLineEnd = lineEnd(text, continuationStart);
1743
+ const indentation = /^[ \t]{0,3}/u.exec(text.slice(continuationStart, destinationLineEnd))?.[0].length ?? 0;
1744
+ destinationStart = continuationStart + indentation;
1745
+ destinationEnd = destinationStart < destinationLineEnd && !isInInertRange(codeIndex, destinationStart)
1746
+ ? referenceDestinationEnd(text, destinationStart, destinationLineEnd)
1747
+ : null;
1748
+ }
1749
+ if (destinationEnd !== null) {
1750
+ const destinationRange = { start: destinationStart, end: destinationEnd };
1751
+ destinationRanges.push(destinationRange);
1752
+ predecessorRanges.push(destinationRange);
1753
+ let titleStart = referenceTitleStartAfterDestination(text, destinationStart, destinationLineEnd);
1754
+ if (titleStart === null && destinationLineEnd < text.length) {
1755
+ const titleLineStart = nextLineStart(text, destinationLineEnd);
1756
+ const titleLineEnd = lineEnd(text, titleLineStart);
1757
+ const titleIndent = /^[ \t]{0,3}/u.exec(text.slice(titleLineStart, titleLineEnd));
1758
+ const candidateStart = titleLineStart + (titleIndent?.[0].length ?? 0);
1759
+ if (titleIndent && /["'(]/u.test(text[candidateStart] ?? ''))
1760
+ titleStart = candidateStart;
1761
+ }
1762
+ if (titleStart !== null) {
1763
+ const titleRange = referenceTitleRange(text, titleStart);
1764
+ destinationRanges.push(titleRange);
1765
+ if (text[titleRange.end - 1] === (text[titleStart] === '(' ? ')' : text[titleStart]))
1766
+ predecessorRanges.push(titleRange);
1767
+ titleMaskEnd = Math.max(titleMaskEnd, titleRange.end);
1768
+ }
1769
+ labels.add(normalizeMarkdownReferenceLabel(text.slice(labelStart + 1, closing)));
1770
+ }
1771
+ }
1772
+ }
1773
+ }
1774
+ if (end === text.length)
1775
+ break;
1776
+ start = nextLineStart(text, end);
1777
+ }
1778
+ return {
1779
+ labels,
1780
+ destinationRanges: createInertRangeIndex(destinationRanges),
1781
+ tableRanges,
1782
+ tablePredecessorRanges,
1783
+ predecessorRanges,
1784
+ };
1785
+ }
1786
+ function hasMarkdownReferenceDefinition(index, label) {
1787
+ return index.labels.has(normalizeMarkdownReferenceLabel(label));
1788
+ }
1789
+ function balancedMarkdownLinkRange(text, labelStart, destinationStart, lineLimit) {
1790
+ let depth = 1;
1791
+ let quote = null;
1792
+ for (let cursor = destinationStart + 2; cursor < lineLimit; cursor += 1) {
1793
+ if (hasOddImmediateBackslashes(text, cursor))
1794
+ continue;
1795
+ const character = text[cursor];
1796
+ if (quote) {
1797
+ if (character === quote)
1798
+ quote = null;
1799
+ continue;
694
1800
  }
695
- captureStarted = true;
696
- lastMatchEnd = matchStart + rawKeyword.length;
697
- if (!registryEntry)
1801
+ if ((character === '"' || character === "'") && /\s/u.test(text[cursor - 1] ?? '')) {
1802
+ quote = character;
698
1803
  continue;
699
- if (results.some((item) => item.skill === normalizedSkill))
1804
+ }
1805
+ if (character === '(')
1806
+ depth += 1;
1807
+ else if (character === ')') {
1808
+ depth -= 1;
1809
+ if (depth === 0)
1810
+ return { start: labelStart, end: cursor + 1 };
1811
+ }
1812
+ }
1813
+ return null;
1814
+ }
1815
+ function enclosingMarkdownLinkRange(text, start, end) {
1816
+ const lineStart = lineStartForPosition(text, start);
1817
+ const lineLimit = lineEnd(text, end);
1818
+ const destinationStart = text.lastIndexOf('](', start);
1819
+ if (destinationStart < lineStart)
1820
+ return null;
1821
+ const labelStart = text.lastIndexOf('[', destinationStart);
1822
+ if (labelStart < lineStart)
1823
+ return null;
1824
+ const range = balancedMarkdownLinkRange(text, labelStart, destinationStart, lineLimit);
1825
+ return range && start >= destinationStart + 2 && end <= range.end ? range : null;
1826
+ }
1827
+ function markdownDocumentationRange(text, start, end, referenceIndex) {
1828
+ const lineStart = lineStartForPosition(text, start);
1829
+ const lineLimit = lineEnd(text, end);
1830
+ const line = text.slice(lineStart, lineLimit);
1831
+ const tableEnd = inertRangeEndAt(referenceIndex.tableRanges, start);
1832
+ if (tableEnd !== null)
1833
+ return { start: lineStart, end: tableEnd };
1834
+ if (/^(?:#{1,6}\s|\|)/u.test(line.trimStart()))
1835
+ return { start: lineStart, end: lineLimit };
1836
+ const enclosingLink = enclosingMarkdownLinkRange(text, start, end);
1837
+ if (enclosingLink)
1838
+ return enclosingLink;
1839
+ const labelStart = text.lastIndexOf('[', start);
1840
+ if (labelStart >= lineStart) {
1841
+ const inlineLabelEnd = text.indexOf('](', end);
1842
+ if (inlineLabelEnd >= end && inlineLabelEnd < lineLimit) {
1843
+ return balancedMarkdownLinkRange(text, labelStart, inlineLabelEnd, lineLimit)
1844
+ ?? { start: labelStart, end: inlineLabelEnd + 2 };
1845
+ }
1846
+ const referenceLabelEnd = text.indexOf('][', end);
1847
+ if (referenceLabelEnd >= end && referenceLabelEnd < lineLimit) {
1848
+ const closingBracket = text.indexOf(']', referenceLabelEnd + 2);
1849
+ return closingBracket >= 0 && closingBracket < lineLimit
1850
+ ? { start: labelStart, end: closingBracket + 1 }
1851
+ : { start: labelStart, end: referenceLabelEnd + 2 };
1852
+ }
1853
+ const closingLabel = markdownLabelClosingIndex(text, end, lineLimit);
1854
+ if (closingLabel >= end && closingLabel < lineLimit) {
1855
+ const beforeLabel = text.slice(lineStart, labelStart);
1856
+ const afterLabel = text.slice(closingLabel + 1, lineLimit);
1857
+ if (!beforeLabel.trim() && (/^\s*:\s*\S/u.test(afterLabel) || !afterLabel.trim())) {
1858
+ return { start: lineStart, end: lineLimit };
1859
+ }
1860
+ const label = text.slice(labelStart + 1, closingLabel);
1861
+ if (hasMarkdownReferenceDefinition(referenceIndex, label))
1862
+ return { start: labelStart, end: closingLabel + 1 };
1863
+ }
1864
+ }
1865
+ if (lineLimit < text.length) {
1866
+ const followingStart = nextLineStart(text, lineLimit);
1867
+ const followingEnd = lineEnd(text, followingStart);
1868
+ const followingLine = text.slice(followingStart, followingEnd);
1869
+ if (/^\s*(?:={3,}|-{3,})\s*$/u.test(followingLine) || (line.includes('|') && isMarkdownTableDelimiter(followingLine))) {
1870
+ return { start: lineStart, end: followingEnd };
1871
+ }
1872
+ }
1873
+ return null;
1874
+ }
1875
+ function isMarkdownDocumentationMention(text, start, end, referenceIndex) {
1876
+ return markdownDocumentationRange(text, start, end, referenceIndex) !== null;
1877
+ }
1878
+ function isInactivePromptsMention(text, start, end, inertRangeIndexes, referenceIndex) {
1879
+ return !hasActivePromptsTokenBoundary(text, start, end)
1880
+ || isMarkdownDocumentationMention(text, start, end, referenceIndex)
1881
+ || isStructurallyInert(inertRangeIndexes, start)
1882
+ || isInInertRange(referenceIndex.destinationRanges, start)
1883
+ || isListItemDocumentation(text, start, end);
1884
+ }
1885
+ function hasDirectPromptsInvocation(text, inertRangeIndexes, referenceIndex) {
1886
+ const leading = leadingDirectiveCursor(text, 0, true);
1887
+ if (isStructurallyInert(inertRangeIndexes, leading.cursor))
1888
+ return false;
1889
+ const pattern = new RegExp(PROMPTS_TOKEN_PATTERN.source, PROMPTS_TOKEN_PATTERN.flags);
1890
+ pattern.lastIndex = leading.cursor;
1891
+ const match = pattern.exec(text);
1892
+ if (!match || match.index !== leading.cursor)
1893
+ return false;
1894
+ const matchEnd = match.index + match[0].length;
1895
+ if (!hasActivePromptsTokenBoundary(text, match.index, matchEnd))
1896
+ return false;
1897
+ return (!leading.listItem || !isListItemDocumentation(text, leading.cursor, matchEnd))
1898
+ && !isMarkdownDocumentationMention(text, match.index, matchEnd, referenceIndex);
1899
+ }
1900
+ function boundedPredecessorRanges(text, referenceIndex) {
1901
+ const inertRangeIndexes = collectInertRangeIndexes(text);
1902
+ const structuralRanges = [
1903
+ ...collectIndentedCodeRanges(text),
1904
+ ...collectFencedCodeRanges(text),
1905
+ ...collectBlockquoteRanges(text),
1906
+ ...collectInlineCodeRanges(text),
1907
+ ...collectQuoteRanges(text),
1908
+ ];
1909
+ const unboundedStructuralRanges = structuralRanges.filter((range) => !range.bounded);
1910
+ const unboundedStructuralIndex = createInertRangeIndex(unboundedStructuralRanges);
1911
+ const isOutsideUnboundedStructuralParent = (range) => {
1912
+ const parentEnd = inertRangeEndAt(unboundedStructuralIndex, range.start);
1913
+ return parentEnd === null || range.end > parentEnd;
1914
+ };
1915
+ const ranges = [
1916
+ ...referenceIndex.predecessorRanges.filter(isOutsideUnboundedStructuralParent),
1917
+ ...referenceIndex.tablePredecessorRanges.filter(isOutsideUnboundedStructuralParent),
1918
+ ...structuralRanges
1919
+ .filter((range) => range.bounded && isOutsideUnboundedStructuralParent(range))
1920
+ .map(({ start, end }) => ({ start, end })),
1921
+ ];
1922
+ const pattern = new RegExp(PROMPTS_TOKEN_PATTERN.source, PROMPTS_TOKEN_PATTERN.flags);
1923
+ let match;
1924
+ while ((match = pattern.exec(text)) !== null) {
1925
+ const start = match.index;
1926
+ const end = start + match[0].length;
1927
+ if (!isExplicitTokenBoundary(text, end))
1928
+ continue;
1929
+ if (isInInertRange(unboundedStructuralIndex, start))
1930
+ continue;
1931
+ const inactive = isInactivePromptsMention(text, start, end, inertRangeIndexes, referenceIndex);
1932
+ const lineStart = lineStartForPosition(text, start);
1933
+ const leading = leadingDirectiveCursor(text, lineStart, false);
1934
+ if (!inactive && directiveCommandTargetStart(text, leading.cursor) !== start)
700
1935
  continue;
701
- results.push({
702
- keyword: rawKeyword,
703
- skill: normalizedSkill,
704
- priority: registryEntry.priority,
1936
+ const markdownRange = inactive ? markdownDocumentationRange(text, start, end, referenceIndex) : null;
1937
+ const referenceEnd = inactive ? inertRangeEndAt(referenceIndex.destinationRanges, start) : null;
1938
+ ranges.push({
1939
+ start: markdownRange?.start ?? start,
1940
+ end: inactive && isListItemDocumentation(text, start, end) ? lineEnd(text, end) : Math.max(markdownRange?.end ?? end, referenceEnd ?? end),
705
1941
  });
706
1942
  }
1943
+ return ranges.sort((left, right) => left.start - right.start || left.end - right.end);
1944
+ }
1945
+ function hasOddImmediateBackslashes(text, index) {
1946
+ let count = 0;
1947
+ for (let cursor = index - 1; cursor >= 0 && text[cursor] === '\\'; cursor -= 1)
1948
+ count += 1;
1949
+ return count % 2 === 1;
1950
+ }
1951
+ function scanExplicitCandidates(text) {
1952
+ const inertRangeIndexes = collectInertRangeIndexes(text);
1953
+ const candidates = [];
1954
+ const dollarScanner = /\$/gu;
1955
+ let dollar;
1956
+ while ((dollar = dollarScanner.exec(text)) !== null) {
1957
+ const start = dollar.index;
1958
+ EXPLICIT_TOKEN_START.lastIndex = start;
1959
+ const canonicalMatch = EXPLICIT_TOKEN_START.exec(text);
1960
+ const canonicalEnd = canonicalMatch?.index === start ? start + canonicalMatch[0].length : null;
1961
+ const firstCharacter = codePointAt(text, start + 1);
1962
+ if (canonicalEnd === null && (!firstCharacter || SAFE_TOKEN_WHITESPACE.test(firstCharacter) || isExplicitTokenBoundary(text, start + 1)))
1963
+ continue;
1964
+ const initialEnd = canonicalEnd ?? start + 1;
1965
+ const end = maximalExplicitTokenEnd(text, initialEnd);
1966
+ const rawKeyword = text.slice(start, end);
1967
+ dollarScanner.lastIndex = Math.max(end, start + 1);
1968
+ const reasons = new Set();
1969
+ for (const reason of STRUCTURAL_INERT_DIAGNOSTICS) {
1970
+ if (isInInertRange(inertRangeIndexes[reason], start))
1971
+ reasons.add(reason);
1972
+ }
1973
+ if (hasOddImmediateBackslashes(text, start))
1974
+ reasons.add('escaped');
1975
+ const canonicalToken = canonicalEnd === end ? (canonicalMatch?.[1] ?? '').toLowerCase() : '';
1976
+ const normalizedToken = canonicalToken || rawKeyword.replace(/^\$(?:(?:[Oo][Hh]-[Mm][Yy]-[Cc][Oo][Dd][Ee][Xx]):)?/u, '').toLowerCase();
1977
+ const definition = canonicalToken ? getExplicitSkillDefinition(canonicalToken) : undefined;
1978
+ candidates.push({
1979
+ rawKeyword,
1980
+ normalizedToken,
1981
+ skill: definition?.skill ?? null,
1982
+ priority: definition?.priority ?? null,
1983
+ start,
1984
+ end,
1985
+ reasons,
1986
+ });
1987
+ }
1988
+ return candidates;
1989
+ }
1990
+ function boundedHorizontalCursor(text, start, limit) {
1991
+ let cursor = start;
1992
+ while (cursor < limit && /[^\S\r\n\u2028\u2029]/u.test(text[cursor] ?? ''))
1993
+ cursor += 1;
1994
+ return cursor;
1995
+ }
1996
+ function documentationFollowupAt(text, start, lineLimit, candidates, candidateIndexByStart, cache) {
1997
+ const cached = cache.get(start);
1998
+ if (cached !== undefined)
1999
+ return cached;
2000
+ let cursor = boundedHorizontalCursor(text, start, lineLimit);
2001
+ const commandTarget = directiveCommandTargetStart(text, cursor, lineLimit);
2002
+ if (commandTarget !== null)
2003
+ cursor = commandTarget;
2004
+ const chain = collectMixedSubjectChain(text, cursor, lineLimit, candidates, candidateIndexByStart);
2005
+ const followup = chain
2006
+ ? {
2007
+ end: chain.end,
2008
+ reopenable: chain.explicitIndexes.length > 0
2009
+ ? !hasExplicitDocumentationSuffixAt(text, chain.end, lineLimit)
2010
+ : !hasImplicitDocumentationSubjectPrefix(text.slice(chain.start, lineLimit)),
2011
+ }
2012
+ : null;
2013
+ cache.set(start, followup);
2014
+ return followup;
2015
+ }
2016
+ function isDocumentationClauseSeparatorAt(text, index) {
2017
+ const character = codePointAt(text, index);
2018
+ const normalized = normalizedPunctuation(character);
2019
+ return /[,;؛.!??؟。!?::،、]/u.test(normalized)
2020
+ && (normalized !== '.' || !isAbbreviationDotAt(text, index));
2021
+ }
2022
+ function isStrongDocumentationClauseSeparatorAt(text, index) {
2023
+ const normalized = normalizedPunctuation(codePointAt(text, index));
2024
+ return /[;؛.!??؟。!?::]/u.test(normalized)
2025
+ && (normalized !== '.' || !isAbbreviationDotAt(text, index));
2026
+ }
2027
+ function explicitDocumentationClauseEnd(text, start, lineLimit, candidates, candidateIndexByStart, followups) {
2028
+ for (let cursor = start; cursor < lineLimit;) {
2029
+ if (isDocumentationClauseSeparatorAt(text, cursor)) {
2030
+ const followup = documentationFollowupAt(text, cursor + codePointAt(text, cursor).length, lineLimit, candidates, candidateIndexByStart, followups);
2031
+ if (followup?.reopenable)
2032
+ return cursor;
2033
+ if (followup) {
2034
+ cursor = followup.end;
2035
+ continue;
2036
+ }
2037
+ }
2038
+ cursor += codePointAt(text, cursor).length;
2039
+ }
2040
+ return documentationDirectiveTransitionStart(text, start, lineLimit) ?? lineLimit;
2041
+ }
2042
+ function explicitDocumentationSuffixClauseEnd(text, start, lineLimit, candidates, candidateIndexByStart, followups) {
2043
+ for (let cursor = start; cursor < lineLimit;) {
2044
+ if (isDocumentationClauseSeparatorAt(text, cursor)) {
2045
+ if (isStrongDocumentationClauseSeparatorAt(text, cursor))
2046
+ return cursor;
2047
+ const followup = documentationFollowupAt(text, cursor + codePointAt(text, cursor).length, lineLimit, candidates, candidateIndexByStart, followups);
2048
+ if (followup?.reopenable)
2049
+ return cursor;
2050
+ if (followup) {
2051
+ cursor = followup.end;
2052
+ continue;
2053
+ }
2054
+ }
2055
+ cursor += codePointAt(text, cursor).length;
2056
+ }
2057
+ return documentationDirectiveTransitionStart(text, start, lineLimit) ?? lineLimit;
2058
+ }
2059
+ function hasStrongDocumentationClauseBoundary(text, start, end) {
2060
+ for (let cursor = start; cursor < end;) {
2061
+ if (isStrongDocumentationClauseSeparatorAt(text, cursor))
2062
+ return true;
2063
+ cursor += codePointAt(text, cursor).length;
2064
+ }
2065
+ return false;
2066
+ }
2067
+ function isCompactSlashDocumentationCandidate(candidate) {
2068
+ const parts = candidate.rawKeyword.split('/');
2069
+ if (parts.length < 2)
2070
+ return false;
2071
+ return parts.every((part) => {
2072
+ const match = /^\$(?:(?:[Oo][Hh]-[Mm][Yy]-[Cc][Oo][Dd][Ee][Xx]):)?([A-Za-z][A-Za-z0-9_-]*)$/u.exec(part);
2073
+ return match !== null && getExplicitSkillDefinition((match[1] ?? '').toLowerCase()) !== undefined;
2074
+ });
2075
+ }
2076
+ function documentationSubjectChainAtClauseStart(text, start, lineLimit, candidates, candidateIndexByStart) {
2077
+ const contentStart = boundedHorizontalCursor(text, start, lineLimit);
2078
+ const commandTarget = directiveCommandTargetStart(text, contentStart, lineLimit);
2079
+ return collectMixedSubjectChain(text, commandTarget ?? contentStart, lineLimit, candidates, candidateIndexByStart);
2080
+ }
2081
+ function independentDocumentaryClauseStart(text, clauseStart, chain, lineLimit, candidateIndexByStart) {
2082
+ const contentStart = boundedHorizontalCursor(text, clauseStart, lineLimit);
2083
+ const directiveTarget = directiveCommandTargetStart(text, contentStart, lineLimit);
2084
+ if (directiveTarget === null || directiveTarget !== chain.start || candidateIndexByStart.has(directiveTarget))
2085
+ return null;
2086
+ const coordinator = new RegExp(`[,,،、]\\s*(?:${ASCII_AND}|${asciiCaseWordPattern('or')}|${asciiCaseWordPattern('nor')})\\s+`, 'u').exec(text.slice(chain.start, chain.end));
2087
+ return coordinator
2088
+ ? boundedHorizontalCursor(text, chain.start + coordinator.index + coordinator[0].length, lineLimit)
2089
+ : null;
2090
+ }
2091
+ function findMixedSubjectChainInDocumentationClause(text, start, limit, candidates, candidateIndexByStart) {
2092
+ for (let cursor = boundedHorizontalCursor(text, start, limit); cursor < limit;) {
2093
+ const chain = collectMixedSubjectChain(text, cursor, limit, candidates, candidateIndexByStart);
2094
+ if (chain) {
2095
+ if (chain.explicitIndexes.length > 0)
2096
+ return chain;
2097
+ cursor = chain.end;
2098
+ continue;
2099
+ }
2100
+ cursor += codePointAt(text, cursor).length;
2101
+ }
2102
+ return null;
2103
+ }
2104
+ function collectImplicitDocumentationLineRanges(text, candidates, candidateIndexByStart) {
2105
+ const ranges = [];
2106
+ const followups = new Map();
2107
+ let lineStart = 0;
2108
+ while (lineStart < text.length) {
2109
+ const lineLimit = lineEnd(text, lineStart);
2110
+ let clauseStart = lineStart;
2111
+ while (clauseStart < lineLimit) {
2112
+ const clauseLimit = documentationSuffixLimit(text, clauseStart, lineLimit);
2113
+ const clauseText = text.slice(clauseStart, clauseLimit);
2114
+ let subjectChain = documentationSubjectChainAtClauseStart(text, clauseStart, clauseLimit, candidates, candidateIndexByStart);
2115
+ const directDocumentation = Boolean(subjectChain
2116
+ && (subjectChain.explicitIndexes.length > 0
2117
+ ? hasExplicitDocumentationSuffixAt(text, subjectChain.end, clauseLimit)
2118
+ : hasImplicitDocumentationSubjectPrefix(text.slice(subjectChain.start, clauseLimit))));
2119
+ const leadingDocumentation = DOCUMENTATION_LEADING_PATTERN.test(clauseText);
2120
+ if (directDocumentation || leadingDocumentation) {
2121
+ if (!subjectChain && leadingDocumentation) {
2122
+ subjectChain = findMixedSubjectChainInDocumentationClause(text, clauseStart, clauseLimit, candidates, candidateIndexByStart);
2123
+ }
2124
+ const rangeStart = subjectChain
2125
+ ? independentDocumentaryClauseStart(text, clauseStart, subjectChain, lineLimit, candidateIndexByStart) ?? clauseStart
2126
+ : clauseStart;
2127
+ const rangeEnd = subjectChain
2128
+ ? explicitDocumentationClauseEnd(text, subjectChain.end, lineLimit, candidates, candidateIndexByStart, followups)
2129
+ : clauseLimit;
2130
+ ranges.push({ start: rangeStart, end: rangeEnd });
2131
+ if (rangeEnd >= lineLimit)
2132
+ break;
2133
+ clauseStart = rangeEnd + codePointAt(text, rangeEnd).length;
2134
+ continue;
2135
+ }
2136
+ if (clauseLimit >= lineLimit)
2137
+ break;
2138
+ clauseStart = clauseLimit + codePointAt(text, clauseLimit).length;
2139
+ }
2140
+ const next = nextLineStart(text, lineLimit);
2141
+ if (next <= lineStart)
2142
+ break;
2143
+ lineStart = next;
2144
+ }
2145
+ return ranges;
2146
+ }
2147
+ function directCandidateIndexes(text, candidates, referenceIndex, postposedNegations) {
2148
+ const inertRangeIndexes = collectInertRangeIndexes(text);
2149
+ const candidateIndexByStart = new Map();
2150
+ for (const [index, candidate] of candidates.entries())
2151
+ candidateIndexByStart.set(candidate.start, index);
2152
+ const embeddedPathOrUrlIndex = collectEmbeddedPathOrUrlMentionIndex(text, candidates);
2153
+ const indexes = new Set();
2154
+ const implicitDocumentationRanges = collectImplicitDocumentationLineRanges(text, candidates, candidateIndexByStart);
2155
+ const documentationRanges = [...implicitDocumentationRanges];
2156
+ const documentationFollowups = new Map();
2157
+ let capturedBlock = false;
2158
+ let latestInertEnd = -1;
2159
+ let latestDocumentationEnd = -1;
2160
+ const recordDocumentationRange = (range) => {
2161
+ documentationRanges.push(range);
2162
+ latestInertEnd = Math.max(latestInertEnd, range.end);
2163
+ latestDocumentationEnd = Math.max(latestDocumentationEnd, range.end);
2164
+ };
2165
+ let candidateDocumentationLineStart = -1;
2166
+ let candidateDocumentationLineEnd = -1;
2167
+ let candidateListDocumentationRange = null;
2168
+ let candidateLineMayContainMarkdownDocumentation = false;
2169
+ const ensureCandidateDocumentationLine = (candidate) => {
2170
+ if (candidate.start >= candidateDocumentationLineStart && candidate.start < candidateDocumentationLineEnd)
2171
+ return;
2172
+ candidateDocumentationLineStart = lineStartForPosition(text, candidate.start);
2173
+ candidateDocumentationLineEnd = lineEnd(text, candidate.end);
2174
+ candidateListDocumentationRange = listItemDocumentationTokenRange(text, candidate.start, candidate.end);
2175
+ const line = text.slice(candidateDocumentationLineStart, candidateDocumentationLineEnd);
2176
+ const followingStart = candidateDocumentationLineEnd < text.length
2177
+ ? nextLineStart(text, candidateDocumentationLineEnd)
2178
+ : text.length;
2179
+ const followingLine = followingStart < text.length
2180
+ ? text.slice(followingStart, lineEnd(text, followingStart))
2181
+ : '';
2182
+ candidateLineMayContainMarkdownDocumentation = /[\[\]|]/u.test(line)
2183
+ || /^\s*#{1,6}\s/u.test(line)
2184
+ || /^\s*(?:={3,}|-{3,})\s*$/u.test(followingLine)
2185
+ || (line.includes('|') && isMarkdownTableDelimiter(followingLine));
2186
+ };
2187
+ const cachedCandidateDocumentationRange = (candidate) => {
2188
+ ensureCandidateDocumentationLine(candidate);
2189
+ const destinationEnd = inertRangeEndAt(referenceIndex.destinationRanges, candidate.start);
2190
+ if (destinationEnd !== null)
2191
+ return { start: candidate.start, end: destinationEnd };
2192
+ if (candidateListDocumentationRange
2193
+ && candidate.start >= candidateListDocumentationRange.start
2194
+ && candidate.end <= candidateListDocumentationRange.end) {
2195
+ return { start: candidate.start, end: candidateDocumentationLineEnd };
2196
+ }
2197
+ if (candidateLineMayContainMarkdownDocumentation) {
2198
+ const markdownRange = markdownDocumentationRange(text, candidate.start, candidate.end, referenceIndex);
2199
+ if (markdownRange)
2200
+ return markdownRange;
2201
+ }
2202
+ return isEmbeddedPathOrUrlMention(embeddedPathOrUrlIndex, candidate.start) ? { start: candidate.start, end: candidate.end } : null;
2203
+ };
2204
+ const addBlock = (start) => {
2205
+ let candidateIndex = candidateIndexByStart.get(start);
2206
+ if (candidateIndex === undefined || candidates[candidateIndex].reasons.size > 0 || isNegativeExplicitMention(candidates[candidateIndex], postposedNegations))
2207
+ return 'none';
2208
+ const added = [];
2209
+ const checkedPostposedSkills = new Set();
2210
+ let blockEnd = start;
2211
+ const blockLineLimit = lineEnd(text, start);
2212
+ while (candidateIndex !== undefined && candidates[candidateIndex].reasons.size === 0) {
2213
+ const candidate = candidates[candidateIndex];
2214
+ if (added.length > 0 && hasStrongDocumentationClauseBoundary(text, blockEnd, candidate.start)) {
2215
+ const candidateClauseEnd = explicitDocumentationSuffixClauseEnd(text, candidate.end, blockLineLimit, candidates, candidateIndexByStart, documentationFollowups);
2216
+ if (hasExplicitDocumentationSuffixAt(text, candidate.end, candidateClauseEnd))
2217
+ break;
2218
+ }
2219
+ const candidateDocumentationRange = cachedCandidateDocumentationRange(candidate);
2220
+ if (candidateDocumentationRange) {
2221
+ recordDocumentationRange(candidateDocumentationRange);
2222
+ if (added.length === 0)
2223
+ return 'documentation';
2224
+ break;
2225
+ }
2226
+ if (added.length > 0 && candidate.skill && !checkedPostposedSkills.has(candidate.skill) && hasPostposedExplicitNegation(postposedNegations, candidate))
2227
+ break;
2228
+ if (candidate.skill)
2229
+ checkedPostposedSkills.add(candidate.skill);
2230
+ indexes.add(candidateIndex);
2231
+ added.push(candidateIndex);
2232
+ blockEnd = candidates[candidateIndex].end;
2233
+ const nextStart = punctuationSeparatedCandidateStart(text, blockEnd);
2234
+ if (nextStart === null)
2235
+ break;
2236
+ candidateIndex = candidateIndexByStart.get(nextStart);
2237
+ }
2238
+ if (added.length === 0)
2239
+ return 'none';
2240
+ const documentationLineEnd = blockLineLimit;
2241
+ const subjectChain = collectMixedSubjectChain(text, candidates[added[0]].start, documentationLineEnd, candidates, candidateIndexByStart) ?? { start: candidates[added[0]].start, explicitIndexes: [], end: blockEnd };
2242
+ const addedSet = new Set(added);
2243
+ const documentationAdded = [...new Set([...added, ...subjectChain.explicitIndexes])];
2244
+ const coordinatedDocumentationAdded = subjectChain.explicitIndexes.filter((index) => !addedSet.has(index));
2245
+ let documentationBlockEnd = Math.max(blockEnd, subjectChain.end);
2246
+ let coordinatedStart = coordinatedDocumentationCandidateStart(text, documentationBlockEnd);
2247
+ while (coordinatedStart !== null) {
2248
+ const coordinatedIndex = candidateIndexByStart.get(coordinatedStart);
2249
+ if (coordinatedIndex === undefined || candidates[coordinatedIndex].reasons.size > 0)
2250
+ break;
2251
+ documentationAdded.push(coordinatedIndex);
2252
+ coordinatedDocumentationAdded.push(coordinatedIndex);
2253
+ documentationBlockEnd = candidates[coordinatedIndex].end;
2254
+ coordinatedStart = coordinatedDocumentationCandidateStart(text, documentationBlockEnd);
2255
+ }
2256
+ const documentationSuffixEnd = explicitDocumentationSuffixClauseEnd(text, documentationBlockEnd, documentationLineEnd, candidates, candidateIndexByStart, documentationFollowups);
2257
+ const documentationSuffix = documentationAdded.some((index) => candidates[index].skill !== null || isCompactSlashDocumentationCandidate(candidates[index]))
2258
+ && hasExplicitDocumentationSuffixAt(text, documentationBlockEnd, documentationSuffixEnd);
2259
+ let documentationRange = null;
2260
+ if (documentationSuffix) {
2261
+ const documentationRangeEnd = explicitDocumentationClauseEnd(text, documentationBlockEnd, documentationLineEnd, candidates, candidateIndexByStart, documentationFollowups);
2262
+ documentationRange = {
2263
+ start,
2264
+ end: documentationRangeEnd,
2265
+ };
2266
+ }
2267
+ else if (isListItemDocumentation(text, start, documentationBlockEnd)) {
2268
+ documentationRange = { start, end: documentationLineEnd };
2269
+ }
2270
+ else {
2271
+ for (const index of coordinatedDocumentationAdded) {
2272
+ const markdownRange = markdownDocumentationRange(text, candidates[index].start, candidates[index].end, referenceIndex);
2273
+ if (!markdownRange)
2274
+ continue;
2275
+ documentationRange = documentationRange
2276
+ ? { start: Math.min(documentationRange.start, markdownRange.start), end: Math.max(documentationRange.end, markdownRange.end) }
2277
+ : markdownRange;
2278
+ }
2279
+ }
2280
+ if (documentationRange) {
2281
+ for (const index of documentationAdded)
2282
+ indexes.delete(index);
2283
+ recordDocumentationRange(documentationRange);
2284
+ return 'documentation';
2285
+ }
2286
+ if (added.length > 0) {
2287
+ capturedBlock = true;
2288
+ return 'direct';
2289
+ }
2290
+ return 'none';
2291
+ };
2292
+ addBlock(promptLeadingExplicitCandidateStart(text));
2293
+ const inertPromptRanges = boundedPredecessorRanges(text, referenceIndex);
2294
+ let inertPromptCursor = 0;
2295
+ let implicitDocumentationCursor = 0;
2296
+ for (const [candidateIndex, candidate] of candidates.entries()) {
2297
+ ensureCandidateDocumentationLine(candidate);
2298
+ while (inertPromptCursor < inertPromptRanges.length && inertPromptRanges[inertPromptCursor].start < candidate.start) {
2299
+ latestInertEnd = Math.max(latestInertEnd, inertPromptRanges[inertPromptCursor].end);
2300
+ inertPromptCursor += 1;
2301
+ }
2302
+ while (implicitDocumentationCursor < implicitDocumentationRanges.length && implicitDocumentationRanges[implicitDocumentationCursor].start < candidate.start) {
2303
+ latestInertEnd = Math.max(latestInertEnd, implicitDocumentationRanges[implicitDocumentationCursor].end);
2304
+ latestDocumentationEnd = Math.max(latestDocumentationEnd, implicitDocumentationRanges[implicitDocumentationCursor].end);
2305
+ implicitDocumentationCursor += 1;
2306
+ }
2307
+ if (candidate.start < latestDocumentationEnd)
2308
+ continue;
2309
+ if (indexes.has(candidateIndex)) {
2310
+ latestInertEnd = -1;
2311
+ latestDocumentationEnd = -1;
2312
+ continue;
2313
+ }
2314
+ if (capturedBlock)
2315
+ continue;
2316
+ if (isInertOrNegativeMention(candidate, postposedNegations)) {
2317
+ const structuralEnd = structuralInertRangeEnd(inertRangeIndexes, candidate.start);
2318
+ let predecessorEnd = structuralEnd ?? candidate.end;
2319
+ predecessorEnd = Math.max(predecessorEnd, postposedExplicitNegationEnd(postposedNegations, candidate) ?? -1);
2320
+ if (candidateLineMayContainMarkdownDocumentation
2321
+ && text.lastIndexOf('[', candidate.start) >= candidateDocumentationLineStart
2322
+ && text.indexOf(']', candidate.end) < candidateDocumentationLineEnd) {
2323
+ predecessorEnd = Math.max(predecessorEnd, markdownDocumentationRange(text, candidate.start, candidate.end, referenceIndex)?.end ?? -1);
2324
+ }
2325
+ latestInertEnd = Math.max(latestInertEnd, predecessorEnd);
2326
+ continue;
2327
+ }
2328
+ const documentationRange = cachedCandidateDocumentationRange(candidate);
2329
+ if (documentationRange) {
2330
+ recordDocumentationRange(documentationRange);
2331
+ continue;
2332
+ }
2333
+ if (!capturedBlock && latestInertEnd >= 0 && !isNegativeExplicitMention(candidate, postposedNegations)) {
2334
+ const lineLeading = leadingDirectiveCursor(text, candidateDocumentationLineStart, false).cursor === candidate.start;
2335
+ const documentationSeparated = latestDocumentationEnd >= 0
2336
+ && hasAdjacentPredecessorSeparator(text, latestDocumentationEnd, candidate.start);
2337
+ if (documentationSeparated || (lineLeading && hasAdjacentPredecessorSeparator(text, latestInertEnd, candidate.start)) || clauseDirectiveStart(text, latestInertEnd, candidate.start)) {
2338
+ const addedBlock = addBlock(candidate.start);
2339
+ if (addedBlock !== 'documentation') {
2340
+ latestInertEnd = -1;
2341
+ latestDocumentationEnd = -1;
2342
+ }
2343
+ }
2344
+ else {
2345
+ latestInertEnd = -1;
2346
+ latestDocumentationEnd = -1;
2347
+ }
2348
+ }
2349
+ }
707
2350
  return {
708
- matches: results,
709
- sawExplicitLikeInvocation: captureStarted,
2351
+ directIndexes: indexes,
2352
+ documentationRanges: createInertRangeIndex(documentationRanges),
2353
+ documentationBlocks: documentationRanges,
710
2354
  };
711
2355
  }
2356
+ function freezeMatches(matches) {
2357
+ return Object.freeze(matches.map((match) => Object.freeze({ ...match })));
2358
+ }
2359
+ function freezeCandidates(candidates) {
2360
+ return Object.freeze(candidates.map((candidate) => Object.freeze({
2361
+ rawKeyword: candidate.rawKeyword,
2362
+ normalizedToken: candidate.normalizedToken,
2363
+ skill: candidate.skill,
2364
+ priority: candidate.priority,
2365
+ reasons: Object.freeze(KEYWORD_INERT_DIAGNOSTIC_ORDER.filter((reason) => candidate.reasons.has(reason))),
2366
+ })));
2367
+ }
2368
+ function lowerBound(values, value) {
2369
+ let lower = 0;
2370
+ let upper = values.length;
2371
+ while (lower < upper) {
2372
+ const middle = lower + Math.floor((upper - lower) / 2);
2373
+ if (values[middle] < value)
2374
+ lower = middle + 1;
2375
+ else
2376
+ upper = middle;
2377
+ }
2378
+ return lower;
2379
+ }
2380
+ function upperBound(values, value) {
2381
+ let lower = 0;
2382
+ let upper = values.length;
2383
+ while (lower < upper) {
2384
+ const middle = lower + Math.floor((upper - lower) / 2);
2385
+ if (values[middle] <= value)
2386
+ lower = middle + 1;
2387
+ else
2388
+ upper = middle;
2389
+ }
2390
+ return lower;
2391
+ }
2392
+ function lineIndexAt(lineStarts, position) {
2393
+ return Math.max(0, upperBound(lineStarts, position) - 1);
2394
+ }
2395
+ function implicitLineIndexAt(index, position) {
2396
+ return lineIndexAt(index.lineStarts, position);
2397
+ }
2398
+ function indexedBoundaryAtOrAfter(values, position, fallback) {
2399
+ return values[lowerBound(values, position)] ?? fallback;
2400
+ }
2401
+ function positiveContrastStartInRange(contrasts, start, end) {
2402
+ const contrast = contrasts[lowerBound(contrasts, start)];
2403
+ return contrast !== undefined && contrast < end ? contrast : null;
2404
+ }
2405
+ function collectImplicitClauseIndex(text, documentationBlocks) {
2406
+ const lineStarts = [];
2407
+ const lineEnds = [];
2408
+ const subjectStarts = [];
2409
+ const clauseEnds = [];
2410
+ const strongClauseEnds = [];
2411
+ let lineStart = 0;
2412
+ while (lineStart <= text.length) {
2413
+ const lineLimit = lineEnd(text, lineStart);
2414
+ lineStarts.push(lineStart);
2415
+ lineEnds.push(lineLimit);
2416
+ subjectStarts.push(lineStart);
2417
+ for (let cursor = lineStart; cursor < lineLimit;) {
2418
+ const character = codePointAt(text, cursor);
2419
+ if (isClauseBoundaryAt(text, cursor)) {
2420
+ clauseEnds.push(cursor);
2421
+ if (isPostposedSubjectBoundaryAt(text, cursor)) {
2422
+ strongClauseEnds.push(cursor);
2423
+ subjectStarts.push(cursor + character.length);
2424
+ }
2425
+ }
2426
+ cursor += character.length;
2427
+ }
2428
+ if (lineLimit === text.length)
2429
+ break;
2430
+ lineStart = nextLineStart(text, lineLimit);
2431
+ }
2432
+ const positiveContrasts = [];
2433
+ const contrastScanner = new RegExp(`${ASCII_BUT}\\s+(?:${ASCII_PLEASE}\\s+)?(?:${ASCII_INSTEAD}\\s+)?(?=${ASCII_DIRECTIVE_COMMAND})`, 'gu');
2434
+ let contrast;
2435
+ while ((contrast = contrastScanner.exec(text)) !== null) {
2436
+ if (hasUnicodeGrammarTokenStart(text, contrast.index))
2437
+ positiveContrasts.push(contrast.index);
2438
+ }
2439
+ const negativeBlocks = [];
2440
+ const normalizedText = normalizeGrammarApostrophes(text);
2441
+ const prefixScanner = new RegExp(NEGATIVE_PREFIX_PATTERN.source, 'gu');
2442
+ let prefix;
2443
+ let prefixCoveredUntil = -1;
2444
+ while ((prefix = prefixScanner.exec(normalizedText)) !== null) {
2445
+ if (!hasUnicodeGrammarTokenBoundaries(normalizedText, prefix.index, prefixScanner.lastIndex)
2446
+ || isDiscourseNegation(normalizedText, prefix.index, prefixScanner.lastIndex))
2447
+ continue;
2448
+ if (prefix.index < prefixCoveredUntil)
2449
+ continue;
2450
+ const lineIndex = lineIndexAt(lineStarts, prefix.index);
2451
+ const lineLimit = lineEnds[lineIndex] ?? text.length;
2452
+ const clauseEnd = Math.min(indexedBoundaryAtOrAfter(strongClauseEnds, prefixScanner.lastIndex, lineLimit), lineLimit);
2453
+ const positiveContrast = positiveContrastStartInRange(positiveContrasts, prefixScanner.lastIndex, clauseEnd);
2454
+ const exclusionTransition = exclusionPrefixDirectiveTransitionStart(text, prefix.index, prefixScanner.lastIndex, clauseEnd);
2455
+ const end = Math.min(positiveContrast ?? clauseEnd, exclusionTransition ?? clauseEnd);
2456
+ negativeBlocks.push({ start: prefix.index, end });
2457
+ prefixCoveredUntil = end;
2458
+ }
2459
+ const predicateScanner = new RegExp(POSTPOSED_NEGATIVE_PREDICATE.source, 'giu');
2460
+ const checkedSubjectStarts = new Set();
2461
+ let predicate;
2462
+ while ((predicate = predicateScanner.exec(normalizedText)) !== null) {
2463
+ const predicateTokenStart = firstGrammarTokenStart(normalizedText, predicate.index, predicateScanner.lastIndex);
2464
+ if (!hasUnicodeGrammarTokenBoundaries(normalizedText, predicateTokenStart, predicateScanner.lastIndex)
2465
+ || !hasAsciiGrammarTokenCharacters(normalizedText, predicateTokenStart, predicateScanner.lastIndex))
2466
+ continue;
2467
+ const subjectStart = subjectStarts[Math.max(0, upperBound(subjectStarts, predicate.index) - 1)] ?? 0;
2468
+ if (checkedSubjectStarts.has(subjectStart))
2469
+ continue;
2470
+ checkedSubjectStarts.add(subjectStart);
2471
+ const lineIndex = lineIndexAt(lineStarts, predicate.index);
2472
+ const lineLimit = lineEnds[lineIndex] ?? text.length;
2473
+ const clauseEnd = Math.min(indexedBoundaryAtOrAfter(strongClauseEnds, predicateScanner.lastIndex, lineLimit), lineLimit);
2474
+ const subject = text.slice(subjectStart, predicate.index);
2475
+ if (hasAsciiDirectiveCommand(subject) || !IMPLICIT_POSTPOSED_SUBJECT_CHAIN.test(subject))
2476
+ continue;
2477
+ const positiveContrast = positiveContrastStartInRange(positiveContrasts, predicateScanner.lastIndex, clauseEnd);
2478
+ negativeBlocks.push({ start: subjectStart, end: positiveContrast ?? clauseEnd });
2479
+ }
2480
+ return {
2481
+ lineStarts,
2482
+ lineEnds,
2483
+ subjectStarts,
2484
+ clauseEnds,
2485
+ strongClauseEnds,
2486
+ documentationBlocks: createInertRangeIndex([...documentationBlocks]),
2487
+ negativeBlocks: createInertRangeIndex(negativeBlocks),
2488
+ };
2489
+ }
2490
+ function hasImplicitDocumentationFrame(text) {
2491
+ return /(?:^|[\s([{])(?:docs?|documentation|examples?|references?|guide|manual|document(?:s|ed|ing)?|describ(?:e|es|ed|ing)|mention(?:s|ed|ing)|explain(?:s|ed|ing)?)(?:\b|\s*:)/iu.test(text);
2492
+ }
2493
+ function hasIntroductoryDocumentationFrame(text) {
2494
+ return /^\s*(?:for\s+(?:example|instance|reference)|as\s+an?\s+example|according\s+to\s+(?:the\s+)?(?:docs?|documentation|guide|manual|reference)|(?:in|per)\s+(?:the\s+)?(?:docs?|documentation|guide|manual|reference))\s*(?:[,,،、::]|[—–-])\s*$/iu.test(text);
2495
+ }
2496
+ function implicitClauseRange(text, candidateStart, candidateEnd, clauses) {
2497
+ const lineIndex = implicitLineIndexAt(clauses, candidateStart);
2498
+ const lineStart = clauses.lineStarts[lineIndex] ?? 0;
2499
+ const logicalLineEnd = clauses.lineEnds[lineIndex] ?? text.length;
2500
+ let start = clauses.subjectStarts[Math.max(0, upperBound(clauses.subjectStarts, candidateStart) - 1)] ?? lineStart;
2501
+ let frameStart = start;
2502
+ const introductoryPrefix = /^[^\r\n,:,:،、—–-]*(?:[,,،、::]|[—–-])\s*/u.exec(text.slice(lineStart, candidateStart))?.[0];
2503
+ const introductoryRemainder = introductoryPrefix
2504
+ ? text.slice(lineStart + introductoryPrefix.length, candidateStart)
2505
+ : '';
2506
+ const hasGoverningIntroductoryFrame = Boolean(introductoryPrefix
2507
+ && hasIntroductoryDocumentationFrame(introductoryPrefix)
2508
+ && !hasLogicalSentenceBoundary(introductoryRemainder));
2509
+ if (hasGoverningIntroductoryFrame)
2510
+ frameStart = lineStart;
2511
+ const discardFrame = new RegExp(`^\\s*(?:${ASCII_PLEASE}\\s+)?${EXCLUSION_PREFIX}`, 'u');
2512
+ const contrastText = text.slice(start, candidateStart);
2513
+ const contrastStart = latestPositiveContrastStart(contrastText);
2514
+ if (contrastStart >= 0) {
2515
+ const transitionStart = start + contrastStart;
2516
+ const discardMatch = discardFrame.exec(text.slice(start, transitionStart));
2517
+ if (!hasGoverningIntroductoryFrame
2518
+ && discardMatch
2519
+ && hasUnicodeGrammarTokenBoundaries(text, start + discardMatch.index, start + discardMatch.index + discardMatch[0].length)) {
2520
+ frameStart = transitionStart;
2521
+ }
2522
+ start = transitionStart;
2523
+ }
2524
+ const directivePrefix = text.slice(start, candidateStart);
2525
+ const directiveTransition = new RegExp(`(?:${ASCII_AND}|${ASCII_THEN})\\s+(?=(?:${ASCII_PLEASE}\\s+)?(?:${ASCII_INSTEAD}\\s+)?${ASCII_DIRECTIVE_COMMAND})[^\\r\\n\\u2028\\u2029]*$`, 'u').exec(directivePrefix);
2526
+ if (directiveTransition
2527
+ && hasUnicodeGrammarTokenBoundaries(directivePrefix, directiveTransition.index, directiveTransition.index + directiveTransition[0].length)) {
2528
+ const transitionStart = start + directiveTransition.index;
2529
+ const hasNegativePrefix = hasUnicodeBoundedNegativePrefix(normalizeGrammarApostrophes(text.slice(frameStart, transitionStart)));
2530
+ const exclusionTransition = isExclusionPrefixDirectiveTransitionAt(text, frameStart, transitionStart, candidateStart);
2531
+ if (!hasGoverningIntroductoryFrame && (!hasNegativePrefix || exclusionTransition)) {
2532
+ frameStart = transitionStart;
2533
+ start = transitionStart;
2534
+ }
2535
+ }
2536
+ let punctuationDirectiveStart = -1;
2537
+ for (let cursor = start; cursor < candidateStart;) {
2538
+ const character = codePointAt(text, cursor);
2539
+ if (isClauseBoundaryAt(text, cursor) || normalizedPunctuation(character) === ':') {
2540
+ const directiveStart = boundedHorizontalCursor(text, cursor + character.length, candidateStart);
2541
+ if (directiveCommandTargetStart(text, directiveStart, candidateStart) === candidateStart) {
2542
+ punctuationDirectiveStart = directiveStart;
2543
+ }
2544
+ }
2545
+ cursor += character.length;
2546
+ }
2547
+ if (punctuationDirectiveStart >= 0) {
2548
+ if (!hasGoverningIntroductoryFrame)
2549
+ frameStart = punctuationDirectiveStart;
2550
+ start = punctuationDirectiveStart;
2551
+ }
2552
+ const end = Math.min(indexedBoundaryAtOrAfter(clauses.clauseEnds, candidateEnd, logicalLineEnd), logicalLineEnd);
2553
+ return { start, frameStart, end, governingDocumentationFrame: hasGoverningIntroductoryFrame };
2554
+ }
2555
+ function isImplicitListDocumentation(text, candidateStart) {
2556
+ const start = lineStartForPosition(text, candidateStart);
2557
+ const leading = leadingDirectiveCursor(text, start, false);
2558
+ if (!leading.listItem || leading.cursor > candidateStart)
2559
+ return false;
2560
+ const content = text.slice(leading.cursor, lineEnd(text, candidateStart));
2561
+ return /(?:\s*(?::|:|[—–])\s*\S|\b(?:is|are|means|refer(?:s)?\s+to|denote(?:s)?)\b).*\b(?:commands?|workflows?|skills?|modes?|files?|documentation)\b/iu.test(content);
2562
+ }
2563
+ function isImplicitDocumentationClause(text, matchStart, matchEnd, clause, referenceIndex) {
2564
+ const prefix = text.slice(clause.frameStart, matchStart);
2565
+ if (clause.governingDocumentationFrame)
2566
+ return true;
2567
+ if (hasImplicitDocumentationFrame(prefix))
2568
+ return true;
2569
+ const introductoryFrame = /^[^,,،、\r\n\u2028\u2029]*[,,،、]\s*/u.exec(prefix)?.[0];
2570
+ if (introductoryFrame && hasIntroductoryDocumentationFrame(introductoryFrame))
2571
+ return true;
2572
+ if (hasImplicitDocumentationSubjectPrefix(text.slice(matchStart, clause.end)))
2573
+ return true;
2574
+ const suffix = text.slice(matchEnd, clause.end);
2575
+ if (/^(?:(?:\s+(?:mode|workflow|skill|loop))?\s+(?:(?:is|are|means|refer(?:s)?\s+to|denote(?:s)?)\b.*\b(?:commands?|workflows?|skills?|modes?|files?|documentation)\b|(?:is|are)\s+(?:(?:(?:also|still)\s+)?(?:(?:its|an?|the)\s+)?alias(?:es)?|documented|described)\b|(?:appears?|occurs?|is\s+(?:also\s+)?mentioned)\b.*\b(?:docs?|documentation|examples?|references?|guide|manual)\b|(?:docs?|documentation|examples?|references?|guide|manual)\b)|(?:\s+(?:mode|workflow|skill|loop))?\s*\/\s*.*\b(?:is|are|means|refer(?:s)?\s+to|denote(?:s)?)\b.*\b(?:commands?|workflows?|skills?|modes?|files?|documentation)\b|(?:\s+(?:mode|workflow|skill|loop))?\s*(?::|[—–-])\s*\S.*\b(?:commands?|workflows?|skills?|modes?|files?|documentation)\b)/iu.test(suffix))
2576
+ return true;
2577
+ const lineStart = lineStartForPosition(text, matchStart);
2578
+ const line = text.slice(lineStart, lineEnd(text, matchEnd));
2579
+ const localEnd = matchEnd - lineStart;
2580
+ if (/^(?:\s+(?:mode|workflow|skill|loop))?\s*[—–-]\s*\S.*\b(?:commands?|workflows?|skills?|modes?|files?|documentation)\b/iu.test(line.slice(localEnd)))
2581
+ return true;
2582
+ if (isMarkdownDocumentationMention(text, matchStart, matchEnd, referenceIndex))
2583
+ return true;
2584
+ return false;
2585
+ }
2586
+ function hasPostposedImplicitNegation(suffix, prefix) {
2587
+ const normalizedSuffix = normalizeGrammarApostrophes(suffix);
2588
+ const predicate = POSTPOSED_NEGATIVE_PREDICATE.exec(normalizedSuffix);
2589
+ const predicateTokenStart = predicate
2590
+ ? firstGrammarTokenStart(normalizedSuffix, predicate.index, predicate.index + predicate[0].length)
2591
+ : -1;
2592
+ if (!predicate
2593
+ || !hasUnicodeGrammarTokenBoundaries(normalizedSuffix, predicateTokenStart, predicate.index + predicate[0].length)
2594
+ || !hasAsciiGrammarTokenCharacters(normalizedSuffix, predicateTokenStart, predicate.index + predicate[0].length))
2595
+ return false;
2596
+ const subjectTail = suffix.slice(0, predicate.index);
2597
+ if (hasAsciiDirectiveCommand(subjectTail))
2598
+ return false;
2599
+ const coordinatedSubject = new RegExp(`^(?:\\s+${IMPLICIT_WORKFLOW_NOUN})?(?:\\s*(?:(?:[,,،、]|/)\\s*(?:${COORDINATED_SUBJECT_JOINER}\\s+)?|${COORDINATED_SUBJECT_JOINER}\\s+)${COORDINATED_WORKFLOW_SUBJECT}(?:\\s+${IMPLICIT_WORKFLOW_NOUN})?)*\\s*[,,、،]?\\s*$`, 'iu');
2600
+ if (!coordinatedSubject.test(subjectTail))
2601
+ return false;
2602
+ const independentCoordinatedClause = /^\s+(?:modes?|workflows?|skills?|loops?)?\s*[,,،、]\s*(?:and|or|nor)\b/iu.test(subjectTail)
2603
+ && hasAsciiDirectiveCommand(prefix);
2604
+ return !independentCoordinatedClause;
2605
+ }
2606
+ function collectEmbeddedPathOrUrlMentionIndex(text, candidates) {
2607
+ const tokenStarts = new Map();
2608
+ const embeddedCandidateStarts = new Set();
2609
+ let candidateIndex = 0;
2610
+ let tokenStart = 0;
2611
+ let penultimateTokenCharacter = '';
2612
+ let previousTokenCharacter = '';
2613
+ let tokenContainsUrlScheme = false;
2614
+ for (let cursor = 0; cursor < text.length;) {
2615
+ while (candidateIndex < candidates.length && candidates[candidateIndex].start === cursor) {
2616
+ tokenStarts.set(cursor, tokenStart);
2617
+ if (tokenContainsUrlScheme || /[\\/?#=&]/u.test(previousTokenCharacter))
2618
+ embeddedCandidateStarts.add(cursor);
2619
+ candidateIndex += 1;
2620
+ }
2621
+ const character = codePointAt(text, cursor);
2622
+ if (SAFE_TOKEN_WHITESPACE.test(character)) {
2623
+ tokenStart = cursor + character.length;
2624
+ penultimateTokenCharacter = '';
2625
+ previousTokenCharacter = '';
2626
+ tokenContainsUrlScheme = false;
2627
+ }
2628
+ else {
2629
+ if (penultimateTokenCharacter === ':' && previousTokenCharacter === '/' && character === '/') {
2630
+ tokenContainsUrlScheme = true;
2631
+ }
2632
+ penultimateTokenCharacter = previousTokenCharacter;
2633
+ previousTokenCharacter = character;
2634
+ }
2635
+ cursor += character.length;
2636
+ }
2637
+ return { tokenStarts, embeddedCandidateStarts };
2638
+ }
2639
+ function isEmbeddedPathOrUrlMention(index, start) {
2640
+ return index.tokenStarts.has(start) && index.embeddedCandidateStarts.has(start);
2641
+ }
2642
+ function hasActiveExplicitLikeInvocation(candidates, documentationRanges, postposedNegations) {
2643
+ return candidates.some((candidate) => {
2644
+ if (isInInertRange(documentationRanges, candidate.start))
2645
+ return false;
2646
+ if ([...candidate.reasons].some((reason) => reason !== 'not-leading-region'))
2647
+ return false;
2648
+ if (isNegativeExplicitMention(candidate, postposedNegations))
2649
+ return false;
2650
+ return true;
2651
+ });
2652
+ }
2653
+ function hasActivePromptsInvocation(text, start, end, inertRangeIndexes, referenceIndex) {
2654
+ const pattern = new RegExp(PROMPTS_TOKEN_PATTERN.source, PROMPTS_TOKEN_PATTERN.flags);
2655
+ pattern.lastIndex = start;
2656
+ let match;
2657
+ while ((match = pattern.exec(text)) !== null && match.index < end) {
2658
+ const matchEnd = match.index + match[0].length;
2659
+ if (!isInactivePromptsMention(text, match.index, matchEnd, inertRangeIndexes, referenceIndex))
2660
+ return true;
2661
+ }
2662
+ return false;
2663
+ }
2664
+ function hasImplicitKeywordBoundaries(text, start, end) {
2665
+ const previous = codePointBefore(text, start);
2666
+ const next = codePointAt(text, end);
2667
+ return (!previous || (previous !== '$' && !TOKEN_CONTINUATION.test(previous))) && (!next || !TOKEN_CONTINUATION.test(next));
2668
+ }
2669
+ function isActiveImplicitMatch(text, matchStart, matchEnd, matchedKeyword, inertRangeIndexes, referenceIndex, clauses, postposedNegations) {
2670
+ if (isStructurallyInert(inertRangeIndexes, matchStart)
2671
+ || isInInertRange(referenceIndex.destinationRanges, matchStart)
2672
+ || isInInertRange(clauses.documentationBlocks, matchStart)
2673
+ || (!isNegativePrefixExemptImplicitKeyword(matchedKeyword) && isInInertRange(clauses.negativeBlocks, matchStart))
2674
+ || isInInertRange(postposedNegations.implicitBlocks, matchStart)
2675
+ || isImplicitListDocumentation(text, matchStart))
2676
+ return null;
2677
+ if (hasOddImmediateBackslashes(text, matchStart))
2678
+ return null;
2679
+ const clause = implicitClauseRange(text, matchStart, matchEnd, clauses);
2680
+ const lineStart = clauses.lineStarts[implicitLineIndexAt(clauses, matchStart)] ?? 0;
2681
+ const prefix = text.slice(clause.start, matchStart);
2682
+ const effectivePrefix = prefix.trim() || text.slice(lineStart, Math.max(lineStart, clause.start - 1));
2683
+ if (!isNegativePrefixExemptImplicitKeyword(matchedKeyword) && hasUnicodeBoundedNegativePrefix(normalizeGrammarApostrophes(effectivePrefix)))
2684
+ return null;
2685
+ const suffix = text.slice(matchEnd, clause.end);
2686
+ const lineEnd = clauses.lineEnds[implicitLineIndexAt(clauses, matchStart)] ?? text.length;
2687
+ const lineSuffix = text.slice(matchEnd, lineEnd);
2688
+ if (hasPostposedImplicitNegation(lineSuffix, effectivePrefix))
2689
+ return null;
2690
+ if (/^(?:\s+(?:mode|workflow|skill|loop))?(?:는|은|이|가)?\s*(?:사용하지\s*마세요|사용하지\s*마|쓰지\s*마세요|쓰지\s*마)/u.test(suffix))
2691
+ return null;
2692
+ if (hasActivePromptsInvocation(text, clause.start, clause.end, inertRangeIndexes, referenceIndex))
2693
+ return null;
2694
+ if (isImplicitDocumentationClause(text, matchStart, matchEnd, clause, referenceIndex))
2695
+ return null;
2696
+ return clause;
2697
+ }
2698
+ function detectImplicitKeywords(normalizedText, explicitCandidates, referenceIndex, clauses, postposedNegations) {
2699
+ const implicit = [];
2700
+ const inertRangeIndexes = collectInertRangeIndexes(normalizedText);
2701
+ const explicitCandidateRanges = createInertRangeIndex(explicitCandidates.map((candidate) => ({ start: candidate.start, end: candidate.end })));
2702
+ for (const { keyword, pattern, skill, priority } of KEYWORD_MAP) {
2703
+ if (keyword.startsWith('$'))
2704
+ continue;
2705
+ const scanner = new RegExp(pattern.source, `${pattern.flags}g`);
2706
+ let match;
2707
+ while ((match = scanner.exec(normalizedText)) !== null) {
2708
+ const matchStart = match.index;
2709
+ const matchEnd = matchStart + match[0].length;
2710
+ if ((inertRangeEndAt(explicitCandidateRanges, matchStart) ?? -1) >= matchEnd)
2711
+ continue;
2712
+ if (match[0].toLowerCase() !== 'ulw' && !hasImplicitKeywordBoundaries(normalizedText, matchStart, matchEnd))
2713
+ continue;
2714
+ const clause = isActiveImplicitMatch(normalizedText, matchStart, matchEnd, match[0], inertRangeIndexes, referenceIndex, clauses, postposedNegations);
2715
+ if (!clause)
2716
+ continue;
2717
+ const clauseText = normalizedText.slice(clause.start, clause.end);
2718
+ if (!hasIntentContextForKeyword(clauseText, match[0].toLowerCase()))
2719
+ continue;
2720
+ implicit.push({ keyword: match[0], skill, priority });
2721
+ break;
2722
+ }
2723
+ }
2724
+ const seenSkills = new Set();
2725
+ return implicit.sort(compareKeywordMatches).filter((match) => {
2726
+ if (seenSkills.has(match.skill))
2727
+ return false;
2728
+ seenSkills.add(match.skill);
2729
+ return true;
2730
+ });
2731
+ }
2732
+ function hasOmxQuestionAnsweredPrefix(text) {
2733
+ return /^\s*\[omx question answered\]/i.test(text);
2734
+ }
712
2735
  function hasIntentContextForKeyword(text, keyword) {
713
2736
  const k = keyword.toLowerCase();
714
2737
  if (k === 'deep interview' || k === 'interview') {
@@ -724,58 +2747,76 @@ function hasIntentContextForKeyword(text, keyword) {
724
2747
  return patterns.some((pattern) => pattern.test(text));
725
2748
  }
726
2749
  /**
727
- * Detect keywords in user input text
728
- * Returns explicit `$skill` matches first (left-to-right),
729
- * then appends implicit keyword matches sorted by priority.
2750
+ * Classify one prompt with the direct-only explicit grammar and immutable
2751
+ * diagnostics. Consumers must share this result rather than re-detecting.
730
2752
  */
731
- export function detectKeywords(text) {
2753
+ export function classifyKeywordInput(text) {
732
2754
  const normalizedText = normalizeWorkflowKeyboardTypos(text);
733
- const explicitParse = parseExplicitSkillInvocations(normalizedText);
734
- const explicit = explicitParse.matches;
735
- if (hasExplicitPromptsInvocation(normalizedText) && explicit.length === 0) {
736
- return [];
737
- }
738
- if (explicit.length === 0 && explicitParse.sawExplicitLikeInvocation) {
739
- return [];
740
- }
741
- if (explicit.length > 0) {
742
- return explicit;
2755
+ const referenceIndex = collectMarkdownReferenceIndex(normalizedText);
2756
+ const candidates = scanExplicitCandidates(normalizedText);
2757
+ const postposedNegations = collectPostposedExplicitNegationIndex(normalizedText, candidates);
2758
+ const { directIndexes, documentationRanges, documentationBlocks } = directCandidateIndexes(normalizedText, candidates, referenceIndex, postposedNegations);
2759
+ const implicitClauses = collectImplicitClauseIndex(normalizedText, documentationBlocks);
2760
+ for (const [index, candidate] of candidates.entries()) {
2761
+ if (!directIndexes.has(index))
2762
+ candidate.reasons.add('not-leading-region');
743
2763
  }
744
- const implicit = [];
745
- for (const { pattern, skill, priority } of KEYWORD_MAP) {
746
- const match = normalizedText.match(pattern);
747
- if (match) {
748
- if (!hasIntentContextForKeyword(normalizedText, match[0].toLowerCase()))
749
- continue;
750
- implicit.push({
751
- keyword: match[0],
752
- skill,
753
- priority,
754
- });
755
- }
756
- }
757
- const merged = [...explicit];
758
- const sortedImplicit = implicit.sort(compareKeywordMatches);
759
- for (const item of sortedImplicit) {
760
- if (merged.some((existing) => existing.skill === item.skill))
2764
+ const explicitMatches = [];
2765
+ const explicitSkills = new Set();
2766
+ for (const index of directIndexes) {
2767
+ const candidate = candidates[index];
2768
+ if (!candidate.skill || candidate.priority === null || explicitSkills.has(candidate.skill))
761
2769
  continue;
762
- merged.push(item);
2770
+ explicitSkills.add(candidate.skill);
2771
+ explicitMatches.push({
2772
+ keyword: candidate.rawKeyword,
2773
+ skill: candidate.skill,
2774
+ priority: candidate.priority,
2775
+ });
763
2776
  }
764
- return merged;
2777
+ const markedQuestionAnswer = hasOmxQuestionAnsweredPrefix(normalizedText);
2778
+ const directPromptsInvocation = hasDirectPromptsInvocation(normalizedText, collectInertRangeIndexes(normalizedText), referenceIndex);
2779
+ const reservedInput = markedQuestionAnswer
2780
+ ? 'omx-question-answered'
2781
+ : directPromptsInvocation
2782
+ ? 'prompts'
2783
+ : null;
2784
+ const hasExplicitLikeInvocation = candidates.length > 0;
2785
+ const hasActiveExplicitLike = hasActiveExplicitLikeInvocation(candidates, documentationRanges, postposedNegations);
2786
+ const finalMatches = reservedInput
2787
+ ? []
2788
+ : explicitMatches.length > 0
2789
+ ? explicitMatches
2790
+ : hasActiveExplicitLike
2791
+ ? []
2792
+ : detectImplicitKeywords(normalizedText, candidates, referenceIndex, implicitClauses, postposedNegations);
2793
+ const implicitMatches = reservedInput || hasActiveExplicitLike
2794
+ ? []
2795
+ : finalMatches;
2796
+ return Object.freeze({
2797
+ originalText: text,
2798
+ normalizedText,
2799
+ candidates: freezeCandidates(candidates),
2800
+ explicitMatches: freezeMatches(explicitMatches),
2801
+ hasExplicitLikeInvocation,
2802
+ reservedInput,
2803
+ implicitMatches: freezeMatches(implicitMatches),
2804
+ matches: freezeMatches(finalMatches),
2805
+ });
765
2806
  }
766
- /**
767
- * Get the highest-priority keyword match
768
- */
2807
+ /** Detect workflow matches using a fresh immutable input classification. */
2808
+ export function detectKeywords(text) {
2809
+ return [...classifyKeywordInput(text).matches];
2810
+ }
2811
+ /** Get the first match in classification order. */
769
2812
  export function detectPrimaryKeyword(text) {
770
- const matches = detectKeywords(text);
771
- return matches.length > 0 ? matches[0] : null;
2813
+ return classifyKeywordInput(text).matches[0] ?? null;
772
2814
  }
773
2815
  function filterMatchesForTeamMode(matches, teamEnabled) {
774
- return teamEnabled ? matches : matches.filter((entry) => entry.skill !== 'team');
2816
+ return teamEnabled ? [...matches] : matches.filter((entry) => entry.skill !== 'team');
775
2817
  }
776
- function detectPrimaryKeywordForTeamMode(text, teamEnabled) {
777
- const matches = filterMatchesForTeamMode(detectKeywords(text), teamEnabled);
778
- return matches[0] ?? null;
2818
+ function detectPrimaryKeywordForTeamMode(classification, teamEnabled) {
2819
+ return filterMatchesForTeamMode(classification.matches, teamEnabled)[0] ?? null;
779
2820
  }
780
2821
  function isActiveSkillContinuationPrompt(text) {
781
2822
  const normalized = text.trim();
@@ -789,17 +2830,15 @@ function isNamedActiveSkillContinuationPrompt(text, skill) {
789
2830
  return false;
790
2831
  return new RegExp(`^[\\\\/]?\\s*${normalizedSkill}\\b(?:\\s+(?:keep\\s+going|continue|resume))(?:\\s+now)?[.!]?\\s*$`, 'i').test(text.trim());
791
2832
  }
792
- function isOmxQuestionAnsweredPrompt(text) {
793
- return /^\s*\[omx question answered\]/i.test(text.trim());
794
- }
795
- function shouldReusePreviousSkillForContinuation(text, previous) {
2833
+ function shouldReusePreviousSkillForContinuation(text, previous, classification) {
796
2834
  const previousSkill = safeString(previous?.skill).trim();
797
2835
  if (!previousSkill || previous?.active !== true || !isTrackedWorkflowMode(previousSkill)) {
798
2836
  return false;
799
2837
  }
800
2838
  return isActiveSkillContinuationPrompt(text)
801
2839
  || isNamedActiveSkillContinuationPrompt(text, previousSkill)
802
- || ((previousSkill === 'autopilot' || previousSkill === 'deep-interview') && isOmxQuestionAnsweredPrompt(text));
2840
+ || (classification.reservedInput === 'omx-question-answered'
2841
+ && (previousSkill === 'autopilot' || previousSkill === 'deep-interview'));
803
2842
  }
804
2843
  function isAutopilotSupervisedChildSkill(skill) {
805
2844
  return skill === 'code-review'
@@ -957,21 +2996,27 @@ function isDeepInterviewRuntimeConfig(value) {
957
2996
  && typeof candidate.sourcePath === 'string'
958
2997
  && candidate.sourcePath.trim().length > 0);
959
2998
  }
960
- function resolveContinuationKeywordMatch(text, previous, fallbackMatch) {
2999
+ function resolveContinuationKeywordMatch(text, previous, fallbackMatch, classification) {
961
3000
  const previousSkill = safeString(previous?.skill).trim();
962
3001
  if (!previousSkill || previous?.active !== true || !isTrackedWorkflowMode(previousSkill)) {
963
3002
  return fallbackMatch;
964
3003
  }
965
- const markedQuestionAnswerContinuation = (previousSkill === 'autopilot' || previousSkill === 'deep-interview')
966
- && isOmxQuestionAnsweredPrompt(text);
967
- if (!markedQuestionAnswerContinuation && parseExplicitSkillInvocations(normalizeWorkflowKeyboardTypos(text)).sawExplicitLikeInvocation) {
968
- return fallbackMatch;
3004
+ const markedQuestionAnswerContinuation = classification.reservedInput === 'omx-question-answered'
3005
+ && (previousSkill === 'autopilot' || previousSkill === 'deep-interview');
3006
+ if (classification.reservedInput || (!markedQuestionAnswerContinuation && classification.hasExplicitLikeInvocation)) {
3007
+ return markedQuestionAnswerContinuation
3008
+ ? {
3009
+ keyword: safeString(previous.keyword).trim() || `$${previousSkill}`,
3010
+ skill: previousSkill,
3011
+ priority: 0,
3012
+ }
3013
+ : fallbackMatch;
969
3014
  }
970
- if (!markedQuestionAnswerContinuation && !shouldReusePreviousSkillForContinuation(text, previous) && !safeString(fallbackMatch?.keyword).trim().startsWith('$')) {
3015
+ if (!shouldReusePreviousSkillForContinuation(text, previous, classification) && !safeString(fallbackMatch?.keyword).trim().startsWith('$')) {
971
3016
  return fallbackMatch;
972
3017
  }
973
3018
  return {
974
- keyword: safeString(previous?.keyword).trim() || `$${previousSkill}`,
3019
+ keyword: safeString(previous.keyword).trim() || `$${previousSkill}`,
975
3020
  skill: previousSkill,
976
3021
  priority: fallbackMatch?.priority ?? 0,
977
3022
  };
@@ -997,29 +3042,81 @@ function resolveRequestedWorkflowSkills(requestedWorkflowSkills) {
997
3042
  deferredSkills: requestedWorkflowSkills.filter((skill) => skill !== firstPlanningSkill),
998
3043
  };
999
3044
  }
1000
- function selectRootSkillStateCopy(previousRoot, nextState, sessionId) {
3045
+ function selectRootSkillStateCopy(previousRoot, nextState, sessionId, suppressRootMutation = false) {
3046
+ if (suppressRootMutation)
3047
+ return null;
1001
3048
  if (!sessionId)
1002
3049
  return nextState;
1003
3050
  if (previousRoot)
1004
3051
  return previousRoot;
1005
3052
  return null;
1006
3053
  }
3054
+ async function preflightKeywordTargetState(stateDir, sessionId, context, nowIso) {
3055
+ const targetDir = join(stateDir, 'sessions', sessionId);
3056
+ const evidence = [];
3057
+ let filenames;
3058
+ try {
3059
+ filenames = await readdir(targetDir);
3060
+ }
3061
+ catch (error) {
3062
+ if (error.code === 'ENOENT')
3063
+ return context;
3064
+ return preflightSelectedTargetOwner(context, [{ ownerCodexSessionId: {} }], 'native', nowIso);
3065
+ }
3066
+ for (const filename of filenames) {
3067
+ if (!filename.endsWith('-state.json') && filename !== SKILL_ACTIVE_STATE_FILE)
3068
+ continue;
3069
+ try {
3070
+ const value = JSON.parse(await readFile(join(targetDir, filename), 'utf8'));
3071
+ evidence.push(...extractSelectedTargetOwnerEvidence(value));
3072
+ }
3073
+ catch {
3074
+ evidence.push({ ownerCodexSessionId: {} });
3075
+ }
3076
+ }
3077
+ return preflightSelectedTargetOwner(context, evidence, 'native', nowIso);
3078
+ }
1007
3079
  export async function recordSkillActivation(input) {
3080
+ const classification = input.classification ?? classifyKeywordInput(input.text);
3081
+ if (classification.originalText !== input.text) {
3082
+ throw new Error('Keyword input classification text does not match activation text');
3083
+ }
3084
+ const resolvedPromptTurnContext = input.resolvedPromptTurnContext;
3085
+ if (resolvedPromptTurnContext && resolvedPromptTurnContext.status !== 'authorized')
3086
+ return null;
3087
+ if (resolvedPromptTurnContext && input.sessionId !== resolvedPromptTurnContext.authorization.targetSessionId)
3088
+ return null;
3089
+ const suppressRootMutation = resolvedPromptTurnContext?.authorization.globalSideEffects === 'suppress';
3090
+ const provenanceOwnerCodexSessionId = resolvedPromptTurnContext?.authorization.ownerCodexSessionId;
3091
+ const applyProvenanceOwner = (state) => (provenanceOwnerCodexSessionId
3092
+ ? {
3093
+ ...state,
3094
+ owner_codex_session_id: provenanceOwnerCodexSessionId,
3095
+ active_skills: state.active_skills?.map((entry) => ({ ...entry, owner_codex_session_id: provenanceOwnerCodexSessionId })),
3096
+ }
3097
+ : state);
1008
3098
  const sourceCwd = input.sourceCwd ?? dirname(dirname(input.stateDir));
1009
3099
  const rootStatePath = join(input.stateDir, SKILL_ACTIVE_STATE_FILE);
1010
3100
  const sessionStatePath = input.sessionId
1011
3101
  ? join(input.stateDir, 'sessions', input.sessionId, SKILL_ACTIVE_STATE_FILE)
1012
3102
  : null;
1013
- const previousRoot = await readExistingSkillState(rootStatePath);
3103
+ if (resolvedPromptTurnContext && input.sessionId) {
3104
+ const preflight = await preflightKeywordTargetState(input.stateDir, input.sessionId, resolvedPromptTurnContext, input.nowIso ?? new Date().toISOString());
3105
+ if (preflight.status === 'rejected') {
3106
+ await input.onProvenanceRejected?.(preflight.diagnostic);
3107
+ return null;
3108
+ }
3109
+ }
3110
+ const previousRoot = suppressRootMutation ? null : await readExistingSkillState(rootStatePath);
1014
3111
  const previousSession = sessionStatePath ? await readExistingSkillState(sessionStatePath) : null;
1015
3112
  const previous = input.sessionId ? previousSession : previousRoot;
1016
3113
  const teamMode = readTeamModeConfig(sourceCwd);
1017
- const match = resolveContinuationKeywordMatch(input.text, previous, detectPrimaryKeywordForTeamMode(input.text, teamMode.enabled));
3114
+ const match = resolveContinuationKeywordMatch(input.text, previous, detectPrimaryKeywordForTeamMode(classification, teamMode.enabled), classification);
1018
3115
  if (!match)
1019
3116
  return null;
1020
3117
  const nowIso = input.nowIso ?? new Date().toISOString();
1021
3118
  const hadDeepInterviewLock = previous?.skill === 'deep-interview' && previous?.input_lock?.active === true;
1022
- const matches = filterMatchesForTeamMode(detectKeywords(input.text), teamMode.enabled);
3119
+ const matches = filterMatchesForTeamMode(classification.matches, teamMode.enabled);
1023
3120
  const hasCancelIntent = matches.some((entry) => entry.skill === 'cancel');
1024
3121
  if (hasCancelIntent && hadDeepInterviewLock) {
1025
3122
  const state = {
@@ -1038,17 +3135,17 @@ export async function recordSkillActivation(input) {
1038
3135
  ...(previous?.input_lock ? { input_lock: releaseDeepInterviewInputLock(previous.input_lock, nowIso, 'abort') } : {}),
1039
3136
  };
1040
3137
  try {
1041
- await writeSkillActiveStateCopiesForStateDir(input.stateDir, state, input.sessionId, selectRootSkillStateCopy(previousRoot, state, input.sessionId));
1042
- await persistDeepInterviewModeState(input.stateDir, state, nowIso, previous, input);
3138
+ await writeSkillActiveStateCopiesForStateDir(input.stateDir, applyProvenanceOwner(state), input.sessionId, selectRootSkillStateCopy(previousRoot, state, input.sessionId, suppressRootMutation));
3139
+ await persistDeepInterviewModeState(input.stateDir, applyProvenanceOwner(state), nowIso, previous, input);
1043
3140
  }
1044
3141
  catch (error) {
1045
3142
  console.warn('[omx] warning: failed to persist keyword activation state', error);
1046
3143
  }
1047
- return state;
3144
+ return applyProvenanceOwner(state);
1048
3145
  }
1049
3146
  const sameSkill = previous?.active === true && previous.skill === match.skill;
1050
3147
  const sameKeyword = previous?.keyword?.toLowerCase() === match.keyword.toLowerCase();
1051
- const sameSkillContinuation = sameSkill && shouldReusePreviousSkillForContinuation(input.text, previous);
3148
+ const sameSkillContinuation = sameSkill && shouldReusePreviousSkillForContinuation(input.text, previous, classification);
1052
3149
  const matchedSeedConfig = STATEFUL_SKILL_SEED_CONFIG[match.skill];
1053
3150
  const matchedModeState = matchedSeedConfig
1054
3151
  ? await readJsonStateIfExists(resolveSeedStateFilePath(input.stateDir, matchedSeedConfig.mode, input.sessionId, matchedSeedConfig.scope).absolutePath)
@@ -1056,9 +3153,12 @@ export async function recordSkillActivation(input) {
1056
3153
  const matchedModeTerminal = matchedSeedConfig
1057
3154
  ? isResettableTerminalModeState(matchedModeState, matchedSeedConfig.mode)
1058
3155
  : false;
3156
+ if (classification.reservedInput === 'omx-question-answered' && matchedModeTerminal)
3157
+ return null;
1059
3158
  const preserveActivatedAt = sameSkill && !matchedModeTerminal && (sameKeyword || sameSkillContinuation);
1060
3159
  const previousEntries = listActiveSkills(previous ?? {});
1061
3160
  const previousWorkflowEntries = previousEntries.filter((entry) => (isTrackedWorkflowMode(entry.skill)
3161
+ && (input.allowSecondaryAutopilot !== false || entry.skill !== 'autopilot' || entry.skill === match.skill)
1062
3162
  && (!input.sessionId
1063
3163
  || !safeString(entry.session_id).trim()
1064
3164
  || safeString(entry.session_id).trim() === safeString(input.sessionId).trim())));
@@ -1066,14 +3166,13 @@ export async function recordSkillActivation(input) {
1066
3166
  const trackedMatchSkill = isTrackedWorkflowMatch ? match.skill : null;
1067
3167
  const markedQuestionAnswerContinuation = sameSkill
1068
3168
  && (match.skill === 'autopilot' || match.skill === 'deep-interview')
1069
- && isOmxQuestionAnsweredPrompt(input.text);
1070
- const normalizedInputText = isTrackedWorkflowMatch
1071
- ? normalizeWorkflowKeyboardTypos(input.text)
1072
- : input.text;
3169
+ && classification.reservedInput === 'omx-question-answered';
1073
3170
  const workflowMatches = isTrackedWorkflowMatch && !markedQuestionAnswerContinuation
1074
- ? parseExplicitSkillInvocations(normalizedInputText).matches
3171
+ ? classification.explicitMatches
1075
3172
  .map((entry) => entry.skill)
1076
3173
  .filter((skill) => teamMode.enabled || skill !== 'team')
3174
+ .filter((skill) => input.allowSecondaryTeam !== false || skill !== 'team' || skill === trackedMatchSkill)
3175
+ .filter((skill) => input.allowSecondaryAutopilot !== false || skill !== 'autopilot' || skill === trackedMatchSkill)
1077
3176
  .filter(isTrackedWorkflowMode)
1078
3177
  : [];
1079
3178
  const resolvedWorkflowRequest = isTrackedWorkflowMatch
@@ -1092,7 +3191,7 @@ export async function recordSkillActivation(input) {
1092
3191
  const deepInterviewConfig = willActivateDeepInterview
1093
3192
  ? reusableDeepInterviewConfig ?? resolveDeepInterviewRuntimeConfig({ cwd: sourceCwd, text: input.text })
1094
3193
  : null;
1095
- if (previous?.active === true && previous.skill === 'autopilot' && isAutopilotSupervisedChildSkill(match.skill)) {
3194
+ if (input.allowSecondaryAutopilot !== false && previous?.active === true && previous.skill === 'autopilot' && isAutopilotSupervisedChildSkill(match.skill)) {
1096
3195
  try {
1097
3196
  // Reconcile first so skill-active phase reflects the gate-held phase the
1098
3197
  // autopilot detail state actually advanced to (a blocked advance keeps the
@@ -1122,8 +3221,8 @@ export async function recordSkillActivation(input) {
1122
3221
  supervised_child_keyword: match.keyword,
1123
3222
  supervised_child_skill: match.skill,
1124
3223
  };
1125
- await writeSkillActiveStateCopiesForStateDir(input.stateDir, nextState, input.sessionId, selectRootSkillStateCopy(previousRoot, nextState, input.sessionId));
1126
- return nextState;
3224
+ await writeSkillActiveStateCopiesForStateDir(input.stateDir, applyProvenanceOwner(nextState), input.sessionId, selectRootSkillStateCopy(previousRoot, nextState, input.sessionId, suppressRootMutation));
3225
+ return applyProvenanceOwner(nextState);
1127
3226
  }
1128
3227
  catch (error) {
1129
3228
  return {
@@ -1254,27 +3353,32 @@ export async function recordSkillActivation(input) {
1254
3353
  ...(primarySkill === 'deep-interview' && deepInterviewConfig ? { deep_interview_config: deepInterviewConfig } : {}),
1255
3354
  };
1256
3355
  try {
1257
- let nextState = { ...workflowState };
3356
+ const ownedWorkflowState = applyProvenanceOwner(workflowState);
3357
+ let nextState = { ...ownedWorkflowState };
1258
3358
  for (const requestedEntry of nextWorkflowEntries) {
1259
3359
  const seeded = await persistStatefulSkillSeedState(input.stateDir, {
1260
- ...workflowState,
3360
+ ...ownedWorkflowState,
1261
3361
  skill: requestedEntry.skill,
1262
3362
  keyword: requestedEntry.skill === workflowState.skill ? workflowState.keyword : `$${requestedEntry.skill}`,
1263
3363
  phase: requestedEntry.phase || workflowState.phase,
1264
3364
  activated_at: requestedEntry.activated_at || workflowState.activated_at,
1265
3365
  updated_at: requestedEntry.updated_at || workflowState.updated_at,
1266
3366
  ...(requestedEntry.skill === 'deep-interview' && deepInterviewConfig ? { deep_interview_config: deepInterviewConfig } : {}),
1267
- }, nowIso, previous, input.text, sourceCwd, { activeContinuation: requestedEntry.skill === 'autopilot' && sameSkillContinuation });
3367
+ }, nowIso, previous, input.text, sourceCwd, {
3368
+ activeContinuation: requestedEntry.skill === 'autopilot' && sameSkillContinuation,
3369
+ forceSessionScope: suppressRootMutation,
3370
+ });
1268
3371
  if (requestedEntry.skill === workflowState.skill) {
1269
3372
  nextState = {
1270
- ...workflowState,
3373
+ ...ownedWorkflowState,
1271
3374
  initialized_mode: seeded.initialized_mode,
1272
3375
  initialized_state_path: seeded.initialized_state_path,
1273
3376
  };
1274
3377
  }
1275
3378
  }
3379
+ nextState = applyProvenanceOwner(nextState);
1276
3380
  nextState.active_skills = buildActiveSkills(nextState);
1277
- await writeSkillActiveStateCopiesForStateDir(input.stateDir, nextState, input.sessionId, selectRootSkillStateCopy(previousRoot, nextState, input.sessionId));
3381
+ await writeSkillActiveStateCopiesForStateDir(input.stateDir, nextState, input.sessionId, selectRootSkillStateCopy(previousRoot, nextState, input.sessionId, suppressRootMutation));
1278
3382
  await persistDeepInterviewModeState(input.stateDir, nextState, nowIso, previous, input);
1279
3383
  return nextState;
1280
3384
  }
@@ -1309,11 +3413,16 @@ export async function recordSkillActivation(input) {
1309
3413
  ...(match.skill === 'deep-interview' && deepInterviewConfig ? { deep_interview_config: deepInterviewConfig } : {}),
1310
3414
  };
1311
3415
  try {
1312
- const nextState = await persistStatefulSkillSeedState(input.stateDir, state, nowIso, previous, input.text, sourceCwd, { activeContinuation: match.skill === 'autopilot' && sameSkillContinuation });
1313
- nextState.active_skills = buildActiveSkills(nextState);
1314
- await writeSkillActiveStateCopiesForStateDir(input.stateDir, nextState, input.sessionId, selectRootSkillStateCopy(previousRoot, nextState, input.sessionId));
1315
- await persistDeepInterviewModeState(input.stateDir, nextState, nowIso, previous, input);
1316
- return nextState;
3416
+ const ownedState = applyProvenanceOwner(state);
3417
+ const nextState = await persistStatefulSkillSeedState(input.stateDir, ownedState, nowIso, previous, input.text, sourceCwd, {
3418
+ activeContinuation: match.skill === 'autopilot' && sameSkillContinuation,
3419
+ forceSessionScope: suppressRootMutation,
3420
+ });
3421
+ const ownedNextState = applyProvenanceOwner(nextState);
3422
+ ownedNextState.active_skills = buildActiveSkills(ownedNextState);
3423
+ await writeSkillActiveStateCopiesForStateDir(input.stateDir, ownedNextState, input.sessionId, selectRootSkillStateCopy(previousRoot, ownedNextState, input.sessionId, suppressRootMutation));
3424
+ await persistDeepInterviewModeState(input.stateDir, ownedNextState, nowIso, previous, input);
3425
+ return ownedNextState;
1317
3426
  }
1318
3427
  catch (error) {
1319
3428
  console.warn('[omx] warning: failed to persist keyword activation state', error);