lazycodex-ai 5.1.44 → 5.1.46

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 (412) hide show
  1. package/README.de.md +0 -5
  2. package/README.es.md +0 -5
  3. package/README.fr.md +0 -5
  4. package/README.it.md +0 -5
  5. package/README.ja.md +0 -5
  6. package/README.ko.md +0 -5
  7. package/README.md +4 -5
  8. package/README.pt-br.md +0 -5
  9. package/README.ru.md +0 -6
  10. package/README.zh-cn.md +0 -5
  11. package/dist/cli/index.js +865 -413
  12. package/dist/cli/service-auth/keystore.d.ts +4 -2
  13. package/dist/cli-node/index.js +865 -413
  14. package/docs/reference/web-terminal-visual-qa.md +8 -0
  15. package/package.json +1 -1
  16. package/packages/lsp-daemon/dist/cli.js +7 -7
  17. package/packages/lsp-daemon/dist/client.js +7 -7
  18. package/packages/lsp-daemon/dist/index.js +7 -7
  19. package/packages/lsp-daemon/package.json +3 -3
  20. package/packages/lsp-tools-mcp/dist/cli.js +7 -7
  21. package/packages/lsp-tools-mcp/dist/mcp.js +7 -7
  22. package/packages/lsp-tools-mcp/dist/tools.js +4 -4
  23. package/packages/lsp-tools-mcp/package.json +3 -3
  24. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  25. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +77 -26
  26. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  27. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  28. package/packages/omo-codex/plugin/components/bootstrap/src/hook.ts +1 -1
  29. package/packages/omo-codex/plugin/components/comment-checker/biome.json +1 -21
  30. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  31. package/packages/omo-codex/plugin/components/comment-checker/package.json +4 -4
  32. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  33. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  34. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/biome.json +1 -21
  35. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  36. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +4 -4
  37. package/packages/omo-codex/plugin/components/lsp/biome.json +1 -21
  38. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  39. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +4 -4
  40. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  41. package/packages/omo-codex/plugin/components/lsp/package.json +4 -4
  42. package/packages/omo-codex/plugin/components/lsp/scripts/build-runtime.mjs +0 -1
  43. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-not-installed.test.ts +1 -1
  44. package/packages/omo-codex/plugin/components/rules/biome.json +1 -21
  45. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  46. package/packages/omo-codex/plugin/components/rules/package.json +4 -4
  47. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  48. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  49. package/packages/omo-codex/plugin/components/telemetry/biome.json +1 -21
  50. package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +90 -1
  51. package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +91 -1
  52. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  53. package/packages/omo-codex/plugin/components/telemetry/package.json +4 -4
  54. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +2 -2
  55. package/packages/omo-codex/plugin/components/ultrawork/biome.json +1 -21
  56. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  57. package/packages/omo-codex/plugin/components/ultrawork/package.json +4 -4
  58. package/packages/omo-codex/plugin/components/ulw-execute-continuation/biome.json +1 -21
  59. package/packages/omo-codex/plugin/components/ulw-execute-continuation/directive.md +1 -1
  60. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  61. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +4 -4
  62. package/packages/omo-codex/plugin/components/ulw-loop/biome.json +1 -21
  63. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +1 -1
  64. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +7 -7
  65. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +5 -5
  66. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +1 -1
  67. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  68. package/packages/omo-codex/plugin/components/ulw-loop/package.json +4 -4
  69. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +1 -1
  70. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +5 -5
  71. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +1 -1
  72. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-mutations.mjs +2 -0
  73. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  74. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  75. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  76. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  77. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  78. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  79. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  80. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  81. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  82. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  83. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  84. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  85. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  86. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  87. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  88. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  89. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  90. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  91. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  92. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  93. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  94. package/packages/omo-codex/plugin/package-lock.json +12 -12
  95. package/packages/omo-codex/plugin/package.json +1 -1
  96. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +14 -9
  97. package/packages/omo-codex/plugin/skills/ast-grep/AGENTS.md +1 -1
  98. package/packages/omo-codex/plugin/skills/browser/SKILL.md +21 -22
  99. package/packages/omo-codex/plugin/skills/browser/references/commands.md +2 -9
  100. package/packages/omo-codex/plugin/skills/browser/references/install.md +1 -2
  101. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  102. package/packages/omo-codex/plugin/skills/browser/scripts/browser-doctor.mjs +2 -2
  103. package/packages/omo-codex/plugin/skills/browser/scripts/browser-engine-guard.mjs +63 -487
  104. package/packages/omo-codex/plugin/skills/browser/scripts/browser-install.mjs +1 -1
  105. package/packages/omo-codex/plugin/skills/browser/scripts/omowright.mjs +1 -10
  106. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +2 -2
  107. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +79 -118
  108. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +75 -224
  109. package/packages/omo-codex/plugin/skills/frontend/references/design/stylegallery.md +32 -27
  110. package/packages/omo-codex/plugin/skills/frontend/references/perfection/README.md +2 -2
  111. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/check-accent-stripes.ts +8 -24
  112. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/check-draft-guard.ts +40 -0
  113. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/collect-files.ts +53 -0
  114. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/findings.ts +16 -0
  115. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/lint.ts +38 -0
  116. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/rules.ts +69 -0
  117. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/text-nodes.ts +121 -0
  118. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/attr-lookalikes.tsx +10 -0
  119. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/bound-values.tsx +12 -0
  120. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/const-generics.tsx +3 -0
  121. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/generics.tsx +7 -0
  122. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/measured-stats.html +6 -0
  123. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/plain-copy.html +9 -0
  124. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/state-copy.html +11 -0
  125. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/attribute-edges.html +7 -0
  126. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/components.tsx +16 -0
  127. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/decorative-badge.vue +7 -0
  128. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/emoji-icons.tsx +9 -0
  129. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/jsx-edges.tsx +1 -0
  130. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.html +6 -0
  131. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.tsx +8 -0
  132. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/vanity-stats.html +5 -0
  133. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +1 -1
  134. package/packages/omo-codex/plugin/skills/programming/SKILL.md +10 -7
  135. package/packages/omo-codex/plugin/skills/programming/references/typescript/README.md +10 -7
  136. package/packages/omo-codex/plugin/skills/programming/references/typescript/bootstrap.md +9 -23
  137. package/packages/omo-codex/plugin/skills/programming/references/typescript/oxc-lint.md +130 -0
  138. package/packages/omo-codex/plugin/skills/programming/references/typescript/tsconfig-strict.md +6 -62
  139. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts +12 -12
  140. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/new-project.ts +20 -81
  141. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/.oxfmtrc.json +9 -0
  142. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/.oxlintrc.json +46 -0
  143. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/LICENSE +21 -0
  144. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/index.js +10 -0
  145. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/no-em-dash-copy.js +28 -0
  146. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/require-suppression-reason.js +62 -0
  147. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/tsconfig.json +29 -0
  148. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +14 -14
  149. package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +1 -1
  150. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +2 -2
  151. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +1 -1
  152. package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +2 -2
  153. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
  154. package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +21 -3
  155. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +37 -12
  156. package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +77 -16
  157. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +134 -0
  158. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +95 -0
  159. package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
  160. package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +68 -3
  161. package/packages/omo-codex/plugin/skills/ulw-research/references/report-visualization.md +2 -0
  162. package/packages/omo-codex/plugin/skills/ulw-research/references/unit-rules.md +157 -0
  163. package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
  164. package/packages/omo-codex/plugin/skills/ulw-research/scripts/claims-index.mjs +125 -0
  165. package/packages/omo-codex/plugin/skills/ulw-research/scripts/cli-support.mjs +19 -5
  166. package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +89 -3
  167. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
  168. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
  169. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +239 -0
  170. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +66 -0
  171. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-expr.mjs +108 -0
  172. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +194 -0
  173. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +250 -0
  174. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-schema.mjs +221 -0
  175. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger.mjs +123 -0
  176. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +231 -0
  177. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
  178. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
  179. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
  180. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-brief.mjs +150 -0
  181. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
  182. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
  183. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
  184. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-state.mjs +64 -0
  185. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
  186. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
  187. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
  188. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +29 -9
  189. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-structure.mjs +1 -1
  190. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-text.mjs +1 -27
  191. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +2 -2
  192. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +182 -0
  193. package/packages/omo-codex/plugin/skills/ulw-research/scripts/lexicon.mjs +47 -0
  194. package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +193 -0
  195. package/packages/omo-codex/plugin/skills/ulw-research/scripts/meaning-check.mjs +196 -0
  196. package/packages/omo-codex/plugin/skills/ulw-research/scripts/numerals.mjs +226 -0
  197. package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +8 -8
  198. package/packages/omo-codex/plugin/skills/ulw-research/scripts/particles-ko.mjs +108 -0
  199. package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +184 -0
  200. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +115 -0
  201. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-diff.mjs +31 -0
  202. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-project.mjs +125 -0
  203. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-rules.mjs +170 -0
  204. package/packages/omo-codex/plugin/skills/ulw-research/scripts/rational.mjs +139 -0
  205. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +21 -10
  206. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +15 -0
  207. package/packages/omo-codex/plugin/skills/ulw-research/scripts/sentence-split.mjs +77 -0
  208. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +140 -0
  209. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
  210. package/packages/omo-codex/plugin/skills/ulw-research/scripts/text-normalize.mjs +82 -0
  211. package/packages/omo-codex/plugin/skills/ulw-research/scripts/text-similarity.mjs +28 -0
  212. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
  213. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-errors.mjs +23 -0
  214. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
  215. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
  216. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-precision.mjs +44 -0
  217. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
  218. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-rules.mjs +173 -0
  219. package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +105 -0
  220. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +71 -64
  221. package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +4 -1
  222. package/packages/omo-codex/plugin/test/sync-skills-codex-compatibility.test.mjs +19 -0
  223. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +12 -8
  224. package/packages/omo-codex/scripts/install-dist/install-local.mjs +169 -30
  225. package/packages/shared-skills/skills/ast-grep/AGENTS.md +1 -1
  226. package/packages/shared-skills/skills/browser/SKILL.md +21 -22
  227. package/packages/shared-skills/skills/browser/references/commands.md +2 -9
  228. package/packages/shared-skills/skills/browser/references/install.md +1 -2
  229. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  230. package/packages/shared-skills/skills/browser/scripts/browser-doctor.mjs +2 -2
  231. package/packages/shared-skills/skills/browser/scripts/browser-engine-guard.mjs +63 -487
  232. package/packages/shared-skills/skills/browser/scripts/browser-install.mjs +1 -1
  233. package/packages/shared-skills/skills/browser/scripts/omowright.mjs +1 -10
  234. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +2 -2
  235. package/packages/shared-skills/skills/frontend/SKILL.md +79 -118
  236. package/packages/shared-skills/skills/frontend/references/design/README.md +75 -224
  237. package/packages/shared-skills/skills/frontend/references/design/stylegallery.md +32 -27
  238. package/packages/shared-skills/skills/frontend/references/perfection/README.md +2 -2
  239. package/packages/shared-skills/skills/frontend/scripts/perfection/check-accent-stripes.ts +8 -24
  240. package/packages/shared-skills/skills/frontend/scripts/perfection/check-draft-guard.test.ts +101 -0
  241. package/packages/shared-skills/skills/frontend/scripts/perfection/check-draft-guard.ts +40 -0
  242. package/packages/shared-skills/skills/frontend/scripts/perfection/collect-files.test.ts +50 -0
  243. package/packages/shared-skills/skills/frontend/scripts/perfection/collect-files.ts +53 -0
  244. package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/findings.ts +16 -0
  245. package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/lint.ts +38 -0
  246. package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/rules.ts +69 -0
  247. package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/text-nodes.ts +121 -0
  248. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/attr-lookalikes.tsx +10 -0
  249. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/bound-values.tsx +12 -0
  250. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/const-generics.tsx +3 -0
  251. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/generics.tsx +7 -0
  252. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/measured-stats.html +6 -0
  253. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/plain-copy.html +9 -0
  254. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/state-copy.html +11 -0
  255. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/attribute-edges.html +7 -0
  256. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/components.tsx +16 -0
  257. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/decorative-badge.vue +7 -0
  258. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/emoji-icons.tsx +9 -0
  259. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/jsx-edges.tsx +1 -0
  260. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.html +6 -0
  261. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.tsx +8 -0
  262. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/vanity-stats.html +5 -0
  263. package/packages/shared-skills/skills/programming/SKILL.md +10 -7
  264. package/packages/shared-skills/skills/programming/references/typescript/README.md +10 -7
  265. package/packages/shared-skills/skills/programming/references/typescript/bootstrap.md +9 -23
  266. package/packages/shared-skills/skills/programming/references/typescript/oxc-lint.md +130 -0
  267. package/packages/shared-skills/skills/programming/references/typescript/tsconfig-strict.md +6 -62
  268. package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.ts +12 -12
  269. package/packages/shared-skills/skills/programming/scripts/typescript/new-project.test.ts +162 -0
  270. package/packages/shared-skills/skills/programming/scripts/typescript/new-project.ts +20 -81
  271. package/packages/shared-skills/skills/programming/scripts/typescript/oxc/.oxfmtrc.json +9 -0
  272. package/packages/shared-skills/skills/programming/scripts/typescript/oxc/.oxlintrc.json +46 -0
  273. package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/LICENSE +21 -0
  274. package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/index.js +10 -0
  275. package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/no-em-dash-copy.js +28 -0
  276. package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/require-suppression-reason.js +62 -0
  277. package/packages/shared-skills/skills/programming/scripts/typescript/oxc/tsconfig.json +29 -0
  278. package/packages/shared-skills/skills/refactor/SKILL.md +13 -13
  279. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +1 -1
  280. package/packages/shared-skills/skills/ulw-execute/SKILL.md +1 -1
  281. package/packages/shared-skills/skills/ulw-research/AGENTS.md +21 -3
  282. package/packages/shared-skills/skills/ulw-research/SKILL.md +37 -12
  283. package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +77 -16
  284. package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +134 -0
  285. package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +95 -0
  286. package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
  287. package/packages/shared-skills/skills/ulw-research/references/report-gates.md +68 -3
  288. package/packages/shared-skills/skills/ulw-research/references/report-visualization.md +2 -0
  289. package/packages/shared-skills/skills/ulw-research/references/unit-rules.md +157 -0
  290. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
  291. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +193 -0
  292. package/packages/shared-skills/skills/ulw-research/scripts/claims-index.mjs +125 -0
  293. package/packages/shared-skills/skills/ulw-research/scripts/claims-index.test.ts +149 -0
  294. package/packages/shared-skills/skills/ulw-research/scripts/cli-support.mjs +19 -5
  295. package/packages/shared-skills/skills/ulw-research/scripts/cli-support.test.ts +13 -2
  296. package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +89 -3
  297. package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +100 -12
  298. package/packages/shared-skills/skills/ulw-research/scripts/copy-returns.test.ts +183 -0
  299. package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +245 -0
  300. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
  301. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
  302. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +239 -0
  303. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +66 -0
  304. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +375 -0
  305. package/packages/shared-skills/skills/ulw-research/scripts/fact-expr.mjs +108 -0
  306. package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +194 -0
  307. package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.test.ts +221 -0
  308. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-grammar.test.ts +197 -0
  309. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +250 -0
  310. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +156 -0
  311. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +160 -0
  312. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-schema.mjs +221 -0
  313. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-schema.test.ts +43 -0
  314. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.mjs +123 -0
  315. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.test.ts +254 -0
  316. package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +231 -0
  317. package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.test.ts +118 -0
  318. package/packages/shared-skills/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
  319. package/packages/shared-skills/skills/ulw-research/scripts/fact-pairs.test.ts +167 -0
  320. package/packages/shared-skills/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
  321. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +409 -0
  322. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +388 -0
  323. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
  324. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-binding.test.ts +81 -0
  325. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-brief.mjs +150 -0
  326. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-brief.test.ts +179 -0
  327. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
  328. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
  329. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.test.ts +117 -0
  330. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
  331. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-state.mjs +64 -0
  332. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-state.test.ts +46 -0
  333. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
  334. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.test.ts +58 -0
  335. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
  336. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +270 -0
  337. package/packages/shared-skills/skills/ulw-research/scripts/gates-context.test.ts +64 -0
  338. package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
  339. package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.test.ts +73 -0
  340. package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +29 -9
  341. package/packages/shared-skills/skills/ulw-research/scripts/gates-structure.mjs +1 -1
  342. package/packages/shared-skills/skills/ulw-research/scripts/gates-text.mjs +1 -27
  343. package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +2 -2
  344. package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +182 -0
  345. package/packages/shared-skills/skills/ulw-research/scripts/lexicon.mjs +47 -0
  346. package/packages/shared-skills/skills/ulw-research/scripts/lexicon.test.ts +57 -0
  347. package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +193 -0
  348. package/packages/shared-skills/skills/ulw-research/scripts/md-lite.test.ts +271 -0
  349. package/packages/shared-skills/skills/ulw-research/scripts/meaning-check.mjs +196 -0
  350. package/packages/shared-skills/skills/ulw-research/scripts/meaning-check.test.ts +228 -0
  351. package/packages/shared-skills/skills/ulw-research/scripts/numerals.mjs +226 -0
  352. package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +120 -0
  353. package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +8 -8
  354. package/packages/shared-skills/skills/ulw-research/scripts/outcome.test.ts +29 -0
  355. package/packages/shared-skills/skills/ulw-research/scripts/particles-ko.mjs +108 -0
  356. package/packages/shared-skills/skills/ulw-research/scripts/particles-ko.test.ts +86 -0
  357. package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +184 -0
  358. package/packages/shared-skills/skills/ulw-research/scripts/placeholder.test.ts +77 -0
  359. package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +115 -0
  360. package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.test.ts +363 -0
  361. package/packages/shared-skills/skills/ulw-research/scripts/polish-diff.mjs +31 -0
  362. package/packages/shared-skills/skills/ulw-research/scripts/polish-diff.test.ts +168 -0
  363. package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +187 -0
  364. package/packages/shared-skills/skills/ulw-research/scripts/polish-project.mjs +125 -0
  365. package/packages/shared-skills/skills/ulw-research/scripts/polish-rules.mjs +170 -0
  366. package/packages/shared-skills/skills/ulw-research/scripts/rational.mjs +139 -0
  367. package/packages/shared-skills/skills/ulw-research/scripts/rational.test.ts +46 -0
  368. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +21 -10
  369. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +15 -0
  370. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +458 -2
  371. package/packages/shared-skills/skills/ulw-research/scripts/sentence-split.mjs +77 -0
  372. package/packages/shared-skills/skills/ulw-research/scripts/sentence-split.test.ts +109 -0
  373. package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +140 -0
  374. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
  375. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +113 -0
  376. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/assets/share.csv +3 -0
  377. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/claim-graph.md +13 -0
  378. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/gen-legacy-dev.mjs +89 -0
  379. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-basic.json +223 -0
  380. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-legacy-dev.json +859 -0
  381. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-verify.json +420 -0
  382. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/report-bad.html +19 -0
  383. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/report-good.html +39 -0
  384. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/acronyms.json +3 -0
  385. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/meta.json +9 -0
  386. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/sentences.jsonl +7 -0
  387. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/text.txt +7 -0
  388. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4.html +19 -0
  389. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/acronyms.json +1 -0
  390. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/meta.json +9 -0
  391. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/sentences.jsonl +1 -0
  392. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/text.txt +1 -0
  393. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S9.txt +1 -0
  394. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity-brief-locked.md +17 -0
  395. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity-brief-pending.md +17 -0
  396. package/packages/shared-skills/skills/ulw-research/scripts/text-normalize.mjs +82 -0
  397. package/packages/shared-skills/skills/ulw-research/scripts/text-normalize.test.ts +73 -0
  398. package/packages/shared-skills/skills/ulw-research/scripts/text-similarity.mjs +28 -0
  399. package/packages/shared-skills/skills/ulw-research/scripts/text-similarity.test.ts +38 -0
  400. package/packages/shared-skills/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
  401. package/packages/shared-skills/skills/ulw-research/scripts/unit-errors.mjs +23 -0
  402. package/packages/shared-skills/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
  403. package/packages/shared-skills/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
  404. package/packages/shared-skills/skills/ulw-research/scripts/unit-precision.mjs +44 -0
  405. package/packages/shared-skills/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
  406. package/packages/shared-skills/skills/ulw-research/scripts/unit-rules.mjs +173 -0
  407. package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +105 -0
  408. package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +83 -0
  409. package/packages/shared-skills/skills/visual-qa/SKILL.md +70 -63
  410. package/packages/shared-skills/skills/visual-qa/scripts/skill-doc-fields.test.ts +34 -0
  411. package/script/qa/web-terminal-visual-qa.mjs +6 -2
  412. package/script/qa/xterm-live-terminal.mjs +26 -3
