oh-my-opencode 5.1.48 → 5.1.51

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 (495) hide show
  1. package/dist/agents/refusal-rule.d.ts +7 -0
  2. package/dist/cli/index.js +1194 -752
  3. package/dist/cli/service-auth/accounts-gate.d.ts +11 -0
  4. package/dist/cli/service-auth/commands.d.ts +2 -4
  5. package/dist/cli/service-auth/index.d.ts +1 -1
  6. package/dist/cli/service-auth/keystore.d.ts +26 -2
  7. package/dist/cli/service-auth/protocol.d.ts +7 -1
  8. package/dist/cli/service-auth/session.d.ts +6 -0
  9. package/dist/cli/service-auth/signed-out-marker.d.ts +11 -0
  10. package/dist/cli-node/index.js +1194 -752
  11. package/dist/config/schema/hooks.d.ts +1 -0
  12. package/dist/create-hooks.d.ts +1 -0
  13. package/dist/features/boulder-state/storage.d.ts +1 -1
  14. package/dist/features/boulder-state/types.d.ts +1 -1
  15. package/dist/hooks/atlas/idle-continuation.d.ts +2 -1
  16. package/dist/hooks/atlas/types.d.ts +9 -1
  17. package/dist/hooks/index.d.ts +1 -0
  18. package/dist/hooks/momus-review-persistence/hook.d.ts +19 -0
  19. package/dist/hooks/momus-review-persistence/index.d.ts +1 -0
  20. package/dist/hooks/momus-review-persistence/review-resolution.d.ts +20 -0
  21. package/dist/hooks/prometheus-md-only/constants.d.ts +2 -0
  22. package/dist/hooks/prometheus-md-only/fixtures/denial-log.d.ts +1 -0
  23. package/dist/hooks/prometheus-md-only/fixtures/guarded-hashline.d.ts +1 -0
  24. package/dist/hooks/prometheus-md-only/fixtures/invoke-before.d.ts +7 -0
  25. package/dist/hooks/prometheus-md-only/fixtures/opencode-patch-parser.d.ts +2 -0
  26. package/dist/hooks/prometheus-md-only/hook.d.ts +5 -1
  27. package/dist/hooks/prometheus-md-only/path-policy.d.ts +2 -9
  28. package/dist/hooks/prometheus-md-only/vendor/opencode-patch-parser.d.ts +22 -0
  29. package/dist/index.js +2322 -1045
  30. package/dist/plugin/hooks/create-core-hooks.d.ts +1 -0
  31. package/dist/plugin/hooks/create-tool-guard-hooks.d.ts +2 -1
  32. package/dist/shared/posthog-activity-state.d.ts +1 -0
  33. package/dist/shared/posthog.d.ts +1 -2
  34. package/dist/shared/resolve-file-write-path.d.ts +6 -0
  35. package/dist/skills/ast-grep/README.md +1 -1
  36. package/dist/skills/ast-grep/install.ps1 +5 -3
  37. package/dist/skills/ast-grep/install.sh +4 -5
  38. package/dist/skills/ast-grep/references/install.md +2 -2
  39. package/dist/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
  40. package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
  41. package/dist/skills/coding-agent-sessions/AGENTS.md +5 -4
  42. package/dist/skills/coding-agent-sessions/SKILL.md +11 -11
  43. package/dist/skills/coding-agent-sessions/references/all-platforms.md +2 -2
  44. package/dist/skills/coding-agent-sessions/references/codex.md +4 -4
  45. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
  46. package/dist/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
  47. package/dist/skills/frontend/ATTRIBUTION.md +1 -1
  48. package/dist/skills/frontend/SKILL.md +34 -12
  49. package/dist/skills/frontend/references/design/README.md +2 -2
  50. package/dist/skills/frontend/references/design/_INDEX.md +2 -1
  51. package/dist/skills/frontend/references/design/component-catalogs.md +14 -10
  52. package/dist/skills/frontend/references/design/design-system-architecture.md +19 -6
  53. package/dist/skills/frontend/references/design/grounding.md +29 -0
  54. package/dist/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
  55. package/dist/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
  56. package/dist/skills/ultimate-browsing/SKILL.md +5 -4
  57. package/dist/skills/ultimate-browsing/engine/AGENTS.md +2 -2
  58. package/dist/skills/ultimate-browsing/engine/__main__.py +9 -6
  59. package/dist/skills/ultimate-browsing/engine/summary.py +1 -1
  60. package/dist/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  61. package/dist/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
  62. package/dist/skills/ulw-research/AGENTS.md +8 -4
  63. package/dist/skills/ulw-research/SKILL.md +7 -316
  64. package/dist/skills/ulw-research/references/deliverable-phase.md +23 -21
  65. package/dist/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
  66. package/dist/skills/ulw-research/references/fact-ledger.md +42 -8
  67. package/dist/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
  68. package/dist/skills/ulw-research/references/markdown-blocks.md +12 -0
  69. package/dist/skills/ulw-research/references/report-gates.md +12 -5
  70. package/dist/skills/ulw-research/references/research-collection.md +195 -0
  71. package/dist/skills/ulw-research/references/research-delivery.md +123 -0
  72. package/dist/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
  73. package/dist/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
  74. package/dist/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
  75. package/dist/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
  76. package/dist/skills/ulw-research/scripts/contracts.mjs +13 -4
  77. package/dist/skills/ulw-research/scripts/contracts.test.ts +6 -110
  78. package/dist/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
  79. package/dist/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
  80. package/dist/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
  81. package/dist/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
  82. package/dist/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
  83. package/dist/skills/ulw-research/scripts/copy-returns-polish.test.ts +4 -3
  84. package/dist/skills/ulw-research/scripts/css-hiding.mjs +158 -0
  85. package/dist/skills/ulw-research/scripts/css-lite.mjs +4 -2
  86. package/dist/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
  87. package/dist/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
  88. package/dist/skills/ulw-research/scripts/entities.mjs +13 -4
  89. package/dist/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
  90. package/dist/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
  91. package/dist/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
  92. package/dist/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
  93. package/dist/skills/ulw-research/scripts/fact-commands.mjs +35 -16
  94. package/dist/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
  95. package/dist/skills/ulw-research/scripts/fact-derive.mjs +7 -2
  96. package/dist/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
  97. package/dist/skills/ulw-research/scripts/fact-fill.mjs +6 -4
  98. package/dist/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
  99. package/dist/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
  100. package/dist/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
  101. package/dist/skills/ulw-research/scripts/fact-locate.mjs +15 -9
  102. package/dist/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
  103. package/dist/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
  104. package/dist/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
  105. package/dist/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
  106. package/dist/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
  107. package/dist/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
  108. package/dist/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
  109. package/dist/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
  110. package/dist/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
  111. package/dist/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
  112. package/dist/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
  113. package/dist/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
  114. package/dist/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
  115. package/dist/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
  116. package/dist/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
  117. package/dist/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
  118. package/dist/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
  119. package/dist/skills/ulw-research/scripts/gates-static.mjs +10 -1
  120. package/dist/skills/ulw-research/scripts/html-lite.mjs +6 -2
  121. package/dist/skills/ulw-research/scripts/html-markup.mjs +2 -2
  122. package/dist/skills/ulw-research/scripts/html-text-map.mjs +140 -0
  123. package/dist/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
  124. package/dist/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
  125. package/dist/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
  126. package/dist/skills/ulw-research/scripts/md-lite.mjs +1 -1
  127. package/dist/skills/ulw-research/scripts/number-grammar.mjs +33 -0
  128. package/dist/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
  129. package/dist/skills/ulw-research/scripts/numerals.mjs +18 -7
  130. package/dist/skills/ulw-research/scripts/numerals.test.ts +3 -0
  131. package/dist/skills/ulw-research/scripts/outcome-start.mjs +66 -0
  132. package/dist/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
  133. package/dist/skills/ulw-research/scripts/outcome.mjs +11 -22
  134. package/dist/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
  135. package/dist/skills/ulw-research/scripts/placeholder.mjs +18 -6
  136. package/dist/skills/ulw-research/scripts/polish-apply.mjs +3 -2
  137. package/dist/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
  138. package/dist/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
  139. package/dist/skills/ulw-research/scripts/raw-decode.mjs +110 -0
  140. package/dist/skills/ulw-research/scripts/raw-locator.mjs +47 -0
  141. package/dist/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
  142. package/dist/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
  143. package/dist/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
  144. package/dist/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
  145. package/dist/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
  146. package/dist/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
  147. package/dist/skills/ulw-research/scripts/report-tools.mjs +7 -1
  148. package/dist/skills/ulw-research/scripts/report-tools.test.ts +5 -800
  149. package/dist/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
  150. package/dist/skills/ulw-research/scripts/source-commands.mjs +79 -26
  151. package/dist/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
  152. package/dist/skills/ulw-research/scripts/source-refresh.mjs +82 -0
  153. package/dist/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
  154. package/dist/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
  155. package/dist/skills/ulw-research/scripts/source-view.mjs +89 -0
  156. package/dist/skills/ulw-research/scripts/sources-citation.mjs +203 -0
  157. package/dist/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
  158. package/dist/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
  159. package/dist/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
  160. package/dist/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
  161. package/dist/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
  162. package/dist/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
  163. package/dist/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
  164. package/dist/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
  165. package/dist/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
  166. package/dist/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
  167. package/dist/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
  168. package/dist/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
  169. package/dist/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
  170. package/dist/skills/ulw-research/scripts/units.mjs +14 -3
  171. package/dist/skills/ulw-research/scripts/units.test.ts +10 -0
  172. package/dist/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
  173. package/dist/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
  174. package/dist/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
  175. package/dist/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
  176. package/dist/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
  177. package/dist/tools/delegate-task/executor-types.d.ts +1 -0
  178. package/dist/tools/delegate-task/sync-session-existence.d.ts +4 -0
  179. package/dist/tools/delegate-task/types.d.ts +1 -0
  180. package/dist/tools/skill/description-formatter.d.ts +2 -1
  181. package/dist/tools/skill/types.d.ts +6 -0
  182. package/dist/tui.js +131 -33
  183. package/package.json +18 -15
  184. package/packages/git-bash-mcp/package.json +1 -1
  185. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  186. package/packages/omo-codex/plugin/README.md +1 -1
  187. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  188. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  189. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  190. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  191. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  192. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  193. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  194. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  195. package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
  196. package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  197. package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/SKILL.md +7 -7
  198. package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/agents/openai.yaml +1 -1
  199. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  200. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  201. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  202. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  203. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  204. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  205. package/packages/omo-codex/plugin/components/teammode/package.json +2 -2
  206. package/packages/omo-codex/plugin/components/telemetry/README.md +2 -2
  207. package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +52 -15
  208. package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +52 -19
  209. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  210. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  211. package/packages/omo-codex/plugin/components/telemetry/src/codex-hook.ts +2 -9
  212. package/packages/omo-codex/plugin/components/telemetry/src/posthog.ts +4 -13
  213. package/packages/omo-codex/plugin/components/telemetry/src/product-identity.ts +0 -2
  214. package/packages/omo-codex/plugin/components/telemetry/test/codex-hook-diagnostics.test.ts +0 -3
  215. package/packages/omo-codex/plugin/components/telemetry/test/codex-hook.test.ts +3 -8
  216. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +3 -0
  217. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  218. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  219. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  220. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  221. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  222. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +3 -0
  223. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  224. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  225. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  226. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  227. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  228. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  229. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  230. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  231. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  232. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  233. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  234. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  235. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  236. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  237. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  238. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  239. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  240. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  241. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  242. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  243. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  244. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  245. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  246. package/packages/omo-codex/plugin/package-lock.json +16 -16
  247. package/packages/omo-codex/plugin/package.json +1 -1
  248. package/packages/omo-codex/plugin/shared/package.json +1 -1
  249. package/packages/omo-codex/plugin/skills/ast-grep/README.md +1 -1
  250. package/packages/omo-codex/plugin/skills/ast-grep/install.ps1 +5 -3
  251. package/packages/omo-codex/plugin/skills/ast-grep/install.sh +4 -5
  252. package/packages/omo-codex/plugin/skills/ast-grep/references/install.md +2 -2
  253. package/packages/omo-codex/plugin/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
  254. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  255. package/packages/omo-codex/plugin/skills/coding-agent-sessions/AGENTS.md +5 -4
  256. package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +11 -11
  257. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +2 -2
  258. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/codex.md +4 -4
  259. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
  260. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
  261. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +1 -1
  262. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +34 -12
  263. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +2 -2
  264. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +2 -1
  265. package/packages/omo-codex/plugin/skills/frontend/references/design/component-catalogs.md +14 -10
  266. package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +19 -6
  267. package/packages/omo-codex/plugin/skills/frontend/references/design/grounding.md +29 -0
  268. package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
  269. package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
  270. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
  271. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  272. package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +7 -7
  273. package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
  274. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +5 -4
  275. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +2 -2
  276. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/__main__.py +9 -6
  277. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/summary.py +1 -1
  278. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  279. package/packages/omo-codex/plugin/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
  280. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +3 -0
  281. package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +8 -4
  282. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +7 -316
  283. package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +23 -21
  284. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
  285. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +42 -8
  286. package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
  287. package/packages/omo-codex/plugin/skills/ulw-research/references/markdown-blocks.md +12 -0
  288. package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +12 -5
  289. package/packages/omo-codex/plugin/skills/ulw-research/references/research-collection.md +195 -0
  290. package/packages/omo-codex/plugin/skills/ulw-research/references/research-delivery.md +123 -0
  291. package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
  292. package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +13 -4
  293. package/packages/omo-codex/plugin/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
  294. package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-hiding.mjs +158 -0
  295. package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-lite.mjs +4 -2
  296. package/packages/omo-codex/plugin/skills/ulw-research/scripts/entities.mjs +13 -4
  297. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
  298. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
  299. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
  300. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
  301. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +35 -16
  302. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +7 -2
  303. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +6 -4
  304. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
  305. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +15 -9
  306. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
  307. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
  308. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
  309. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
  310. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
  311. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
  312. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
  313. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
  314. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
  315. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
  316. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
  317. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
  318. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +10 -1
  319. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +6 -2
  320. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +2 -2
  321. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-text-map.mjs +140 -0
  322. package/packages/omo-codex/plugin/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
  323. package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +1 -1
  324. package/packages/omo-codex/plugin/skills/ulw-research/scripts/number-grammar.mjs +33 -0
  325. package/packages/omo-codex/plugin/skills/ulw-research/scripts/numerals.mjs +18 -7
  326. package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome-start.mjs +66 -0
  327. package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +11 -22
  328. package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +18 -6
  329. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +3 -2
  330. package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-decode.mjs +110 -0
  331. package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-locator.mjs +47 -0
  332. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
  333. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +7 -1
  334. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +79 -26
  335. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-refresh.mjs +82 -0
  336. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
  337. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-view.mjs +89 -0
  338. package/packages/omo-codex/plugin/skills/ulw-research/scripts/sources-citation.mjs +203 -0
  339. package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +14 -3
  340. package/packages/omo-codex/plugin/test/payload-equivalence.test.mjs +6 -4
  341. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +4 -3
  342. package/packages/omo-codex/scripts/install-dist/install-local.mjs +338 -237
  343. package/packages/prompts-core/prompts/ultrawork/codex.md +3 -0
  344. package/packages/shared-skills/skill-source-filter.mjs +6 -2
  345. package/packages/shared-skills/skills/ast-grep/README.md +1 -1
  346. package/packages/shared-skills/skills/ast-grep/install.ps1 +5 -3
  347. package/packages/shared-skills/skills/ast-grep/install.sh +4 -5
  348. package/packages/shared-skills/skills/ast-grep/references/install.md +2 -2
  349. package/packages/shared-skills/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
  350. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  351. package/packages/shared-skills/skills/coding-agent-sessions/AGENTS.md +5 -4
  352. package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +11 -11
  353. package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +2 -2
  354. package/packages/shared-skills/skills/coding-agent-sessions/references/codex.md +4 -4
  355. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
  356. package/packages/shared-skills/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
  357. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +1 -1
  358. package/packages/shared-skills/skills/frontend/SKILL.md +34 -12
  359. package/packages/shared-skills/skills/frontend/references/design/README.md +2 -2
  360. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +2 -1
  361. package/packages/shared-skills/skills/frontend/references/design/component-catalogs.md +14 -10
  362. package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +19 -6
  363. package/packages/shared-skills/skills/frontend/references/design/grounding.md +29 -0
  364. package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
  365. package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
  366. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +5 -4
  367. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +2 -2
  368. package/packages/shared-skills/skills/ultimate-browsing/engine/__main__.py +9 -6
  369. package/packages/shared-skills/skills/ultimate-browsing/engine/summary.py +1 -1
  370. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  371. package/packages/shared-skills/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
  372. package/packages/shared-skills/skills/ulw-research/AGENTS.md +8 -4
  373. package/packages/shared-skills/skills/ulw-research/SKILL.md +7 -316
  374. package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +23 -21
  375. package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
  376. package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +42 -8
  377. package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
  378. package/packages/shared-skills/skills/ulw-research/references/markdown-blocks.md +12 -0
  379. package/packages/shared-skills/skills/ulw-research/references/report-gates.md +12 -5
  380. package/packages/shared-skills/skills/ulw-research/references/research-collection.md +195 -0
  381. package/packages/shared-skills/skills/ulw-research/references/research-delivery.md +123 -0
  382. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
  383. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
  384. package/packages/shared-skills/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
  385. package/packages/shared-skills/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
  386. package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +13 -4
  387. package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +6 -110
  388. package/packages/shared-skills/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
  389. package/packages/shared-skills/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
  390. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
  391. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
  392. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
  393. package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-polish.test.ts +4 -3
  394. package/packages/shared-skills/skills/ulw-research/scripts/css-hiding.mjs +158 -0
  395. package/packages/shared-skills/skills/ulw-research/scripts/css-lite.mjs +4 -2
  396. package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
  397. package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
  398. package/packages/shared-skills/skills/ulw-research/scripts/entities.mjs +13 -4
  399. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
  400. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
  401. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
  402. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
  403. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +35 -16
  404. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
  405. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +7 -2
  406. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
  407. package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +6 -4
  408. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
  409. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
  410. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
  411. package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +15 -9
  412. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
  413. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
  414. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
  415. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
  416. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
  417. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
  418. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
  419. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
  420. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
  421. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
  422. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
  423. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
  424. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
  425. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
  426. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
  427. package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
  428. package/packages/shared-skills/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
  429. package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +10 -1
  430. package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +6 -2
  431. package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +2 -2
  432. package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.mjs +140 -0
  433. package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
  434. package/packages/shared-skills/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
  435. package/packages/shared-skills/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
  436. package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +1 -1
  437. package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.mjs +33 -0
  438. package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
  439. package/packages/shared-skills/skills/ulw-research/scripts/numerals.mjs +18 -7
  440. package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +3 -0
  441. package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.mjs +66 -0
  442. package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
  443. package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +11 -22
  444. package/packages/shared-skills/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
  445. package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +18 -6
  446. package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +3 -2
  447. package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
  448. package/packages/shared-skills/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
  449. package/packages/shared-skills/skills/ulw-research/scripts/raw-decode.mjs +110 -0
  450. package/packages/shared-skills/skills/ulw-research/scripts/raw-locator.mjs +47 -0
  451. package/packages/shared-skills/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
  452. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
  453. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
  454. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
  455. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
  456. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
  457. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +7 -1
  458. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +5 -800
  459. package/packages/shared-skills/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
  460. package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +79 -26
  461. package/packages/shared-skills/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
  462. package/packages/shared-skills/skills/ulw-research/scripts/source-refresh.mjs +82 -0
  463. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
  464. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
  465. package/packages/shared-skills/skills/ulw-research/scripts/source-view.mjs +89 -0
  466. package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.mjs +203 -0
  467. package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
  468. package/packages/shared-skills/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
  469. package/packages/shared-skills/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
  470. package/packages/shared-skills/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
  471. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
  472. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
  473. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
  474. package/packages/shared-skills/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
  475. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
  476. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
  477. package/packages/shared-skills/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
  478. package/packages/shared-skills/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
  479. package/packages/shared-skills/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
  480. package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +14 -3
  481. package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +10 -0
  482. package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
  483. package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
  484. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
  485. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
  486. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
  487. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/amp_redirect_stub.html +0 -7
  488. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/search_interstitial.html +0 -19
  489. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_available.json +0 -1
  490. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_snapshot.html +0 -1128
  491. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_fetch_chain.py +0 -77
  492. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +0 -86
  493. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +0 -279
  494. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate.py +0 -252
  495. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate_validators.py +0 -78
