lazycodex-ai 5.1.44 → 5.1.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.de.md +0 -5
- package/README.es.md +0 -5
- package/README.fr.md +0 -5
- package/README.it.md +0 -5
- package/README.ja.md +0 -5
- package/README.ko.md +0 -5
- package/README.md +4 -5
- package/README.pt-br.md +0 -5
- package/README.ru.md +0 -6
- package/README.zh-cn.md +0 -5
- package/dist/cli/index.js +865 -413
- package/dist/cli/service-auth/keystore.d.ts +4 -2
- package/dist/cli-node/index.js +865 -413
- package/docs/reference/web-terminal-visual-qa.md +8 -0
- package/package.json +1 -1
- package/packages/lsp-daemon/dist/cli.js +7 -7
- package/packages/lsp-daemon/dist/client.js +7 -7
- package/packages/lsp-daemon/dist/index.js +7 -7
- package/packages/lsp-daemon/package.json +3 -3
- package/packages/lsp-tools-mcp/dist/cli.js +7 -7
- package/packages/lsp-tools-mcp/dist/mcp.js +7 -7
- package/packages/lsp-tools-mcp/dist/tools.js +4 -4
- package/packages/lsp-tools-mcp/package.json +3 -3
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +77 -26
- 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/bootstrap/src/hook.ts +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/biome.json +1 -21
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +4 -4
- 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/biome.json +1 -21
- 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 +4 -4
- package/packages/omo-codex/plugin/components/lsp/biome.json +1 -21
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +4 -4
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +4 -4
- package/packages/omo-codex/plugin/components/lsp/scripts/build-runtime.mjs +0 -1
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook-not-installed.test.ts +1 -1
- package/packages/omo-codex/plugin/components/rules/biome.json +1 -21
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +4 -4
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/biome.json +1 -21
- package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +90 -1
- package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +91 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +4 -4
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/biome.json +1 -21
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/biome.json +1 -21
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/directive.md +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 +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/biome.json +1 -21
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +7 -7
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-mutations.mjs +2 -0
- 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 +12 -12
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +14 -9
- package/packages/omo-codex/plugin/skills/ast-grep/AGENTS.md +1 -1
- package/packages/omo-codex/plugin/skills/browser/SKILL.md +21 -22
- package/packages/omo-codex/plugin/skills/browser/references/commands.md +2 -9
- package/packages/omo-codex/plugin/skills/browser/references/install.md +1 -2
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/omo-codex/plugin/skills/browser/scripts/browser-doctor.mjs +2 -2
- package/packages/omo-codex/plugin/skills/browser/scripts/browser-engine-guard.mjs +63 -487
- package/packages/omo-codex/plugin/skills/browser/scripts/browser-install.mjs +1 -1
- package/packages/omo-codex/plugin/skills/browser/scripts/omowright.mjs +1 -10
- package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +2 -2
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +79 -118
- package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +75 -224
- package/packages/omo-codex/plugin/skills/frontend/references/design/stylegallery.md +32 -27
- package/packages/omo-codex/plugin/skills/frontend/references/perfection/README.md +2 -2
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/check-accent-stripes.ts +8 -24
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/check-draft-guard.ts +40 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/collect-files.ts +53 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/findings.ts +16 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/lint.ts +38 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/rules.ts +69 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/text-nodes.ts +121 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/attr-lookalikes.tsx +10 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/bound-values.tsx +12 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/const-generics.tsx +3 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/generics.tsx +7 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/measured-stats.html +6 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/plain-copy.html +9 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/state-copy.html +11 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/attribute-edges.html +7 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/components.tsx +16 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/decorative-badge.vue +7 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/emoji-icons.tsx +9 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/jsx-edges.tsx +1 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.html +6 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.tsx +8 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/vanity-stats.html +5 -0
- package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/programming/SKILL.md +10 -7
- package/packages/omo-codex/plugin/skills/programming/references/typescript/README.md +10 -7
- package/packages/omo-codex/plugin/skills/programming/references/typescript/bootstrap.md +9 -23
- package/packages/omo-codex/plugin/skills/programming/references/typescript/oxc-lint.md +130 -0
- package/packages/omo-codex/plugin/skills/programming/references/typescript/tsconfig-strict.md +6 -62
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts +12 -12
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/new-project.ts +20 -81
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/.oxfmtrc.json +9 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/.oxlintrc.json +46 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/LICENSE +21 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/index.js +10 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/no-em-dash-copy.js +28 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/require-suppression-reason.js +62 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/tsconfig.json +29 -0
- package/packages/omo-codex/plugin/skills/refactor/SKILL.md +14 -14
- package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +21 -3
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +37 -12
- package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +77 -16
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +134 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +95 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +68 -3
- package/packages/omo-codex/plugin/skills/ulw-research/references/report-visualization.md +2 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/unit-rules.md +157 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/claims-index.mjs +125 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/cli-support.mjs +19 -5
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +89 -3
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +239 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +66 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-expr.mjs +108 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +194 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +250 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-schema.mjs +221 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger.mjs +123 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +231 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-brief.mjs +150 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-state.mjs +64 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +29 -9
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-structure.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-text.mjs +1 -27
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +2 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +182 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/lexicon.mjs +47 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +193 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/meaning-check.mjs +196 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/numerals.mjs +226 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +8 -8
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/particles-ko.mjs +108 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +184 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +115 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-diff.mjs +31 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-project.mjs +125 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-rules.mjs +170 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/rational.mjs +139 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +21 -10
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +15 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/sentence-split.mjs +77 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +140 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/text-normalize.mjs +82 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/text-similarity.mjs +28 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-errors.mjs +23 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-precision.mjs +44 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-rules.mjs +173 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +105 -0
- package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +71 -64
- package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +4 -1
- package/packages/omo-codex/plugin/test/sync-skills-codex-compatibility.test.mjs +19 -0
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +12 -8
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +169 -30
- package/packages/shared-skills/skills/ast-grep/AGENTS.md +1 -1
- package/packages/shared-skills/skills/browser/SKILL.md +21 -22
- package/packages/shared-skills/skills/browser/references/commands.md +2 -9
- package/packages/shared-skills/skills/browser/references/install.md +1 -2
- package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/shared-skills/skills/browser/scripts/browser-doctor.mjs +2 -2
- package/packages/shared-skills/skills/browser/scripts/browser-engine-guard.mjs +63 -487
- package/packages/shared-skills/skills/browser/scripts/browser-install.mjs +1 -1
- package/packages/shared-skills/skills/browser/scripts/omowright.mjs +1 -10
- package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +2 -2
- package/packages/shared-skills/skills/frontend/SKILL.md +79 -118
- package/packages/shared-skills/skills/frontend/references/design/README.md +75 -224
- package/packages/shared-skills/skills/frontend/references/design/stylegallery.md +32 -27
- package/packages/shared-skills/skills/frontend/references/perfection/README.md +2 -2
- package/packages/shared-skills/skills/frontend/scripts/perfection/check-accent-stripes.ts +8 -24
- package/packages/shared-skills/skills/frontend/scripts/perfection/check-draft-guard.test.ts +101 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/check-draft-guard.ts +40 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/collect-files.test.ts +50 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/collect-files.ts +53 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/findings.ts +16 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/lint.ts +38 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/rules.ts +69 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/text-nodes.ts +121 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/attr-lookalikes.tsx +10 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/bound-values.tsx +12 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/const-generics.tsx +3 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/generics.tsx +7 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/measured-stats.html +6 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/plain-copy.html +9 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/state-copy.html +11 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/attribute-edges.html +7 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/components.tsx +16 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/decorative-badge.vue +7 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/emoji-icons.tsx +9 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/jsx-edges.tsx +1 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.html +6 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.tsx +8 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/vanity-stats.html +5 -0
- package/packages/shared-skills/skills/programming/SKILL.md +10 -7
- package/packages/shared-skills/skills/programming/references/typescript/README.md +10 -7
- package/packages/shared-skills/skills/programming/references/typescript/bootstrap.md +9 -23
- package/packages/shared-skills/skills/programming/references/typescript/oxc-lint.md +130 -0
- package/packages/shared-skills/skills/programming/references/typescript/tsconfig-strict.md +6 -62
- package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.ts +12 -12
- package/packages/shared-skills/skills/programming/scripts/typescript/new-project.test.ts +162 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/new-project.ts +20 -81
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/.oxfmtrc.json +9 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/.oxlintrc.json +46 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/LICENSE +21 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/index.js +10 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/no-em-dash-copy.js +28 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/require-suppression-reason.js +62 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/tsconfig.json +29 -0
- package/packages/shared-skills/skills/refactor/SKILL.md +13 -13
- package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +1 -1
- package/packages/shared-skills/skills/ulw-execute/SKILL.md +1 -1
- package/packages/shared-skills/skills/ulw-research/AGENTS.md +21 -3
- package/packages/shared-skills/skills/ulw-research/SKILL.md +37 -12
- package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +77 -16
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +134 -0
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +95 -0
- package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
- package/packages/shared-skills/skills/ulw-research/references/report-gates.md +68 -3
- package/packages/shared-skills/skills/ulw-research/references/report-visualization.md +2 -0
- package/packages/shared-skills/skills/ulw-research/references/unit-rules.md +157 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +193 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claims-index.mjs +125 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claims-index.test.ts +149 -0
- package/packages/shared-skills/skills/ulw-research/scripts/cli-support.mjs +19 -5
- package/packages/shared-skills/skills/ulw-research/scripts/cli-support.test.ts +13 -2
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +89 -3
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +100 -12
- package/packages/shared-skills/skills/ulw-research/scripts/copy-returns.test.ts +183 -0
- package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +245 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +239 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +66 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +375 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-expr.mjs +108 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +194 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.test.ts +221 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-grammar.test.ts +197 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +250 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +156 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +160 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-schema.mjs +221 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-schema.test.ts +43 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.mjs +123 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.test.ts +254 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +231 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.test.ts +118 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-pairs.test.ts +167 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +409 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +388 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-binding.test.ts +81 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-brief.mjs +150 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-brief.test.ts +179 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.test.ts +117 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-state.mjs +64 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-state.test.ts +46 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.test.ts +58 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +270 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-context.test.ts +64 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.test.ts +73 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +29 -9
- package/packages/shared-skills/skills/ulw-research/scripts/gates-structure.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/gates-text.mjs +1 -27
- package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +2 -2
- package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +182 -0
- package/packages/shared-skills/skills/ulw-research/scripts/lexicon.mjs +47 -0
- package/packages/shared-skills/skills/ulw-research/scripts/lexicon.test.ts +57 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +193 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite.test.ts +271 -0
- package/packages/shared-skills/skills/ulw-research/scripts/meaning-check.mjs +196 -0
- package/packages/shared-skills/skills/ulw-research/scripts/meaning-check.test.ts +228 -0
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.mjs +226 -0
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +120 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +8 -8
- package/packages/shared-skills/skills/ulw-research/scripts/outcome.test.ts +29 -0
- package/packages/shared-skills/skills/ulw-research/scripts/particles-ko.mjs +108 -0
- package/packages/shared-skills/skills/ulw-research/scripts/particles-ko.test.ts +86 -0
- package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +184 -0
- package/packages/shared-skills/skills/ulw-research/scripts/placeholder.test.ts +77 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +115 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.test.ts +363 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-diff.mjs +31 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-diff.test.ts +168 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +187 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-project.mjs +125 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-rules.mjs +170 -0
- package/packages/shared-skills/skills/ulw-research/scripts/rational.mjs +139 -0
- package/packages/shared-skills/skills/ulw-research/scripts/rational.test.ts +46 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +21 -10
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +15 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +458 -2
- package/packages/shared-skills/skills/ulw-research/scripts/sentence-split.mjs +77 -0
- package/packages/shared-skills/skills/ulw-research/scripts/sentence-split.test.ts +109 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +140 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +113 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/assets/share.csv +3 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/claim-graph.md +13 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/gen-legacy-dev.mjs +89 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-basic.json +223 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-legacy-dev.json +859 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-verify.json +420 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/report-bad.html +19 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/report-good.html +39 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/acronyms.json +3 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/meta.json +9 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/sentences.jsonl +7 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/text.txt +7 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4.html +19 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/acronyms.json +1 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/meta.json +9 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/sentences.jsonl +1 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/text.txt +1 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S9.txt +1 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity-brief-locked.md +17 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity-brief-pending.md +17 -0
- package/packages/shared-skills/skills/ulw-research/scripts/text-normalize.mjs +82 -0
- package/packages/shared-skills/skills/ulw-research/scripts/text-normalize.test.ts +73 -0
- package/packages/shared-skills/skills/ulw-research/scripts/text-similarity.mjs +28 -0
- package/packages/shared-skills/skills/ulw-research/scripts/text-similarity.test.ts +38 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-errors.mjs +23 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-precision.mjs +44 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-rules.mjs +173 -0
- package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +105 -0
- package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +83 -0
- package/packages/shared-skills/skills/visual-qa/SKILL.md +70 -63
- package/packages/shared-skills/skills/visual-qa/scripts/skill-doc-fields.test.ts +34 -0
- package/script/qa/web-terminal-visual-qa.mjs +6 -2
- package/script/qa/xterm-live-terminal.mjs +26 -3
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// polish-rules.mjs - the one rule set behind polish-apply (per edit) and polish-diff (whole document).
|
|
2
|
+
//
|
|
3
|
+
// polishDefects(beforeHtml, afterHtml, ledger, {ordered}) compares the visible projections
|
|
4
|
+
// (html-markup.markupParts, then polish-project.project) and returns {code, message} per failure.
|
|
5
|
+
// Polish may change text nodes only. The primary rule is the whole-document skeleton comparison
|
|
6
|
+
// (html-markup.skeletonChange): every markup token (tags, comments, bogus comments and frozen
|
|
7
|
+
// script/style/template content) and every character reference of a text node must be byte-identical
|
|
8
|
+
// in document order; an edited span holds no skeleton token, so any difference is polish_diff_fail
|
|
9
|
+
// naming the first differing token and its offset. The remaining checks compare the projections:
|
|
10
|
+
// polish_diff_fail: fact spans (id + rendered markup), numerals outside fact spans (with units),
|
|
11
|
+
// tickers (TICKER: 1-5 capitals with an optional ".XX" class, or a 6-digit KRX code), or a
|
|
12
|
+
// table's row count changed; any tag, attribute, comment, declaration, or script/style/template
|
|
13
|
+
// content changed; protected tokens also stay within their original text-node boundaries;
|
|
14
|
+
// polish_hedging_decreased: a hedge or qualifier the before text holds and must keep is gone;
|
|
15
|
+
// polish_text_churn: more than POLISH_CHURN_LIMIT of the normalized visible prose changed;
|
|
16
|
+
// polish_link_changed: href values changed or an internal #target stopped resolving;
|
|
17
|
+
// polish_name_changed: a proper noun (POLISH_NAME) of a ledger quote, present before, is gone.
|
|
18
|
+
// A document holding a construct html-markup.ambiguousMarkup lists (</ + non-letter, <!-- inside
|
|
19
|
+
// script/style, <![CDATA[, <?, --!>, or < inside a tag) is refused first with polish_diff_fail
|
|
20
|
+
// "ambiguous markup": the byte comparison is only trusted where the tokenizer is exact.
|
|
21
|
+
// ordered (polish-apply) also refuses a reordering of fact spans, numerals, tickers and links;
|
|
22
|
+
// without it they compare as multisets. Severity lives only in contracts.DEFECT_CODES.
|
|
23
|
+
// No IO, no model. Plain Node ESM.
|
|
24
|
+
|
|
25
|
+
import { ambiguousMarkup, markupParts, skeletonChange, tagAttributes } from "./html-markup.mjs"
|
|
26
|
+
import { missingTerms, numeralKeys, project, requiredTerms, termCounts, textOf } from "./polish-project.mjs"
|
|
27
|
+
import { splitSentences } from "./sentence-split.mjs"
|
|
28
|
+
import { normalizeText } from "./text-normalize.mjs"
|
|
29
|
+
|
|
30
|
+
/** The codes polishDefects can emit, in report order. */
|
|
31
|
+
export const POLISH_CODES = Object.freeze(["polish_diff_fail", "polish_hedging_decreased", "polish_text_churn", "polish_link_changed", "polish_name_changed"])
|
|
32
|
+
export const TICKER = /(?<![\p{L}\p{N}])(?:[A-Z]{1,5}(?:\.[A-Z]{1,2})?|\d{6})(?![\p{L}\p{N}])/gu
|
|
33
|
+
/** The capitalized-token heuristic for proper nouns in ledger quotes. */
|
|
34
|
+
export const POLISH_NAME = /\p{Lu}[\p{L}\p{N}&'-]+/gu
|
|
35
|
+
/** Above this share of changed normalized prose a polish composed instead of proofreading. */
|
|
36
|
+
export const POLISH_CHURN_LIMIT = 0.5
|
|
37
|
+
/** Above this many cells a gap counts as fully rewritten instead of running the quadratic distance. */
|
|
38
|
+
const DISTANCE_CELLS = 25_000_000
|
|
39
|
+
|
|
40
|
+
const sorted = (list) => [...list].sort()
|
|
41
|
+
const same = (a, b) => a.length === b.length && a.every((item, k) => item === b[k])
|
|
42
|
+
|
|
43
|
+
/** null when unchanged; a reorder counts only when ordered. */
|
|
44
|
+
function listChange(label, before, after, ordered) {
|
|
45
|
+
if (same(before, after)) return null
|
|
46
|
+
const remaining = sorted(before)
|
|
47
|
+
const added = []
|
|
48
|
+
for (const item of after) {
|
|
49
|
+
const at = remaining.indexOf(item)
|
|
50
|
+
if (at === -1) added.push(item)
|
|
51
|
+
else remaining.splice(at, 1)
|
|
52
|
+
}
|
|
53
|
+
if (remaining.length > 0 || added.length > 0) return `${label} changed: removed [${remaining.join(", ")}], added [${added.join(", ")}]`
|
|
54
|
+
return ordered ? `${label} reordered: [${before.join(", ")}] became [${after.join(", ")}]` : null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const tableRows = (html) => html.split(/<table\b/i).slice(1).map((part) => (part.split(/<\/table\s*>/i)[0].match(/<tr\b/gi) ?? []).length)
|
|
58
|
+
|
|
59
|
+
function levenshtein(a, b) {
|
|
60
|
+
if (a === b) return 0
|
|
61
|
+
if (a.length * b.length > DISTANCE_CELLS) return Math.max(a.length, b.length)
|
|
62
|
+
let previous = Array.from({ length: b.length + 1 }, (_, k) => k)
|
|
63
|
+
for (let i = 1; i <= a.length; i++) {
|
|
64
|
+
const current = [i]
|
|
65
|
+
for (let j = 1; j <= b.length; j++) {
|
|
66
|
+
current[j] = Math.min(previous[j] + 1, current[j - 1] + 1, previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1))
|
|
67
|
+
}
|
|
68
|
+
previous = current
|
|
69
|
+
}
|
|
70
|
+
return previous[b.length]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Changed share of normalized prose: edit distance over unmatched sentence runs / longer length. */
|
|
74
|
+
export function churnRatio(beforeText, afterText) {
|
|
75
|
+
const [a, b] = [normalizeText(beforeText), normalizeText(afterText)]
|
|
76
|
+
const longest = Math.max(a.length, b.length)
|
|
77
|
+
if (longest === 0) return 0
|
|
78
|
+
const [sa, sb] = [splitSentences(a).map((s) => s.text), splitSentences(b).map((s) => s.text)]
|
|
79
|
+
const lcs = Array.from({ length: sa.length + 1 }, () => new Array(sb.length + 1).fill(0))
|
|
80
|
+
for (let i = sa.length - 1; i >= 0; i--) {
|
|
81
|
+
for (let j = sb.length - 1; j >= 0; j--) lcs[i][j] = sa[i] === sb[j] ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1])
|
|
82
|
+
}
|
|
83
|
+
let distance = 0
|
|
84
|
+
let gap = [[], []]
|
|
85
|
+
const flush = () => {
|
|
86
|
+
distance += levenshtein(gap[0].join(" "), gap[1].join(" "))
|
|
87
|
+
gap = [[], []]
|
|
88
|
+
}
|
|
89
|
+
let [i, j] = [0, 0]
|
|
90
|
+
while (i < sa.length || j < sb.length) {
|
|
91
|
+
if (i < sa.length && j < sb.length && sa[i] === sb[j]) {
|
|
92
|
+
flush()
|
|
93
|
+
i++
|
|
94
|
+
j++
|
|
95
|
+
} else if (j >= sb.length || (i < sa.length && lcs[i + 1][j] >= lcs[i][j + 1])) gap[0].push(sa[i++])
|
|
96
|
+
else gap[1].push(sb[j++])
|
|
97
|
+
}
|
|
98
|
+
flush()
|
|
99
|
+
return distance / longest
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function linkChange(from, to, before, after, ordered) {
|
|
103
|
+
const change = listChange("href and data-claim values", from.links, to.links, ordered)
|
|
104
|
+
if (change) return change
|
|
105
|
+
const ids = (html) => new Set(tagAttributes(html).flatMap(({ attrs }) => (Object.hasOwn(attrs, "id") ? [attrs.id] : [])))
|
|
106
|
+
const [idsBefore, idsAfter] = [ids(before), ids(after)]
|
|
107
|
+
const targets = to.links.filter((link) => link.startsWith("href=#")).map((link) => link.slice(5))
|
|
108
|
+
const broken = targets.filter((href) => idsBefore.has(href.slice(1)) && !idsAfter.has(href.slice(1)))
|
|
109
|
+
return broken.length > 0 ? `internal link targets stopped resolving: [${[...new Set(broken)].join(", ")}]` : null
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Capitalized tokens of the ledger quotes, minus each sentence's first word. */
|
|
113
|
+
function ledgerNames(rows) {
|
|
114
|
+
const names = new Set()
|
|
115
|
+
for (const row of rows) {
|
|
116
|
+
for (const { text } of splitSentences(row.quote ?? "")) {
|
|
117
|
+
const first = text.match(/^\P{L}*\p{L}+/u)?.[0].length ?? 0
|
|
118
|
+
for (const match of text.matchAll(POLISH_NAME)) if (match.index >= first) names.add(match[0])
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return names
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const hasWord = (text, word) => new RegExp(`(?<![\\p{L}\\p{N}])${word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\p{L}\\p{N}])`, "u").test(text)
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @param {string} beforeHtml
|
|
128
|
+
* @param {string} afterHtml
|
|
129
|
+
* @param {{ rows: object[] }} ledger
|
|
130
|
+
* @param {{ ordered?: boolean }} [options]
|
|
131
|
+
* @returns {{ code: string, message: string }[]} in POLISH_CODES order
|
|
132
|
+
*/
|
|
133
|
+
export function polishDefects(beforeHtml, afterHtml, ledger, { ordered = false } = {}) {
|
|
134
|
+
const ambiguous = [...new Set([...ambiguousMarkup(beforeHtml), ...ambiguousMarkup(afterHtml)])]
|
|
135
|
+
if (ambiguous.length > 0) return [{ code: "polish_diff_fail", message: `ambiguous markup: ${ambiguous.join(", ")}` }]
|
|
136
|
+
const [beforeParts, afterParts] = [markupParts(beforeHtml), markupParts(afterHtml)]
|
|
137
|
+
const [before, after] = [beforeParts.source, afterParts.source]
|
|
138
|
+
const [from, to] = [project(before), project(after)]
|
|
139
|
+
const rows = ledger?.rows ?? []
|
|
140
|
+
const defects = []
|
|
141
|
+
const note = (code, message) => message && defects.push({ code, message })
|
|
142
|
+
const factKey = (fact) => `${fact.id} ${fact.html}`
|
|
143
|
+
const tickers = (text) => text.match(TICKER) ?? []
|
|
144
|
+
const markupEqual = same(beforeParts.markup, afterParts.markup)
|
|
145
|
+
note("polish_diff_fail", markupEqual ? null : "markup changed")
|
|
146
|
+
note("polish_diff_fail", skeletonChange(beforeHtml, afterHtml))
|
|
147
|
+
note("polish_diff_fail", listChange("fact spans", from.facts.map(factKey), to.facts.map(factKey), ordered))
|
|
148
|
+
note("polish_diff_fail", listChange("numerals outside fact spans", numeralKeys(from.masked), numeralKeys(to.masked), ordered))
|
|
149
|
+
note("polish_diff_fail", listChange("tickers", tickers(from.masked), tickers(to.masked), ordered))
|
|
150
|
+
const [rowsBefore, rowsAfter] = [tableRows(before), tableRows(after)]
|
|
151
|
+
if (!same(rowsBefore, rowsAfter)) note("polish_diff_fail", `table row counts changed: [${rowsBefore}] became [${rowsAfter}]`)
|
|
152
|
+
const required = requiredTerms(from, new Map(rows.map((row) => [row.fact_id, row])))
|
|
153
|
+
if (markupEqual) {
|
|
154
|
+
for (const [index, raw] of beforeParts.text.entries()) {
|
|
155
|
+
const [a, b] = [raw, afterParts.text[index]].map(textOf)
|
|
156
|
+
note("polish_diff_fail", listChange("text-node numerals", numeralKeys(a), numeralKeys(b), ordered))
|
|
157
|
+
note("polish_diff_fail", listChange("text-node tickers", tickers(a), tickers(b), ordered))
|
|
158
|
+
const lost = missingTerms(termCounts(a, [...required.keys()]), b)
|
|
159
|
+
if (lost.length) note("polish_hedging_decreased", `text-node hedges removed: ${lost.join(", ")}`)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const missing = missingTerms(required, to.visible)
|
|
163
|
+
if (missing.length > 0) note("polish_hedging_decreased", `hedges or qualifiers removed: ${missing.map((term) => `"${term}"`).join(", ")}`)
|
|
164
|
+
const ratio = churnRatio(from.visible, to.visible)
|
|
165
|
+
if (ratio > POLISH_CHURN_LIMIT) note("polish_text_churn", `${(ratio * 100).toFixed(1)} percent of the normalized prose changed`)
|
|
166
|
+
note("polish_link_changed", linkChange(from, to, before, after, ordered))
|
|
167
|
+
const lost = [...ledgerNames(rows)].filter((name) => hasWord(from.visible, name) && !hasWord(to.visible, name))
|
|
168
|
+
if (lost.length > 0) note("polish_name_changed", `ledger-quote names missing after polish: ${lost.join(", ")}`)
|
|
169
|
+
return defects.sort((a, b) => POLISH_CODES.indexOf(a.code) - POLISH_CODES.indexOf(b.code))
|
|
170
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// rational.mjs - exact rational arithmetic over BigInt numerator/denominator pairs.
|
|
2
|
+
//
|
|
3
|
+
// A rational is { num: bigint, den: bigint }, reduced, with den > 0. Numerals enter as decimal
|
|
4
|
+
// strings and never pass through Number(), so no value is ever rounded by floating point.
|
|
5
|
+
// Plain Node ESM, no imports.
|
|
6
|
+
|
|
7
|
+
const DECIMAL = /^([+-]?)(\d+)(?:\.(\d+))?$/
|
|
8
|
+
|
|
9
|
+
const abs = (n) => (n < 0n ? -n : n)
|
|
10
|
+
|
|
11
|
+
function gcd(a, b) {
|
|
12
|
+
let x = abs(a)
|
|
13
|
+
let y = abs(b)
|
|
14
|
+
while (y !== 0n) [x, y] = [y, x % y]
|
|
15
|
+
return x
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function make(num, den) {
|
|
19
|
+
if (den === 0n) throw new RangeError("rational: zero denominator")
|
|
20
|
+
const sign = den < 0n ? -1n : 1n
|
|
21
|
+
const g = gcd(num, den) || 1n
|
|
22
|
+
return Object.freeze({ num: (sign * num) / g, den: (sign * den) / g })
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Accepts a rational, a bigint, a safe integer, or a decimal string. */
|
|
26
|
+
function coerce(value) {
|
|
27
|
+
if (typeof value === "bigint") return make(value, 1n)
|
|
28
|
+
if (typeof value === "number") {
|
|
29
|
+
if (!Number.isSafeInteger(value)) throw new TypeError(`rational: ${value} is not a safe integer; pass a decimal string`)
|
|
30
|
+
return make(BigInt(value), 1n)
|
|
31
|
+
}
|
|
32
|
+
if (typeof value === "string") return fromDecimal(value)
|
|
33
|
+
if (value && typeof value.num === "bigint" && typeof value.den === "bigint") return make(value.num, value.den)
|
|
34
|
+
throw new TypeError("rational: expected a rational, bigint, integer, or decimal string")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** "12.50" -> 25/2. Accepts an optional sign, digits, and an optional fraction; nothing else. */
|
|
38
|
+
export function fromDecimal(str) {
|
|
39
|
+
const match = DECIMAL.exec(String(str).trim())
|
|
40
|
+
if (!match) throw new SyntaxError(`rational: "${str}" is not a plain decimal`)
|
|
41
|
+
const [, sign, whole, frac = ""] = match
|
|
42
|
+
const num = BigInt(whole + frac) * (sign === "-" ? -1n : 1n)
|
|
43
|
+
return make(num, 10n ** BigInt(frac.length))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function add(a, b) {
|
|
47
|
+
const x = coerce(a)
|
|
48
|
+
const y = coerce(b)
|
|
49
|
+
return make(x.num * y.den + y.num * x.den, x.den * y.den)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function sub(a, b) {
|
|
53
|
+
const x = coerce(a)
|
|
54
|
+
const y = coerce(b)
|
|
55
|
+
return make(x.num * y.den - y.num * x.den, x.den * y.den)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function mul(a, b) {
|
|
59
|
+
const x = coerce(a)
|
|
60
|
+
const y = coerce(b)
|
|
61
|
+
return make(x.num * y.num, x.den * y.den)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function div(a, b) {
|
|
65
|
+
const x = coerce(a)
|
|
66
|
+
const y = coerce(b)
|
|
67
|
+
if (y.num === 0n) throw new RangeError("rational: division by zero")
|
|
68
|
+
return make(x.num * y.den, x.den * y.num)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function eq(a, b) {
|
|
72
|
+
const x = coerce(a)
|
|
73
|
+
const y = coerce(b)
|
|
74
|
+
return x.num === y.num && x.den === y.den
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Digits of a non-negative integer scaled by 10^-scale, as a decimal string. */
|
|
78
|
+
function placeDecimal(digits, scale) {
|
|
79
|
+
if (scale <= 0) return digits + "0".repeat(-scale)
|
|
80
|
+
const padded = digits.padStart(scale + 1, "0")
|
|
81
|
+
return `${padded.slice(0, padded.length - scale)}.${padded.slice(padded.length - scale)}`
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Exact decimal; throws RangeError when the expansion does not terminate. */
|
|
85
|
+
function exactDecimal(r) {
|
|
86
|
+
let den = r.den
|
|
87
|
+
let twos = 0
|
|
88
|
+
let fives = 0
|
|
89
|
+
while (den % 2n === 0n) [den, twos] = [den / 2n, twos + 1]
|
|
90
|
+
while (den % 5n === 0n) [den, fives] = [den / 5n, fives + 1]
|
|
91
|
+
if (den !== 1n) throw new RangeError(`rational: ${r.num}/${r.den} has no finite decimal; pass sigFigs`)
|
|
92
|
+
const scale = Math.max(twos, fives)
|
|
93
|
+
const digits = (abs(r.num) * 10n ** BigInt(scale)) / r.den
|
|
94
|
+
return (r.num < 0n ? "-" : "") + placeDecimal(digits.toString(), scale)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Number of decimal digits in the integer part of |num/den| (0 when below 1). */
|
|
98
|
+
function integerDigits(num, den) {
|
|
99
|
+
const whole = num / den
|
|
100
|
+
if (whole > 0n) return whole.toString().length
|
|
101
|
+
let exp = 0
|
|
102
|
+
let scaled = num
|
|
103
|
+
while (scaled < den) [scaled, exp] = [scaled * 10n, exp - 1]
|
|
104
|
+
return exp + 1
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Decimal string. Without sigFigs the value must terminate and is rendered exactly. With sigFigs it
|
|
109
|
+
* is rounded half away from zero to that many significant figures; integers are never written
|
|
110
|
+
* with an exponent, so 319999 at 2 figures renders "320000".
|
|
111
|
+
*/
|
|
112
|
+
export function toDecimal(value, sigFigs) {
|
|
113
|
+
const r = coerce(value)
|
|
114
|
+
if (sigFigs === undefined) return exactDecimal(r)
|
|
115
|
+
if (!Number.isInteger(sigFigs) || sigFigs < 1) throw new RangeError("rational: sigFigs must be a positive integer")
|
|
116
|
+
if (r.num === 0n) return sigFigs > 1 ? `0.${"0".repeat(sigFigs - 1)}` : "0"
|
|
117
|
+
const num = abs(r.num)
|
|
118
|
+
const scale = sigFigs - integerDigits(num, r.den)
|
|
119
|
+
const numer = scale >= 0 ? num * 10n ** BigInt(scale) : num
|
|
120
|
+
const denom = scale >= 0 ? r.den : r.den * 10n ** BigInt(-scale)
|
|
121
|
+
let digits = numer / denom
|
|
122
|
+
let places = scale
|
|
123
|
+
if ((numer % denom) * 2n >= denom) digits += 1n
|
|
124
|
+
if (digits.toString().length > sigFigs) [digits, places] = [digits / 10n, places - 1]
|
|
125
|
+
return (r.num < 0n ? "-" : "") + placeDecimal(digits.toString(), places)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Significant figures of a decimal string. With a decimal point: digits from the first nonzero
|
|
130
|
+
* digit to the end, trailing zeros included ("3.2" -> 2, "0.050" -> 2). Without one: every digit
|
|
131
|
+
* after leading zeros counts ("320" -> 3, "100" -> 3, "20" -> 2).
|
|
132
|
+
*/
|
|
133
|
+
export function sigFigsOf(decimalStr) {
|
|
134
|
+
const match = DECIMAL.exec(String(decimalStr).trim())
|
|
135
|
+
if (!match) throw new SyntaxError(`rational: "${decimalStr}" is not a plain decimal`)
|
|
136
|
+
const [, , whole, frac] = match
|
|
137
|
+
const digits = (whole + (frac ?? "")).replace(/^0+/, "")
|
|
138
|
+
return Math.max(digits.length, 1)
|
|
139
|
+
}
|
|
@@ -5,7 +5,7 @@ import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs"
|
|
|
5
5
|
import { dirname, join, resolve } from "node:path"
|
|
6
6
|
|
|
7
7
|
import { CliError, parseArgs, readJsonFile, writeJsonAtomic } from "./cli-support.mjs"
|
|
8
|
-
import { REPAIR_DEFAULTS } from "./contracts.mjs"
|
|
8
|
+
import { GATE_NAMES, GATE_STATUSES, REPAIR_DEFAULTS } from "./contracts.mjs"
|
|
9
9
|
import { parseLineageMode, parsePalette, renderDesignSpec } from "./design-spec.mjs"
|
|
10
10
|
import { extractFromHtml, extractFromPath, fetchHtml } from "./format-extract.mjs"
|
|
11
11
|
import { checkLayout } from "./gates-layout.mjs"
|
|
@@ -80,12 +80,16 @@ export function runLayoutProbe(argv) {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
export function runRepairDecide(argv) {
|
|
83
|
-
const args = parseArgs(argv)
|
|
83
|
+
const args = parseArgs(argv, { repeatable: ["defects"] })
|
|
84
84
|
const statePath = args.required("state")
|
|
85
85
|
const now = nowFrom(args.flags)
|
|
86
|
-
|
|
87
|
-
const defects =
|
|
88
|
-
|
|
86
|
+
// Every --defects file (check result, fidelity record, or bare array) joins one defect list.
|
|
87
|
+
const defects = args.required("defects").flatMap((path) => {
|
|
88
|
+
const input = readJsonFile(path)
|
|
89
|
+
const list = Array.isArray(input) ? input : input.defects
|
|
90
|
+
if (!Array.isArray(list)) throw new CliError(`--defects ${path} must be a JSON array or a check result with a defects array`)
|
|
91
|
+
return list
|
|
92
|
+
})
|
|
89
93
|
const state = existsSync(statePath)
|
|
90
94
|
? deserializeState(readText(statePath))
|
|
91
95
|
: createTracker({
|
|
@@ -122,7 +126,7 @@ function manifestPath(flags) {
|
|
|
122
126
|
return join(sessionDirFrom(flags), outcome.MANIFEST_FILENAME)
|
|
123
127
|
}
|
|
124
128
|
|
|
125
|
-
export function runOutcome(argv) {
|
|
129
|
+
export async function runOutcome(argv) {
|
|
126
130
|
const [sub, ...rest] = argv
|
|
127
131
|
const args = parseArgs(rest, { booleans: ["json"] })
|
|
128
132
|
const file = sub === "state" ? null : manifestPath(args.flags)
|
|
@@ -135,7 +139,7 @@ export function runOutcome(argv) {
|
|
|
135
139
|
}
|
|
136
140
|
case "set": {
|
|
137
141
|
const [format, status] = args.positionals
|
|
138
|
-
if (!format || !status) throw new CliError("usage: outcome set <format> <status> [--path p] [--reason r] [--pages n]")
|
|
142
|
+
if (!format || !status) throw new CliError("usage: outcome set <format> <status> [--path p] [--reason r] [--pages n] [--verified-by fidelity-defects.json] [--rendered-from source]")
|
|
139
143
|
const manifest = outcome.readManifest(file)
|
|
140
144
|
const detail = {}
|
|
141
145
|
if (args.flags.path !== undefined) {
|
|
@@ -144,13 +148,14 @@ export function runOutcome(argv) {
|
|
|
144
148
|
}
|
|
145
149
|
if (args.flags.reason !== undefined) detail.reason = args.flags.reason
|
|
146
150
|
if (args.flags.pages !== undefined) detail.pages = args.int("pages")
|
|
151
|
+
for (const [flag, key] of [["verified-by", "verifiedBy"], ["rendered-from", "renderedFrom"]]) if (args.flags[flag] !== undefined) detail[key] = resolve(args.flags[flag])
|
|
147
152
|
outcome.writeManifest(file, outcome.setDeliverable(manifest, format, status, { ...detail, now: nowFrom(args.flags) }))
|
|
148
153
|
return { json: { command: "outcome set", format, status }, exitCode: 0 }
|
|
149
154
|
}
|
|
150
155
|
case "gate": {
|
|
151
156
|
const [gate, status] = args.positionals
|
|
152
|
-
if (!gate || !status) throw new CliError(
|
|
153
|
-
outcome.writeManifest(file, outcome.setGate(outcome.readManifest(file), gate, status))
|
|
157
|
+
if (!gate || !status) throw new CliError(`usage: outcome gate <${GATE_NAMES.join("|")}> <${GATE_STATUSES.join("|")}>`)
|
|
158
|
+
outcome.writeManifest(file, outcome.setGate(outcome.readManifest(file), gate, status, { reason: args.flags.reason, now: nowFrom(args.flags) }))
|
|
154
159
|
return { json: { command: "outcome gate", gate, status }, exitCode: 0 }
|
|
155
160
|
}
|
|
156
161
|
case "render": {
|
|
@@ -169,11 +174,17 @@ export function runOutcome(argv) {
|
|
|
169
174
|
return { json: { command: "outcome state", state }, summary: `state: ${state}`, exitCode: 0 }
|
|
170
175
|
}
|
|
171
176
|
case "verify": {
|
|
172
|
-
const
|
|
177
|
+
const manifest = outcome.readManifest(file)
|
|
178
|
+
const base = outcome.verifyManifest(manifest)
|
|
179
|
+
const { fidelityProblems } = await import("./fidelity-records.mjs")
|
|
180
|
+
const problems = [...base.problems, ...fidelityProblems(sessionDirFrom(args.flags), manifest)]
|
|
181
|
+
const result = { ok: problems.length === 0, problems }
|
|
173
182
|
return { json: { command: "outcome verify", ...result }, summary: `verify: ${result.ok ? "OK" : `${result.problems.length} problem(s)`}`, exitCode: result.ok ? 0 : 1 }
|
|
174
183
|
}
|
|
175
184
|
case "finish": {
|
|
176
185
|
const manifest = outcome.readManifest(file)
|
|
186
|
+
const refused = (await import("./fidelity-records.mjs")).fidelityProblems(sessionDirFrom(args.flags), manifest)
|
|
187
|
+
if (refused.length > 0) return { json: { command: "outcome finish", refused: true, problems: refused }, summary: `finish: refused (${refused.map((p) => p.code).join(", ")})`, exitCode: 1 }
|
|
177
188
|
const ledgerPath = args.flags.ledger ?? join(sessionDirFrom(args.flags), "sources-ledger.md")
|
|
178
189
|
const now = nowFrom(args.flags)
|
|
179
190
|
const facts = outcome.briefingFacts(manifest, existsSync(ledgerPath) ? readText(ledgerPath) : "", now)
|
|
@@ -20,6 +20,15 @@ export const COMMANDS = Object.freeze([
|
|
|
20
20
|
"outcome finish",
|
|
21
21
|
"outcome briefing",
|
|
22
22
|
"format-extract",
|
|
23
|
+
"fact add",
|
|
24
|
+
"fact quote",
|
|
25
|
+
"fact retract",
|
|
26
|
+
"fact export",
|
|
27
|
+
"fact fill",
|
|
28
|
+
"fact verify",
|
|
29
|
+
"fact derive",
|
|
30
|
+
"source snapshot",
|
|
31
|
+
"claims index",
|
|
23
32
|
])
|
|
24
33
|
|
|
25
34
|
export async function run(argv) {
|
|
@@ -36,6 +45,12 @@ export async function run(argv) {
|
|
|
36
45
|
return runOutcome(rest)
|
|
37
46
|
case "format-extract":
|
|
38
47
|
return runFormatExtract(rest)
|
|
48
|
+
case "fact":
|
|
49
|
+
return (await import("./fact-commands.mjs")).runFact(rest)
|
|
50
|
+
case "source":
|
|
51
|
+
return (await import("./source-commands.mjs")).runSource(rest)
|
|
52
|
+
case "claims":
|
|
53
|
+
return (await import("./source-commands.mjs")).runClaims(rest)
|
|
39
54
|
case "--help":
|
|
40
55
|
case "help":
|
|
41
56
|
if (rest.includes("--json")) return { json: { commands: COMMANDS }, exitCode: 0 }
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// sentence-split.mjs - deterministic sentence index for snapshot text.
|
|
2
|
+
//
|
|
3
|
+
// splitSentences(text) returns [{ sid, start, end, text }] with sid "s<k>" (1-based) and
|
|
4
|
+
// start/end as offsets into the raw text, so text.slice(start, end) === record.text.
|
|
5
|
+
// Boundaries: a line break; ". ! ?" followed by whitespace or end of line (known
|
|
6
|
+
// abbreviations such as "e.g." excepted); and the Korean endings da. yo. nida. kka? even
|
|
7
|
+
// with no following space, unless closing punctuation follows (quoted speech such as
|
|
8
|
+
// '...da."rago' then needs whitespace like any other terminator). "no." is an abbreviation
|
|
9
|
+
// only before a number ("No. 5"). Plain Node ESM, no dependencies.
|
|
10
|
+
|
|
11
|
+
import { normalizeText } from "./text-normalize.mjs"
|
|
12
|
+
|
|
13
|
+
const TERMINATORS = ".!?"
|
|
14
|
+
const CLOSERS = "\"')]}\u201D\u2019\u00BB\u300D\u300F"
|
|
15
|
+
const KOREAN_PERIOD_ENDINGS = new Set(["\uB2E4", "\uC694"]) // da, yo (nida ends in da)
|
|
16
|
+
const KOREAN_QUESTION_ENDING = "\uAE4C" // kka
|
|
17
|
+
const ABBREVIATIONS = new Set([
|
|
18
|
+
"e.g", "i.e", "etc", "vs", "cf", "approx", "al", "fig", "no", "mr", "mrs", "ms", "dr", "prof", "inc", "ltd", "jr", "sr",
|
|
19
|
+
])
|
|
20
|
+
const SPACE = /\s/u
|
|
21
|
+
|
|
22
|
+
function isKoreanEnding(line, at) {
|
|
23
|
+
const prev = line[at - 1]
|
|
24
|
+
if (line[at] === ".") return KOREAN_PERIOD_ENDINGS.has(prev)
|
|
25
|
+
return line[at] === "?" && prev === KOREAN_QUESTION_ENDING
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isAbbreviation(line, from, at) {
|
|
29
|
+
let k = at
|
|
30
|
+
while (k > from && !SPACE.test(line[k - 1])) k--
|
|
31
|
+
const word = line.slice(k, at).replace(/^[("'[]+/u, "").toLowerCase()
|
|
32
|
+
if (word === "no") return /^\s*\d/u.test(line.slice(at + 1))
|
|
33
|
+
return ABBREVIATIONS.has(word)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function pushSegment(line, base, from, to, out) {
|
|
37
|
+
let start = from
|
|
38
|
+
let end = to
|
|
39
|
+
while (start < end && SPACE.test(line[start])) start++
|
|
40
|
+
while (end > start && SPACE.test(line[end - 1])) end--
|
|
41
|
+
const text = line.slice(start, end)
|
|
42
|
+
if (normalizeText(text) === "") return
|
|
43
|
+
out.push({ sid: `s${out.length + 1}`, start: base + start, end: base + end, text })
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function splitLine(line, base, out) {
|
|
47
|
+
let from = 0
|
|
48
|
+
for (let i = 0; i < line.length; i++) {
|
|
49
|
+
if (!TERMINATORS.includes(line[i])) continue
|
|
50
|
+
let j = i + 1
|
|
51
|
+
while (j < line.length && (TERMINATORS.includes(line[j]) || CLOSERS.includes(line[j]))) j++
|
|
52
|
+
const atBreak = j >= line.length || SPACE.test(line[j])
|
|
53
|
+
const closed = [...line.slice(i + 1, j)].some((c) => CLOSERS.includes(c))
|
|
54
|
+
const korean = !closed && isKoreanEnding(line, i)
|
|
55
|
+
const abbreviation = line[i] === "." && !korean && isAbbreviation(line, from, i)
|
|
56
|
+
if ((atBreak || korean) && !abbreviation) {
|
|
57
|
+
pushSegment(line, base, from, j, out)
|
|
58
|
+
from = j
|
|
59
|
+
}
|
|
60
|
+
i = j - 1
|
|
61
|
+
}
|
|
62
|
+
pushSegment(line, base, from, line.length, out)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function splitSentences(text) {
|
|
66
|
+
const source = String(text ?? "")
|
|
67
|
+
const out = []
|
|
68
|
+
let base = 0
|
|
69
|
+
while (base <= source.length) {
|
|
70
|
+
const newline = source.indexOf("\n", base)
|
|
71
|
+
const stop = newline === -1 ? source.length : newline
|
|
72
|
+
splitLine(source.slice(base, stop), base, out)
|
|
73
|
+
if (newline === -1) break
|
|
74
|
+
base = newline + 1
|
|
75
|
+
}
|
|
76
|
+
return out
|
|
77
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// source-commands.mjs - the `source snapshot` and `claims index` subcommands of report-tools.
|
|
2
|
+
// A source dir is immutable: the same id with identical text is a no-op, different text fails
|
|
3
|
+
// (exit 1, snapshot_exists), because ledger rows pin the old hash. `--url` fetches with plain
|
|
4
|
+
// fetch (30 s timeout, no browser) and records extractor tool:source-snapshot@1; `--html` and
|
|
5
|
+
// `--text` import a browser-lane capture and record extractor model (lower trust), or the named
|
|
6
|
+
// extractor for `--text --extractor NAME@VERSION`; `--command` runs an argv without a shell and
|
|
7
|
+
// snapshots its stdout, with the argv, cwd, exit status, and output hashes in command.json.
|
|
8
|
+
// node: builtins and sibling imports only.
|
|
9
|
+
|
|
10
|
+
import { spawnSync } from "node:child_process"
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs"
|
|
12
|
+
import { join, resolve } from "node:path"
|
|
13
|
+
|
|
14
|
+
import { indexClaims } from "./claims-index.mjs"
|
|
15
|
+
import { CliError, parseArgs, sha256Of, writeJsonAtomic } from "./cli-support.mjs"
|
|
16
|
+
import { parseFidelityBlock } from "./fidelity-brief.mjs"
|
|
17
|
+
import { writeSnapshot } from "./source-snapshot.mjs"
|
|
18
|
+
|
|
19
|
+
const FETCH_TIMEOUT_MS = 30_000
|
|
20
|
+
const TOOL_EXTRACTOR = "tool:source-snapshot@1"
|
|
21
|
+
const SNAPSHOT_USAGE =
|
|
22
|
+
"usage: source snapshot --id S<n> (--url <u> | --html <file> | --text <file> [--extractor NAME@VERSION] | --command '<argv json>' --cwd DIR) [--kind page|command|document] [--provenance primary|secondary] [--revision R] --session-dir D"
|
|
23
|
+
|
|
24
|
+
function readInput(path) {
|
|
25
|
+
try {
|
|
26
|
+
return readFileSync(path, "utf8")
|
|
27
|
+
} catch (error) {
|
|
28
|
+
throw new CliError(`cannot read ${path}: ${error.code ?? error.message}`)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The session dir from --session-dir or SESSION_DIR; a usage error when neither is set. */
|
|
33
|
+
export function sessionDirOf(flags) {
|
|
34
|
+
const dir = flags["session-dir"] ?? process.env.SESSION_DIR
|
|
35
|
+
if (!dir || dir === true) throw new CliError("--session-dir is required (or set SESSION_DIR)")
|
|
36
|
+
return resolve(dir)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The snapshot of one source: meta.json, text.txt, and source.html when the source was a page. */
|
|
40
|
+
export function snapshotOf(sessionDir, id) {
|
|
41
|
+
const dir = join(sessionDir, "sources", String(id))
|
|
42
|
+
if (!existsSync(join(dir, "meta.json"))) throw new CliError(`source ${id} has no snapshot; run source snapshot --id ${id} first`)
|
|
43
|
+
const html = join(dir, "source.html")
|
|
44
|
+
return { meta: JSON.parse(readInput(join(dir, "meta.json"))), text: readInput(join(dir, "text.txt")), html: existsSync(html) ? readInput(html) : null }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Whether the brief's source floor lists this source as pointed at (`pointed only: a, b`). */
|
|
48
|
+
export function pointedFlag(sessionDir, url) {
|
|
49
|
+
const brief = join(sessionDir, "brief.md")
|
|
50
|
+
const floor = existsSync(brief) ? (parseFidelityBlock(readInput(brief)).fields?.["source floor"] ?? "") : ""
|
|
51
|
+
const match = /^pointed only:\s*(.+)$/i.exec(floor.trim())
|
|
52
|
+
return match ? match[1].split(",").map((entry) => entry.trim()).includes(url) : false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function fetchSource(url) {
|
|
56
|
+
if (!URL.canParse(url)) throw new CliError(`--url is not a URL: ${url}`)
|
|
57
|
+
let response
|
|
58
|
+
try {
|
|
59
|
+
response = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) })
|
|
60
|
+
} catch (error) {
|
|
61
|
+
throw new CliError(`fetch failed for ${url}: ${error.message}`)
|
|
62
|
+
}
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
await response.body?.cancel()
|
|
65
|
+
throw new CliError(`fetch failed: HTTP ${response.status} for ${url}`)
|
|
66
|
+
}
|
|
67
|
+
const type = response.headers.get("content-type") ?? ""
|
|
68
|
+
if (/pdf/i.test(type)) {
|
|
69
|
+
await response.body?.cancel()
|
|
70
|
+
throw new CliError(`pdf response: ${url}; extract its text and use --text <file> --extractor NAME@VERSION`)
|
|
71
|
+
}
|
|
72
|
+
const body = await response.text()
|
|
73
|
+
return /html|xml/i.test(type) || /^\s*</.test(body) ? { url, html: body } : { url, text: body }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function runCommand(argvJson, cwd) {
|
|
77
|
+
let argv
|
|
78
|
+
try {
|
|
79
|
+
argv = JSON.parse(argvJson)
|
|
80
|
+
} catch (error) {
|
|
81
|
+
throw new CliError(`--command must be a JSON array of strings: ${error.message}`)
|
|
82
|
+
}
|
|
83
|
+
if (!Array.isArray(argv) || argv.length === 0 || !argv.every((part) => typeof part === "string" && part.length > 0)) {
|
|
84
|
+
throw new CliError("--command must be a non-empty JSON array of non-empty strings")
|
|
85
|
+
}
|
|
86
|
+
if (!cwd || cwd === true) throw new CliError("--command needs --cwd DIR")
|
|
87
|
+
const proc = spawnSync(argv[0], argv.slice(1), { cwd: resolve(cwd), encoding: "utf8", shell: false, maxBuffer: 64 * 1024 * 1024 })
|
|
88
|
+
if (proc.error) throw new CliError(`--command could not start: ${proc.error.message}`)
|
|
89
|
+
const receipt = { argv, cwd: resolve(cwd), exit_status: proc.status, stdout_sha256: sha256Of(proc.stdout), stderr_sha256: sha256Of(proc.stderr) }
|
|
90
|
+
return { path: `command:${argv.join(" ")}`, text: proc.stdout, kind: "command", receipt }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function acquire(flags) {
|
|
94
|
+
const given = ["url", "html", "text", "command"].filter((name) => flags[name] !== undefined)
|
|
95
|
+
if (given.length !== 1) throw new CliError(SNAPSHOT_USAGE)
|
|
96
|
+
if (flags.extractor !== undefined && flags.text === undefined) throw new CliError("--extractor goes with --text only")
|
|
97
|
+
if (flags.url !== undefined) return { ...(await fetchSource(flags.url)), extractor: TOOL_EXTRACTOR }
|
|
98
|
+
if (flags.html !== undefined) return { path: resolve(flags.html), html: readInput(flags.html), extractor: "model" }
|
|
99
|
+
if (flags.text !== undefined) return { path: resolve(flags.text), text: readInput(flags.text), extractor: flags.extractor === undefined ? "model" : `extractor:${flags.extractor}` }
|
|
100
|
+
return { ...runCommand(flags.command, flags.cwd), extractor: TOOL_EXTRACTOR }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function runSource(argv) {
|
|
104
|
+
const [sub, ...rest] = argv
|
|
105
|
+
if (sub !== "snapshot") throw new CliError(SNAPSHOT_USAGE)
|
|
106
|
+
const args = parseArgs(rest)
|
|
107
|
+
const id = args.required("id")
|
|
108
|
+
const sourcesDir = join(sessionDirOf(args.flags), "sources")
|
|
109
|
+
const { receipt, ...input } = await acquire(args.flags)
|
|
110
|
+
if (args.flags.kind !== undefined) input.kind = args.flags.kind
|
|
111
|
+
if (args.flags.provenance !== undefined) input.provenance = args.flags.provenance
|
|
112
|
+
if (args.flags.revision !== undefined) input.revision = args.flags.revision
|
|
113
|
+
let written
|
|
114
|
+
try {
|
|
115
|
+
written = writeSnapshot(sourcesDir, id, input)
|
|
116
|
+
} catch (error) {
|
|
117
|
+
if (String(error.message).startsWith("snapshot_exists")) {
|
|
118
|
+
return { json: { command: "source snapshot", status: "fail", exitCode: 1, error: "snapshot_exists", message: error.message }, summary: `source snapshot: ${error.message}`, exitCode: 1 }
|
|
119
|
+
}
|
|
120
|
+
if (error instanceof TypeError) throw new CliError(error.message)
|
|
121
|
+
throw error
|
|
122
|
+
}
|
|
123
|
+
if (receipt) writeFileSync(join(written.dir, "command.json"), `${JSON.stringify(receipt, null, 2)}\n`)
|
|
124
|
+
const meta = JSON.parse(readFileSync(join(written.dir, "meta.json"), "utf8"))
|
|
125
|
+
const data = { source_id: id, directory: written.dir, snapshot_sha256: written.sha256, extractor: meta.extractor, kind: meta.kind, provenance: meta.provenance }
|
|
126
|
+
return { json: { command: "source snapshot", status: "pass", exitCode: 0, data }, summary: `source snapshot: ${id} ${written.sha256.slice(0, 12)} (${meta.extractor})`, exitCode: 0 }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function runClaims(argv) {
|
|
130
|
+
const [sub, ...rest] = argv
|
|
131
|
+
const args = parseArgs(rest)
|
|
132
|
+
const [graph] = args.positionals
|
|
133
|
+
if (sub !== "index" || !graph) throw new CliError("usage: claims index <claim-graph.md> --out claims.json")
|
|
134
|
+
const out = args.required("out")
|
|
135
|
+
const index = indexClaims(readInput(graph))
|
|
136
|
+
if (index.errors.length > 0) throw new CliError(`claims index ${graph}: ${index.errors.join("; ")}`)
|
|
137
|
+
writeJsonAtomic(out, { schemaVersion: 1, graph: resolve(graph), graph_sha256: sha256Of(readFileSync(graph)), ...index })
|
|
138
|
+
const verified = index.claims.filter((claim) => claim.in_verified).length
|
|
139
|
+
return { json: { command: "claims index", out: resolve(out), claims: index.claims.length, verified }, summary: `claims index: ${index.claims.length} claim(s), ${verified} verified`, exitCode: 0 }
|
|
140
|
+
}
|