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.
- 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 -316
- package/dist/skills/ulw-research/references/deliverable-phase.md +23 -21
- 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 +12 -5
- 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 +13 -4
- package/dist/skills/ulw-research/scripts/contracts.test.ts +6 -110
- 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-polish.test.ts +4 -3
- 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 +11 -1
- package/dist/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
- package/dist/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
- package/dist/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
- package/dist/skills/ulw-research/scripts/fact-commands.mjs +35 -16
- 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 +6 -4
- 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 +4 -4
- 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 +5 -800
- package/dist/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
- package/dist/skills/ulw-research/scripts/source-commands.mjs +79 -26
- 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 -316
- package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +23 -21
- 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 +12 -5
- 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 +13 -4
- 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 +11 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +35 -16
- 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 +6 -4
- 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 +4 -4
- 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 +79 -26
- 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 -316
- package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +23 -21
- 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 +12 -5
- 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 +13 -4
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +6 -110
- 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-polish.test.ts +4 -3
- 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 +11 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +35 -16
- 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 +6 -4
- 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 +4 -4
- 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 +5 -800
- 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 +79 -26
- 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
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
|
|
3
|
+
import { extractHtml } from "./html-text-map.mjs"
|
|
4
|
+
|
|
5
|
+
/** {T} is the target sentence: every row runs once with English text and once with Korean text. */
|
|
6
|
+
const TARGET = { en: "Target length is 999 m.", ko: "\ubaa9\ud45c \uae38\uc774\ub294 999 m\uc774\ub2e4." }
|
|
7
|
+
const CONTROL = "Control length is 410 m."
|
|
8
|
+
|
|
9
|
+
type Row = { form: string; html: string; expected: "hidden" | "visible" }
|
|
10
|
+
|
|
11
|
+
const css = (sheet: string, element = '<p class="x">{T}</p>') => `<style>${sheet}</style>${element}`
|
|
12
|
+
|
|
13
|
+
const ROWS: Row[] = [
|
|
14
|
+
// conditional and grouping at-rules: only an unconditional rule hides; a conditional one never does
|
|
15
|
+
{ form: "@media (max-width:600px)", html: css("@media (max-width:600px){.x{display:none}}"), expected: "visible" },
|
|
16
|
+
{ form: "@media print", html: css("@media print{.x{display:none}}"), expected: "visible" },
|
|
17
|
+
{ form: "@media screen and (min-width:0)", html: css("@media screen and (min-width: 0){.x{display:none}}"), expected: "visible" },
|
|
18
|
+
{ form: "@media not print", html: css("@media not print{.x{display:none}}"), expected: "visible" },
|
|
19
|
+
{ form: "@media all", html: css("@media all{.x{display:none}}"), expected: "hidden" },
|
|
20
|
+
{ form: "@media all and (min-width:1px)", html: css("@media all and (min-width:1px){.x{display:none}}"), expected: "visible" },
|
|
21
|
+
{ form: "@supports (display:grid)", html: css("@supports (display:grid){.x{display:none}}"), expected: "visible" },
|
|
22
|
+
{ form: "@container (min-width:400px)", html: css("@container (min-width: 400px){.x{display:none}}"), expected: "visible" },
|
|
23
|
+
{ form: "@scope (.card)", html: css("@scope (.card){.x{display:none}}", '<div class="card"><p class="x">{T}</p></div>'), expected: "visible" },
|
|
24
|
+
{ form: "@starting-style", html: css("@starting-style{.x{display:none}}"), expected: "visible" },
|
|
25
|
+
{ form: "@document url-prefix()", html: css("@-moz-document url-prefix(){.x{display:none}}"), expected: "visible" },
|
|
26
|
+
{ form: "@layer base (block)", html: css("@layer base{.x{display:none}}"), expected: "hidden" },
|
|
27
|
+
{ form: "@layer statement then block", html: css("@layer a, b; @layer b{.x{display:none}}"), expected: "hidden" },
|
|
28
|
+
{ form: "@layer inside @media", html: css("@media print{@layer a{.x{display:none}}}"), expected: "visible" },
|
|
29
|
+
{ form: "@media inside @layer", html: css("@layer a{@media print{.x{display:none}}}"), expected: "visible" },
|
|
30
|
+
{ form: "@media inside @supports", html: css("@supports (display:grid){@media (min-width:1px){.x{display:none}}}"), expected: "visible" },
|
|
31
|
+
{ form: "@media nested in a style rule", html: css(".x{color:red;@media print{display:none}}"), expected: "visible" },
|
|
32
|
+
{ form: "@import before a rule", html: css('@import url("print.css") print; .x{display:none}'), expected: "hidden" },
|
|
33
|
+
{ form: "@font-face before a rule", html: css("@font-face{font-family:F;src:url(f.woff2)} .x{display:none}"), expected: "hidden" },
|
|
34
|
+
{ form: "@keyframes hiding steps", html: css("@keyframes k{from{visibility:hidden}to{display:none}} .x{animation:k 1s}"), expected: "visible" },
|
|
35
|
+
{ form: "@page", html: css("@page{display:none} .y{display:none}"), expected: "visible" },
|
|
36
|
+
{ form: "CSS nesting (combinator, not evaluated)", html: css(".card{.x{display:none}}", '<div class="card"><p class="x">{T}</p></div>'), expected: "visible" },
|
|
37
|
+
{ form: "conditional rule after an unconditional one", html: css(".x{display:none} @media print{.x{color:red}}"), expected: "hidden" },
|
|
38
|
+
// comments
|
|
39
|
+
{ form: "rule inside a comment", html: css("/* .x{display:none} */"), expected: "visible" },
|
|
40
|
+
{ form: "comment before the declaration", html: css(".x{/* display:block */display:none}"), expected: "hidden" },
|
|
41
|
+
{ form: "comment inside the value", html: css(".x{display:/**/none}"), expected: "hidden" },
|
|
42
|
+
{ form: "HTML comment markers in <style>", html: css("<!-- .x{display:none} -->"), expected: "hidden" },
|
|
43
|
+
{ form: "comment in an inline style", html: '<p style="display:/* hide */none">{T}</p>', expected: "hidden" },
|
|
44
|
+
// selector lists and the cascade
|
|
45
|
+
{ form: "selector list", html: css(".a, .x {display:none}"), expected: "hidden" },
|
|
46
|
+
{ form: "selector list with a compound part", html: css("div p, .x{display:none}"), expected: "hidden" },
|
|
47
|
+
{ form: "compound selector alone (not evaluated)", html: css("p.x{display:none}"), expected: "visible" },
|
|
48
|
+
{ form: "descendant selector alone (not evaluated)", html: css("div .x{display:none}", '<div><p class="x">{T}</p></div>'), expected: "visible" },
|
|
49
|
+
{ form: "attribute selector (not evaluated)", html: css("[data-off]{display:none}", "<p data-off>{T}</p>"), expected: "visible" },
|
|
50
|
+
{ form: "later rule shows again", html: css(".x{display:none} .x{display:block}"), expected: "visible" },
|
|
51
|
+
{ form: "later rule hides", html: css(".x{display:block} .x{display:none}"), expected: "hidden" },
|
|
52
|
+
{ form: "id outranks class", html: css("#y{display:block} .x{display:none}", '<p id="y" class="x">{T}</p>'), expected: "visible" },
|
|
53
|
+
{ form: "class outranks tag", html: css(".x{display:none} p{display:block}"), expected: "hidden" },
|
|
54
|
+
{ form: "responsive show (d-none d-md-block)", html: css(".x{display:none} @media (min-width:768px){.y{display:block}}", '<p class="x y">{T}</p>'), expected: "visible" },
|
|
55
|
+
{ form: "important hide beats a conditional show", html: css(".x{display:none!important} @media (min-width:768px){.y{display:block}}", '<p class="x y">{T}</p>'), expected: "hidden" },
|
|
56
|
+
{ form: "important hide and important conditional show", html: css(".x{display:none!important} @media (min-width:768px){.y{display:block!important}}", '<p class="x y">{T}</p>'), expected: "visible" },
|
|
57
|
+
{ form: "compound show may apply", html: css(".x{display:none} div .x{display:block}", '<div><p class="x">{T}</p></div>'), expected: "visible" },
|
|
58
|
+
{ form: "compound show that cannot match", html: css(".x{display:none} div .z{display:block}", '<div><p class="x">{T}</p></div>'), expected: "hidden" },
|
|
59
|
+
{ form: "layered show loses to unlayered hide", html: css("@layer base{.x{display:block}} .x{display:none}"), expected: "hidden" },
|
|
60
|
+
{ form: "unlayered show beats layered hide", html: css("@layer base{.x{display:none}} p{display:block}"), expected: "visible" },
|
|
61
|
+
{ form: "later layer beats earlier layer", html: css("@layer a, b; @layer b{.x{display:none}} @layer a{#y{display:block}}", '<p id="y" class="x">{T}</p>'), expected: "hidden" },
|
|
62
|
+
// !important against inline styles
|
|
63
|
+
{ form: "important rule beats inline show", html: css(".x{display:none !important}", '<p class="x" style="display:block">{T}</p>'), expected: "hidden" },
|
|
64
|
+
{ form: "inline show beats a normal rule", html: css(".x{display:none}", '<p class="x" style="display:block">{T}</p>'), expected: "visible" },
|
|
65
|
+
{ form: "important show beats inline hide", html: css(".x{display:block !important}", '<p class="x" style="display:none">{T}</p>'), expected: "visible" },
|
|
66
|
+
{ form: "important without a space", html: css(".x{display:none!important}"), expected: "hidden" },
|
|
67
|
+
{ form: "inline important beats important rule", html: css(".x{display:block!important}", '<p class="x" style="display:none!important">{T}</p>'), expected: "hidden" },
|
|
68
|
+
// which properties hide
|
|
69
|
+
{ form: "display:none (rule)", html: css(".x{display:none}"), expected: "hidden" },
|
|
70
|
+
{ form: "visibility:hidden (rule)", html: css(".x{visibility:hidden}"), expected: "hidden" },
|
|
71
|
+
{ form: "visibility:collapse", html: '<p style="visibility:collapse">{T}</p>', expected: "hidden" },
|
|
72
|
+
{ form: "visibility:hidden parent, visible child", html: '<div style="visibility:hidden"><p style="visibility:visible">{T}</p></div>', expected: "visible" },
|
|
73
|
+
{ form: "visibility:hidden parent, plain child", html: '<div style="visibility:hidden"><p>{T}</p></div>', expected: "hidden" },
|
|
74
|
+
{ form: "visibility:hidden parent, child by rule", html: css(".x{visibility:visible}", '<div style="visibility:hidden"><p class="x">{T}</p></div>'), expected: "visible" },
|
|
75
|
+
{ form: "display:none parent, visible child", html: '<div style="display:none"><p style="visibility:visible;display:block">{T}</p></div>', expected: "hidden" },
|
|
76
|
+
{ form: "content-visibility:hidden", html: '<div style="content-visibility:hidden"><p>{T}</p></div>', expected: "hidden" },
|
|
77
|
+
{ form: "display:contents", html: '<p style="display:contents">{T}</p>', expected: "visible" },
|
|
78
|
+
{ form: "opacity:0", html: '<p style="opacity:0">{T}</p>', expected: "visible" },
|
|
79
|
+
{ form: "clip rect (sr-only)", html: '<p style="position:absolute;clip:rect(0 0 0 0);width:1px;height:1px;overflow:hidden">{T}</p>', expected: "visible" },
|
|
80
|
+
{ form: "clip-path inset", html: '<p style="clip-path:inset(50%)">{T}</p>', expected: "visible" },
|
|
81
|
+
{ form: "height:0 with overflow hidden", html: '<p style="height:0;overflow:hidden">{T}</p>', expected: "visible" },
|
|
82
|
+
{ form: "font-size:0", html: '<p style="font-size:0">{T}</p>', expected: "visible" },
|
|
83
|
+
{ form: "off-screen position", html: '<p style="position:absolute;left:-9999px">{T}</p>', expected: "visible" },
|
|
84
|
+
{ form: "transparent color", html: '<p style="color:transparent">{T}</p>', expected: "visible" },
|
|
85
|
+
// the hidden attribute
|
|
86
|
+
{ form: "hidden attribute", html: "<p hidden>{T}</p>", expected: "hidden" },
|
|
87
|
+
{ form: 'hidden="hidden"', html: '<p hidden="hidden">{T}</p>', expected: "hidden" },
|
|
88
|
+
{ form: "HIDDEN in upper case", html: "<p HIDDEN>{T}</p>", expected: "hidden" },
|
|
89
|
+
{ form: 'hidden="until-found"', html: '<p hidden="until-found">{T}</p>', expected: "visible" },
|
|
90
|
+
{ form: "hidden attribute under an author display rule", html: css(".x{display:block}", '<p class="x" hidden>{T}</p>'), expected: "visible" },
|
|
91
|
+
{ form: "hidden attribute under an inline display", html: '<p hidden style="display:block">{T}</p>', expected: "visible" },
|
|
92
|
+
{ form: "descendant of a hidden element", html: "<div hidden><p>{T}</p></div>", expected: "hidden" },
|
|
93
|
+
// aria-hidden
|
|
94
|
+
{ form: 'aria-hidden="true"', html: '<p aria-hidden="true">{T}</p>', expected: "hidden" },
|
|
95
|
+
{ form: 'aria-hidden=" TRUE "', html: '<p aria-hidden=" TRUE ">{T}</p>', expected: "hidden" },
|
|
96
|
+
{ form: 'aria-hidden="false"', html: '<p aria-hidden="false">{T}</p>', expected: "visible" },
|
|
97
|
+
{ form: "descendant of aria-hidden", html: '<div aria-hidden="true"><p>{T}</p></div>', expected: "hidden" },
|
|
98
|
+
// inline style spellings
|
|
99
|
+
{ form: "inline display:none", html: '<p style="display:none">{T}</p>', expected: "hidden" },
|
|
100
|
+
{ form: "inline with spaces and a trailing semicolon", html: '<p style=" display : none ; ">{T}</p>', expected: "hidden" },
|
|
101
|
+
{ form: "inline in upper case", html: '<p style="DISPLAY:NONE">{T}</p>', expected: "hidden" },
|
|
102
|
+
{ form: "inline after another declaration", html: '<p style="color:red;display:none">{T}</p>', expected: "hidden" },
|
|
103
|
+
{ form: "inline: last declaration wins", html: '<p style="display:none;display:block">{T}</p>', expected: "visible" },
|
|
104
|
+
{ form: "inline custom property", html: '<p style="--display:none">{T}</p>', expected: "visible" },
|
|
105
|
+
{ form: "inline var() value", html: '<p style="display:var(--d)">{T}</p>', expected: "visible" },
|
|
106
|
+
{ form: "inline display:none in a string value", html: '<p style="font-family:\'display:none\'">{T}</p>', expected: "visible" },
|
|
107
|
+
// case: tag names are case-insensitive, class and id names are case-sensitive
|
|
108
|
+
{ form: ".Foo vs class=foo", html: css(".Foo{display:none}", '<p class="foo">{T}</p>'), expected: "visible" },
|
|
109
|
+
{ form: ".foo vs class=Foo", html: css(".foo{display:none}", '<p class="Foo">{T}</p>'), expected: "visible" },
|
|
110
|
+
{ form: ".Foo vs class=Foo", html: css(".Foo{display:none}", '<p class="Foo">{T}</p>'), expected: "hidden" },
|
|
111
|
+
{ form: "#Gone vs id=gone", html: css("#Gone{display:none}", '<p id="gone">{T}</p>'), expected: "visible" },
|
|
112
|
+
{ form: "#gone vs id=gone", html: css("#gone{display:none}", '<p id="gone">{T}</p>'), expected: "hidden" },
|
|
113
|
+
{ form: "P{} vs <p>", html: css("P{display:none}", "<p>{T}</p>"), expected: "hidden" },
|
|
114
|
+
{ form: "p{} vs <P>", html: css("p{display:none}", "<P>{T}</P>"), expected: "hidden" },
|
|
115
|
+
{ form: "Hangul class name", html: css(".\uc228\uae40{display:none}", '<p class="\uc228\uae40">{T}</p>'), expected: "hidden" },
|
|
116
|
+
{ form: "upper-case property and value", html: css(".x{DISPLAY:NONE}"), expected: "hidden" },
|
|
117
|
+
// elements whose contents a reader never sees
|
|
118
|
+
{ form: "<noscript>", html: "<noscript><p>{T}</p></noscript>", expected: "hidden" },
|
|
119
|
+
{ form: "<template>", html: "<template><p>{T}</p></template>", expected: "hidden" },
|
|
120
|
+
{ form: "<script>", html: '<script>var note = "{T}"</script>', expected: "hidden" },
|
|
121
|
+
{ form: "<style> content", html: '<style>.x::after{content:"{T}"}</style>', expected: "hidden" },
|
|
122
|
+
{ form: "<title> in <head>", html: "<head><title>{T}</title></head>", expected: "hidden" },
|
|
123
|
+
{ form: "<svg> text", html: "<svg><text>{T}</text></svg>", expected: "hidden" },
|
|
124
|
+
{ form: "closed <dialog>", html: "<dialog><p>{T}</p></dialog>", expected: "hidden" },
|
|
125
|
+
{ form: "open <dialog>", html: "<dialog open><p>{T}</p></dialog>", expected: "visible" },
|
|
126
|
+
{ form: "closed <details> body", html: "<details><summary>More</summary><p>{T}</p></details>", expected: "visible" },
|
|
127
|
+
// which <style> elements apply
|
|
128
|
+
{ form: "<style> inside <template> is inert", html: '<template><style>.x{display:none}</style></template><p class="x">{T}</p>', expected: "visible" },
|
|
129
|
+
{ form: "<style> inside <noscript> applies only without scripts", html: '<noscript><style>.x{display:none}</style></noscript><p class="x">{T}</p>', expected: "visible" },
|
|
130
|
+
{ form: "<style> inside a hidden element still applies", html: '<div hidden><style>.x{display:none}</style></div><p class="x">{T}</p>', expected: "hidden" },
|
|
131
|
+
{ form: "<style> inside <svg> applies to the page", html: '<svg><style>.x{display:none}</style></svg><p class="x">{T}</p>', expected: "hidden" },
|
|
132
|
+
{ form: '<style media="print">', html: '<style media="print">.x{display:none}</style><p class="x">{T}</p>', expected: "visible" },
|
|
133
|
+
{ form: '<style media="all">', html: '<style media="all">.x{display:none}</style><p class="x">{T}</p>', expected: "hidden" },
|
|
134
|
+
{ form: '<style type="text/plain">', html: '<style type="text/plain">.x{display:none}</style><p class="x">{T}</p>', expected: "visible" },
|
|
135
|
+
{ form: "external stylesheet (not evaluated)", html: '<link rel="stylesheet" href="hide.css"><p class="d-none">{T}</p>', expected: "visible" },
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
const CASES = ROWS.flatMap((row) =>
|
|
139
|
+
(["en", "ko"] as const).map((lang) => [`${row.form} [${lang}] -> ${row.expected}`, row.html.replaceAll("{T}", TARGET[lang]), TARGET[lang], row.expected] as const),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
describe("reader-visible text: one row per hiding form, English and Korean", () => {
|
|
143
|
+
test.each(CASES)("#given %s #when extracted #then the target is that", (_name, html, target, expected) => {
|
|
144
|
+
const { text } = extractHtml(`${html}<blockquote>${CONTROL}</blockquote>`)
|
|
145
|
+
expect(text).toContain(CONTROL)
|
|
146
|
+
expect(text.includes(target) ? "visible" : "hidden").toBe(expected)
|
|
147
|
+
})
|
|
148
|
+
})
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// markdown-closers.mjs - the Markdown closer runs a converter writes after a piece of text, read with
|
|
2
|
+
// the CommonMark inline grammar instead of a hand-written pattern. A closer run is any sequence of:
|
|
3
|
+
// an inline link ending `](dest "title")` (the destination either in <...> or bare with balanced
|
|
4
|
+
// parentheses, the optional title in "...", '...' or (...), backslash escapes inside both), a full or
|
|
5
|
+
// collapsed reference ending `][ref]` / `][]`, a whole image `` or `![alt][ref]` (its alt
|
|
6
|
+
// text is not context), the `[` that opened a link whose text is all markup (`[](/p)`),
|
|
7
|
+
// an emphasis, strike or code delimiter, a bare `)`, `]` or `>`, and a backslash-escaped closer
|
|
8
|
+
// (`\*`, `\_`, `\]`, `\)`, `\~`, a backtick), which stands for the raw character and is skipped like
|
|
9
|
+
// it. An escaped opener (`\[`, `\(`) is a real character and ends the run. Shared by the conversion
|
|
10
|
+
// probe's sign reading and its link-syntax spans. Pure; no imports.
|
|
11
|
+
|
|
12
|
+
const SIMPLE = new Set(["*", "_", "~", "`", ")", "]", ">"])
|
|
13
|
+
const BREAK = "\u0001"
|
|
14
|
+
|
|
15
|
+
/** Whether the character at `index` is backslash-escaped (an odd run of backslashes before it). */
|
|
16
|
+
export function escapedAt(text, index) {
|
|
17
|
+
let count = 0
|
|
18
|
+
for (let i = index - 1; i >= 0 && text[i] === "\\"; i--) count++
|
|
19
|
+
return count % 2 === 1
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** The end (exclusive) of an inline link target `dest "title")` that starts right after its "(", or -1. */
|
|
23
|
+
export function inlineTargetEnd(text, from) {
|
|
24
|
+
let i = from
|
|
25
|
+
const space = () => {
|
|
26
|
+
while (text[i] === " " || text[i] === "\t") i++
|
|
27
|
+
}
|
|
28
|
+
space()
|
|
29
|
+
if (text[i] === "<") {
|
|
30
|
+
for (i++; i < text.length && text[i] !== ">"; i++) {
|
|
31
|
+
if (text[i] === "\\") i++
|
|
32
|
+
else if (text[i] === "<" || text[i] === BREAK) return -1
|
|
33
|
+
}
|
|
34
|
+
if (text[i++] !== ">") return -1
|
|
35
|
+
} else {
|
|
36
|
+
let depth = 0
|
|
37
|
+
for (; i < text.length; i++) {
|
|
38
|
+
const ch = text[i]
|
|
39
|
+
if (ch === "\\") i++
|
|
40
|
+
else if (ch === "(") depth++
|
|
41
|
+
else if (ch === ")" && depth === 0) break
|
|
42
|
+
else if (ch === ")") depth--
|
|
43
|
+
else if (ch === " " || ch === "\t" || ch === BREAK) break
|
|
44
|
+
}
|
|
45
|
+
if (depth !== 0) return -1
|
|
46
|
+
}
|
|
47
|
+
const destEnd = i
|
|
48
|
+
space()
|
|
49
|
+
const open = text[i]
|
|
50
|
+
if (i > destEnd && (open === '"' || open === "'" || open === "(")) {
|
|
51
|
+
const close = open === "(" ? ")" : open
|
|
52
|
+
for (i++; i < text.length && text[i] !== close; i++) {
|
|
53
|
+
if (text[i] === "\\") i++
|
|
54
|
+
else if (text[i] === BREAK || (open === "(" && text[i] === "(")) return -1
|
|
55
|
+
}
|
|
56
|
+
if (text[i++] !== close) return -1
|
|
57
|
+
space()
|
|
58
|
+
}
|
|
59
|
+
return text[i] === ")" ? i + 1 : -1
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** The unescaped "[" that the unescaped "]" at `close` closes (brackets balance), or -1. */
|
|
63
|
+
function openerOf(text, close) {
|
|
64
|
+
let depth = 0
|
|
65
|
+
for (let i = close; i >= 0 && text[i] !== BREAK; i--) {
|
|
66
|
+
if ((text[i] !== "[" && text[i] !== "]") || escapedAt(text, i)) continue
|
|
67
|
+
depth += text[i] === "]" ? 1 : -1
|
|
68
|
+
if (depth === 0) return i
|
|
69
|
+
}
|
|
70
|
+
return -1
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The "](" positions whose inline target ends exactly at `end`, leftmost first (so a title holding "](" is not cut). */
|
|
74
|
+
function inlineLinkClose(text, end) {
|
|
75
|
+
const from = Math.max(0, text.lastIndexOf(BREAK, end - 1))
|
|
76
|
+
for (let k = text.indexOf("](", from); k !== -1 && k < end; k = text.indexOf("](", k + 1)) {
|
|
77
|
+
if (!escapedAt(text, k) && inlineTargetEnd(text, k + 2) === end) return k
|
|
78
|
+
}
|
|
79
|
+
return -1
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Where the link or image tail ending at `end` starts, and whether its "[" is still open; null when none ends there. */
|
|
83
|
+
function linkTail(text, end) {
|
|
84
|
+
let close = -1
|
|
85
|
+
if (text[end - 1] === ")") close = inlineLinkClose(text, end)
|
|
86
|
+
else if (text[end - 1] === "]" && !escapedAt(text, end - 1)) {
|
|
87
|
+
const ref = openerOf(text, end - 1)
|
|
88
|
+
if (ref > 0 && text[ref - 1] === "]" && !escapedAt(text, ref - 1)) close = ref - 1
|
|
89
|
+
}
|
|
90
|
+
if (close === -1) return null
|
|
91
|
+
const open = openerOf(text, close)
|
|
92
|
+
if (open > 0 && text[open - 1] === "!" && !escapedAt(text, open - 1)) return { start: open - 1, opened: 0 }
|
|
93
|
+
return { start: close, opened: open === -1 ? 0 : 1 }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** `text` without the Markdown closer run it ends with. */
|
|
97
|
+
export function stripClosers(text) {
|
|
98
|
+
let end = text.length
|
|
99
|
+
let owed = 0
|
|
100
|
+
for (;;) {
|
|
101
|
+
const tail = linkTail(text, end)
|
|
102
|
+
if (tail) {
|
|
103
|
+
end = tail.start
|
|
104
|
+
owed += tail.opened
|
|
105
|
+
continue
|
|
106
|
+
}
|
|
107
|
+
const ch = text[end - 1]
|
|
108
|
+
if (SIMPLE.has(ch)) end -= escapedAt(text, end - 1) ? 2 : 1
|
|
109
|
+
else if (ch === "[" && owed > 0 && !escapedAt(text, end - 1)) {
|
|
110
|
+
end--
|
|
111
|
+
owed--
|
|
112
|
+
} else return text.slice(0, end)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** [start, end) spans of every inline link or image with its target (an image from its "!"), for link-syntax cover. */
|
|
117
|
+
export function inlineLinkSpans(text) {
|
|
118
|
+
const spans = []
|
|
119
|
+
for (let k = text.indexOf("]("); k !== -1; k = text.indexOf("](", k + 1)) {
|
|
120
|
+
const end = escapedAt(text, k) ? -1 : inlineTargetEnd(text, k + 2)
|
|
121
|
+
if (end === -1) continue
|
|
122
|
+
const open = openerOf(text, k)
|
|
123
|
+
spans.push([open > 0 && text[open - 1] === "!" ? open - 1 : k, end])
|
|
124
|
+
}
|
|
125
|
+
return spans
|
|
126
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
|
|
3
|
+
import { parseMarkdown } from "./md-lite.mjs"
|
|
4
|
+
import { parseNumeral } from "./numerals.mjs"
|
|
5
|
+
|
|
6
|
+
describe("Markdown list continuation boundaries", () => {
|
|
7
|
+
test("#given a continuation after a list item #when parsed #then a later marker stays paragraph text", () => {
|
|
8
|
+
const text = "1. a\ncontinued\n2. b"
|
|
9
|
+
const { blocks } = parseMarkdown(text)
|
|
10
|
+
expect(blocks.map((block: { kind: string; text: string }) => [block.kind, block.text])).toEqual([
|
|
11
|
+
["list_item", "a"],
|
|
12
|
+
["paragraph", "continued\n2. b"],
|
|
13
|
+
])
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test("#given numbers beside a list continuation #when parsed #then fidelity numerals retain source offsets", () => {
|
|
17
|
+
const text = "1. a 12%\ncontinued\n2. b 34%"
|
|
18
|
+
const { blocks } = parseMarkdown(text)
|
|
19
|
+
const found = blocks.flatMap((block: { text: string; start: number }) => {
|
|
20
|
+
const numerals = []
|
|
21
|
+
for (let numeral = parseNumeral(block.text); numeral; numeral = parseNumeral(block.text, numeral.span[1])) {
|
|
22
|
+
const [start, end] = numeral.span
|
|
23
|
+
numerals.push([numeral.value, text.slice(block.start + start, block.start + end)])
|
|
24
|
+
}
|
|
25
|
+
return numerals
|
|
26
|
+
})
|
|
27
|
+
expect(found).toEqual([["12", "12%"], ["2", "2"], ["34", "34%"]])
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
describe("Markdown closing fence indentation", () => {
|
|
32
|
+
test.each(["```", "~~~"])("#given a four-space %s closing candidate #when parsed #then the code runs to EOF", (fence: string) => {
|
|
33
|
+
const text = `${fence}\ncode 12\n ${fence}\nclaim 34`
|
|
34
|
+
const { blocks, problems } = parseMarkdown(text)
|
|
35
|
+
expect(blocks.map((block: { kind: string; text: string }) => [block.kind, block.text])).toEqual([
|
|
36
|
+
["code", `code 12\n ${fence}\nclaim 34`],
|
|
37
|
+
])
|
|
38
|
+
expect(problems).toEqual([{ code: "unterminated_fence", line: 1, start: 0 }])
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test.each([0, 1, 2, 3])("#given a closing fence indented %s spaces #when parsed #then later prose is outside code", (spaces: number) => {
|
|
42
|
+
const { blocks, problems } = parseMarkdown(`\`\`\`\ncode\n${" ".repeat(spaces)}\`\`\`\nclaim 34`)
|
|
43
|
+
expect(blocks.map((block: { kind: string }) => block.kind)).toEqual(["code", "paragraph"])
|
|
44
|
+
expect(problems).toEqual([])
|
|
45
|
+
})
|
|
46
|
+
})
|
|
@@ -113,7 +113,7 @@ export function parseMarkdown(text) {
|
|
|
113
113
|
const codeStart = Math.min(line.next, source.length)
|
|
114
114
|
let close = lines.length
|
|
115
115
|
for (let k = i + 1; k < lines.length; k++) {
|
|
116
|
-
const candidate = lines[k].raw.
|
|
116
|
+
const candidate = lines[k].raw.replace(/^ {0,3}/, "").trimEnd()
|
|
117
117
|
if (candidate.startsWith(open[0].repeat(open.length)) && /^(`+|~+)$/.test(candidate)) {
|
|
118
118
|
close = k
|
|
119
119
|
break
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// number-grammar.mjs - the one grouped-number grammar. A grouped number is a 1-3 digit leading
|
|
2
|
+
// group, then one or more 3-digit groups joined by ONE separator type, with no digit right after the
|
|
3
|
+
// last group, then an optional decimal part. "12,3451" is therefore not "12,345" plus "1". A period
|
|
4
|
+
// groups only when the reading is unambiguous: two or more period groups ("1.234.567") or one group
|
|
5
|
+
// before a decimal comma ("1.234,5"); a lone "1.234" is a decimal, as numerals.mjs reads it. Shared by
|
|
6
|
+
// numerals.mjs (comma grouping), the conversion probe (every separator), the value-mode unit check,
|
|
7
|
+
// and the converted-half display check. Pure; no imports.
|
|
8
|
+
|
|
9
|
+
/** Separators a thousands group may use: comma, period, space, NBSP, narrow NBSP, thin space, figure space. */
|
|
10
|
+
export const GROUP_SEPARATORS = Object.freeze([",", ".", " ", "\u00A0", "\u202F", "\u2009", "\u2007"])
|
|
11
|
+
|
|
12
|
+
const literal = (sep) => (sep === "." ? "\\." : sep === " " ? " " : sep === "," ? "," : `\\u${sep.charCodeAt(0).toString(16).padStart(4, "0")}`)
|
|
13
|
+
const decimalAfter = (sep) => (sep === "," ? "\\." : sep === "." ? "," : "[.,]")
|
|
14
|
+
|
|
15
|
+
/** Regex source of one grouped number using any of `separators` (one type per number); no flags needed. */
|
|
16
|
+
export function groupedNumberSource(separators = GROUP_SEPARATORS) {
|
|
17
|
+
const one = (sep) => sep === "."
|
|
18
|
+
? "\\d{1,3}(?:(?:\\.\\d{3}(?!\\d)){2,}(?:,\\d+)?|\\.\\d{3}(?!\\d),\\d+)"
|
|
19
|
+
: `\\d{1,3}(?:${literal(sep)}\\d{3}(?!\\d))+(?:${decimalAfter(sep)}\\d+)?`
|
|
20
|
+
return `(?<!\\d)(?:${separators.map(one).join("|")})`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** A grouped number or a plain one ("4.22", "12,3451" read as 12.3451), as regex source. */
|
|
24
|
+
export const NUMBER_SOURCE = `(?:${groupedNumberSource()}|(?<!\\d)\\d+(?:[.,]\\d+)?)`
|
|
25
|
+
|
|
26
|
+
const WHOLE = new Map(GROUP_SEPARATORS.map((sep) => [sep, new RegExp(`^${groupedNumberSource([sep])}$`)]))
|
|
27
|
+
|
|
28
|
+
/** The plain decimal a number token spells: group separators dropped, a decimal comma read as a point. */
|
|
29
|
+
export function plainDecimal(token) {
|
|
30
|
+
const sep = GROUP_SEPARATORS.find((candidate) => WHOLE.get(candidate).test(token))
|
|
31
|
+
const ungrouped = sep === undefined ? token : token.split(sep).join("")
|
|
32
|
+
return ungrouped.replace(",", ".")
|
|
33
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
|
|
3
|
+
import { NUMBER_SOURCE, groupedNumberSource, plainDecimal } from "./number-grammar.mjs"
|
|
4
|
+
import { parseNumeral } from "./numerals.mjs"
|
|
5
|
+
|
|
6
|
+
const numbers = (text: string) => [...text.matchAll(new RegExp(NUMBER_SOURCE, "g"))].map((match) => match[0])
|
|
7
|
+
|
|
8
|
+
describe("the one grouped-number grammar", () => {
|
|
9
|
+
for (const [text, expected] of [
|
|
10
|
+
["12,3451", ["12,3451"]],
|
|
11
|
+
["212,34515,678", ["212,34515", "678"]],
|
|
12
|
+
["12,345.6 and 1 234 567", ["12,345.6", "1 234 567"]],
|
|
13
|
+
["1\u2009234\u2009567", ["1\u2009234\u2009567"]],
|
|
14
|
+
["2,500 300", ["2,500", "300"]],
|
|
15
|
+
["2023 500", ["2023", "500"]],
|
|
16
|
+
["1.234,5", ["1.234,5"]],
|
|
17
|
+
["1.234.567 and 1.234", ["1.234.567", "1.234"]],
|
|
18
|
+
] as const) {
|
|
19
|
+
test(`#given ${JSON.stringify(text)} #when tokenized #then ${JSON.stringify(expected)}`, () => expect(numbers(text)).toEqual([...expected]))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
test("#given grouped and plain tokens #when normalized #then group separators drop and a decimal comma reads as a point", () => {
|
|
23
|
+
expect(["12,345.6", "1 234 567", "1.234,5", "12,3451", "4.22"].map(plainDecimal)).toEqual(["12345.6", "1234567", "1234.5", "12.3451", "4.22"])
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test("#given a single period group #when normalized #then it is a decimal, as parseNumeral reads it", () => {
|
|
27
|
+
expect(["1.234", "1.234.567"].map(plainDecimal)).toEqual(["1.234", "1234567"])
|
|
28
|
+
expect(parseNumeral("1.234 units")?.value).toBe("1.234")
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test("#given comma grouping #when parseNumeral reads it #then only a well-formed group is one value", () => {
|
|
32
|
+
expect(new RegExp(`^${groupedNumberSource([","])}$`).test("1,237")).toBe(true)
|
|
33
|
+
expect(parseNumeral("1,237 units")?.value).toBe("1237")
|
|
34
|
+
expect(parseNumeral("12,3451 units")?.value).not.toBe("123451")
|
|
35
|
+
})
|
|
36
|
+
})
|
|
@@ -6,9 +6,12 @@
|
|
|
6
6
|
// English number words one to twelve, an adjacent currency symbol or unit, and an adjacent bounding
|
|
7
7
|
// qualifier. No numeral is ever passed through Number(). Plain Node ESM, sibling imports only.
|
|
8
8
|
|
|
9
|
+
import { groupedNumberSource } from "./number-grammar.mjs"
|
|
9
10
|
import { add, fromDecimal, mul, toDecimal } from "./rational.mjs"
|
|
10
11
|
import { normalizeUnit } from "./units.mjs"
|
|
11
12
|
|
|
13
|
+
/** European digit grouping ("1.234.567"): two or more dot groups of three digits, never a version. */
|
|
14
|
+
export const DOT_GROUPED = "\\d{1,3}(?:\\.\\d{3}){2,}"
|
|
12
15
|
const MONTH = "(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*\\.?"
|
|
13
16
|
/** Stripped before claim detection: dates, versions, figure refs, times, bare years. */
|
|
14
17
|
export const NOT_CLAIMS = [
|
|
@@ -19,7 +22,7 @@ export const NOT_CLAIMS = [
|
|
|
19
22
|
/(?:19|20)\d{2}\s*\uB144/g,
|
|
20
23
|
new RegExp(`\\b${MONTH}\\s+\\d{1,2}(?:st|nd|rd|th)?(?:,?\\s+\\d{4})?`, "gi"),
|
|
21
24
|
new RegExp(`\\b\\d{1,2}(?:st|nd|rd|th)?\\s+${MONTH}(?:\\s+\\d{4})?`, "gi"),
|
|
22
|
-
|
|
25
|
+
new RegExp(`\\bv\\d+(?:\\.\\d+)*\\b|\\b(?!${DOT_GROUPED}(?![\\d,]|\\.\\d))\\d+(?:\\.\\d+){2,}\\b`, "gi"),
|
|
23
26
|
/\b(?:version|ver\.?|release)\s*\d+(?:\.\d+)*/gi,
|
|
24
27
|
/\b(?:fig(?:ure)?\.?|table|chart|section|step|phase|wave|part)\s*\d+(?:\.\d+)*/gi,
|
|
25
28
|
/(?:\uADF8\uB9BC|\uB3C4\uD45C|\uD45C|\uBC84\uC804)\s*\d+(?:\.\d+)*/g,
|
|
@@ -45,8 +48,8 @@ const BIG = new Map([["\uB9CC", 10000n], ["\uC5B5", 100000000n], ["\uC870", 1000
|
|
|
45
48
|
/** A scale word that starts a longer word is not a scale: jogak (pieces), mankeum (as much as). */
|
|
46
49
|
const KO_GROUP = /^(\s?)(?:(\uCC9C)?([\uB9CC\uC5B5\uC870])|(\uCC9C))(?![\uAC01\uD07C])/
|
|
47
50
|
const KO_NEXT = new RegExp(`^\\s?(${NUM})(\uCC9C)?([\uB9CC\uC5B5\uC870])?`, "u")
|
|
48
|
-
const LATIN_SCALE = /^(?:(k|K|M|bn|B)|\s(thousand|million|billion|trillion|bn))(?![\p{L}\d])/u
|
|
49
|
-
const LATIN_FACTOR = { k: 1000n, K: 1000n, M: 1000000n, bn: 1000000000n, B: 1000000000n, thousand: 1000n, million: 1000000n, billion: 1000000000n, trillion: 1000000000000n }
|
|
51
|
+
const LATIN_SCALE = /^(?:(k|K|M|mn|bn|B)|\s(thousand|million|billion|trillion|mn|bn))(?![\p{L}\d])/u
|
|
52
|
+
const LATIN_FACTOR = { k: 1000n, K: 1000n, M: 1000000n, mn: 1000000n, bn: 1000000000n, B: 1000000000n, thousand: 1000n, million: 1000000n, billion: 1000000000n, trillion: 1000000000000n }
|
|
50
53
|
const CURRENCY = "$\u20AC\u00A3\u00A5\u20A9"
|
|
51
54
|
const SYMBOL_UNIT = /^\s?(%p|%|\u2030|\u00D7|\u00B0C|\u00B0F|\u2103|\u2109)/
|
|
52
55
|
const LATIN_UNIT = /^\s?([A-Za-z]+)(?![\p{L}\d])/u
|
|
@@ -60,15 +63,23 @@ const RANGE_SEP = /^(?:\s?[-\u2013~]\s?|\sto\s)/
|
|
|
60
63
|
const SIGN = "-\u2212"
|
|
61
64
|
/** Bounding qualifiers: before the value (Korean and English) and right after it (Korean). */
|
|
62
65
|
const QUALIFIERS_BEFORE = ["up to", "at least", "at most", "more than", "less than", "about", "approximately", "nearly", "over", "under", "\uCD5C\uB300", "\uCD5C\uC18C", "\uC57D"]
|
|
63
|
-
|
|
66
|
+
/** Korean bounding qualifiers written right after a value (isang, iha, miman, chogwa, naeoe, kkaji). */
|
|
67
|
+
export const QUALIFIERS_AFTER = Object.freeze(["\uC774\uC0C1", "\uC774\uD558", "\uBBF8\uB9CC", "\uCD08\uACFC", "\uB0B4\uC678", "\uAE4C\uC9C0"])
|
|
68
|
+
const QUALIFIER_AFTER = new RegExp(`^\\s?(${QUALIFIERS_AFTER.join("|")})`)
|
|
64
69
|
|
|
65
|
-
/**
|
|
70
|
+
/** Comma grouping from the one grouped-number grammar (number-grammar.mjs). */
|
|
71
|
+
const COMMA_GROUPED = new RegExp(`^${groupedNumberSource([","])}$`)
|
|
72
|
+
|
|
73
|
+
/** "1,237" -> "1237"; a malformed group ("1,23", "12,3451") keeps only the leading plain number. */
|
|
66
74
|
function cleanDigits(raw) {
|
|
67
|
-
if (
|
|
75
|
+
if (COMMA_GROUPED.test(raw)) return { digits: raw.replace(/,/g, ""), length: raw.length }
|
|
68
76
|
const plain = /^\d+(?:\.\d+)?/.exec(raw)[0]
|
|
69
77
|
return { digits: plain, length: plain.length }
|
|
70
78
|
}
|
|
71
79
|
|
|
80
|
+
/** A numeral's digits, reading a European period grouping ("1.234.567") as one integer. */
|
|
81
|
+
const digitsOf = (raw) => (new RegExp(`^${DOT_GROUPED}$`).test(raw) ? { digits: raw.replace(/\./g, ""), length: raw.length } : cleanDigits(raw))
|
|
82
|
+
|
|
72
83
|
/** The bare-year and four-digit-date patterns: a numeral they cover can still be a value. */
|
|
73
84
|
const YEAR_SHAPED = new Set([NOT_CLAIMS[0], NOT_CLAIMS[12]])
|
|
74
85
|
|
|
@@ -95,7 +106,7 @@ function findStart(text, from, ranges) {
|
|
|
95
106
|
let digit = null
|
|
96
107
|
for (const match of text.slice(from).matchAll(new RegExp(NUM, "gu"))) {
|
|
97
108
|
const at = from + match.index
|
|
98
|
-
const candidate = { at, ...
|
|
109
|
+
const candidate = { at, ...digitsOf(match[0]) }
|
|
99
110
|
const end = at + candidate.length
|
|
100
111
|
const cover = covering(at)
|
|
101
112
|
const lifted = cover.every(([s, e, yearShaped]) => yearShaped && s === at && e <= end)
|
|
@@ -37,6 +37,9 @@ describe("parseNumeral", () => {
|
|
|
37
37
|
expect(parseNumeral("1.2k")?.value).toBe("1200")
|
|
38
38
|
expect(parseNumeral("$3M")).toMatchObject({ value: "3000000", raw: "$3M", unitRaw: "$" })
|
|
39
39
|
expect(parseNumeral("2.5bn")?.value).toBe("2500000000")
|
|
40
|
+
expect(parseNumeral("5mn users")?.value).toBe("5000000")
|
|
41
|
+
expect(parseNumeral("1.234.567 users")?.value).toBe("1234567")
|
|
42
|
+
expect(parseNumeral("build 0.30.14 shipped")).toBeNull()
|
|
40
43
|
expect(parseNumeral("$4B")?.value).toBe("4000000000")
|
|
41
44
|
expect(parseNumeral("3 million users")?.value).toBe("3000000")
|
|
42
45
|
expect(parseNumeral("5 km")).toMatchObject({ value: "5", unitRaw: "km" })
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// outcome-start.mjs - the run clock (outcome.json startedAt). `outcome init` is its one writer and
|
|
2
|
+
// records it once: --started-at from the orchestrator, else the startedAt of a valid manifest an
|
|
3
|
+
// earlier init wrote (a re-init keeps the clock), else terminal-state.json.started_at when a
|
|
4
|
+
// supervisor wrote one, else the YYYYMMDD-HHMMSS session dir name. A recorded value must be a real
|
|
5
|
+
// ISO time with Z or an offset, not after `now`, and within a day of a stamped dir name; otherwise
|
|
6
|
+
// init refuses instead of falling back. Never the interview's as-of date. No clock reads.
|
|
7
|
+
import { readFileSync } from "node:fs"
|
|
8
|
+
import { basename, join, resolve } from "node:path"
|
|
9
|
+
|
|
10
|
+
const ISO = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-](\d{2}):(\d{2}))$/
|
|
11
|
+
const SESSION_STAMP = /^(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})(\d{2})$/
|
|
12
|
+
const DAY_MS = 86_400_000
|
|
13
|
+
|
|
14
|
+
/** A real calendar time (no 2026-02-30, no T24:00), or null. */
|
|
15
|
+
function strictIso(value) {
|
|
16
|
+
const match = typeof value === "string" ? ISO.exec(value) : null
|
|
17
|
+
if (!match) return null
|
|
18
|
+
const [y, mo, d, h, mi, s, oh = 0, om = 0] = match.slice(1).map((part) => Number(part ?? 0))
|
|
19
|
+
const day = new Date(Date.UTC(y, mo - 1, d))
|
|
20
|
+
if (day.getUTCMonth() !== mo - 1 || day.getUTCDate() !== d || h > 23 || mi > 59 || s > 59 || oh > 23 || om > 59) return null
|
|
21
|
+
const date = new Date(value)
|
|
22
|
+
return Number.isNaN(date.getTime()) ? null : date
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Local start time from a YYYYMMDD-HHMMSS dir name, null when the name is not a stamp. */
|
|
26
|
+
function dirStamp(sessionDir, fail) {
|
|
27
|
+
const match = SESSION_STAMP.exec(basename(sessionDir))
|
|
28
|
+
if (!match) return null
|
|
29
|
+
const [y, mo, d, h, mi, s] = match.slice(1).map(Number)
|
|
30
|
+
const date = new Date(y, mo - 1, d, h, mi, s)
|
|
31
|
+
if (date.getMonth() !== mo - 1 || date.getDate() !== d || date.getHours() !== h) fail(`session dir basename is not a real local time: ${sessionDir}`)
|
|
32
|
+
return date
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function readJson(sessionDir, file) {
|
|
36
|
+
try {
|
|
37
|
+
return { data: JSON.parse(readFileSync(join(sessionDir, file), "utf8")) }
|
|
38
|
+
} catch (error) {
|
|
39
|
+
if (error.code === "ENOENT" || error.code === "ENOTDIR") return null
|
|
40
|
+
if (error instanceof SyntaxError) return { corrupt: `${join(sessionDir, file)} is not valid JSON (${error.message})` }
|
|
41
|
+
throw error
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param {{ sessionDir: string, startedAt?: string, now?: Date, validManifest: (data: unknown) => boolean, fail: (message: string) => never }} input
|
|
47
|
+
* @returns {Date}
|
|
48
|
+
*/
|
|
49
|
+
export function runStart({ sessionDir, startedAt, now, validManifest, fail }) {
|
|
50
|
+
const dir = dirStamp(sessionDir, fail)
|
|
51
|
+
const check = (value, location) => {
|
|
52
|
+
const date = strictIso(value)
|
|
53
|
+
if (!date) fail(`invalid run timestamp in ${location}: ${JSON.stringify(value)} is not a real ISO time with Z or an offset`)
|
|
54
|
+
if (now && date > now) fail(`run timestamp in ${location} (${value}) is after now (${now.toISOString()})`)
|
|
55
|
+
if (dir && Math.abs(date - dir) > DAY_MS) fail(`run timestamp in ${location} (${value}) is not within a day of the session dir name ${basename(sessionDir)}: a copied or stale value`)
|
|
56
|
+
return date
|
|
57
|
+
}
|
|
58
|
+
if (startedAt !== undefined) return check(startedAt, "--started-at")
|
|
59
|
+
const manifest = readJson(sessionDir, "outcome.json")
|
|
60
|
+
if (manifest?.data && validManifest(manifest.data) && resolve(String(manifest.data.sessionDir)) === resolve(sessionDir)) return check(manifest.data.startedAt, "outcome.json.startedAt")
|
|
61
|
+
const terminal = readJson(sessionDir, "terminal-state.json")
|
|
62
|
+
if (terminal?.corrupt) fail(terminal.corrupt)
|
|
63
|
+
if (terminal?.data?.started_at !== undefined) return check(terminal.data.started_at, "terminal-state.json.started_at")
|
|
64
|
+
if (dir) return dir
|
|
65
|
+
return fail(`missing run timestamp: pass --started-at <ISO>, record terminal-state.json.started_at, or name the session dir YYYYMMDD-HHMMSS (${sessionDir})`)
|
|
66
|
+
}
|