@@ -14,6 +14,7 @@ import { figureGates } from "./gates-figures.mjs"
14
14
  import { structureGates } from "./gates-structure.mjs"
15
15
  import { textGates } from "./gates-text.mjs"
16
16
  import { PROSE_TAGS, proseText, selectorPath } from "./html-lite.mjs"
17
+ import { sourcesClaims } from "./sources-citation.mjs"
17
18
 
18
19
  const CITATION_MARKER = /\[S\d+\]|\[Source\s*\d+\]|\[\uCD9C\uCC98[^\]]*\]|\[\^\d+\]/i
19
20
  const ANCHOR_HREF = /^#(?:s|src|source|ref|fn)/i
@@ -65,6 +66,11 @@ function hasCitation(node) {
65
66
  return CITATION_MARKER.test(proseText(node)) || descendants(node).some(isCitationAnchor)
66
67
  }
67
68
 
69
+ function proseScope(scope, owner) {
70
+ if (scope.section?.kind !== "sources" || !owner || sourcesClaims(owner).length === 0) return scope
71
+ return { ...scope, section: { ...scope.section, kind: null } }
72
+ }
73
+
68
74
  function toRegExp(pattern) {
69
75
  const re = pattern instanceof RegExp ? pattern : new RegExp(String(pattern), "i")
70
76
  return new RegExp(re.source, re.flags.replace(/[gy]/g, ""))
@@ -181,7 +187,10 @@ export function checkStatic(doc, options = {}) {
181
187
  continue
182
188
  }
183
189
  gatesRun.push(...gate.codes)
184
- defects.push(...gate.run(ctx))
190
+ const context = gate.codes.includes("unsourced_number")
191
+ ? { ...ctx, elements: ctx.elements.map(({ node, scope }) => ({ node, scope: proseScope(scope, node) })) }
192
+ : ctx
193
+ defects.push(...gate.run(context))
185
194
  }