@@ -0,0 +1,47 @@
1
+ // Bilingual fidelity lexicon: data only, read by the fidelity checks and the meaning-pair router.
2
+ // Korean entries are \u escapes (this directory ships no Hangul); each list is frozen.
3
+
4
+ // Hedge tokens: a source hedge the report drops is hedge_dropped.
5
+ export const HEDGES = Object.freeze([
6
+ ...["estimated", "about", "approximately", "may", "might", "expected", "forecast", "provisional", "not yet"],
7
+ ...["\uCD94\uC815", "\uC57D", "\uAC00\uB2A5", "\uC804\uB9DD", "\uC608\uC0C1", "\uC7A0\uC815", "\uC544\uC9C1", "\uBBF8\uD655\uC815", "\uAC83\uC73C\uB85C \uBCF4\uC778\uB2E4"],
8
+ ])
9
+
10
+ // Closed qualifier list for the ledger qualifier field (bounding or approximation words).
11
+ export const QUALIFIERS = Object.freeze([
12
+ ...["up to", "at least", "at most", "about", "approximately", "nearly", "over", "under", "more than", "less than"],
13
+ ...["\uCD5C\uB300", "\uCD5C\uC18C", "\uC57D", "\uC774\uC0C1", "\uC774\uD558", "\uBBF8\uB9CC", "\uCD08\uACFC", "\uB0B4\uC678", "\uAE4C\uC9C0"],
14
+ ])
15
+
16
+ // Direction classes: a source in one class and an assertion in the other is direction_flipped.
17
+ export const DIRECTION = Object.freeze({
18
+ up: Object.freeze([...["increase", "rise", "grow", "up"], ...["\uC99D\uAC00", "\uC0C1\uC2B9", "\uB298"]]),
19
+ down: Object.freeze([...["decrease", "fall", "decline", "down"], ...["\uAC10\uC18C", "\uD558\uB77D", "\uC904"]]),
20
+ })
21
+
22
+ // Causal connectives: one in the assertion but in no supporting quote is causality_added.
23
+ export const CAUSAL = Object.freeze([
24
+ ...["because", "due to", "leads to", "results in", "causes", "therefore"],
25
+ ...["\uB54C\uBB38\uC5D0", "\uB85C \uC778\uD574", "\uB530\uB77C\uC11C", "\uADF8\uB798\uC11C", "\uCD08\uB798"],
26
+ ])
27
+
28
+ // Concessive connectives for the hedge-position routing heuristic.
29
+ export const CONCESSIVE = Object.freeze([
30
+ ...["although", "but", "however", "despite", "while"],
31
+ ...["\uC73C\uB098", "\uC9C0\uB9CC", "\uC5D0\uB3C4 \uBD88\uAD6C\uD558\uACE0", "\uBC18\uBA74", "\uC640 \uB2EC\uB9AC"],
32
+ ])
33
+
34
+ // Narrow-to-broad scope pairs [narrow, broad] for scope_widened_warn.
35
+ export const SCOPE_NARROW_TO_BROAD = Object.freeze([
36
+ Object.freeze(["\uBD81\uBBF8", "\uC804 \uC138\uACC4"]),
37
+ Object.freeze(["pilot", "system"]),
38
+ Object.freeze(["one region", "all regions"]),
39
+ Object.freeze(["\uC77C\uBD80", "\uC804\uCCB4"]),
40
+ Object.freeze(["\uD55C \uC9C0\uC5ED", "\uC804\uAD6D"]),
41
+ ])
42
+
43
+ // Negation tokens; negated direction cases route to a reader instead of the lexicon.
44
+ export const NEGATION = Object.freeze([
45
+ ...["not", "no", "never"],
46
+ ...["\uC54A", "\uC5C6", "\uBABB"],
47
+ ])
@@ -0,0 +1,193 @@
1
+ // md-lite.mjs - a light Markdown block model for md/post deliverables.
2
+ //
3
+ // parseMarkdown(text) returns { blocks } in document order, each block one of heading,
4
+ // paragraph, list_item, table_row, code, blockquote, with start/end offsets into the raw text
5
+ // so text.slice(start, end) === block.text (table cells carry their own offsets). Headings carry
6
+ // level 1-6. Code blocks are fenced (``` or ~~~); an unterminated fence runs to the end of the
7
+ // text and is reported in problems as unterminated_fence, which a caller must refuse rather than
8
+ // skip. Callers skip kind 'code' when they look for numbers. sectionKindsMd(blocks) classifies
9
+ // sections with the gates-static classifier itself (sectionKind, closingTest). It never renders
10
+ // HTML. Plain Node ESM, sibling imports only.
11
+
12
+ import { closingTest, sectionKind } from "./gates-static.mjs"
13
+
14
+ export { closingTest, sectionKind }
15
+
16
+ /** A fence opener: a backtick fence's info string holds no backtick (else it is an inline span). */
17
+ const FENCE = /^ {0,3}(?:(`{3,})[^`]*|(~{3,}).*)$/
18
+ const HEADING = /^ {0,3}(#{1,6})(?=[ \t]|$)/
19
+ const BLOCKQUOTE = /^ {0,3}> ?/
20
+ /** A bullet or an ordered marker (digits, then . or ), then a space); group 1 is the number. */
21
+ const LIST_ITEM = /^[ \t]*(?:[-*+]|(\d{1,9})[.)])[ \t]+/
22
+ const TABLE_SEPARATOR = /^[ \t]*\|?[ \t]*:?-+:?[ \t]*(?:\|[ \t]*:?-+:?[ \t]*)*\|?[ \t]*$/
23
+ const CLOSING_HASHES = /[ \t]+#+[ \t]*$|^#+[ \t]*$/
24
+ const THEMATIC_BREAK = /^ {0,3}([-*_])(?:[ \t]*\1){2,}[ \t]*$/
25
+
26
+ /** The Markdown block grammar, one named pattern per syntax. */
27
+ export const MD_BLOCK_PATTERNS = Object.freeze({ FENCE, HEADING, BLOCKQUOTE, LIST_ITEM, TABLE_SEPARATOR, CLOSING_HASHES, THEMATIC_BREAK })
28
+
29
+ /** Split into lines with offsets; end excludes the newline and a trailing carriage return. */
30
+ function linesOf(text) {
31
+ const lines = []
32
+ let start = 0
33
+ while (start <= text.length) {
34
+ const newline = text.indexOf("\n", start)
35
+ const next = newline === -1 ? text.length + 1 : newline + 1
36
+ let end = newline === -1 ? text.length : newline
37
+ if (end > start && text[end - 1] === "\r") end--
38
+ lines.push({ start, end, next, raw: text.slice(start, end) })
39
+ start = next
40
+ }
41
+ return lines
42
+ }
43
+
44
+ /** Narrow [start, end) to its non-whitespace core. */
45
+ function trimRange(text, start, end) {
46
+ let from = start
47
+ let to = end
48
+ while (from < to && /\s/.test(text[from])) from++
49
+ while (to > from && /\s/.test(text[to - 1])) to--
50
+ return { start: from, end: to }
51
+ }
52
+
53
+ function block(text, kind, range, extra = {}) {
54
+ return { kind, ...extra, text: text.slice(range.start, range.end), start: range.start, end: range.end }
55
+ }
56
+
57
+ function isSeparator(line) {
58
+ return line !== undefined && line.raw.includes("-") && TABLE_SEPARATOR.test(line.raw)
59
+ }
60
+
61
+ /** Cells split on unescaped pipes, outer pipes dropped, each cell trimmed with offsets. */
62
+ function tableCells(text, range) {
63
+ const bounds = []
64
+ for (let k = range.start; k < range.end; k++) {
65
+ let slashes = 0
66
+ while (k - slashes - 1 >= range.start && text[k - slashes - 1] === "\\") slashes++
67
+ if (text[k] === "|" && slashes % 2 === 0) bounds.push(k)
68
+ }
69
+ const edges = [range.start - 1, ...bounds, range.end]
70
+ if (bounds[0] === range.start) edges.shift()
71
+ if (bounds.at(-1) === range.end - 1 && bounds.length > 0) edges.pop()
72
+ const cells = []
73
+ for (let k = 0; k + 1 < edges.length; k++) {
74
+ const cell = trimRange(text, edges[k] + 1, edges[k + 1])
75
+ cells.push({ text: text.slice(cell.start, cell.end), start: cell.start, end: cell.end })
76
+ }
77
+ return cells
78
+ }
79
+
80
+ function headingBlock(text, line, marker) {
81
+ const after = line.start + line.raw.indexOf(marker) + marker.length
82
+ const range = trimRange(text, after, line.end)
83
+ const closing = text.slice(range.start, range.end).match(CLOSING_HASHES)
84
+ if (closing) range.end = trimRange(text, range.start, range.start + (closing.index ?? 0)).end
85
+ return block(text, "heading", range, { level: marker.length })
86
+ }
87
+
88
+ /**
89
+ * Parse Markdown into a flat block list.
90
+ * @param {string} text
91
+ * @returns {{ blocks: { kind: string, level?: number, text: string, start: number, end: number,
92
+ * cells?: { text: string, start: number, end: number }[] }[],
93
+ * problems: { code: "unterminated_fence", line: number, start: number }[] }}
94
+ */
95
+ export function parseMarkdown(text) {
96
+ const source = String(text ?? "")
97
+ const lines = linesOf(source)
98
+ const blocks = []
99
+ const problems = []
100
+ let paragraph = null
101
+ let inTable = false
102
+ const flush = () => {
103
+ if (paragraph) blocks.push(block(source, "paragraph", paragraph))
104
+ paragraph = null
105
+ }
106
+ for (let i = 0; i < lines.length; i++) {
107
+ const line = lines[i]
108
+ const fence = line.raw.match(FENCE)
109
+ if (fence) {
110
+ flush()
111
+ inTable = false
112
+ const open = fence[1] ?? fence[2]
113
+ const codeStart = Math.min(line.next, source.length)
114
+ let close = lines.length
115
+ for (let k = i + 1; k < lines.length; k++) {
116
+ const candidate = lines[k].raw.trim()
117
+ if (candidate.startsWith(open[0].repeat(open.length)) && /^(`+|~+)$/.test(candidate)) {
118
+ close = k
119
+ break
120
+ }
121
+ }
122
+ let codeEnd = source.length
123
+ if (close === lines.length) problems.push({ code: "unterminated_fence", line: i + 1, start: line.start })
124
+ else {
125
+ codeEnd = Math.max(codeStart, lines[close].start - 1)
126
+ if (codeEnd > codeStart && source[codeEnd - 1] === "\r") codeEnd--
127
+ }
128
+ blocks.push(block(source, "code", { start: codeStart, end: codeEnd }))
129
+ i = close
130
+ continue
131
+ }
132
+ if (line.raw.trim() === "") {
133
+ flush()
134
+ inTable = false
135
+ continue
136
+ }
137
+ const range = trimRange(source, line.start, line.end)
138
+ if (!inTable && line.raw.includes("|") && isSeparator(lines[i + 1])) inTable = true
139
+ if (inTable && line.raw.includes("|")) {
140
+ flush()
141
+ if (!isSeparator(line)) blocks.push(block(source, "table_row", range, { cells: tableCells(source, range) }))
142
+ continue
143
+ }
144
+ inTable = false
145
+ const heading = line.raw.match(HEADING)
146
+ if (heading) {
147
+ flush()
148
+ blocks.push(headingBlock(source, line, heading[1]))
149
+ continue
150
+ }
151
+ if (THEMATIC_BREAK.test(line.raw)) {
152
+ flush()
153
+ continue
154
+ }
155
+ const marker = line.raw.match(BLOCKQUOTE) ?? line.raw.match(LIST_ITEM)
156
+ // Only an ordered marker numbered 1 may interrupt a paragraph (CommonMark); else it is text.
157
+ const continues = paragraph !== null && marker?.[1] !== undefined && Number(marker[1]) !== 1
158
+ if (marker && !continues) {
159
+ flush()
160
+ const kind = BLOCKQUOTE.test(line.raw) ? "blockquote" : "list_item"
161
+ const content = trimRange(source, line.start + marker[0].length, line.end)
162
+ if (content.end > content.start) blocks.push(block(source, kind, content))
163
+ continue
164
+ }
165
+ if (paragraph) paragraph.end = range.end
166
+ else paragraph = { start: range.start, end: range.end }
167
+ }
168
+ flush()
169
+ return { blocks, problems }
170
+ }
171
+
172
+ /**
173
+ * Section kind of every block, parallel to blocks: an h2 opens a section whose kind comes from
174
+ * its heading text (sources, closing, toc, appendix, glossary, or null for findings); an h3 that
175
+ * reads as a closing heading marks its blocks closing until the next h2 or h3, as in gates-static.
176
+ * @param {{ kind: string, level?: number, text: string }[]} blocks
177
+ * @param {{ closingPattern?: RegExp|string }} [options] extra closing heading pattern (the spec's)
178
+ * @returns {(string|null)[]}
179
+ */
180
+ export function sectionKindsMd(blocks, options = {}) {
181
+ const isClosing = closingTest(options.closingPattern)
182
+ let section = null
183
+ let closingH3 = false
184
+ return blocks.map((entry) => {
185
+ if (entry.kind === "heading" && entry.level === 2) {
186
+ section = sectionKind(entry.text, isClosing)
187
+ closingH3 = false
188
+ } else if (entry.kind === "heading" && entry.level === 3) {
189
+ closingH3 = isClosing(entry.text)
190
+ }
191
+ return closingH3 ? "closing" : section
192
+ })
193
+ }
@@ -0,0 +1,196 @@
1
+ // meaning-check.mjs - ingest the meaning readers' verdicts over fact-pairs output.
2
+ //
3
+ // ingestVerdicts reads verdict files written by separate reader lanes (cheap and strong tiers),
4
+ // keeps a drift verdict only when its pair exists, its tier and drift_kind are in the closed sets,
5
+ // and both its fragment (from the sentence) and source_fragment (from the quote or statement) are
6
+ // non-empty substrings after whitespace normalization; every other verdict is dropped and logged.
7
+ // Both tiers naming the same drift_kind (not "other") on a pair is meaning_drift_confirmed (major,
8
+ // never integrity); one tier is meaning_drift_warn (minor). Each flagged pair yields one
9
+ // calibration line; a dispute is recorded there and never removes the defect. Coverage is
10
+ // fail-closed: every pair no reader judged, and every force_strong pair no strong reader judged,
11
+ // is listed in `unverified`, so a gate can tell a clean check apart from a pair nobody read.
12
+ // No model, no fetch.
13
+
14
+ import { appendFileSync, readFileSync } from "node:fs"
15
+
16
+ import { DEFECT_CODES } from "./contracts.mjs"
17
+
18
+ export const DRIFT_KINDS = Object.freeze([
19
+ "subject_changed", "scope_widened", "hedge_dropped", "certainty_added", "causality_added",
20
+ "direction_flipped", "population_changed", "evidence_grade_raised", "other",
21
+ ])
22
+ export const MODEL_TIERS = Object.freeze(["cheap", "strong"])
23
+ export const VERDICTS = Object.freeze(["drift", "ok"])
24
+ export const isDriftKind = (kind) => DRIFT_KINDS.includes(kind)
25
+ /** The machine-read verdict contract a reader lane receives with pairs.json; built from the registries above. */
26
+ export const VERDICT_SCHEMA = Object.freeze({
27
+ model_tier: MODEL_TIERS,
28
+ verdict: VERDICTS,
29
+ drift_kind: DRIFT_KINDS,
30
+ required: Object.freeze(["pair_id", "verdict"]),
31
+ drift_required: Object.freeze(["drift_kind", "fragment", "source_fragment"]),
32
+ })
33
+
34
+ /** NFC, every whitespace run collapsed to one space, trimmed. */
35
+ const squash = (text) => String(text ?? "").normalize("NFC").replace(/\s+/gu, " ").trim()
36
+
37
+ /** Read a verdict file, mapping an unreadable path or invalid JSON to a short typed error. */
38
+ function parseFile(path) {
39
+ let text
40
+ try {
41
+ text = readFileSync(path, "utf8")
42
+ } catch {
43
+ throw new TypeError(`${path}: cannot read the verdict file`)
44
+ }
45
+ try {
46
+ return JSON.parse(text)
47
+ } catch {
48
+ throw new TypeError(`${path}: the verdict file is not valid JSON`)
49
+ }
50
+ }
51
+
52
+ function verdictsOf(input) {
53
+ const label = typeof input === "string" ? input : "<inline>"
54
+ const parsed = typeof input === "string" ? parseFile(input) : input
55
+ const list = Array.isArray(parsed) ? parsed : parsed?.verdicts
56
+ if (!Array.isArray(list)) throw new TypeError(`${label}: a verdict file is an array of verdicts or { verdicts: [...] }`)
57
+ const tier = Array.isArray(parsed) ? undefined : parsed.model_tier
58
+ return list.map((verdict) => ({ verdict: tier === undefined || verdict?.model_tier !== undefined ? verdict : { ...verdict, model_tier: tier }, file: label }))
59
+ }
60
+
61
+ /** The reason a verdict is dropped, or null when it is accepted. */
62
+ function dropReason(verdict, pair) {
63
+ if (verdict === null || typeof verdict !== "object") return "malformed_verdict"
64
+ if (!pair) return "unknown_pair"
65
+ if (!VERDICTS.includes(verdict.verdict)) return "invalid_verdict"
66
+ if (!MODEL_TIERS.includes(verdict.model_tier)) return "invalid_model_tier"
67
+ if (verdict.verdict === "ok") return null
68
+ if (!isDriftKind(verdict.drift_kind)) return "unknown_drift_kind"
69
+ const [fragment, sourceFragment] = [squash(verdict.fragment), squash(verdict.source_fragment)]
70
+ if (fragment === "" || sourceFragment === "") return "empty_fragment"
71
+ if (!squash(pair.sentence).includes(fragment)) return "fragment_not_found"
72
+ const source = pair.kind === "claim" ? pair.statement : pair.quote
73
+ if (!squash(source).includes(sourceFragment)) return "source_fragment_not_found"
74
+ return null
75
+ }
76
+
77
+ function defect(code, pair, driftKind, tiers, verdicts) {
78
+ const entry = DEFECT_CODES[code]
79
+ // The fragment must come from the verdict whose drift_kind is being reported, not from whichever
80
+ // verdict happened to arrive first on the pair.
81
+ const first = verdicts.find((verdict) => verdict.drift_kind === driftKind) ?? verdicts[0]
82
+ return {
83
+ code,
84
+ severity: entry.severity,
85
+ integrity: entry.integrity,
86
+ pair_id: pair.pair_id,
87
+ sentence_key: pair.sentence_key ?? null,
88
+ drift_kind: driftKind,
89
+ tiers,
90
+ fragment: first.fragment,
91
+ source_fragment: first.source_fragment,
92
+ selector: null,
93
+ line: null,
94
+ message: `${pair.pair_id}: ${tiers.join(" and ")} reader${tiers.length > 1 ? "s" : ""} found ${driftKind} at "${squash(first.fragment)}"`,
95
+ hint: entry.hint,
96
+ }
97
+ }
98
+
99
+ /** One meaning_unverified defect per coverage gap, so blocksDelivery sees an unread pair through the registry. */
100
+ export function unverifiedDefects(unverified) {
101
+ const entry = DEFECT_CODES.meaning_unverified
102
+ return unverified.map((gap) => ({
103
+ code: "meaning_unverified",
104
+ severity: entry.severity,
105
+ integrity: entry.integrity,
106
+ pair_id: gap.pair_id,
107
+ reason: gap.reason,
108
+ selector: null,
109
+ line: null,
110
+ message: `${gap.pair_id}: ${gap.reason === "no_strong_verdict" ? "no strong reader judged this force_strong pair" : "no reader judged this pair"}`,
111
+ hint: entry.hint,
112
+ }))
113
+ }
114
+
115
+ /** Pairs with no accepted verdict, and force_strong pairs with no accepted strong verdict, in pair order. */
116
+ function coverageGaps(list, accepted) {
117
+ const tiersByPair = new Map()
118
+ for (const verdict of accepted) {
119
+ tiersByPair.set(verdict.pair_id, (tiersByPair.get(verdict.pair_id) ?? new Set()).add(verdict.model_tier))
120
+ }
121
+ const gaps = []
122
+ for (const pair of list) {
123
+ const tiers = tiersByPair.get(pair.pair_id)
124
+ if (tiers === undefined || tiers.size === 0) gaps.push({ pair_id: pair.pair_id, reason: "no_verdict" })
125
+ else if (pair.force_strong === true && !tiers.has("strong")) gaps.push({ pair_id: pair.pair_id, reason: "no_strong_verdict" })
126
+ }
127
+ return gaps
128
+ }
129
+
130
+ /** Group accepted drift verdicts per pair, then per tier. */
131
+ function flaggedPairs(accepted) {
132
+ const byPair = new Map()
133
+ for (const verdict of accepted) {
134
+ if (verdict.verdict !== "drift") continue
135
+ const tiers = byPair.get(verdict.pair_id) ?? new Map()
136
+ tiers.set(verdict.model_tier, [...(tiers.get(verdict.model_tier) ?? []), verdict])
137
+ byPair.set(verdict.pair_id, tiers)
138
+ }
139
+ return byPair
140
+ }
141
+
142
+ /** The drift_kind both tiers named (never "other"), or null. */
143
+ function agreedKind(tiers) {
144
+ const [cheap, strong] = MODEL_TIERS.map((tier) => tiers.get(tier) ?? [])
145
+ return cheap.map((verdict) => verdict.drift_kind).find((kind) => kind !== "other" && strong.some((verdict) => verdict.drift_kind === kind)) ?? null
146
+ }
147
+
148
+ /**
149
+ * @param {object[] | { pairs: object[] }} pairs extractPairs(...) output
150
+ * @param {(string | object[] | { verdicts: object[], model_tier?: string })[]} verdictFiles paths, or parsed verdict lists
151
+ * @param {{ dispute?: { pair_id: string, reason: string }, calibrationPath?: string }} [options]
152
+ * calibrationPath: append each calibration line to this JSONL file (fidelity-calibration.jsonl)
153
+ * @returns {{ defects: object[], dropped: object[], unverified: { pair_id: string, reason: string }[], calibrationLines: string[] }}
154
+ * unverified lists, in pair order, every pair no reader judged (`no_verdict`) and every
155
+ * force_strong pair no strong reader judged (`no_strong_verdict`); a verdict that was dropped
156
+ * does not count as read. A consumer MUST treat a non-empty list as blocking: the pairs were
157
+ * never checked, so an empty `defects` list does not mean the report is clean.
158
+ * @throws {TypeError} when pairs is neither an array nor `{ pairs: [...] }` (fail closed: a missing
159
+ * pairs input never reads as a fully verified report)
160
+ */
161
+ export function ingestVerdicts(pairs, verdictFiles, options = {}) {
162
+ const list = Array.isArray(pairs) ? pairs : pairs?.pairs
163
+ if (!Array.isArray(list)) throw new TypeError("pairs: expected an array of pairs or { pairs: [...] }")
164
+ const byId = new Map(list.map((pair) => [pair.pair_id, pair]))
165
+ const accepted = []
166
+ const dropped = []
167
+ for (const input of verdictFiles) {
168
+ for (const { verdict, file } of verdictsOf(input)) {
169
+ const reason = dropReason(verdict, byId.get(verdict?.pair_id))
170
+ if (reason === null) accepted.push(verdict)
171
+ else dropped.push({ file, pair_id: verdict?.pair_id ?? null, model_tier: verdict?.model_tier ?? null, reason })
172
+ }
173
+ }
174
+ const dispute = options.dispute ?? null
175
+ const defects = []
176
+ const calibrationLines = []
177
+ const flagged = flaggedPairs(accepted)
178
+ for (const pair of list) {
179
+ const tiers = flagged.get(pair.pair_id)
180
+ if (!tiers) continue
181
+ const tierNames = MODEL_TIERS.filter((tier) => tiers.has(tier))
182
+ const agreed = agreedKind(tiers)
183
+ const verdicts = MODEL_TIERS.flatMap((tier) => tiers.get(tier) ?? [])
184
+ const kind = agreed ?? verdicts[0].drift_kind
185
+ const confirmed = agreed !== null
186
+ defects.push(defect(confirmed ? "meaning_drift_confirmed" : "meaning_drift_warn", pair, kind, tierNames, verdicts))
187
+ const disputed = dispute !== null && dispute.pair_id === pair.pair_id
188
+ calibrationLines.push(JSON.stringify({ kind: pair.kind, pair_id: pair.pair_id, tiers: tierNames, drift_kind: kind, confirmed, disputed, reason: disputed ? String(dispute.reason ?? "") : null }))
189
+ }
190
+ const unverified = coverageGaps(list, accepted)
191
+ if (dispute !== null && !flagged.has(dispute.pair_id)) {
192
+ dropped.push({ file: "<dispute>", pair_id: dispute.pair_id, model_tier: null, reason: "dispute_not_flagged" })
193
+ }
194
+ if (options.calibrationPath && calibrationLines.length > 0) appendFileSync(options.calibrationPath, `${calibrationLines.join("\n")}\n`)
195
+ return { defects, dropped, unverified, calibrationLines }
196
+ }
@@ -0,0 +1,226 @@
1
+ // numerals.mjs - shared numeral patterns and the numeral parser.
2
+ //
3
+ // NUM, UNIT, NOT_CLAIMS and CLAIMS are the claim-detection patterns the unsourced-number gate uses
4
+ // (gates-text.mjs imports them from here). parseNumeral reads one numeral into an exact decimal
5
+ // string: thousands separators, Korean and Latin scale words, percent and percentage points, the
6
+ // English number words one to twelve, an adjacent currency symbol or unit, and an adjacent bounding
7
+ // qualifier. No numeral is ever passed through Number(). Plain Node ESM, sibling imports only.
8
+
9
+ import { add, fromDecimal, mul, toDecimal } from "./rational.mjs"
10
+ import { normalizeUnit } from "./units.mjs"
11
+
12
+ const MONTH = "(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*\\.?"
13
+ /** Stripped before claim detection: dates, versions, figure refs, times, bare years. */
14
+ export const NOT_CLAIMS = [
15
+ /\b\d{4}[-./]\d{1,2}(?:[-./]\d{1,2})?\b/g,
16
+ /\b\d{2}\.\d{2}\.\d{2}\b/g,
17
+ /\d{2,4}\s*\uB144\s*\d{1,2}\s*\uC6D4(?:\s*\d{1,2}\s*\uC77C)?/g,
18
+ /\d{1,2}\s*\uC6D4(?:\s*\d{1,2}\s*\uC77C)?/g,
19
+ /(?:19|20)\d{2}\s*\uB144/g,
20
+ new RegExp(`\\b${MONTH}\\s+\\d{1,2}(?:st|nd|rd|th)?(?:,?\\s+\\d{4})?`, "gi"),
21
+ 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,
23
+ /\b(?:version|ver\.?|release)\s*\d+(?:\.\d+)*/gi,
24
+ /\b(?:fig(?:ure)?\.?|table|chart|section|step|phase|wave|part)\s*\d+(?:\.\d+)*/gi,
25
+ /(?:\uADF8\uB9BC|\uB3C4\uD45C|\uD45C|\uBC84\uC804)\s*\d+(?:\.\d+)*/g,
26
+ /\b\d{1,2}:\d{2}\b/g,
27
+ /\b(?:19|20)\d{2}s?\b/g,
28
+ ]
29
+ export const NUM = "(?<![\\p{L}\\d.,])\\d+(?:[.,]\\d+)*"
30
+ export const UNIT =
31
+ "(?:%|\\u2030|\\u00D7|\\uD37C\\uC13C\\uD2B8|\\uB2EC\\uB7EC|\\uC720\\uB85C|\\uC5D4|\\uC6D0|\\uB9CC|\\uC5B5|\\uC870|\\uCC9C|\\uCD08|\\uBD84|\\uC2DC\\uAC04|\\uC77C|\\uC8FC|\\uAC1C\\uC6D4|\\uB144|\\uBA85|\\uAC74|\\uAC1C|\\uD68C|\\uBC88|\\uBC30" +
32
+ "|(?:percent|pp|bps?|x|k|m|b|bn|million|billion|thousand|trillion|usd|krw|eur|ms|s|secs?|seconds?|mins?|minutes?|h|hrs?|hours?|days?|weeks?|months?|years?|kb|mb|gb|tb|users?|people|employees|engineers|tokens?|requests?|stars?|downloads?|customers?)(?![\\p{L}\\d]))"
33
+ export const CLAIMS = [
34
+ new RegExp(`[$\\u20AC\\u00A3\\u00A5\\u20A9]\\s?${NUM}`, "u"),
35
+ new RegExp(`${NUM}\\s?${UNIT}`, "iu"),
36
+ /(?<![\d.,])\d{1,3}(?:,\d{3})+(?![\d,])/,
37
+ /(?<![\d.])\d+\.\d+(?![\d.])/,
38
+ ]
39
+
40
+ const WORDS = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve"]
41
+ const WORD_RE = new RegExp(`\\b(?:${WORDS.join("|")})\\b`, "gi")
42
+ /** Korean scale words: cheon (1e3) inside a group, then man, eok, jo as group scales. */
43
+ const CHEON = "\uCC9C"
44
+ const BIG = new Map([["\uB9CC", 10000n], ["\uC5B5", 100000000n], ["\uC870", 1000000000000n]])
45
+ /** A scale word that starts a longer word is not a scale: jogak (pieces), mankeum (as much as). */
46
+ const KO_GROUP = /^(\s?)(?:(\uCC9C)?([\uB9CC\uC5B5\uC870])|(\uCC9C))(?![\uAC01\uD07C])/
47
+ 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 }
50
+ const CURRENCY = "$\u20AC\u00A3\u00A5\u20A9"
51
+ const SYMBOL_UNIT = /^\s?(%p|%|\u2030|\u00D7|\u00B0C|\u00B0F|\u2103|\u2109)/
52
+ const LATIN_UNIT = /^\s?([A-Za-z]+)(?![\p{L}\d])/u
53
+ const HANGUL_UNIT = /^\s?([\uAC00-\uD7A3]+)/
54
+ /** "3 bun-ui 1" is the fraction one third, not 3 minutes. */
55
+ const FRACTION = /^\s?\uBD84\uC758\s?\d/
56
+ const UNIT_AFTER = new RegExp(`^\\s?${UNIT}`, "iu")
57
+ const NUM_AT = new RegExp(`^${NUM}`, "u")
58
+ /** Range separators: hyphen, en dash, tilde, "to". */
59
+ const RANGE_SEP = /^(?:\s?[-\u2013~]\s?|\sto\s)/
60
+ const SIGN = "-\u2212"
61
+ /** Bounding qualifiers: before the value (Korean and English) and right after it (Korean). */
62
+ 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)/
64
+
65
+ /** "1,237" -> "1237"; a malformed group ("1,23") keeps only the leading plain number. */
66
+ function cleanDigits(raw) {
67
+ if (/^\d{1,3}(?:,\d{3})+(?:\.\d+)?$/.test(raw)) return { digits: raw.replace(/,/g, ""), length: raw.length }
68
+ const plain = /^\d+(?:\.\d+)?/.exec(raw)[0]
69
+ return { digits: plain, length: plain.length }
70
+ }
71
+
72
+ /** The bare-year and four-digit-date patterns: a numeral they cover can still be a value. */
73
+ const YEAR_SHAPED = new Set([NOT_CLAIMS[0], NOT_CLAIMS[12]])
74
+
75
+ function excludedRanges(text) {
76
+ const ranges = []
77
+ for (const re of NOT_CLAIMS) {
78
+ for (const match of text.matchAll(re)) ranges.push([match.index, match.index + match[0].length, YEAR_SHAPED.has(re)])
79
+ }
80
+ return ranges
81
+ }
82
+
83
+ /** A year-shaped numeral is a value when it has a decimal part or a currency, scale word or unit touches it. */
84
+ function anchored(text, at, end, digits) {
85
+ if (digits.includes(".") || (at > 0 && CURRENCY.includes(text[at - 1]))) return true
86
+ return UNIT_AFTER.test(text.slice(end)) || unitAfter(text, end) !== null
87
+ }
88
+
89
+ /**
90
+ * First digit run or number word at or after `from` that no NOT_CLAIMS pattern covers. A year-shaped
91
+ * match exactly over the numeral (not "2020s", not "2020-03") is lifted when the numeral is anchored.
92
+ */
93
+ function findStart(text, from, ranges) {
94
+ const covering = (at) => ranges.filter(([s, e]) => at >= s && at < e)
95
+ let digit = null
96
+ for (const match of text.slice(from).matchAll(new RegExp(NUM, "gu"))) {
97
+ const at = from + match.index
98
+ const candidate = { at, ...cleanDigits(match[0]) }
99
+ const end = at + candidate.length
100
+ const cover = covering(at)
101
+ const lifted = cover.every(([s, e, yearShaped]) => yearShaped && s === at && e <= end)
102
+ if (cover.length === 0 || (lifted && anchored(text, at, end, candidate.digits))) {
103
+ digit = candidate
104
+ break
105
+ }
106
+ }
107
+ const free = (at) => covering(at).length === 0
108
+ for (const match of text.slice(from).matchAll(WORD_RE)) {
109
+ const at = from + match.index
110
+ if (digit && digit.at < at) break
111
+ if (free(at)) return { at, digits: String(WORDS.indexOf(match[0].toLowerCase()) + 1), length: match[0].length }
112
+ }
113
+ return digit
114
+ }
115
+
116
+ /** Korean groups such as "4jo 2,000eok": each later group needs a smaller scale. */
117
+ function koreanScale(text, at, digits) {
118
+ const first = KO_GROUP.exec(text.slice(at))
119
+ if (!first) return null
120
+ const big = first[3] ? BIG.get(first[3]) : 1n
121
+ const firstFactor = (first[2] || first[4] ? 1000n : 1n) * big
122
+ let value = mul(fromDecimal(digits), firstFactor)
123
+ let end = at + first[0].length
124
+ let last = first[3] ? big : 0n
125
+ let groups = 1
126
+ while (last > 1n) {
127
+ const next = KO_NEXT.exec(text.slice(end))
128
+ if (!next) break
129
+ const factor = (next[2] ? 1000n : 1n) * (next[3] ? BIG.get(next[3]) : 1n)
130
+ if (factor <= 1n || factor >= last) break
131
+ value = add(value, mul(fromDecimal(cleanDigits(next[1]).digits), factor))
132
+ end += next[0].length
133
+ last = next[3] ? BIG.get(next[3]) : 0n
134
+ groups++
135
+ }
136
+ return { value, end, factor: groups === 1 ? firstFactor : null }
137
+ }
138
+
139
+ /** Scale words after a numeral: { value (rational) | null, end, factor | null (one plain factor) }. */
140
+ function scaled(text, end, digits) {
141
+ const korean = koreanScale(text, end, digits)
142
+ if (korean) return korean
143
+ const latin = LATIN_SCALE.exec(text.slice(end))
144
+ if (!latin) return { value: null, end, factor: null }
145
+ const factor = LATIN_FACTOR[latin[1] ?? latin[2].toLowerCase()]
146
+ return { value: mul(fromDecimal(digits), factor), end: end + latin[0].length, factor }
147
+ }
148
+
149
+ /** The second endpoint of "5-10", "5 to 10", "5~10" or "5\u201310", or null. */
150
+ function rangeEnd(text, at, ranges) {
151
+ const sep = RANGE_SEP.exec(text.slice(at))
152
+ if (!sep) return null
153
+ const pos = at + sep[0].length
154
+ const num = NUM_AT.exec(text.slice(pos))
155
+ if (!num || ranges.some(([s, e]) => pos >= s && pos < e)) return null
156
+ const { digits, length } = cleanDigits(num[0])
157
+ const scale = scaled(text, pos + length, digits)
158
+ return { value: scale.value ? toDecimal(scale.value) : digits, end: scale.end, factor: scale.factor }
159
+ }
160
+
161
+ function unitAfter(text, at) {
162
+ const rest = text.slice(at)
163
+ if (FRACTION.test(rest)) return null
164
+ const symbol = SYMBOL_UNIT.exec(rest)
165
+ if (symbol) return { unitRaw: symbol[1], end: at + symbol[0].length }
166
+ const latin = LATIN_UNIT.exec(rest)
167
+ if (latin) return normalizeUnit(latin[1]) ? { unitRaw: latin[1], end: at + latin[0].length } : null
168
+ const hangul = HANGUL_UNIT.exec(rest)
169
+ if (!hangul) return null
170
+ const lead = hangul[0].length - hangul[1].length
171
+ for (let size = hangul[1].length; size > 0; size--) {
172
+ const candidate = hangul[1].slice(0, size)
173
+ if (!BIG.has(candidate) && candidate !== CHEON && normalizeUnit(candidate)) return { unitRaw: candidate, end: at + lead + size }
174
+ }
175
+ return null
176
+ }
177
+
178
+ function qualifierBefore(text, at) {
179
+ const tokens = text.slice(0, at).trim().split(/\s+/).slice(-2).map((token) => token.replace(/^[^\p{L}]+/u, ""))
180
+ for (const phrase of QUALIFIERS_BEFORE) {
181
+ const words = phrase.split(" ")
182
+ for (let skip = 0; skip + words.length <= tokens.length; skip++) {
183
+ const window = tokens.slice(tokens.length - skip - words.length, tokens.length - skip)
184
+ if (window.join(" ").toLowerCase() === phrase) return window.join(" ")
185
+ }
186
+ }
187
+ return null
188
+ }
189
+
190
+ /**
191
+ * The first numeral at or after `from`: { value, raw, span: [start, end], unitRaw, qualifierRaw },
192
+ * or null. `value` is an exact decimal string with scale words folded in ("3eok won" -> "300000000",
193
+ * unit "won") and a leading minus sign ("-" or U+2212) kept as "-"; `span` covers the sign, an
194
+ * adjacent currency symbol, the numeral, scale words and the unit. A range ("5-10 m", "5 to 10 m",
195
+ * "5~10%") adds `valueEnd` and `range: true`; the unit, and a scale word written only on the second
196
+ * endpoint ("3~5eok"), apply to both endpoints.
197
+ */
198
+ export function parseNumeral(text, from = 0) {
199
+ const ranges = excludedRanges(text)
200
+ const found = findStart(text, from, ranges)
201
+ if (!found) return null
202
+ let start = found.at
203
+ const first = scaled(text, found.at + found.length, found.digits)
204
+ let value = first.value
205
+ let end = first.end
206
+ const second = rangeEnd(text, end, ranges)
207
+ if (second) {
208
+ end = second.end
209
+ if (!value && second.factor) value = mul(fromDecimal(found.digits), second.factor)
210
+ }
211
+ let unitRaw = null
212
+ if (start > 0 && CURRENCY.includes(text[start - 1])) [unitRaw, start] = [text[start - 1], start - 1]
213
+ const negative = start > 0 && SIGN.includes(text[start - 1]) && !/[\p{L}\p{N}]/u.test(text[start - 2] ?? "")
214
+ if (negative) start--
215
+ const unit = unitRaw ? null : unitAfter(text, end)
216
+ if (unit) [unitRaw, end] = [unit.unitRaw, unit.end]
217
+ const after = QUALIFIER_AFTER.exec(text.slice(end))
218
+ return {
219
+ value: (negative ? "-" : "") + (value ? toDecimal(value) : found.digits),
220
+ ...(second ? { valueEnd: second.value, range: true } : {}),
221
+ raw: text.slice(start, end),
222
+ span: [start, end],
223
+ unitRaw,
224
+ qualifierRaw: qualifierBefore(text, start) ?? (after ? after[1] : null),
225
+ }
226
+ }