oh-my-opencode 5.1.47 → 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.
- package/dist/agents/refusal-rule.d.ts +7 -0
- package/dist/cli/index.js +1194 -752
- package/dist/cli/service-auth/accounts-gate.d.ts +11 -0
- package/dist/cli/service-auth/commands.d.ts +2 -4
- package/dist/cli/service-auth/index.d.ts +1 -1
- package/dist/cli/service-auth/keystore.d.ts +26 -2
- package/dist/cli/service-auth/protocol.d.ts +7 -1
- package/dist/cli/service-auth/session.d.ts +6 -0
- package/dist/cli/service-auth/signed-out-marker.d.ts +11 -0
- package/dist/cli-node/index.js +1194 -752
- package/dist/config/schema/hooks.d.ts +1 -0
- package/dist/create-hooks.d.ts +1 -0
- package/dist/features/boulder-state/storage.d.ts +1 -1
- package/dist/features/boulder-state/types.d.ts +1 -1
- package/dist/hooks/atlas/idle-continuation.d.ts +2 -1
- package/dist/hooks/atlas/types.d.ts +9 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/momus-review-persistence/hook.d.ts +19 -0
- package/dist/hooks/momus-review-persistence/index.d.ts +1 -0
- package/dist/hooks/momus-review-persistence/review-resolution.d.ts +20 -0
- package/dist/hooks/prometheus-md-only/constants.d.ts +2 -0
- package/dist/hooks/prometheus-md-only/fixtures/denial-log.d.ts +1 -0
- package/dist/hooks/prometheus-md-only/fixtures/guarded-hashline.d.ts +1 -0
- package/dist/hooks/prometheus-md-only/fixtures/invoke-before.d.ts +7 -0
- package/dist/hooks/prometheus-md-only/fixtures/opencode-patch-parser.d.ts +2 -0
- package/dist/hooks/prometheus-md-only/hook.d.ts +5 -1
- package/dist/hooks/prometheus-md-only/path-policy.d.ts +2 -9
- package/dist/hooks/prometheus-md-only/vendor/opencode-patch-parser.d.ts +22 -0
- package/dist/index.js +2322 -1045
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -0
- package/dist/plugin/hooks/create-tool-guard-hooks.d.ts +2 -1
- package/dist/shared/posthog-activity-state.d.ts +1 -0
- package/dist/shared/posthog.d.ts +1 -2
- package/dist/shared/resolve-file-write-path.d.ts +6 -0
- package/dist/skills/ast-grep/README.md +1 -1
- package/dist/skills/ast-grep/install.ps1 +5 -3
- package/dist/skills/ast-grep/install.sh +4 -5
- package/dist/skills/ast-grep/references/install.md +2 -2
- package/dist/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
- package/dist/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/dist/skills/coding-agent-sessions/SKILL.md +11 -11
- package/dist/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/dist/skills/coding-agent-sessions/references/codex.md +4 -4
- package/dist/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/dist/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/dist/skills/frontend/ATTRIBUTION.md +1 -1
- package/dist/skills/frontend/SKILL.md +34 -12
- package/dist/skills/frontend/references/design/README.md +2 -2
- package/dist/skills/frontend/references/design/_INDEX.md +2 -1
- package/dist/skills/frontend/references/design/component-catalogs.md +14 -10
- package/dist/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/dist/skills/frontend/references/design/grounding.md +29 -0
- package/dist/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/dist/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/dist/skills/ultimate-browsing/SKILL.md +5 -4
- package/dist/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/dist/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/dist/skills/ultimate-browsing/engine/summary.py +1 -1
- package/dist/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/dist/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/dist/skills/ulw-research/AGENTS.md +8 -4
- package/dist/skills/ulw-research/SKILL.md +7 -259
- package/dist/skills/ulw-research/references/deliverable-phase.md +68 -24
- package/dist/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/dist/skills/ulw-research/references/fact-ledger.md +42 -8
- package/dist/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/dist/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/dist/skills/ulw-research/references/report-gates.md +14 -6
- package/dist/skills/ulw-research/references/research-collection.md +195 -0
- package/dist/skills/ulw-research/references/research-delivery.md +123 -0
- package/dist/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/dist/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
- package/dist/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
- package/dist/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
- package/dist/skills/ulw-research/scripts/contracts.mjs +16 -15
- package/dist/skills/ulw-research/scripts/contracts.test.ts +6 -109
- package/dist/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
- package/dist/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
- package/dist/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
- package/dist/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/dist/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
- package/dist/skills/ulw-research/scripts/copy-returns-fidelity-policy.test.ts +156 -0
- package/dist/skills/ulw-research/scripts/copy-returns-polish.test.ts +10 -9
- package/dist/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/dist/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/dist/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
- package/dist/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
- package/dist/skills/ulw-research/scripts/entities.mjs +13 -4
- package/dist/skills/ulw-research/scripts/fact-commands-fill.mjs +96 -0
- package/dist/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
- package/dist/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
- package/dist/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
- package/dist/skills/ulw-research/scripts/fact-commands.mjs +40 -62
- package/dist/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
- package/dist/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/dist/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
- package/dist/skills/ulw-research/scripts/fact-fill.mjs +9 -6
- package/dist/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/dist/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
- package/dist/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
- package/dist/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/dist/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
- package/dist/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
- package/dist/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
- package/dist/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/dist/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/dist/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/dist/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/dist/skills/ulw-research/scripts/fidelity-policy.mjs +183 -0
- package/dist/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/dist/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/dist/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/dist/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/dist/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/dist/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/dist/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
- package/dist/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/dist/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
- package/dist/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/dist/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/dist/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/dist/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/dist/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
- package/dist/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/dist/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
- package/dist/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/dist/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/dist/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
- package/dist/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/dist/skills/ulw-research/scripts/numerals.test.ts +3 -0
- package/dist/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/dist/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
- package/dist/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/dist/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
- package/dist/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/dist/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/dist/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
- package/dist/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
- package/dist/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/dist/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/dist/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
- package/dist/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
- package/dist/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
- package/dist/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
- package/dist/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
- package/dist/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/dist/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/dist/skills/ulw-research/scripts/report-tools.test.ts +6 -560
- package/dist/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
- package/dist/skills/ulw-research/scripts/source-commands.mjs +87 -29
- package/dist/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
- package/dist/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/dist/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/dist/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
- package/dist/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/dist/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/dist/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
- package/dist/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
- package/dist/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
- package/dist/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
- package/dist/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
- package/dist/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
- package/dist/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
- package/dist/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
- package/dist/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
- package/dist/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
- package/dist/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
- package/dist/skills/ulw-research/scripts/units.mjs +14 -3
- package/dist/skills/ulw-research/scripts/units.test.ts +10 -0
- package/dist/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
- package/dist/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
- package/dist/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
- package/dist/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
- package/dist/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
- package/dist/tools/delegate-task/executor-types.d.ts +1 -0
- package/dist/tools/delegate-task/sync-session-existence.d.ts +4 -0
- package/dist/tools/delegate-task/types.d.ts +1 -0
- package/dist/tools/skill/description-formatter.d.ts +2 -1
- package/dist/tools/skill/types.d.ts +6 -0
- package/dist/tui.js +131 -33
- package/package.json +18 -15
- package/packages/git-bash-mcp/package.json +1 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/README.md +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/SKILL.md +7 -7
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +2 -2
- package/packages/omo-codex/plugin/components/telemetry/README.md +2 -2
- package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +52 -15
- package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +52 -19
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/src/codex-hook.ts +2 -9
- package/packages/omo-codex/plugin/components/telemetry/src/posthog.ts +4 -13
- package/packages/omo-codex/plugin/components/telemetry/src/product-identity.ts +0 -2
- package/packages/omo-codex/plugin/components/telemetry/test/codex-hook-diagnostics.test.ts +0 -3
- package/packages/omo-codex/plugin/components/telemetry/test/codex-hook.test.ts +3 -8
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +3 -0
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +16 -16
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/shared/package.json +1 -1
- package/packages/omo-codex/plugin/skills/ast-grep/README.md +1 -1
- package/packages/omo-codex/plugin/skills/ast-grep/install.ps1 +5 -3
- package/packages/omo-codex/plugin/skills/ast-grep/install.sh +4 -5
- package/packages/omo-codex/plugin/skills/ast-grep/references/install.md +2 -2
- package/packages/omo-codex/plugin/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +11 -11
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/codex.md +4 -4
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +1 -1
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +34 -12
- package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +2 -2
- package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +2 -1
- package/packages/omo-codex/plugin/skills/frontend/references/design/component-catalogs.md +14 -10
- package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/packages/omo-codex/plugin/skills/frontend/references/design/grounding.md +29 -0
- package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +7 -7
- package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +5 -4
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/summary.py +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/packages/omo-codex/plugin/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +3 -0
- package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +8 -4
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +7 -259
- package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +68 -24
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +42 -8
- package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +14 -6
- package/packages/omo-codex/plugin/skills/ulw-research/references/research-collection.md +195 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/research-delivery.md +123 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +16 -15
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/entities.mjs +13 -4
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-fill.mjs +96 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +40 -62
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +9 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-policy.mjs +183 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +87 -29
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +14 -3
- package/packages/omo-codex/plugin/test/payload-equivalence.test.mjs +6 -4
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +4 -3
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +338 -237
- package/packages/prompts-core/prompts/ultrawork/codex.md +3 -0
- package/packages/shared-skills/skill-source-filter.mjs +6 -2
- package/packages/shared-skills/skills/ast-grep/README.md +1 -1
- package/packages/shared-skills/skills/ast-grep/install.ps1 +5 -3
- package/packages/shared-skills/skills/ast-grep/install.sh +4 -5
- package/packages/shared-skills/skills/ast-grep/references/install.md +2 -2
- package/packages/shared-skills/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/shared-skills/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +11 -11
- package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/packages/shared-skills/skills/coding-agent-sessions/references/codex.md +4 -4
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +1 -1
- package/packages/shared-skills/skills/frontend/SKILL.md +34 -12
- package/packages/shared-skills/skills/frontend/references/design/README.md +2 -2
- package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +2 -1
- package/packages/shared-skills/skills/frontend/references/design/component-catalogs.md +14 -10
- package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/packages/shared-skills/skills/frontend/references/design/grounding.md +29 -0
- package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +5 -4
- package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/packages/shared-skills/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/packages/shared-skills/skills/ultimate-browsing/engine/summary.py +1 -1
- package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/packages/shared-skills/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/packages/shared-skills/skills/ulw-research/AGENTS.md +8 -4
- package/packages/shared-skills/skills/ulw-research/SKILL.md +7 -259
- package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +68 -24
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +42 -8
- package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/packages/shared-skills/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/packages/shared-skills/skills/ulw-research/references/report-gates.md +14 -6
- package/packages/shared-skills/skills/ulw-research/references/research-collection.md +195 -0
- package/packages/shared-skills/skills/ulw-research/references/research-delivery.md +123 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
- package/packages/shared-skills/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
- package/packages/shared-skills/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +16 -15
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +6 -109
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
- package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-fidelity-policy.test.ts +156 -0
- package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-polish.test.ts +10 -9
- package/packages/shared-skills/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/packages/shared-skills/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
- package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
- package/packages/shared-skills/skills/ulw-research/scripts/entities.mjs +13 -4
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-fill.mjs +96 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +40 -62
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
- package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +9 -6
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-policy.mjs +183 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
- package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/packages/shared-skills/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
- package/packages/shared-skills/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +3 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/packages/shared-skills/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
- package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
- package/packages/shared-skills/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +6 -560
- package/packages/shared-skills/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +87 -29
- package/packages/shared-skills/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
- package/packages/shared-skills/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
- package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +14 -3
- package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +10 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/amp_redirect_stub.html +0 -7
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/search_interstitial.html +0 -19
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_available.json +0 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_snapshot.html +0 -1128
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_fetch_chain.py +0 -77
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +0 -86
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +0 -279
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate.py +0 -252
- 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
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
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 {
|
|
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
|
|
33
|
-
* texts with "\n"
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
131
|
-
|
|
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
|
|
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 }
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
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
|
|
151
|
-
const
|
|
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
|
|
154
|
-
if (
|
|
155
|
-
throw new Error(`snapshot_exists: ${sourceId} already holds
|
|
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(
|
|
159
|
-
|
|
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 ||
|
|
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
|
-
|
|
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
|
}
|
|
@@ -40,13 +40,14 @@ describe("snapshotFromHtml", () => {
|
|
|
40
40
|
})
|
|
41
41
|
|
|
42
42
|
describe("writeSnapshot", () => {
|
|
43
|
-
test("#given html #when written #then
|
|
43
|
+
test("#given html #when written #then raw.bin preserves the body and sha256 covers its bytes", () => {
|
|
44
44
|
const sources = tempDir()
|
|
45
45
|
const result = writeSnapshot(sources, "S4", { url: "https://example.test/bridge", html: S4_HTML, extractor: "tool:source-snapshot@1", observed_at: "2026-10-23T00:00:00Z" })
|
|
46
46
|
expect(result.dir).toBe(join(sources, "S4"))
|
|
47
|
-
for (const name of ["text.txt", "source.html", "meta.json", "sentences.jsonl", "acronyms.json"]) expect(existsSync(join(result.dir, name))).toBe(true)
|
|
48
|
-
const
|
|
49
|
-
expect(
|
|
47
|
+
for (const name of ["raw.bin", "text.txt", "source.html", "meta.json", "sentences.jsonl", "acronyms.json"]) expect(existsSync(join(result.dir, name))).toBe(true)
|
|
48
|
+
const raw = readFileSync(join(result.dir, "raw.bin"))
|
|
49
|
+
expect(raw.toString("utf8")).toBe(S4_HTML)
|
|
50
|
+
expect(result.sha256).toBe(createHash("sha256").update(raw).digest("hex"))
|
|
50
51
|
expect(readFileSync(join(result.dir, "source.html"), "utf8")).toBe(S4_HTML)
|
|
51
52
|
const meta = JSON.parse(readFileSync(join(result.dir, "meta.json"), "utf8"))
|
|
52
53
|
expect(meta).toEqual({
|
|
@@ -58,6 +59,8 @@ describe("writeSnapshot", () => {
|
|
|
58
59
|
revision: null,
|
|
59
60
|
kind: "page",
|
|
60
61
|
provenance: "unknown",
|
|
62
|
+
text_source: "html",
|
|
63
|
+
charset: "utf-8",
|
|
61
64
|
})
|
|
62
65
|
})
|
|
63
66
|
|
|
@@ -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
|
+
}
|