186
195
  return { defects, gatesRun, gatesSkipped }
187
196
  }
@@ -23,7 +23,7 @@ const PROSE_SKIP = new Set(["code", "pre", "kbd", "samp", "script", "style", "sv
23
23
  const SAFE_ID = /^[A-Za-z_][\w-]*$/
24
24
 
25
25
  export const PROSE_TAGS = new Set(["p", "li", ...HEADINGS, "td", "th", "figcaption", "blockquote", "dd", "dt"])
26
- const PROSE_BREAKS = new Set([...P_CLOSERS, ...PROSE_TAGS, "br", "tr"])
26
+ export const PROSE_BREAKS = new Set([...P_CLOSERS, ...PROSE_TAGS, "br", "tr"])
27
27
 
28
28
  function makeNode(tag, attrs, parent, line) {
29
29
  return { tag, attrs, children: [], parent, line, text: "", raw: null }
@@ -130,6 +130,8 @@ export function parseHtml(source) {
130
130
  const flushText = (to) => {
131
131
  if (to > textStart) {
132
132
  const node = makeNode("#text", {}, top(), lineAt(starts, textStart))
133
+ node.source_start = textStart
134
+ node.source_end = to
133
135
  node.text = decodeEntities(src.slice(textStart, to))
134
136
  top().children.push(node)
135
137
  }
@@ -157,7 +159,7 @@ export function parseHtml(source) {
157
159
  const tag = readTag(src, lt + 2)
158
160
  if (tag.name !== "body" && tag.name !== "html") {
159
161
  for (let k = stack.length - 1; k > 0; k--) {
160
- if (stack[k].tag === tag.name) { stack.length = k; break }
162
+ if (stack[k].tag === tag.name) { stack[k].source_end = lt; stack.length = k; break }
161
163
  }
162
164
  }
163
165
  at = textStart = tag.end
@@ -169,6 +171,8 @@ export function parseHtml(source) {
169
171
  if (P_CLOSERS.has(tag.name)) closeWithin("p", P_SCOPE)
170
172
  if (tag.name === "li") closeWithin("li", LI_SCOPE)
171
173
  const node = makeNode(tag.name, tag.attrs, top(), lineAt(starts, lt))
174
+ node.source_start = tag.end
175
+ node.source_end = src.length
172
176
  top().children.push(node)
173
177
  if (tag.name === "body") sawBody = true
174
178
  if (tag.attrs.id) root.ids.set(tag.attrs.id, (root.ids.get(tag.attrs.id) ?? 0) + 1)
@@ -61,10 +61,10 @@ function scriptEnd(src, from) {
61
61
  function closeOf(src, from, name) {
62
62
  if (name === "script") return scriptEnd(src, from)
63
63
  if (RAW.has(name)) {
64
- const close = new RegExp(`</${name}(?=[\\s/>])[^>]*>`, "gi")
64
+ const close = new RegExp(`</${name}(?=[\\s/>])`, "gi")
65
65
  close.lastIndex = from
66
66
  const match = close.exec(src)
67
- return match ? match.index + match[0].length : src.length
67
+ return match ? readTag(src, match.index + 2).end : src.length
68
68
  }
69
69
  let depth = 0
70
70
  let stop = src.length
@@ -0,0 +1,140 @@
1
+ // html-text-map.mjs - the reader-visible text of a page, with a source map back into the markup.
2
+ //
3
+ // extractHtml(html) returns { root, text, blocks, starts, ends }: `text` is one line per block
4
+ // (prose blocks and table cells; whitespace collapsed, entities decoded) and text unit k came from
5
+ // html.slice(starts[k], ends[k]). Only text nodes are read: attribute values, CSS, scripts, and
6
+ // anything a reader never sees (head, script, style, svg, noscript, template, code-like elements,
7
+ // aria-hidden="true" and their descendants, and what the page's CSS hides: css-hiding.mjs)
8
+ // contribute nothing, so a locator over `text` can never land in markup. A synthetic separator
9
+ // (collapsed break, line feed) maps to an empty range at the previous character's end.
10
+ // Plain Node ESM, sibling imports only.
11
+
12
+ import { cssVisibility } from "./css-hiding.mjs"
13
+ import { decodeOne } from "./entities.mjs"
14
+ import { parseHtml, PROSE_BREAKS, PROSE_TAGS } from "./html-lite.mjs"
15
+
16
+ const SKIP = new Set(["head", "script", "style", "svg", "code", "pre", "kbd", "samp", "template", "noscript"])
17
+ const INLINE = new Set([
18
+ "a", "abbr", "b", "bdi", "bdo", "cite", "data", "del", "dfn", "em", "font", "i", "ins", "label", "mark", "q", "s",
19
+ "small", "span", "strong", "sub", "sup", "time", "u", "var",
20
+ ])
21
+ const SPACE = /\s/
22
+
23
+ /** null when a reader never sees the element or anything inside it, else whether its own text shows (css: cssVisibility). */
24
+ function shownState(node, css, parentShown) {
25
+ if (SKIP.has(node.tag) || String(node.attrs?.["aria-hidden"] ?? "").trim().toLowerCase() === "true") return null
26
+ return css(node, parentShown)
27
+ }
28
+
29
+ function newLine() {
30
+ return { node: null, text: "", starts: [], ends: [], space: null }
31
+ }
32
+
33
+ function put(line, unit, start, end) {
34
+ if (SPACE.test(unit)) {
35
+ if (line.text !== "" && line.space === null) line.space = [start, end]
36
+ return
37
+ }
38
+ if (line.space !== null) {
39
+ line.text += " "
40
+ line.starts.push(line.space[0])
41
+ line.ends.push(line.space[1])
42
+ line.space = null
43
+ }
44
+ line.text += unit
45
+ line.starts.push(start)
46
+ line.ends.push(end)
47
+ }
48
+
49
+ /** A collapsible break with no source character of its own. */
50
+ function softBreak(line) {
51
+ if (line.text !== "" && line.space === null) {
52
+ const at = line.ends[line.ends.length - 1]
53
+ line.space = [at, at]
54
+ }
55
+ }
56
+
57
+ /** Append a text node's source range, decoding entities exactly as decodeEntities does. */
58
+ function putText(line, src, from, to) {
59
+ for (let at = from; at < to; ) {
60
+ if (src[at] === "&") {
61
+ const semi = src.indexOf(";", at + 1)
62
+ const decoded = semi !== -1 && semi < to && semi - at <= 12 ? decodeOne(src.slice(at + 1, semi)) : null
63
+ if (decoded !== null) {
64
+ for (let k = 0; k < decoded.length; k++) put(line, decoded[k], at, semi + 1)
65
+ at = semi + 1
66
+ continue
67
+ }
68
+ }
69
+ put(line, src[at], at, at + 1)
70
+ at++
71
+ }
72
+ }
73
+
74
+ function putProse(line, src, node, css, shown) {
75
+ for (const child of node.children) {
76
+ if (child.tag === "#text") {
77
+ if (shown) putText(line, src, child.source_start, child.source_end)
78
+ continue
79
+ }
80
+ const state = shownState(child, css, shown)
81
+ if (state === null) continue
82
+ const breaks = PROSE_BREAKS.has(child.tag)
83
+ if (breaks) softBreak(line)
84
+ putProse(line, src, child, css, state)
85
+ if (breaks) softBreak(line)
86
+ }
87
+ }
88
+
89
+ /** See the file comment. blocks: [{ node, start, end }] with text.slice(start, end) one block line. */
90
+ export function extractHtml(html) {
91
+ const src = String(html)
92
+ const root = parseHtml(src)
93
+ const css = cssVisibility(root)
94
+ const lines = []
95
+ let line = newLine()
96
+ const flush = (node) => {
97
+ if (line.text !== "") lines.push({ ...line, node })
98
+ line = newLine()
99
+ }
100
+ const walk = (node, shown) => {
101
+ for (const child of node.children) {
102
+ if (child.tag === "#text") {
103
+ if (shown) putText(line, src, child.source_start, child.source_end)
104
+ continue
105
+ }
106
+ const state = shownState(child, css, shown)
107
+ if (state === null) continue
108
+ if (child.tag === "br") flush(node)
109
+ else if (INLINE.has(child.tag)) walk(child, state)
110
+ else if (PROSE_TAGS.has(child.tag)) {
111
+ flush(node)
112
+ putProse(line, src, child, css, state)
113
+ flush(child)
114
+ } else {
115
+ flush(node)
116
+ walk(child, state)
117
+ flush(child)
118
+ }
119
+ }
120
+ }
121
+ walk(root, true)
122
+ flush(root)
123
+ const blocks = []
124
+ const starts = []
125
+ const ends = []
126
+ let text = ""
127
+ for (const entry of lines) {
128
+ if (text !== "") {
129
+ const at = ends[ends.length - 1]
130
+ text += "\n"
131
+ starts.push(at)
132
+ ends.push(at)
133
+ }
134
+ blocks.push({ node: entry.node, start: text.length, end: text.length + entry.text.length })
135
+ text += entry.text
136
+ starts.push(...entry.starts)
137
+ ends.push(...entry.ends)
138
+ }
139
+ return { root, text, blocks, starts, ends }
140
+ }
@@ -0,0 +1,126 @@
1
+ // markdown-closers.mjs - the Markdown closer runs a converter writes after a piece of text, read with
2
+ // the CommonMark inline grammar instead of a hand-written pattern. A closer run is any sequence of:
3
+ // an inline link ending `](dest "title")` (the destination either in <...> or bare with balanced
4
+ // parentheses, the optional title in "...", '...' or (...), backslash escapes inside both), a full or
5
+ // collapsed reference ending `][ref]` / `][]`, a whole image `![alt](...)` or `![alt][ref]` (its alt
6
+ // text is not context), the `[` that opened a link whose text is all markup (`[![x](i.png)](/p)`),
7
+ // an emphasis, strike or code delimiter, a bare `)`, `]` or `>`, and a backslash-escaped closer
8
+ // (`\*`, `\_`, `\]`, `\)`, `\~`, a backtick), which stands for the raw character and is skipped like
9
+ // it. An escaped opener (`\[`, `\(`) is a real character and ends the run. Shared by the conversion
10
+ // probe's sign reading and its link-syntax spans. Pure; no imports.
11
+
12
+ const SIMPLE = new Set(["*", "_", "~", "`", ")", "]", ">"])
13
+ const BREAK = "\u0001"
14
+
15
+ /** Whether the character at `index` is backslash-escaped (an odd run of backslashes before it). */
16
+ export function escapedAt(text, index) {
17
+ let count = 0
18
+ for (let i = index - 1; i >= 0 && text[i] === "\\"; i--) count++
19
+ return count % 2 === 1
20
+ }
21
+
22
+ /** The end (exclusive) of an inline link target `dest "title")` that starts right after its "(", or -1. */
23
+ export function inlineTargetEnd(text, from) {
24
+ let i = from
25
+ const space = () => {
26
+ while (text[i] === " " || text[i] === "\t") i++
27
+ }
28
+ space()
29
+ if (text[i] === "<") {
30
+ for (i++; i < text.length && text[i] !== ">"; i++) {
31
+ if (text[i] === "\\") i++
32
+ else if (text[i] === "<" || text[i] === BREAK) return -1
33
+ }
34
+ if (text[i++] !== ">") return -1
35
+ } else {
36
+ let depth = 0
37
+ for (; i < text.length; i++) {
38
+ const ch = text[i]
39
+ if (ch === "\\") i++
40
+ else if (ch === "(") depth++
41
+ else if (ch === ")" && depth === 0) break
42
+ else if (ch === ")") depth--
43
+ else if (ch === " " || ch === "\t" || ch === BREAK) break
44
+ }
45
+ if (depth !== 0) return -1
46
+ }
47
+ const destEnd = i
48
+ space()
49
+ const open = text[i]
50
+ if (i > destEnd && (open === '"' || open === "'" || open === "(")) {
51
+ const close = open === "(" ? ")" : open
52
+ for (i++; i < text.length && text[i] !== close; i++) {
53
+ if (text[i] === "\\") i++
54
+ else if (text[i] === BREAK || (open === "(" && text[i] === "(")) return -1
55
+ }
56
+ if (text[i++] !== close) return -1
57
+ space()
58
+ }
59
+ return text[i] === ")" ? i + 1 : -1
60
+ }
61
+
62
+ /** The unescaped "[" that the unescaped "]" at `close` closes (brackets balance), or -1. */
63
+ function openerOf(text, close) {
64
+ let depth = 0
65
+ for (let i = close; i >= 0 && text[i] !== BREAK; i--) {
66
+ if ((text[i] !== "[" && text[i] !== "]") || escapedAt(text, i)) continue
67
+ depth += text[i] === "]" ? 1 : -1
68
+ if (depth === 0) return i
69
+ }
70
+ return -1
71
+ }
72
+
73
+ /** The "](" positions whose inline target ends exactly at `end`, leftmost first (so a title holding "](" is not cut). */
74
+ function inlineLinkClose(text, end) {
75
+ const from = Math.max(0, text.lastIndexOf(BREAK, end - 1))
76
+ for (let k = text.indexOf("](", from); k !== -1 && k < end; k = text.indexOf("](", k + 1)) {
77
+ if (!escapedAt(text, k) && inlineTargetEnd(text, k + 2) === end) return k
78
+ }
79
+ return -1
80
+ }
81
+
82
+ /** Where the link or image tail ending at `end` starts, and whether its "[" is still open; null when none ends there. */
83
+ function linkTail(text, end) {
84
+ let close = -1
85
+ if (text[end - 1] === ")") close = inlineLinkClose(text, end)
86
+ else if (text[end - 1] === "]" && !escapedAt(text, end - 1)) {
87
+ const ref = openerOf(text, end - 1)
88
+ if (ref > 0 && text[ref - 1] === "]" && !escapedAt(text, ref - 1)) close = ref - 1
89
+ }
90
+ if (close === -1) return null
91
+ const open = openerOf(text, close)
92
+ if (open > 0 && text[open - 1] === "!" && !escapedAt(text, open - 1)) return { start: open - 1, opened: 0 }
93
+ return { start: close, opened: open === -1 ? 0 : 1 }
94
+ }
95
+
96
+ /** `text` without the Markdown closer run it ends with. */
97
+ export function stripClosers(text) {
98
+ let end = text.length
99
+ let owed = 0
100
+ for (;;) {
101
+ const tail = linkTail(text, end)
102
+ if (tail) {
103
+ end = tail.start
104
+ owed += tail.opened
105
+ continue
106
+ }
107
+ const ch = text[end - 1]
108
+ if (SIMPLE.has(ch)) end -= escapedAt(text, end - 1) ? 2 : 1
109
+ else if (ch === "[" && owed > 0 && !escapedAt(text, end - 1)) {
110
+ end--
111
+ owed--
112
+ } else return text.slice(0, end)
113
+ }
114
+ }
115
+
116
+ /** [start, end) spans of every inline link or image with its target (an image from its "!"), for link-syntax cover. */
117
+ export function inlineLinkSpans(text) {
118
+ const spans = []
119
+ for (let k = text.indexOf("]("); k !== -1; k = text.indexOf("](", k + 1)) {
120
+ const end = escapedAt(text, k) ? -1 : inlineTargetEnd(text, k + 2)
121
+ if (end === -1) continue
122
+ const open = openerOf(text, k)
123
+ spans.push([open > 0 && text[open - 1] === "!" ? open - 1 : k, end])
124
+ }
125
+ return spans
126
+ }
@@ -113,7 +113,7 @@ export function parseMarkdown(text) {
113
113
  const codeStart = Math.min(line.next, source.length)
114
114
  let close = lines.length
115
115
  for (let k = i + 1; k < lines.length; k++) {
116
- const candidate = lines[k].raw.trim()
116
+ const candidate = lines[k].raw.replace(/^ {0,3}/, "").trimEnd()
117
117
  if (candidate.startsWith(open[0].repeat(open.length)) && /^(`+|~+)$/.test(candidate)) {
118
118
  close = k
119
119
  break
@@ -0,0 +1,33 @@
1
+ // number-grammar.mjs - the one grouped-number grammar. A grouped number is a 1-3 digit leading
2
+ // group, then one or more 3-digit groups joined by ONE separator type, with no digit right after the
3
+ // last group, then an optional decimal part. "12,3451" is therefore not "12,345" plus "1". A period
4
+ // groups only when the reading is unambiguous: two or more period groups ("1.234.567") or one group
5
+ // before a decimal comma ("1.234,5"); a lone "1.234" is a decimal, as numerals.mjs reads it. Shared by
6
+ // numerals.mjs (comma grouping), the conversion probe (every separator), the value-mode unit check,
7
+ // and the converted-half display check. Pure; no imports.
8
+
9
+ /** Separators a thousands group may use: comma, period, space, NBSP, narrow NBSP, thin space, figure space. */
10
+ export const GROUP_SEPARATORS = Object.freeze([",", ".", " ", "\u00A0", "\u202F", "\u2009", "\u2007"])
11
+
12
+ const literal = (sep) => (sep === "." ? "\\." : sep === " " ? " " : sep === "," ? "," : `\\u${sep.charCodeAt(0).toString(16).padStart(4, "0")}`)
13
+ const decimalAfter = (sep) => (sep === "," ? "\\." : sep === "." ? "," : "[.,]")
14
+
15
+ /** Regex source of one grouped number using any of `separators` (one type per number); no flags needed. */
16
+ export function groupedNumberSource(separators = GROUP_SEPARATORS) {
17
+ const one = (sep) => sep === "."
18
+ ? "\\d{1,3}(?:(?:\\.\\d{3}(?!\\d)){2,}(?:,\\d+)?|\\.\\d{3}(?!\\d),\\d+)"
19
+ : `\\d{1,3}(?:${literal(sep)}\\d{3}(?!\\d))+(?:${decimalAfter(sep)}\\d+)?`
20
+ return `(?<!\\d)(?:${separators.map(one).join("|")})`
21
+ }
22
+
23
+ /** A grouped number or a plain one ("4.22", "12,3451" read as 12.3451), as regex source. */
24
+ export const NUMBER_SOURCE = `(?:${groupedNumberSource()}|(?<!\\d)\\d+(?:[.,]\\d+)?)`
25
+
26
+ const WHOLE = new Map(GROUP_SEPARATORS.map((sep) => [sep, new RegExp(`^${groupedNumberSource([sep])}$`)]))
27
+
28
+ /** The plain decimal a number token spells: group separators dropped, a decimal comma read as a point. */
29
+ export function plainDecimal(token) {
30
+ const sep = GROUP_SEPARATORS.find((candidate) => WHOLE.get(candidate).test(token))
31
+ const ungrouped = sep === undefined ? token : token.split(sep).join("")
32
+ return ungrouped.replace(",", ".")
33
+ }
@@ -6,9 +6,12 @@
6
6
  // English number words one to twelve, an adjacent currency symbol or unit, and an adjacent bounding
7
7
  // qualifier. No numeral is ever passed through Number(). Plain Node ESM, sibling imports only.
8
8
 
9
+ import { groupedNumberSource } from "./number-grammar.mjs"
9
10
  import { add, fromDecimal, mul, toDecimal } from "./rational.mjs"
10
11
  import { normalizeUnit } from "./units.mjs"
11
12
 
13
+ /** European digit grouping ("1.234.567"): two or more dot groups of three digits, never a version. */
14
+ export const DOT_GROUPED = "\\d{1,3}(?:\\.\\d{3}){2,}"
12
15
  const MONTH = "(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*\\.?"
13
16
  /** Stripped before claim detection: dates, versions, figure refs, times, bare years. */
14
17
  export const NOT_CLAIMS = [
@@ -19,7 +22,7 @@ export const NOT_CLAIMS = [
19
22
  /(?:19|20)\d{2}\s*\uB144/g,
20
23
  new RegExp(`\\b${MONTH}\\s+\\d{1,2}(?:st|nd|rd|th)?(?:,?\\s+\\d{4})?`, "gi"),
21
24
  new RegExp(`\\b\\d{1,2}(?:st|nd|rd|th)?\\s+${MONTH}(?:\\s+\\d{4})?`, "gi"),
22
- /\bv\d+(?:\.\d+)*\b|\b\d+(?:\.\d+){2,}\b/gi,
25
+ new RegExp(`\\bv\\d+(?:\\.\\d+)*\\b|\\b(?!${DOT_GROUPED}(?![\\d,]|\\.\\d))\\d+(?:\\.\\d+){2,}\\b`, "gi"),
23
26
  /\b(?:version|ver\.?|release)\s*\d+(?:\.\d+)*/gi,
24
27
  /\b(?:fig(?:ure)?\.?|table|chart|section|step|phase|wave|part)\s*\d+(?:\.\d+)*/gi,
25
28
  /(?:\uADF8\uB9BC|\uB3C4\uD45C|\uD45C|\uBC84\uC804)\s*\d+(?:\.\d+)*/g,
@@ -45,8 +48,8 @@ const BIG = new Map([["\uB9CC", 10000n], ["\uC5B5", 100000000n], ["\uC870", 1000
45
48
  /** A scale word that starts a longer word is not a scale: jogak (pieces), mankeum (as much as). */
46
49
  const KO_GROUP = /^(\s?)(?:(\uCC9C)?([\uB9CC\uC5B5\uC870])|(\uCC9C))(?![\uAC01\uD07C])/
47
50
  const KO_NEXT = new RegExp(`^\\s?(${NUM})(\uCC9C)?([\uB9CC\uC5B5\uC870])?`, "u")
48
- const LATIN_SCALE = /^(?:(k|K|M|bn|B)|\s(thousand|million|billion|trillion|bn))(?![\p{L}\d])/u
49
- const LATIN_FACTOR = { k: 1000n, K: 1000n, M: 1000000n, bn: 1000000000n, B: 1000000000n, thousand: 1000n, million: 1000000n, billion: 1000000000n, trillion: 1000000000000n }
51
+ const LATIN_SCALE = /^(?:(k|K|M|mn|bn|B)|\s(thousand|million|billion|trillion|mn|bn))(?![\p{L}\d])/u
52
+ const LATIN_FACTOR = { k: 1000n, K: 1000n, M: 1000000n, mn: 1000000n, bn: 1000000000n, B: 1000000000n, thousand: 1000n, million: 1000000n, billion: 1000000000n, trillion: 1000000000000n }
50
53
  const CURRENCY = "$\u20AC\u00A3\u00A5\u20A9"
51
54
  const SYMBOL_UNIT = /^\s?(%p|%|\u2030|\u00D7|\u00B0C|\u00B0F|\u2103|\u2109)/
52
55
  const LATIN_UNIT = /^\s?([A-Za-z]+)(?![\p{L}\d])/u
@@ -60,15 +63,23 @@ const RANGE_SEP = /^(?:\s?[-\u2013~]\s?|\sto\s)/
60
63
  const SIGN = "-\u2212"
61
64
  /** Bounding qualifiers: before the value (Korean and English) and right after it (Korean). */
62
65
  const QUALIFIERS_BEFORE = ["up to", "at least", "at most", "more than", "less than", "about", "approximately", "nearly", "over", "under", "\uCD5C\uB300", "\uCD5C\uC18C", "\uC57D"]
63
- const QUALIFIER_AFTER = /^\s?(\uC774\uC0C1|\uC774\uD558|\uBBF8\uB9CC|\uCD08\uACFC|\uB0B4\uC678|\uAE4C\uC9C0)/
66
+ /** Korean bounding qualifiers written right after a value (isang, iha, miman, chogwa, naeoe, kkaji). */
67
+ export const QUALIFIERS_AFTER = Object.freeze(["\uC774\uC0C1", "\uC774\uD558", "\uBBF8\uB9CC", "\uCD08\uACFC", "\uB0B4\uC678", "\uAE4C\uC9C0"])
68
+ const QUALIFIER_AFTER = new RegExp(`^\\s?(${QUALIFIERS_AFTER.join("|")})`)
64
69
 
65
- /** "1,237" -> "1237"; a malformed group ("1,23") keeps only the leading plain number. */
70
+ /** Comma grouping from the one grouped-number grammar (number-grammar.mjs). */
71
+ const COMMA_GROUPED = new RegExp(`^${groupedNumberSource([","])}$`)
72
+
73
+ /** "1,237" -> "1237"; a malformed group ("1,23", "12,3451") keeps only the leading plain number. */
66
74
  function cleanDigits(raw) {
67
- if (/^\d{1,3}(?:,\d{3})+(?:\.\d+)?$/.test(raw)) return { digits: raw.replace(/,/g, ""), length: raw.length }
75
+ if (COMMA_GROUPED.test(raw)) return { digits: raw.replace(/,/g, ""), length: raw.length }
68
76
  const plain = /^\d+(?:\.\d+)?/.exec(raw)[0]
69
77
  return { digits: plain, length: plain.length }
70
78
  }
71
79
 
80
+ /** A numeral's digits, reading a European period grouping ("1.234.567") as one integer. */
81
+ const digitsOf = (raw) => (new RegExp(`^${DOT_GROUPED}$`).test(raw) ? { digits: raw.replace(/\./g, ""), length: raw.length } : cleanDigits(raw))
82
+
72
83
  /** The bare-year and four-digit-date patterns: a numeral they cover can still be a value. */
73
84
  const YEAR_SHAPED = new Set([NOT_CLAIMS[0], NOT_CLAIMS[12]])
74
85
 
@@ -95,7 +106,7 @@ function findStart(text, from, ranges) {
95
106
  let digit = null
96
107
  for (const match of text.slice(from).matchAll(new RegExp(NUM, "gu"))) {
97
108
  const at = from + match.index
98
- const candidate = { at, ...cleanDigits(match[0]) }
109
+ const candidate = { at, ...digitsOf(match[0]) }
99
110
  const end = at + candidate.length
100
111
  const cover = covering(at)
101
112
  const lifted = cover.every(([s, e, yearShaped]) => yearShaped && s === at && e <= end)
@@ -0,0 +1,66 @@
1
+ // outcome-start.mjs - the run clock (outcome.json startedAt). `outcome init` is its one writer and
2
+ // records it once: --started-at from the orchestrator, else the startedAt of a valid manifest an
3
+ // earlier init wrote (a re-init keeps the clock), else terminal-state.json.started_at when a
4
+ // supervisor wrote one, else the YYYYMMDD-HHMMSS session dir name. A recorded value must be a real
5
+ // ISO time with Z or an offset, not after `now`, and within a day of a stamped dir name; otherwise
6
+ // init refuses instead of falling back. Never the interview's as-of date. No clock reads.
7
+ import { readFileSync } from "node:fs"
8
+ import { basename, join, resolve } from "node:path"
9
+
10
+ const ISO = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-](\d{2}):(\d{2}))$/
11
+ const SESSION_STAMP = /^(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})(\d{2})$/
12
+ const DAY_MS = 86_400_000
13
+
14
+ /** A real calendar time (no 2026-02-30, no T24:00), or null. */
15
+ function strictIso(value) {
16
+ const match = typeof value === "string" ? ISO.exec(value) : null
17
+ if (!match) return null
18
+ const [y, mo, d, h, mi, s, oh = 0, om = 0] = match.slice(1).map((part) => Number(part ?? 0))
19
+ const day = new Date(Date.UTC(y, mo - 1, d))
20
+ if (day.getUTCMonth() !== mo - 1 || day.getUTCDate() !== d || h > 23 || mi > 59 || s > 59 || oh > 23 || om > 59) return null
21
+ const date = new Date(value)
22
+ return Number.isNaN(date.getTime()) ? null : date
23
+ }
24
+
25
+ /** Local start time from a YYYYMMDD-HHMMSS dir name, null when the name is not a stamp. */
26
+ function dirStamp(sessionDir, fail) {
27
+ const match = SESSION_STAMP.exec(basename(sessionDir))
28
+ if (!match) return null
29
+ const [y, mo, d, h, mi, s] = match.slice(1).map(Number)
30
+ const date = new Date(y, mo - 1, d, h, mi, s)
31
+ if (date.getMonth() !== mo - 1 || date.getDate() !== d || date.getHours() !== h) fail(`session dir basename is not a real local time: ${sessionDir}`)
32
+ return date
33
+ }
34
+
35
+ function readJson(sessionDir, file) {
36
+ try {
37
+ return { data: JSON.parse(readFileSync(join(sessionDir, file), "utf8")) }
38
+ } catch (error) {
39
+ if (error.code === "ENOENT" || error.code === "ENOTDIR") return null
40
+ if (error instanceof SyntaxError) return { corrupt: `${join(sessionDir, file)} is not valid JSON (${error.message})` }
41
+ throw error
42
+ }
43
+ }
44
+
45
+ /**
46
+ * @param {{ sessionDir: string, startedAt?: string, now?: Date, validManifest: (data: unknown) => boolean, fail: (message: string) => never }} input
47
+ * @returns {Date}
48
+ */
49
+ export function runStart({ sessionDir, startedAt, now, validManifest, fail }) {
50
+ const dir = dirStamp(sessionDir, fail)
51
+ const check = (value, location) => {
52
+ const date = strictIso(value)
53
+ if (!date) fail(`invalid run timestamp in ${location}: ${JSON.stringify(value)} is not a real ISO time with Z or an offset`)
54
+ if (now && date > now) fail(`run timestamp in ${location} (${value}) is after now (${now.toISOString()})`)
55
+ if (dir && Math.abs(date - dir) > DAY_MS) fail(`run timestamp in ${location} (${value}) is not within a day of the session dir name ${basename(sessionDir)}: a copied or stale value`)
56
+ return date
57
+ }
58
+ if (startedAt !== undefined) return check(startedAt, "--started-at")
59
+ const manifest = readJson(sessionDir, "outcome.json")
60
+ if (manifest?.data && validManifest(manifest.data) && resolve(String(manifest.data.sessionDir)) === resolve(sessionDir)) return check(manifest.data.startedAt, "outcome.json.startedAt")
61
+ const terminal = readJson(sessionDir, "terminal-state.json")
62
+ if (terminal?.corrupt) fail(terminal.corrupt)
63
+ if (terminal?.data?.started_at !== undefined) return check(terminal.data.started_at, "terminal-state.json.started_at")
64
+ if (dir) return dir
65
+ return fail(`missing run timestamp: pass --started-at <ISO>, record terminal-state.json.started_at, or name the session dir YYYYMMDD-HHMMSS (${sessionDir})`)
66
+ }
@@ -1,14 +1,13 @@
1
1
  // outcome.mjs - the per-run outcome manifest (`outcome.json`) and the closing briefing. Manifest functions are pure (they return a new manifest).
2
2
  // The briefing reads only the manifest, the sources-ledger text, and an injected `now`. node: builtins only.
3
-
4
3
  import * as nodeFs from "node:fs"
5
4
  import { basename, dirname, join } from "node:path"
6
5
 
7
6
  import { DELIVERABLE_FORMATS, DELIVERABLE_STATUSES, GATE_NAMES, GATE_STATUSES, LANES, MANIFEST_CODES, normalizeManifest, SCHEMA_VERSION, validateOutcomeManifest } from "./contracts.mjs"
7
+ import { runStart } from "./outcome-start.mjs"
8
8
 
9
9
  export const MANIFEST_FILENAME = "outcome.json"
10
10
  const MISSING = MANIFEST_CODES[0]
11
- const SESSION_STAMP = /^(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})(\d{2})$/
12
11
  const SOURCE_LINE = /^\[(S|Source )\d+\]/
13
12
  const URL_PATTERN = /https?:\/\/[^\s)<>\]"'`]+/g
14
13
  const DRAFT_MARKER = "STATUS: draft"
@@ -33,26 +32,20 @@ function assertOneOf(code, label, value, allowed) {
33
32
 
34
33
  const clone = (manifest) => structuredClone(manifest)
35
34
  const isMissing = (error) => error?.code === "ENOENT" || error?.code === "ENOTDIR"
36
-
37
35
  function stamp(now) {
38
36
  return (now ?? new Date()).toISOString()
39
37
  }
40
-
41
- /** Local wall-clock start time from a `YYYYMMDD-HHMMSS` session dir basename. */
42
- export function parseSessionStart(sessionDir) {
43
- const match = SESSION_STAMP.exec(basename(sessionDir))
44
- if (!match) throw new OutcomeError("invalid_session_dir", `session dir basename must be YYYYMMDD-HHMMSS: ${sessionDir}`)
45
- const [y, mo, d, h, mi, s] = match.slice(1).map(Number)
46
- const date = new Date(y, mo - 1, d, h, mi, s)
47
- if (date.getMonth() !== mo - 1 || date.getDate() !== d || date.getHours() !== h) {
48
- throw new OutcomeError("invalid_session_dir", `session dir basename is not a real local time: ${sessionDir}`)
38
+ /** The run clock outcome init records (outcome-start.mjs owns the contract). */
39
+ export function parseSessionStart(sessionDir, { startedAt, now } = {}) {
40
+ const fail = (message) => {
41
+ throw new OutcomeError("invalid_session_dir", message)
49
42
  }
50
- return date
43
+ return runStart({ sessionDir, startedAt, now, validManifest: (data) => validateOutcomeManifest(normalizeManifest(data)).ok, fail })
51
44
  }
52
45
 
53
- /** @param {{ sessionDir: string, promised: string[], lane: string|null, interview?: object|null, now?: Date }} input */
54
- export function initManifest({ sessionDir, promised, lane, interview = null, now }) {
55
- const startedAt = parseSessionStart(sessionDir).toISOString()
46
+ /** @param {{ sessionDir: string, promised: string[], lane: string|null, interview?: object|null, now?: Date, startedAt?: string }} input */
47
+ export function initManifest({ sessionDir, promised, lane, interview = null, now, startedAt: recorded }) {
48
+ const startedAt = parseSessionStart(sessionDir, { startedAt: recorded, now }).toISOString()
56
49
  if (lane !== null && lane !== undefined) assertOneOf("unknown_lane", "lane", lane, LANES)
57
50
  const formats = [...new Set(promised)]
58
51
  for (const format of formats) assertOneOf("unknown_format", "deliverable format", format, DELIVERABLE_FORMATS)
@@ -74,9 +67,7 @@ export function initManifest({ sessionDir, promised, lane, interview = null, now
74
67
  }
75
68
  }
76
69
 
77
- /**
78
- * Set one format's row; every other row is kept as is. An unpromised format gets a new
79
- * row with promised=false. The row's detail fields are replaced by the ones given.
70
+ /** Set one format's row, keeping other rows. New formats have promised=false.
80
71
  * @param {{ path?: string, reason?: string, pages?: number, bytes?: number, now?: Date }} [detail]
81
72
  */
82
73
  export function setDeliverable(manifest, format, status, detail = {}) {
@@ -143,9 +134,7 @@ export function verifyManifest(manifest, fs = nodeFs) {
143
134
  return { ok: problems.length === 0, problems }
144
135
  }
145
136
 
146
- /**
147
- * none = missing or empty file; partial = `STATUS: draft` in the first 5 lines, or the
148
- * manifest (given, else a sibling outcome.json) still has a pending row; else complete.
137
+ /** Missing/empty = none; draft marker or pending manifest = partial; else complete.
149
138
  * @returns {"none"|"partial"|"complete"}
150
139
  */
151
140
  export function deliverableState(deliverablePath, manifestOrNull) {