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
@@ -1,77 +1,44 @@
1
1
  // source-snapshot.mjs - the per-source snapshot that fact locators read.
2
2
  //
3
- // snapshotFromHtml turns a page into text.txt form: one line per block (prose blocks via
4
- // proseText, one table cell per line; head, scripts, styles and code are dropped). writeSnapshot
5
- // writes <sourcesDir>/<S-id>/ with text.txt (pages joined by a form feed), source.html when html
6
- // was given, meta.json, sentences.jsonl (sid S<n>:s<k>) and acronyms.json. The sha256 covers
7
- // text.txt only. Nothing here touches the network: the caller supplies the html or text.
3
+ // writeSnapshot writes <sourcesDir>/<S-id>/ with raw.bin (the fetched or captured bytes, absent
4
+ // only for converted-only sources), meta.json (sha256 of the pinned body, text_source, charset),
5
+ // text.txt (the locator text of source-view.mjs: the visible text nodes of a page, the decoded
6
+ // bytes of a document, or a declared/converted extraction), source.html for pages (decoded, a
7
+ // reading aid), sentences.jsonl (sid S<n>:s<k>, indexed over text.txt) and acronyms.json; a page
8
+ // also gets conversion.json, the conversion probe's record of the numbers its text.txt altered.
8
9
  // Plain Node ESM, node: builtins and sibling imports only.
9
10
 
10
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
11
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
11
12
  import { join } from "node:path"
12
13
 
13
14
  import { sha256Of } from "./cli-support.mjs"
14
- import { parseHtml, PROSE_TAGS, proseText } from "./html-lite.mjs"
15
+ import { probeConversion } from "./conversion-probe.mjs"
16
+ import { writeFidelityRecord } from "./fidelity-records.mjs"
17
+ import { extractHtml } from "./html-text-map.mjs"
15
18
  import { splitSentences } from "./sentence-split.mjs"
19
+ import { buildView } from "./source-view.mjs"
16
20
 
17
- const SKIP = new Set(["head", "script", "style", "svg", "code", "pre", "kbd", "samp", "template", "noscript"])
18
- const INLINE = new Set([
19
- "a", "abbr", "b", "bdi", "bdo", "cite", "data", "del", "dfn", "em", "font", "i", "ins", "label", "mark", "q", "s",
20
- "small", "span", "strong", "sub", "sup", "time", "u", "var",
21
- ])
22
21
  const SOURCE_ID = /^S[1-9]\d*$/
23
22
  const EXTRACTOR = /^(?:tool:source-snapshot@1|model|extractor:[\w.@+-]+)$/
24
- const KINDS = new Set(["page", "command", "document"])
23
+ const KINDS = new Set(["page", "command", "document", "converted-only"])
25
24
  const PROVENANCE = new Set(["primary", "secondary", "unknown"])
26
25
  const ACRONYM_AFTER = /\(([A-Z]{2,6})\)/g
27
26
  const ACRONYM_BEFORE = /(?<![\p{L}\p{N}])([A-Z]{2,6})\s?\(([A-Z][^()\n]{2,79})\)/gu
28
27
  const NAME_WORD = /^[A-Z][\p{L}\p{N}-]*$/u
29
28
  const CONNECTOR = new Set(["of", "and", "for", "the", "in", "on", "to", "&"])
29
+ const MARKUP = /^\s*</
30
30
 
31
31
  /**
32
- * Block lines of a page: { root, text, blocks: [{ node, start, end }] } where text joins the block
33
- * texts with "\n" and text.slice(start, end) is one block. fact-locate reads cells through `node`.
32
+ * Block lines of a page: { root, text, blocks: [{ node, start, end }], starts, ends } where text
33
+ * joins the visible block texts with "\n" (html-text-map.mjs). fact-locate reads cells through `node`.
34
34
  */
35
35
  export function snapshotBlocks(html) {
36
- const root = parseHtml(html)
37
- const lines = []
38
- let buffer = ""
39
- const flush = (node) => {
40
- const line = buffer.replace(/\s+/g, " ").trim()
41
- buffer = ""
42
- if (line) lines.push({ node, text: line })
43
- }
44
- const walk = (node) => {
45
- for (const child of node.children) {
46
- if (child.tag === "#text") buffer += child.text
47
- else if (SKIP.has(child.tag)) continue
48
- else if (child.tag === "br") flush(node)
49
- else if (INLINE.has(child.tag)) walk(child)
50
- else if (PROSE_TAGS.has(child.tag)) {
51
- flush(node)
52
- const line = proseText(child)
53
- if (line) lines.push({ node: child, text: line })
54
- } else {
55
- flush(node)
56
- walk(child)
57
- flush(child)
58
- }
59
- }
60
- }
61
- walk(root)
62
- flush(root)
63
- const blocks = []
64
- let at = 0
65
- for (const line of lines) {
66
- blocks.push({ node: line.node, start: at, end: at + line.text.length })
67
- at += line.text.length + 1
68
- }
69
- return { root, text: lines.map((line) => line.text).join("\n"), blocks }
36
+ return extractHtml(html)
70
37
  }
71
38
 
72
39
  /** text.txt form of an html page. */
73
40
  export function snapshotFromHtml(html) {
74
- return snapshotBlocks(html).text
41
+ return extractHtml(html).text
75
42
  }
76
43
 
77
44
  /** Sentence index rows { sid: "S<n>:s<k>", start, end, text }; a form feed (page break) always ends a sentence. */
@@ -127,36 +94,61 @@ export function findAcronyms(text, sentences) {
127
94
  function snapshotText(input) {
128
95
  if (Array.isArray(input.pages)) return input.pages.join("\f")
129
96
  if (typeof input.text === "string") return input.text
130
- if (typeof input.html === "string") return snapshotFromHtml(input.html)
131
- throw new TypeError("writeSnapshot needs text, pages, or html")
97
+ throw new TypeError("writeSnapshot needs raw, html, text, or pages")
98
+ }
99
+
100
+ /** Which source-view text source the input makes, with the bytes it pins. */
101
+ function bodiesOf(input) {
102
+ if (input.kind === "converted-only") return { mode: "converted", raw: null, textBody: Buffer.from(snapshotText(input)) }
103
+ if (input.declared === true) return { mode: "declared", raw: Buffer.from(input.raw), textBody: Buffer.from(snapshotText(input)) }
104
+ if (input.raw === undefined && typeof input.html === "string") return { mode: "html", raw: Buffer.from(input.html), textBody: null }
105
+ if (input.raw === undefined) return { mode: "decoded", raw: Buffer.from(snapshotText(input)), textBody: null }
106
+ const raw = Buffer.from(input.raw)
107
+ if (input.markup === true || typeof input.html === "string" || input.kind === "page" || MARKUP.test(raw.subarray(0, 1024).toString("latin1").replace(/^\u00ef\u00bb\u00bf/, ""))) return { mode: "html", raw, textBody: null }
108
+ if (input.text !== undefined || input.pages !== undefined) return { mode: "declared", raw, textBody: Buffer.from(snapshotText(input)) }
109
+ return { mode: "decoded", raw, textBody: null }
132
110
  }
133
111
 
134
112
  function checkInput(sourceId, input) {
135
113
  if (!SOURCE_ID.test(String(sourceId))) throw new TypeError(`source id must be S<n>, got ${sourceId}`)
136
114
  if (!EXTRACTOR.test(String(input.extractor))) throw new TypeError(`extractor must be tool:source-snapshot@1, model, or extractor:<name>, got ${input.extractor}`)
137
115
  if (typeof (input.url ?? input.path) !== "string") throw new TypeError("writeSnapshot needs a url or a path")
138
- if (input.kind !== undefined && !KINDS.has(input.kind)) throw new TypeError(`kind must be page, command, or document, got ${input.kind}`)
116
+ if (input.kind !== undefined && !KINDS.has(input.kind)) throw new TypeError(`kind must be page, command, document, or converted-only, got ${input.kind}`)
139
117
  if (input.provenance !== undefined && !PROVENANCE.has(input.provenance)) throw new TypeError(`provenance must be primary, secondary, or unknown, got ${input.provenance}`)
118
+ if (input.observed_at !== undefined && (typeof input.observed_at !== "string" || Number.isNaN(new Date(input.observed_at).getTime()))) throw new TypeError(`observed_at must be an ISO timestamp, got ${input.observed_at}`)
119
+ }
120
+
121
+ function pinnedHash(dir) {
122
+ const metaPath = join(dir, "meta.json")
123
+ return existsSync(metaPath) ? JSON.parse(readFileSync(metaPath, "utf8")).sha256 : null
140
124
  }
141
125
 
142
126
  /**
143
- * Write <sourcesDir>/<sourceId>/ for one source; returns { dir, sha256 } with sha256 over text.txt.
144
- * input: { url | path, text | pages | html, extractor, observed_at?, revision?, kind?, provenance? }.
145
- * Re-writing the same text is a no-op change; a different text over an existing snapshot throws
146
- * snapshot_exists, because ledger rows pin the old hash.
127
+ * Write <sourcesDir>/<sourceId>/ for one source; returns { dir, sha256, altered } over the pinned body,
128
+ * where altered lists the numbers the page conversion altered (null when no page conversion ran).
129
+ * input: { url | path, raw?, markup?, html?, text? | pages?, charset?, extractor, observed_at?, revision?,
130
+ * kind?, provenance? } (see bodiesOf). Re-writing the same bytes is a no-op; different bytes over
131
+ * an existing snapshot throw snapshot_exists, because ledger rows pin the old hash, unless
132
+ * options.replace (`source refresh`, which re-anchors those rows, or `source snapshot --replace`, which
133
+ * has checked that no active row pins it) is set; a replacement drops the old capture's command.json.
147
134
  */
148
- export function writeSnapshot(sourcesDir, sourceId, input) {
135
+ export function writeSnapshot(sourcesDir, sourceId, input, options = {}) {
149
136
  checkInput(sourceId, input)
150
- const text = snapshotText(input)
151
- const sha256 = sha256Of(Buffer.from(text, "utf8"))
137
+ const { mode, raw, textBody } = bodiesOf(input)
138
+ const view = buildView(mode, { raw, textBody, charset: input.charset ?? null })
139
+ const sha256 = sha256Of(raw ?? textBody)
152
140
  const dir = join(sourcesDir, sourceId)
153
- const textPath = join(dir, "text.txt")
154
- if (existsSync(textPath) && sha256Of(readFileSync(textPath)) !== sha256) {
155
- throw new Error(`snapshot_exists: ${sourceId} already holds a different text.txt`)
141
+ const pinned = pinnedHash(dir)
142
+ if (pinned !== null && pinned !== sha256) {
143
+ if (options.replace !== true) throw new Error(`snapshot_exists: ${sourceId} already holds different snapshot bytes`)
144
+ rmSync(join(dir, "command.json"), { force: true })
156
145
  }
157
146
  mkdirSync(dir, { recursive: true })
158
- writeFileSync(textPath, text)
159
- if (typeof input.html === "string") writeFileSync(join(dir, "source.html"), input.html)
147
+ if (raw) writeFileSync(join(dir, "raw.bin"), raw)
148
+ else rmSync(join(dir, "raw.bin"), { force: true })
149
+ writeFileSync(join(dir, "text.txt"), textBody ?? view.text)
150
+ if (view.html !== null) writeFileSync(join(dir, "source.html"), view.html)
151
+ else rmSync(join(dir, "source.html"), { force: true })
160
152
  const meta = {
161
153
  source_id: sourceId,
162
154
  url: input.url ?? input.path,
@@ -164,13 +156,25 @@ export function writeSnapshot(sourcesDir, sourceId, input) {
164
156
  sha256,
165
157
  extractor: input.extractor,
166
158
  revision: input.revision ?? null,
167
- kind: input.kind ?? (input.url !== undefined || typeof input.html === "string" ? "page" : "document"),
159
+ kind: input.kind ?? (input.url !== undefined || mode === "html" ? "page" : "document"),
168
160
  provenance: input.provenance ?? "unknown",
161
+ text_source: mode,
162
+ ...(raw ? { charset: view.encoding } : {}),
163
+ ...(mode === "declared" ? { text_sha256: sha256Of(textBody) } : {}),
169
164
  }
170
165
  writeFileSync(join(dir, "meta.json"), `${JSON.stringify(meta, null, 2)}\n`)
171
- const sentences = indexSentences(text, sourceId)
166
+ let altered = null
167
+ if (mode === "html") {
168
+ const { altered: changed, ...counts } = probeConversion(view.html, view.text, { attributes: false })
169
+ altered = changed
170
+ writeFidelityRecord(join(dir, "conversion.json"), "source_conversion", {
171
+ artifact_sha256: sha256Of(view.html), report: join(dir, "source.html"), defects: [],
172
+ conversion_altered_numbers: changed, ...counts,
173
+ }, new Date(meta.observed_at))
174
+ } else rmSync(join(dir, "conversion.json"), { force: true })
175
+ const sentences = indexSentences(view.text, sourceId)
172
176
  writeFileSync(join(dir, "sentences.jsonl"), sentences.map((row) => `${JSON.stringify(row)}\n`).join(""))
173
- const acronyms = { schemaVersion: 1, source_id: sourceId, snapshot_sha256: sha256, entries: findAcronyms(text, sentences) }
177
+ const acronyms = { schemaVersion: 1, source_id: sourceId, snapshot_sha256: sha256, entries: findAcronyms(view.text, sentences) }
174
178
  writeFileSync(join(dir, "acronyms.json"), `${JSON.stringify(acronyms, null, 2)}\n`)
175
- return { dir, sha256 }
179
+ return { dir, sha256, altered }
176
180
  }
@@ -0,0 +1,89 @@
1
+ // source-view.mjs - the one reading of a source snapshot that locators, verify, and refresh share.
2
+ //
3
+ // A snapshot's meta.json `text_source` says where its locator text comes from:
4
+ // html - text nodes of raw.bin (html-text-map.mjs), offsets are raw.bin bytes
5
+ // decoded - raw.bin decoded in its charset, offsets are raw.bin bytes
6
+ // declared - text.txt is a declared extraction (--raw + --text) of non-HTML raw.bin; offsets
7
+ // are text.txt bytes, and raw.bin and text.txt are both pinned
8
+ // converted - converted-only: no raw.bin, text.txt is all there is (quotes only)
9
+ // A snapshot without text_source and without raw.bin is a legacy text.txt snapshot. byteSpan maps
10
+ // a [start, end) range of `text` to [startByte, endByte) in `artifact`, never through a re-encode.
11
+ // node: builtins and sibling imports only.
12
+
13
+ import { readFileSync } from "node:fs"
14
+ import { join } from "node:path"
15
+
16
+ import { sha256Of } from "./cli-support.mjs"
17
+ import { extractHtml } from "./html-text-map.mjs"
18
+ import { decodeRaw } from "./raw-decode.mjs"
19
+
20
+ export const TEXT_SOURCES = Object.freeze(["html", "decoded", "declared", "converted"])
21
+
22
+ /** Read one snapshot file; the error names the file and the cause (missing, EACCES, ...). */
23
+ export function readPart(dir, name, encoding) {
24
+ try {
25
+ return readFileSync(join(dir, name), encoding)
26
+ } catch (error) {
27
+ const cause = error.code === "ENOENT" ? "is missing" : error.code === "EISDIR" ? "is not a readable file" : `is unreadable (${error.code ?? error.message})`
28
+ throw Object.assign(new Error(`${name} ${cause}`), { code: error.code, part: name })
29
+ }
30
+ }
31
+
32
+ /** The text source a meta.json declares, inferring it for snapshots written before the field. */
33
+ export function textSourceOf(meta, hasRaw) {
34
+ if (TEXT_SOURCES.includes(meta.text_source)) return meta.text_source
35
+ if (!hasRaw) return meta.kind === "converted-only" ? "converted" : "legacy"
36
+ return meta.kind === "page" ? "html" : "decoded"
37
+ }
38
+
39
+ /**
40
+ * Locator text and byte map for one snapshot body. raw is raw.bin (null for converted/legacy),
41
+ * textBody is text.txt bytes (needed for declared, converted, legacy).
42
+ */
43
+ export function buildView(mode, { raw = null, textBody = null, charset = null }) {
44
+ if (mode === "html") {
45
+ const decoded = decodeRaw(raw, charset, true)
46
+ const map = extractHtml(decoded.text)
47
+ const byteSpan = (start, end) => {
48
+ if (end <= start) return [decoded.bytes[map.ends[start - 1] ?? 0], decoded.bytes[map.ends[start - 1] ?? 0]]
49
+ return [decoded.bytes[map.starts[start]], decoded.bytes[map.ends[end - 1]]]
50
+ }
51
+ return { mode, artifact: "raw.bin", encoding: decoded.encoding, text: map.text, html: decoded.text, root: map.root, blocks: map.blocks, byteSpan }
52
+ }
53
+ if (mode === "legacy") return { mode, artifact: "text.txt", encoding: "utf-8", text: Buffer.from(textBody).toString("utf8"), html: null, root: null, blocks: null, byteSpan: null }
54
+ const body = mode === "decoded" ? raw : textBody
55
+ const decoded = decodeRaw(body, mode === "decoded" ? charset : "utf-8")
56
+ const byteSpan = (start, end) => [decoded.bytes[start], decoded.bytes[Math.max(start, end)]]
57
+ return { mode, artifact: mode === "decoded" ? "raw.bin" : "text.txt", encoding: decoded.encoding, text: decoded.text, html: null, root: null, blocks: null, byteSpan }
58
+ }
59
+
60
+ /**
61
+ * Read and hash a snapshot directory. Throws (with the file and cause) when meta.json, raw.bin, or
62
+ * text.txt cannot be read; hash drift is returned, not thrown: { meta, view, raw, sha256, drift }.
63
+ * sha256 is the hash of the pinned body (raw.bin, or text.txt for converted and legacy snapshots).
64
+ */
65
+ export function readSnapshot(dir) {
66
+ let meta
67
+ try {
68
+ meta = JSON.parse(readPart(dir, "meta.json", "utf8"))
69
+ } catch (error) {
70
+ throw error.part ? error : new Error("meta.json is not valid JSON")
71
+ }
72
+ if (meta === null || typeof meta !== "object" || typeof meta.sha256 !== "string") throw new Error("has invalid snapshot metadata (meta.json has no sha256)")
73
+ let raw = null
74
+ if (meta.text_source === undefined) {
75
+ try {
76
+ raw = readPart(dir, "raw.bin")
77
+ } catch (error) {
78
+ if (error.code !== "ENOENT") throw error
79
+ }
80
+ }
81
+ const mode = textSourceOf(meta, raw !== null)
82
+ if (raw === null && ["html", "decoded", "declared"].includes(mode)) raw = readPart(dir, "raw.bin")
83
+ const textBody = ["declared", "converted", "legacy"].includes(mode) ? readPart(dir, "text.txt") : null
84
+ const sha256 = sha256Of(raw ?? textBody)
85
+ const drift = []
86
+ if (sha256 !== meta.sha256) drift.push(`hash mismatch: ${raw ? "raw.bin" : "text.txt"} changed after its snapshot was pinned`)
87
+ if (mode === "declared" && sha256Of(textBody) !== meta.text_sha256) drift.push("hash mismatch: text.txt (the declared extraction) changed after it was pinned")
88
+ return { meta, raw, sha256, drift, view: buildView(mode, { raw, textBody, charset: meta.charset ?? null }) }
89
+ }
@@ -0,0 +1,203 @@
1
+ // sources-citation.mjs - the one Sources predicate shared by the static, trace, and claim gates.
2
+ //
3
+ // Under a Sources heading an element is split at <br>/block breaks and at sentence ends, and every
4
+ // sentence holding a numeral is checked exactly as under any other heading, unless EVERY numeral in
5
+ // it is a citation-field number. That is decided by the number's type and position only: a year
6
+ // (1900-2099), a full date, or a month/day (10/26, Oct 26, 2026. 10. 26.; a day count is not one);
7
+ // a page, volume, issue, number, or edition marker; a number inside a URL, DOI, arXiv id, ISBN or
8
+ // ISSN; a number inside a quoted title ("...", \u201C...\u201D, \u300C...\u300D,
9
+ // \u300E...\u300F, <cite>) or an <i>/<em> title directly before a URL or a date; a number inside link
10
+ // text; or a citation tag's digits. There is no word list: any other numeral makes the sentence a
11
+ // claim. A digit run glued to letters is a name, not a numeral (a hash, #12, GPT-4, v2.3.1, Q3, FY27),
12
+ // unless the letters are a unit, multiplier, or currency of units.mjs / numerals.mjs (50M, 10k, 3x,
13
+ // 2.5GB, 320m, 12%, 40percent, x3). A unit, multiplier, or percent on the right is checked before the
14
+ // letters on the left, so a word glued in front of a measure does not make it a name. A dotted version
15
+ // is a name, a European digit grouping (1.234.567) a numeral; a Korean ordinal (the je prefix, the
16
+ // jjae suffix) is a name and a Korean quarter a date. numeralKinds names the class of every digit run
17
+ // (citation-field, date, name, numeral); only a numeral is checked. A heading is not a prose sentence. Pure.
18
+ // Korean markers are \u escapes.
19
+
20
+ import { DOT_GROUPED, UNIT } from "./numerals.mjs"
21
+ import { dimensionOf, normalizeUnit } from "./units.mjs"
22
+
23
+ const SKIP = new Set(["script", "style", "template"])
24
+ const BREAK = new Set(["br", "p", "li", "div", "tr", "dd", "dt"])
25
+ // Abbreviations whose period never ends a citation sentence: pp., p., vol., no., ed., eds., et al., J. (initials).
26
+ const NOT_END = /(?:\b(?:pp?|vol|vols|no|nos|ed|eds|al|ch|sec|fig|vs|e\.g|i\.e|[A-Z])\.)$/i
27
+ const FACT = "\u0000"
28
+ const LINK = "\u0002"
29
+ const [CITE_OPEN, CITE_CLOSE, EM_OPEN, EM_CLOSE, A_OPEN, A_CLOSE] = ["\u0003", "\u0004", "\u0005", "\u0006", "\u0007", "\u0008"]
30
+ const MONTH = "(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|june?|july?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)\\b\\.?"
31
+ const YEAR = "(?:19|20)\\d{2}"
32
+ const URL = `(?:https?://|www\\.)[^\\s<>"${LINK}${A_CLOSE}]+?(?=[.,;:)\\]]?(?:\\s|$))`
33
+ /** A date at the start of a string: ISO/dotted/slashed, month-name, or Korean nyeon-wol-il. */
34
+ const DATE_AHEAD = new RegExp(`^(?:${YEAR}[-./]\\d{1,2}(?:[-./]\\d{1,2})?|\\d{1,2}[-./]\\d{1,2}[-./]${YEAR}|${MONTH}\\s+\\d{1,2},?\\s+${YEAR}|\\d{1,2}\\s+${MONTH}\\s+${YEAR}|${MONTH}\\s+${YEAR}|${YEAR}\\s*\\uB144|${YEAR}\\b)`, "i")
35
+ /** Citation fields a number can sit INSIDE (a checked sentence is traced without them, so a title is never a quotation). */
36
+ const CONTAINERS = [
37
+ new RegExp(URL, "gi"),
38
+ /\b(?:doi:\s*)?10\.\d{4,9}\/[^\s<>"]+/gi,
39
+ /\b(?:arXiv:\s*)?\d{4}\.\d{4,5}(?:v\d+)?\b/gi,
40
+ /\bIS[BS]N(?:-1[03])?:?\s*[\dXx][\dXx\s-]{6,16}[\dXx]/g,
41
+ /\[(?:S|Source\s*)?\^?\d+\]/gi,
42
+ /"[^"]*"|\u201C[^\u201D]*\u201D|\u300C[^\u300D]*\u300D|\u300E[^\u300F]*\u300F/g,
43
+ new RegExp(`${CITE_OPEN}[^${CITE_CLOSE}]*${CITE_CLOSE}`, "g"),
44
+ new RegExp(`${A_OPEN}[^${A_CLOSE}]*${A_CLOSE}`, "g"),
45
+ ]
46
+ /** Date fields: full dates, month/day, a Korean quarter, dotted dates (with spaces, or DD.MM.YY), years and year ranges. */
47
+ const DATES = [
48
+ new RegExp(`${YEAR}[-./]\\d{1,2}(?:[-./]\\d{1,2})?|\\d{1,2}[-./]\\d{1,2}[-./]${YEAR}`, "g"),
49
+ new RegExp(`\\b${MONTH}\\s+\\d{1,2}(?:st|nd|rd|th)?\\b(?:,?\\s+${YEAR})?|\\b\\d{1,2}(?:st|nd|rd|th)?\\s+${MONTH}`, "gi"),
50
+ /\d{1,2}\s*\uC6D4(?:\s*\d{1,2}\s*\uC77C)?|(?<!\d)[1-4]\s*\uBD84\uAE30/g,
51
+ new RegExp(`${YEAR}\\.\\s+\\d{1,2}\\.\\s+\\d{1,2}\\.?`, "g"),
52
+ /(?<![\d/.,])(?:1[0-2]|0?[1-9])\/(?:3[01]|[12]\d|0?[1-9])(?![\d/]|[.,]\d)/g,
53
+ /(?<![\d.])\d{2}\.\d{2}\.\d{2}(?!\d|\.\d)/g,
54
+ new RegExp(`(?<![\\d.,])${YEAR}(?:\\s*[-\\u2013~]\\s*${YEAR})?(?![\\d]|[.,]\\d)`, "g"),
55
+ ]
56
+ /** Marker fields: page, volume, issue, number, and edition markers. */
57
+ const MARKERS = [
58
+ /\b(?:pp?|pg|pages?)\.?\s*\d+(?:\s*[-\u2013]\s*\d+)?/gi,
59
+ /\b(?:vols?|nos?|iss|ed)\.\s*\d+(?:\s*[-\u2013/]\s*\d+)*/gi,
60
+ /\b(?:Vol|Volume|No|Issue|Number|volume|issue)\s+\d+(?:\s*[-\u2013/]\s*\d+)*/g,
61
+ /\b\d+(?:st|nd|rd|th)\s+(?:ed\.|edition)|\bedition\s+\d+/gi,
62
+ /\uC81C?\s*\d+(?:\s*[-\u2013~]\s*\d+)?\s*(?:\uCABD|\uBA74|\uAD8C|\uD638|\uD310)/g,
63
+ ]
64
+ const TYPED = [...DATES, ...MARKERS]
65
+ const NUMERAL = /\d+(?:[.,]\d+)*/g
66
+ const UNIT_WORD = new RegExp(`^${UNIT}$`, "iu")
67
+ const UNIT_LEAD = new RegExp(`^${UNIT}`, "iu")
68
+ const LETTERS_AFTER = /^(?:[A-Za-z]+|[\uAC00-\uD7A3]+|\p{L}+)/u
69
+ /** A unit or currency symbol right after the digits (numerals.mjs SYMBOL_UNIT plus the currency signs). */
70
+ const SYMBOL_AFTER = /^(?:%|\u2030|\u00D7|\u00B0[CF]|\u2103|\u2109|[$\u20AC\u00A3\u00A5\u20A9])/
71
+ /** Korean ordinals: the prefix je ("je 3 hoe") and a counter followed by jjae ("3 beon jjae"). */
72
+ const KO_ORDINAL_BEFORE = /(?:^|[^\p{L}])\uC81C$/u
73
+ const KO_ORDINAL_AFTER = /^[\uAC00-\uD7A3]?\uC9F8/
74
+ const GROUPED = new RegExp(`^${DOT_GROUPED}$`)
75
+
76
+ /** Letters that make a glued digit run a quantity; a Korean run may carry a particle after its unit. */
77
+ function isUnit(letters) {
78
+ if (!/^[\uAC00-\uD7A3]/.test(letters)) return UNIT_WORD.test(letters) || normalizeUnit(letters) !== null || normalizeUnit(letters.toLowerCase()) !== null
79
+ if (UNIT_LEAD.test(letters)) return true
80
+ for (let size = letters.length; size > 0; size--) if (normalizeUnit(letters.slice(0, size))) return true
81
+ return false
82
+ }
83
+ /** A multiplier written before its number ("x3", "x1.5"): a standalone x, never a label ("w1:x02") or a leading zero. */
84
+ const isMultiplierBefore = (before, left, run) => dimensionOf(left) === "multiple" && /(?:^|[\s(])\p{L}+$/u.test(before) && !/^0\d/.test(run)
85
+ const isCurrency = (letters) => /^currency:/.test(dimensionOf(letters) ?? dimensionOf(letters.toUpperCase()) ?? "")
86
+
87
+ /**
88
+ * The digit run [start, end) is part of a name: a dotted version ("0.30.14", not the grouping
89
+ * "1.234.567"), a run after # or inside a word (letters on its left that are not a currency or a
90
+ * standalone multiplier x: "W3C", "eb2b", "Q3", "FY27"; or glued on the left through - . _ /: "GPT-4", "H.15"),
91
+ * or a run whose right-hand letters are not a unit, multiplier, or currency ("3D", "21st", "10-Q",
92
+ * "1H25"). A unit, multiplier, or percent on the right makes the run a numeral first, whatever is
93
+ * glued on its left ("revenue40%", "about300 people" written solid), unless Latin letters sit on both
94
+ * sides, which is an identifier ("W3C", "eb2b").
95
+ */
96
+ function isName(text, start, end) {
97
+ const run = text.slice(start, end)
98
+ if (/^\d+(?:\.\d+){2,}$/.test(run) && !GROUPED.test(run)) return true
99
+ const before = text.slice(0, start)
100
+ const rest = text.slice(end)
101
+ if (KO_ORDINAL_BEFORE.test(before) || KO_ORDINAL_AFTER.test(rest)) return true
102
+ const left = /(?<![A-Za-z])[A-Za-z]+$/.exec(before)?.[0] ?? /\p{L}+$/u.exec(before)?.[0]
103
+ const right = LETTERS_AFTER.exec(rest)?.[0]
104
+ const measure = right ? !/^\d/.test(rest.slice(right.length)) && isUnit(right) : SYMBOL_AFTER.test(rest)
105
+ if (measure && !(right && /^[A-Za-z]/.test(right) && /[A-Za-z]$/.test(before))) return false
106
+ if (left) return !isCurrency(left) && !(!right && isMultiplierBefore(before, left, run))
107
+ if (right) return true
108
+ if (before.endsWith("#") || /\p{L}/u.test(/([\p{L}\p{N}]+)[-._/]$/u.exec(before)?.[1] ?? "")) return true
109
+ const joined = /^-(\p{L}[\p{L}\p{N}]*)/u.exec(rest)?.[1]
110
+ return joined !== undefined && !isUnit(joined)
111
+ }
112
+
113
+ /** A fact element is bound by its ledger row, not traced here; it reads as one opaque token. */
114
+ const isFact = (node) => node.attrs?.["data-fact"] !== undefined || node.attrs?.["data-translates"] !== undefined || node.tag === "q" || node.tag === "blockquote"
115
+
116
+ function lines(node, out = [""]) {
117
+ for (const child of node.children ?? []) {
118
+ if (child.tag === "#text") out[out.length - 1] += child.text
119
+ else if (SKIP.has(child.tag)) continue
120
+ else if (isFact(child)) out[out.length - 1] += ` ${FACT} `
121
+ else if (child.tag === "cite" || child.tag === "i" || child.tag === "em" || child.tag === "a") {
122
+ const [open, close] = child.tag === "cite" ? [CITE_OPEN, CITE_CLOSE] : child.tag === "a" ? [A_OPEN, A_CLOSE] : [EM_OPEN, EM_CLOSE]
123
+ out[out.length - 1] += open
124
+ lines(child, out)
125
+ out[out.length - 1] += close
126
+ if (child.tag === "a" && /^https?:\/\//i.test(child.attrs?.href ?? "")) out[out.length - 1] += ` ${LINK} `
127
+ } else {
128
+ if (BREAK.has(child.tag)) out.push("")
129
+ lines(child, out)
130
+ if (BREAK.has(child.tag)) out.push("")
131
+ }
132
+ }
133
+ return out
134
+ }
135
+
136
+ /** Spans [start, end) of the citation fields in `text`; an <i>/<em> run is a title only right before a URL, a link, or a date. */
137
+ function containerSpans(text) {
138
+ const spans = []
139
+ for (const re of CONTAINERS) for (const match of text.matchAll(re)) spans.push([match.index, match.index + match[0].length])
140
+ for (const match of text.matchAll(new RegExp(`${EM_OPEN}[^${EM_CLOSE}]*${EM_CLOSE}`, "g"))) {
141
+ const after = text.slice(match.index + match[0].length).replace(/^[\s,.:;()\u2013-]*/, "")
142
+ if (after.startsWith(LINK) || after.startsWith(A_OPEN) || /^(?:https?:\/\/|www\.)/i.test(after) || DATE_AHEAD.test(after)) spans.push([match.index, match.index + match[0].length])
143
+ }
144
+ return spans
145
+ }
146
+
147
+ const typedSpans = (text, list) => list.flatMap((re) => [...text.matchAll(re)].map((match) => [match.index, match.index + match[0].length]))
148
+
149
+ const fieldSpans = (text) => [...containerSpans(text), ...typedSpans(text, TYPED)]
150
+
151
+ const inside = (spans, start, end) => spans.some(([s, e]) => start >= s && end <= e)
152
+
153
+ /** Sentences of one line; a period after an abbreviation, an initial, or inside a field never ends one. */
154
+ function sentences(line) {
155
+ const spans = fieldSpans(line)
156
+ const out = []
157
+ let start = 0
158
+ for (const match of line.matchAll(/[.!?](?=\s+\S|$)/g)) {
159
+ const end = match.index + 1
160
+ if (NOT_END.test(line.slice(start, end)) || inside(spans, match.index, end)) continue
161
+ out.push(line.slice(start, end))
162
+ start = end
163
+ }
164
+ out.push(line.slice(start))
165
+ return out.map((text) => text.replace(/\s+/g, " ").trim()).filter(Boolean)
166
+ }
167
+
168
+ /** Each digit run of `text` with its class: "citation-field" (a container or a marker), "date", "name", or "numeral". */
169
+ export function numeralKinds(text) {
170
+ const [containers, markers, dates] = [containerSpans(text), typedSpans(text, MARKERS), typedSpans(text, DATES)]
171
+ return [...text.matchAll(NUMERAL)].map((match) => {
172
+ const [start, end] = [match.index, match.index + match[0].length]
173
+ const kind = inside(containers, start, end) || inside(markers, start, end) ? "citation-field" : inside(dates, start, end) ? "date" : isName(text, start, end) ? "name" : "numeral"
174
+ return { text: match[0], kind }
175
+ })
176
+ }
177
+
178
+ /** The sentence holds a numeral that is not a citation-field number, so the gates check it. */
179
+ export const isClaimSegment = (text) => numeralKinds(text).some(({ kind }) => kind === "numeral")
180
+
181
+ /** A checked sentence as the gates read it: container fields (URL, DOI, title, tag) removed, every other number kept. */
182
+ function plain(text) {
183
+ let out = ""
184
+ let at = 0
185
+ for (const [start, end] of containerSpans(text).sort((a, b) => a[0] - b[0])) {
186
+ if (end <= at) continue
187
+ out += `${text.slice(at, Math.max(at, start))} `
188
+ at = end
189
+ }
190
+ return `${out}${text.slice(at)}`.replace(/[\u0002-\u0008]/g, " ").replace(/\s+/g, " ").trim()
191
+ }
192
+ const lineClaims = (line) => sentences(line).filter(isClaimSegment).map(plain)
193
+
194
+ /** The checked sentences of an element under Sources ([] when every numeral is a citation field). */
195
+ export function sourcesClaims(node) {
196
+ if (/^h[1-6]$/.test(node.tag)) return []
197
+ return lines(node).flatMap(lineClaims)
198
+ }
199
+
200
+ /** The checked sentences of loose text (a text node owned by a container element rather than a prose element). */
201
+ export function textClaims(text) {
202
+ return lines({ children: [{ tag: "#text", text }] }).flatMap(lineClaims)
203
+ }
@@ -21,6 +21,9 @@ const TABLE = [
21
21
  ["s", "time", "1", "open", ["sec", "secs", "second", "seconds", "\uCD08"]], // cho
22
22
  ["min", "time", "60", "closed", ["mins", "minute", "minutes", "\uBD84"]], // bun
23
23
  ["h", "time", "3600", "closed", ["hr", "hrs", "hour", "hours", "\uC2DC\uAC04"]], // sigan
24
+ // Months and years are a calendar dimension of their own (a year is no fixed number of seconds).
25
+ ["mo", "calendar", "1", "l", []], // gaewol
26
+ ["yr", "calendar", "12", "closed", ["yrs"]], // nyeon
24
27
  ["B", "data", "1", "open", ["byte", "bytes", "\uBC14\uC774\uD2B8"]], // baiteu
25
28
  ["KB", "data", "1000", "open", ["\uD0AC\uB85C\uBC14\uC774\uD2B8"]],
26
29
  ["MB", "data", "1000000", "open", ["\uBA54\uAC00\uBC14\uC774\uD2B8"]],
@@ -28,13 +31,13 @@ const TABLE = [
28
31
  ["TB", "data", "1000000000000", "open", ["\uD14C\uB77C\uBC14\uC774\uD2B8"]],
29
32
  ["C", "temperature", "1", "open", ["\u00B0C", "\u2103", "celsius", "\uC12D\uC528"], "0"], // do
30
33
  ["F", "temperature", "5/9", "open", ["\u00B0F", "\u2109", "fahrenheit", "\uD654\uC528"], "-160/9"], // do
31
- ["%", "percent", "1", "open", ["percent", "\uD37C\uC13C\uD2B8", "\uD504\uB85C"]], // peosenteu
34
+ ["%", "percent", "1", "open", ["percent", "pct", "\uD37C\uC13C\uD2B8", "\uD504\uB85C"]], // peosenteu
32
35
  ["\u2030", "percent", "0.1", "l", ["permille", "\uD37C\uBC00"]], // peomil
33
- ["%p", "percentage-point", "1", "open", ["pp", "%pt", "percentage point", "percentage points", "\uD37C\uC13C\uD2B8\uD3EC\uC778\uD2B8", "%\uD3EC\uC778\uD2B8"]],
36
+ ["%p", "percentage-point", "1", "open", ["pp", "%pt", "percentage point", "percentage points", "percent point", "percent points", "% point", "% points", "\uD37C\uC13C\uD2B8\uD3EC\uC778\uD2B8", "%\uD3EC\uC778\uD2B8"]],
34
37
  ["x", "multiple", "1", "open", ["\u00D7", "times", "\uBC30"]], // bae
35
38
  ["e3", "scale", "1000", "closed", ["k", "thousand", "\uCC9C"]], // cheon
36
39
  ["e4", "scale", "10000", "closed", ["\uB9CC"]], // man
37
- ["e6", "scale", "1000000", "closed", ["M", "million", "\uBC31\uB9CC"]], // baengman
40
+ ["e6", "scale", "1000000", "closed", ["M", "mn", "million", "\uBC31\uB9CC"]], // baengman
38
41
  ["e8", "scale", "100000000", "closed", ["\uC5B5"]], // eok
39
42
  ["e9", "scale", "1000000000", "closed", ["bn", "billion", "\uC2ED\uC5B5"]], // sibeok
40
43
  ["e12", "scale", "1000000000000", "open", ["trillion", "\uC870"]], // jo
@@ -59,6 +62,8 @@ const ENTRIES = new Map()
59
62
  const EXACT = new Map()
60
63
  /** Case-insensitive keys: multi-letter ASCII aliases only, so m/M and B/b never collide. */
61
64
  const WORD = new Map()
65
+ /** Aliases written with their own spacing ("percentage points"), by lower-case text. */
66
+ const SPACED = new Map()
62
67
  for (const [token, dimension, factor, reading, aliases, offset] of TABLE) {
63
68
  const entry = Object.freeze({ token, dimension, factor: parse(factor), offset: parse(offset ?? "0"), reading })
64
69
  ENTRIES.set(token, entry)
@@ -66,6 +71,7 @@ for (const [token, dimension, factor, reading, aliases, offset] of TABLE) {
66
71
  const key = squash(alias)
67
72
  EXACT.set(key, entry)
68
73
  if (alias !== token && key.length > 2 && /^[a-z]+$/.test(key) && !WORD.has(key.toLowerCase())) WORD.set(key.toLowerCase(), entry)
74
+ if (/\s/.test(alias)) SPACED.set(alias.toLowerCase(), entry)
69
75
  }
70
76
  }
71
77
 
@@ -81,6 +87,11 @@ export function normalizeUnit(raw) {
81
87
  return entryOf(raw)?.token ?? null
82
88
  }
83
89
 
90
+ /** Canonical token for a multi-word unit written with its alias's own spacing, or null (never a squashed join). */
91
+ export function spacedAliasUnit(raw) {
92
+ return SPACED.get(String(raw).trim().replace(/\s+/g, " ").toLowerCase())?.token ?? null
93
+ }
94
+
84
95
  /** Dimension of a unit ("length", "currency:KRW", ...), or null. */
85
96
  export function dimensionOf(token) {
86
97
  return entryOf(token)?.dimension ?? null
@@ -1,5 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
- import { mkdtempSync, rmSync } from "node:fs";
2
+ import { createHash } from "node:crypto";
3
+ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
3
4
  import { readFile } from "node:fs/promises";
4
5
  import { tmpdir } from "node:os";
5
6
  import { join } from "node:path";
@@ -59,9 +60,11 @@ test("payload-equivalence: #given telemetry-core and component shim #when sessio
59
60
  stateDir,
60
61
  transportFactory: componentRecorder.factory,
61
62
  });
62
- const expectedDistinctId = componentPostHog.getPostHogDistinctIdForTesting(createOsProvider());
63
- componentClient.trackActive(expectedDistinctId, "session_start");
63
+ componentClient.trackActive("session_start");
64
64
  await componentClient.shutdown();
65
+ const installId = readFileSync(join(stateDir, "install-id"), "utf8").trim();
66
+ assert.match(installId, /^[0-9a-f]{64}$/);
67
+ const expectedDistinctId = createHash("sha256").update(installId).digest("hex");
65
68
 
66
69
  assert.deepEqual(componentMessages, [
67
70
  {
@@ -104,6 +107,5 @@ test("payload-equivalence: #given built telemetry CLI #when inspected #then tele
104
107
  assert.doesNotMatch(bundle, new RegExp(`from ["']${TELEMETRY_CORE_PACKAGE}`));
105
108
  assert.doesNotMatch(bundle, new RegExp(`import\\(["']${TELEMETRY_CORE_PACKAGE}`));
106
109
  assert.match(bundle, /omo_codex_daily_active/);
107
- assert.match(bundle, /omo-codex:/);
108
110
  assert.match(bundle, /posthog-activity\.json/);
109
111
  });
@@ -146,10 +146,11 @@ test("#given shared skill source tests #when aggregate Codex skills are synced #
146
146
  for (const file of await listSkillFiles(join(aggregateSkillsRoot, skillName))) {
147
147
  const normalized = file.replaceAll("\\", "/");
148
148
  const segments = normalized.split("/");
149
- const scriptsIndex = segments.lastIndexOf("scripts");
150
- const hasPythonTestDir = scriptsIndex !== -1 && segments[scriptsIndex + 1] === "tests";
149
+ const hasNestedTestDir = segments
150
+ .slice(0, -1)
151
+ .some((segment, index) => /^(?:tests?|__tests__|.+_test)$/.test(segment) && !(index === 0 && (segment === "test" || segment === "tests")));
151
152
  const isSourceMetadata = normalized === ".gitignore" || normalized === ".npmignore" || normalized === "pyrightconfig.json";
152
- if (normalized.endsWith(".test.ts") || hasPythonTestDir || segments.includes("__pycache__") || normalized.endsWith(".pyc") || isSourceMetadata) {
153
+ if (normalized.endsWith(".test.ts") || hasNestedTestDir || segments.includes("__pycache__") || normalized.endsWith(".pyc") || isSourceMetadata) {
153
154
  forbiddenFiles.push(`${skillName}/${normalized}`);
154
155
  }
155
156
  }