chaffjs 0.14.0 → 0.16.0
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.md +27 -2
- package/dist/address-chain.d.ts +4 -1
- package/dist/address-chain.d.ts.map +1 -1
- package/dist/address-chain.js +6 -3
- package/dist/address-chain.js.map +1 -1
- package/dist/backends/anthropic.d.ts +2 -1
- package/dist/backends/anthropic.d.ts.map +1 -1
- package/dist/backends/anthropic.js +4 -1
- package/dist/backends/anthropic.js.map +1 -1
- package/dist/backends/openai.d.ts +2 -1
- package/dist/backends/openai.d.ts.map +1 -1
- package/dist/backends/openai.js +1 -1
- package/dist/backends/openai.js.map +1 -1
- package/dist/backends/types.d.ts +2 -1
- package/dist/backends/types.d.ts.map +1 -1
- package/dist/backends/types.js +1 -1
- package/dist/backends/types.js.map +1 -1
- package/dist/baseline.js +1 -1
- package/dist/baseline.js.map +1 -1
- package/dist/calendar-number.d.ts +26 -0
- package/dist/calendar-number.d.ts.map +1 -0
- package/dist/calendar-number.js +51 -0
- package/dist/calendar-number.js.map +1 -0
- package/dist/cli-args.d.ts +0 -5
- package/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +2 -2
- package/dist/cli-args.js.map +1 -1
- package/dist/cli-text.d.ts +10 -3
- package/dist/cli-text.d.ts.map +1 -1
- package/dist/cli-text.js +33 -14
- package/dist/cli-text.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +53 -47
- package/dist/cli.js.map +1 -1
- package/dist/commands/cite.d.ts.map +1 -1
- package/dist/commands/cite.js +4 -4
- package/dist/commands/cite.js.map +1 -1
- package/dist/commands/eval.d.ts +4 -0
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +21 -7
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/feedback.d.ts.map +1 -1
- package/dist/commands/feedback.js +2 -1
- package/dist/commands/feedback.js.map +1 -1
- package/dist/commands/init-ask.d.ts +19 -0
- package/dist/commands/init-ask.d.ts.map +1 -0
- package/dist/commands/init-ask.js +43 -0
- package/dist/commands/init-ask.js.map +1 -0
- package/dist/commands/test.d.ts +8 -0
- package/dist/commands/test.d.ts.map +1 -1
- package/dist/commands/test.js +73 -40
- package/dist/commands/test.js.map +1 -1
- package/dist/commands/tree.d.ts +1 -8
- package/dist/commands/tree.d.ts.map +1 -1
- package/dist/commands/tree.js +9 -7
- package/dist/commands/tree.js.map +1 -1
- package/dist/config/by-path.d.ts.map +1 -1
- package/dist/config/by-path.js +10 -9
- package/dist/config/by-path.js.map +1 -1
- package/dist/config/load.d.ts +4 -0
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +19 -0
- package/dist/config/load.js.map +1 -1
- package/dist/config/name-problems.d.ts +5 -0
- package/dist/config/name-problems.d.ts.map +1 -0
- package/dist/config/name-problems.js +11 -0
- package/dist/config/name-problems.js.map +1 -0
- package/dist/date-stamp.js +1 -1
- package/dist/date-stamp.js.map +1 -1
- package/dist/detectors/acronym-compound.d.ts +8 -0
- package/dist/detectors/acronym-compound.d.ts.map +1 -0
- package/dist/detectors/acronym-compound.js +8 -0
- package/dist/detectors/acronym-compound.js.map +1 -0
- package/dist/detectors/acronym-context.d.ts +4 -0
- package/dist/detectors/acronym-context.d.ts.map +1 -1
- package/dist/detectors/acronym-context.js +17 -1
- package/dist/detectors/acronym-context.js.map +1 -1
- package/dist/detectors/acronym-expansion.d.ts +10 -0
- package/dist/detectors/acronym-expansion.d.ts.map +1 -1
- package/dist/detectors/acronym-expansion.js +63 -5
- package/dist/detectors/acronym-expansion.js.map +1 -1
- package/dist/detectors/char-shape.d.ts.map +1 -1
- package/dist/detectors/char-shape.js +47 -15
- package/dist/detectors/char-shape.js.map +1 -1
- package/dist/detectors/citation-key.d.ts +13 -0
- package/dist/detectors/citation-key.d.ts.map +1 -0
- package/dist/detectors/citation-key.js +9 -0
- package/dist/detectors/citation-key.js.map +1 -0
- package/dist/detectors/cited-name.d.ts +11 -0
- package/dist/detectors/cited-name.d.ts.map +1 -0
- package/dist/detectors/cited-name.js +15 -0
- package/dist/detectors/cited-name.js.map +1 -0
- package/dist/detectors/concrete-evidence.d.ts +7 -0
- package/dist/detectors/concrete-evidence.d.ts.map +1 -0
- package/dist/detectors/concrete-evidence.js +21 -0
- package/dist/detectors/concrete-evidence.js.map +1 -0
- package/dist/detectors/counter-edge.d.ts +8 -0
- package/dist/detectors/counter-edge.d.ts.map +1 -0
- package/dist/detectors/counter-edge.js +27 -0
- package/dist/detectors/counter-edge.js.map +1 -0
- package/dist/detectors/dotted-name.d.ts +13 -0
- package/dist/detectors/dotted-name.d.ts.map +1 -0
- package/dist/detectors/dotted-name.js +10 -0
- package/dist/detectors/dotted-name.js.map +1 -0
- package/dist/detectors/doubled-word.d.ts +6 -1
- package/dist/detectors/doubled-word.d.ts.map +1 -1
- package/dist/detectors/doubled-word.js +22 -2
- package/dist/detectors/doubled-word.js.map +1 -1
- package/dist/detectors/en-shape.d.ts +1 -1
- package/dist/detectors/en-shape.d.ts.map +1 -1
- package/dist/detectors/en-shape.js +112 -29
- package/dist/detectors/en-shape.js.map +1 -1
- package/dist/detectors/enumerated-runs.d.ts.map +1 -1
- package/dist/detectors/enumerated-runs.js +4 -2
- package/dist/detectors/enumerated-runs.js.map +1 -1
- package/dist/detectors/gram-place.d.ts +0 -4
- package/dist/detectors/gram-place.d.ts.map +1 -1
- package/dist/detectors/gram-place.js +7 -2
- package/dist/detectors/gram-place.js.map +1 -1
- package/dist/detectors/gram-predicate.d.ts +7 -0
- package/dist/detectors/gram-predicate.d.ts.map +1 -0
- package/dist/detectors/gram-predicate.js +44 -0
- package/dist/detectors/gram-predicate.js.map +1 -0
- package/dist/detectors/heading-case.d.ts +21 -0
- package/dist/detectors/heading-case.d.ts.map +1 -0
- package/dist/detectors/heading-case.js +40 -0
- package/dist/detectors/heading-case.js.map +1 -0
- package/dist/detectors/heading-echo.d.ts.map +1 -1
- package/dist/detectors/heading-echo.js +4 -1
- package/dist/detectors/heading-echo.js.map +1 -1
- package/dist/detectors/index.d.ts.map +1 -1
- package/dist/detectors/index.js +4 -3
- package/dist/detectors/index.js.map +1 -1
- package/dist/detectors/lettered-index.d.ts +9 -0
- package/dist/detectors/lettered-index.d.ts.map +1 -0
- package/dist/detectors/lettered-index.js +54 -0
- package/dist/detectors/lettered-index.js.map +1 -0
- package/dist/detectors/lexicon.d.ts +5 -1
- package/dist/detectors/lexicon.d.ts.map +1 -1
- package/dist/detectors/lexicon.js +51 -5
- package/dist/detectors/lexicon.js.map +1 -1
- package/dist/detectors/name-numeral.d.ts +16 -0
- package/dist/detectors/name-numeral.d.ts.map +1 -0
- package/dist/detectors/name-numeral.js +30 -0
- package/dist/detectors/name-numeral.js.map +1 -0
- package/dist/detectors/name-part.d.ts +8 -0
- package/dist/detectors/name-part.d.ts.map +1 -0
- package/dist/detectors/name-part.js +20 -0
- package/dist/detectors/name-part.js.map +1 -0
- package/dist/detectors/name-run.d.ts +8 -0
- package/dist/detectors/name-run.d.ts.map +1 -0
- package/dist/detectors/name-run.js +13 -0
- package/dist/detectors/name-run.js.map +1 -0
- package/dist/detectors/orthography.d.ts.map +1 -1
- package/dist/detectors/orthography.js +35 -7
- package/dist/detectors/orthography.js.map +1 -1
- package/dist/detectors/phrase-match.d.ts +3 -1
- package/dist/detectors/phrase-match.d.ts.map +1 -1
- package/dist/detectors/phrase-match.js +20 -15
- package/dist/detectors/phrase-match.js.map +1 -1
- package/dist/detectors/placeholder.d.ts +14 -0
- package/dist/detectors/placeholder.d.ts.map +1 -0
- package/dist/detectors/placeholder.js +24 -0
- package/dist/detectors/placeholder.js.map +1 -0
- package/dist/detectors/quoted-variant.d.ts +7 -0
- package/dist/detectors/quoted-variant.d.ts.map +1 -0
- package/dist/detectors/quoted-variant.js +18 -0
- package/dist/detectors/quoted-variant.js.map +1 -0
- package/dist/detectors/repeated-head.d.ts.map +1 -1
- package/dist/detectors/repeated-head.js +5 -4
- package/dist/detectors/repeated-head.js.map +1 -1
- package/dist/detectors/series-label.d.ts +13 -0
- package/dist/detectors/series-label.d.ts.map +1 -0
- package/dist/detectors/series-label.js +26 -0
- package/dist/detectors/series-label.js.map +1 -0
- package/dist/detectors/signals.d.ts.map +1 -1
- package/dist/detectors/signals.js +44 -29
- package/dist/detectors/signals.js.map +1 -1
- package/dist/detectors/stacked-hedge.d.ts +23 -0
- package/dist/detectors/stacked-hedge.d.ts.map +1 -0
- package/dist/detectors/stacked-hedge.js +64 -0
- package/dist/detectors/stacked-hedge.js.map +1 -0
- package/dist/detectors/structure-tree.js +1 -1
- package/dist/detectors/structure-tree.js.map +1 -1
- package/dist/detectors/superlative-amount.d.ts +9 -0
- package/dist/detectors/superlative-amount.d.ts.map +1 -0
- package/dist/detectors/superlative-amount.js +20 -0
- package/dist/detectors/superlative-amount.js.map +1 -0
- package/dist/detectors/superlative-clause.d.ts +18 -0
- package/dist/detectors/superlative-clause.d.ts.map +1 -0
- package/dist/detectors/superlative-clause.js +61 -0
- package/dist/detectors/superlative-clause.js.map +1 -0
- package/dist/detectors/superlative-name.d.ts +9 -0
- package/dist/detectors/superlative-name.d.ts.map +1 -0
- package/dist/detectors/superlative-name.js +24 -0
- package/dist/detectors/superlative-name.js.map +1 -0
- package/dist/detectors/superlative-scope.d.ts +6 -0
- package/dist/detectors/superlative-scope.d.ts.map +1 -1
- package/dist/detectors/superlative-scope.js +3 -3
- package/dist/detectors/superlative-scope.js.map +1 -1
- package/dist/detectors/team.d.ts +0 -6
- package/dist/detectors/team.d.ts.map +1 -1
- package/dist/detectors/team.js +17 -1
- package/dist/detectors/team.js.map +1 -1
- package/dist/detectors/token-shape.d.ts.map +1 -1
- package/dist/detectors/token-shape.js +15 -5
- package/dist/detectors/token-shape.js.map +1 -1
- package/dist/document.d.ts +3 -0
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +28 -7
- package/dist/document.js.map +1 -1
- package/dist/eval.js +1 -1
- package/dist/eval.js.map +1 -1
- package/dist/feedback/draft.js +1 -1
- package/dist/feedback/draft.js.map +1 -1
- package/dist/feedback/excerpt.d.ts +0 -1
- package/dist/feedback/excerpt.d.ts.map +1 -1
- package/dist/feedback/excerpt.js +2 -2
- package/dist/feedback/excerpt.js.map +1 -1
- package/dist/file-header.d.ts +20 -0
- package/dist/file-header.d.ts.map +1 -0
- package/dist/file-header.js +21 -0
- package/dist/file-header.js.map +1 -0
- package/dist/files.js +3 -3
- package/dist/files.js.map +1 -1
- package/dist/genre-check.d.ts +34 -0
- package/dist/genre-check.d.ts.map +1 -0
- package/dist/genre-check.js +21 -0
- package/dist/genre-check.js.map +1 -0
- package/dist/genre-load.d.ts +6 -0
- package/dist/genre-load.d.ts.map +1 -0
- package/dist/genre-load.js +22 -0
- package/dist/genre-load.js.map +1 -0
- package/dist/genre-parse.d.ts +40 -0
- package/dist/genre-parse.d.ts.map +1 -0
- package/dist/genre-parse.js +104 -0
- package/dist/genre-parse.js.map +1 -0
- package/dist/genre-suggest.d.ts +15 -0
- package/dist/genre-suggest.d.ts.map +1 -0
- package/dist/genre-suggest.js +26 -0
- package/dist/genre-suggest.js.map +1 -0
- package/dist/genre.d.ts +8 -1
- package/dist/genre.d.ts.map +1 -1
- package/dist/genre.js +19 -5
- package/dist/genre.js.map +1 -1
- package/dist/init-choice.d.ts +12 -0
- package/dist/init-choice.d.ts.map +1 -0
- package/dist/init-choice.js +41 -0
- package/dist/init-choice.js.map +1 -0
- package/dist/init.d.ts +0 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +16 -17
- package/dist/init.js.map +1 -1
- package/dist/joined-view.d.ts +25 -0
- package/dist/joined-view.d.ts.map +1 -0
- package/dist/joined-view.js +76 -0
- package/dist/joined-view.js.map +1 -0
- package/dist/judge.d.ts +2 -1
- package/dist/judge.d.ts.map +1 -1
- package/dist/judge.js +1 -1
- package/dist/judge.js.map +1 -1
- package/dist/line-continues.d.ts +6 -0
- package/dist/line-continues.d.ts.map +1 -0
- package/dist/line-continues.js +33 -0
- package/dist/line-continues.js.map +1 -0
- package/dist/line-paragraphs.d.ts +4 -0
- package/dist/line-paragraphs.d.ts.map +1 -0
- package/dist/line-paragraphs.js +69 -0
- package/dist/line-paragraphs.js.map +1 -0
- package/dist/mask.d.ts.map +1 -1
- package/dist/mask.js +7 -6
- package/dist/mask.js.map +1 -1
- package/dist/measure.d.ts +1 -0
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +5 -1
- package/dist/measure.js.map +1 -1
- package/dist/number-name.d.ts +6 -3
- package/dist/number-name.d.ts.map +1 -1
- package/dist/number-name.js +31 -14
- package/dist/number-name.js.map +1 -1
- package/dist/orthography.d.ts +4 -0
- package/dist/orthography.d.ts.map +1 -1
- package/dist/orthography.js +11 -9
- package/dist/orthography.js.map +1 -1
- package/dist/plain-source.d.ts +6 -0
- package/dist/plain-source.d.ts.map +1 -0
- package/dist/plain-source.js +6 -0
- package/dist/plain-source.js.map +1 -0
- package/dist/plugin.d.ts +22 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/profile/check.d.ts +28 -0
- package/dist/profile/check.d.ts.map +1 -0
- package/dist/profile/check.js +12 -0
- package/dist/profile/check.js.map +1 -0
- package/dist/profile/for-file.d.ts +2 -2
- package/dist/profile/for-file.d.ts.map +1 -1
- package/dist/profile/for-file.js +9 -2
- package/dist/profile/for-file.js.map +1 -1
- package/dist/profile/load.js +1 -1
- package/dist/profile/load.js.map +1 -1
- package/dist/profile/select.d.ts +4 -2
- package/dist/profile/select.d.ts.map +1 -1
- package/dist/profile/select.js +8 -3
- package/dist/profile/select.js.map +1 -1
- package/dist/quoted-span.d.ts +22 -0
- package/dist/quoted-span.d.ts.map +1 -0
- package/dist/quoted-span.js +25 -0
- package/dist/quoted-span.js.map +1 -0
- package/dist/reasons.d.ts +1 -0
- package/dist/reasons.d.ts.map +1 -1
- package/dist/reasons.js +4 -1
- package/dist/reasons.js.map +1 -1
- package/dist/render/compact.d.ts.map +1 -1
- package/dist/render/compact.js +1 -2
- package/dist/render/compact.js.map +1 -1
- package/dist/render/eval.d.ts +2 -1
- package/dist/render/eval.d.ts.map +1 -1
- package/dist/render/eval.js +85 -43
- package/dist/render/eval.js.map +1 -1
- package/dist/render/friendly.d.ts +2 -1
- package/dist/render/friendly.d.ts.map +1 -1
- package/dist/render/friendly.js +14 -6
- package/dist/render/friendly.js.map +1 -1
- package/dist/render/genres.d.ts +5 -0
- package/dist/render/genres.d.ts.map +1 -0
- package/dist/render/genres.js +28 -0
- package/dist/render/genres.js.map +1 -0
- package/dist/render/plural.d.ts +3 -0
- package/dist/render/plural.d.ts.map +1 -0
- package/dist/render/plural.js +5 -0
- package/dist/render/plural.js.map +1 -0
- package/dist/render/rules-json.d.ts.map +1 -1
- package/dist/render/rules-json.js +9 -3
- package/dist/render/rules-json.js.map +1 -1
- package/dist/render/semantic.d.ts +3 -2
- package/dist/render/semantic.d.ts.map +1 -1
- package/dist/render/semantic.js +94 -34
- package/dist/render/semantic.js.map +1 -1
- package/dist/render/summary.d.ts.map +1 -1
- package/dist/render/summary.js +4 -3
- package/dist/render/summary.js.map +1 -1
- package/dist/render/suppressions.d.ts.map +1 -1
- package/dist/render/suppressions.js +3 -2
- package/dist/render/suppressions.js.map +1 -1
- package/dist/render/text.d.ts +2 -0
- package/dist/render/text.d.ts.map +1 -1
- package/dist/render/text.js +11 -3
- package/dist/render/text.js.map +1 -1
- package/dist/resolve-genre.d.ts +11 -0
- package/dist/resolve-genre.d.ts.map +1 -0
- package/dist/resolve-genre.js +18 -0
- package/dist/resolve-genre.js.map +1 -0
- package/dist/rule-load.d.ts.map +1 -1
- package/dist/rule-load.js +4 -1
- package/dist/rule-load.js.map +1 -1
- package/dist/run-semantic.js +1 -1
- package/dist/run-semantic.js.map +1 -1
- package/dist/run.d.ts +2 -0
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +25 -15
- package/dist/run.js.map +1 -1
- package/dist/sentence-shape.d.ts +0 -1
- package/dist/sentence-shape.d.ts.map +1 -1
- package/dist/sentence-shape.js +3 -5
- package/dist/sentence-shape.js.map +1 -1
- package/dist/setting-problems.d.ts +5 -0
- package/dist/setting-problems.d.ts.map +1 -0
- package/dist/setting-problems.js +15 -0
- package/dist/setting-problems.js.map +1 -0
- package/dist/soft-break.d.ts +19 -0
- package/dist/soft-break.d.ts.map +1 -0
- package/dist/soft-break.js +78 -0
- package/dist/soft-break.js.map +1 -0
- package/dist/structure/build.d.ts.map +1 -1
- package/dist/structure/build.js +24 -14
- package/dist/structure/build.js.map +1 -1
- package/dist/structure/cite.js +1 -1
- package/dist/structure/cite.js.map +1 -1
- package/dist/structure/date-order.d.ts +3 -0
- package/dist/structure/date-order.d.ts.map +1 -1
- package/dist/structure/date-order.js +9 -1
- package/dist/structure/date-order.js.map +1 -1
- package/dist/structure/issues.d.ts +1 -1
- package/dist/structure/issues.d.ts.map +1 -1
- package/dist/structure/issues.js +19 -8
- package/dist/structure/issues.js.map +1 -1
- package/dist/structure/listed-tags.d.ts +3 -0
- package/dist/structure/listed-tags.d.ts.map +1 -0
- package/dist/structure/listed-tags.js +6 -0
- package/dist/structure/listed-tags.js.map +1 -0
- package/dist/structure/markdown-path.d.ts.map +1 -1
- package/dist/structure/markdown-path.js +2 -2
- package/dist/structure/markdown-path.js.map +1 -1
- package/dist/structure/number-in-sentence.d.ts +15 -0
- package/dist/structure/number-in-sentence.d.ts.map +1 -0
- package/dist/structure/number-in-sentence.js +13 -0
- package/dist/structure/number-in-sentence.js.map +1 -0
- package/dist/structure/numbering.d.ts.map +1 -1
- package/dist/structure/numbering.js +1 -1
- package/dist/structure/numbering.js.map +1 -1
- package/dist/structure/relative-find.js +1 -1
- package/dist/structure/relative-find.js.map +1 -1
- package/dist/structure/sexp.js +1 -1
- package/dist/structure/sexp.js.map +1 -1
- package/dist/structure/unit-word.d.ts +8 -0
- package/dist/structure/unit-word.d.ts.map +1 -0
- package/dist/structure/unit-word.js +10 -0
- package/dist/structure/unit-word.js.map +1 -0
- package/dist/structure/unnumbered.js +2 -2
- package/dist/structure/unnumbered.js.map +1 -1
- package/dist/structure/wrapped-tail.d.ts +18 -0
- package/dist/structure/wrapped-tail.d.ts.map +1 -0
- package/dist/structure/wrapped-tail.js +11 -0
- package/dist/structure/wrapped-tail.js.map +1 -0
- package/dist/team-names.d.ts +9 -0
- package/dist/team-names.d.ts.map +1 -0
- package/dist/team-names.js +34 -0
- package/dist/team-names.js.map +1 -0
- package/dist/ui.d.ts +2 -0
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +6 -0
- package/dist/ui.js.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/watch.d.ts +2 -2
- package/dist/watch.d.ts.map +1 -1
- package/dist/watch.js +5 -5
- package/dist/watch.js.map +1 -1
- package/genres.yaml +363 -0
- package/package.json +4 -3
- package/rules/adverb-overuse.yaml +2 -2
- package/rules/agentless-passive.yaml +1 -1
- package/rules/ai-generated-composite.yaml +2 -2
- package/rules/ai-tell.yaml +2 -2
- package/rules/bold-density.yaml +2 -2
- package/rules/closing-cliche.yaml +1 -1
- package/rules/concrete-evidence-density.yaml +2 -2
- package/rules/contraction-consistency.yaml +1 -1
- package/rules/cushion-phrase-density.yaml +1 -1
- package/rules/dangling-reference.yaml +3 -3
- package/rules/date-order.yaml +3 -3
- package/rules/date-weekday-mismatch.yaml +1 -1
- package/rules/double-keigo.yaml +1 -1
- package/rules/doubled-word.yaml +1 -1
- package/rules/duplicate-definition.yaml +1 -1
- package/rules/emoji-density.yaml +1 -1
- package/rules/empty-conclusion.yaml +2 -1
- package/rules/empty-intensifier.yaml +1 -1
- package/rules/excessive-hedging.yaml +16 -6
- package/rules/expletive-construction.yaml +2 -2
- package/rules/heading-echo.yaml +1 -1
- package/rules/hiragana-fukushi.yaml +1 -1
- package/rules/internal-jargon.yaml +2 -2
- package/rules/latin-spacing.yaml +6 -3
- package/rules/max-kanji-continuous.yaml +5 -4
- package/rules/max-paragraph-length.yaml +9 -2
- package/rules/max-sentence-length.yaml +14 -2
- package/rules/ngram-repetition.yaml +4 -4
- package/rules/no-doubled-joshi.yaml +4 -4
- package/rules/no-em-dash.yaml +2 -2
- package/rules/no-mixed-desumasu.yaml +1 -1
- package/rules/no-nakaguro-parallel.yaml +2 -2
- package/rules/numbering-gap.yaml +1 -1
- package/rules/oxford-comma-consistency.yaml +2 -1
- package/rules/padded-intro.yaml +2 -1
- package/rules/paragraph-length-variance.yaml +1 -1
- package/rules/preamble-length.yaml +2 -2
- package/rules/preferred-term.yaml +1 -1
- package/rules/proper-noun-density.yaml +3 -2
- package/rules/repeated-conjunction.yaml +2 -2
- package/rules/repeated-sentence-head.yaml +2 -2
- package/rules/required-sections.yaml +1 -1
- package/rules/rule-of-three.yaml +2 -2
- package/rules/sasete-itadaku.yaml +3 -3
- package/rules/section-length-uniformity.yaml +1 -1
- package/rules/sentence-initial-conjunction-run.yaml +2 -2
- package/rules/sentence-rhythm.yaml +1 -1
- package/rules/taigen-dome-in-prose.yaml +2 -2
- package/rules/title-case-consistency.yaml +3 -3
- package/rules/total-mismatch.yaml +1 -1
- package/rules/undefined-acronym.yaml +18 -5
- package/rules/unqualified-superlative.yaml +3 -3
- package/skills/chaff/SKILL.md +28 -9
- package/src/address-chain.ts +6 -3
- package/src/backends/anthropic.ts +5 -1
- package/src/backends/openai.ts +2 -1
- package/src/backends/types.ts +3 -2
- package/src/baseline.ts +1 -1
- package/src/calendar-number.ts +75 -0
- package/src/cli-args.ts +2 -2
- package/src/cli-text.ts +48 -18
- package/src/cli.ts +51 -47
- package/src/commands/cite.ts +4 -4
- package/src/commands/eval.ts +27 -6
- package/src/commands/feedback.ts +2 -1
- package/src/commands/init-ask.ts +53 -0
- package/src/commands/test.ts +105 -47
- package/src/commands/tree.ts +9 -7
- package/src/config/by-path.ts +8 -9
- package/src/config/load.ts +24 -0
- package/src/config/name-problems.ts +15 -0
- package/src/date-stamp.ts +1 -1
- package/src/detectors/acronym-compound.ts +9 -0
- package/src/detectors/acronym-context.ts +22 -1
- package/src/detectors/acronym-expansion.ts +72 -5
- package/src/detectors/char-shape.ts +56 -16
- package/src/detectors/citation-key.ts +13 -0
- package/src/detectors/cited-name.ts +23 -0
- package/src/detectors/concrete-evidence.ts +31 -0
- package/src/detectors/counter-edge.ts +29 -0
- package/src/detectors/dotted-name.ts +14 -0
- package/src/detectors/doubled-word.ts +25 -2
- package/src/detectors/en-shape.ts +123 -38
- package/src/detectors/enumerated-runs.ts +3 -2
- package/src/detectors/gram-place.ts +9 -2
- package/src/detectors/gram-predicate.ts +55 -0
- package/src/detectors/heading-case.ts +44 -0
- package/src/detectors/heading-echo.ts +5 -1
- package/src/detectors/index.ts +4 -3
- package/src/detectors/lettered-index.ts +78 -0
- package/src/detectors/lexicon.ts +68 -8
- package/src/detectors/name-numeral.ts +37 -0
- package/src/detectors/name-part.ts +24 -0
- package/src/detectors/name-run.ts +16 -0
- package/src/detectors/orthography.ts +43 -10
- package/src/detectors/phrase-match.ts +23 -12
- package/src/detectors/placeholder.ts +38 -0
- package/src/detectors/quoted-variant.ts +20 -0
- package/src/detectors/repeated-head.ts +3 -4
- package/src/detectors/series-label.ts +33 -0
- package/src/detectors/signals.ts +49 -35
- package/src/detectors/stacked-hedge.ts +93 -0
- package/src/detectors/structure-tree.ts +2 -2
- package/src/detectors/superlative-amount.ts +24 -0
- package/src/detectors/superlative-clause.ts +82 -0
- package/src/detectors/superlative-name.ts +30 -0
- package/src/detectors/superlative-scope.ts +3 -3
- package/src/detectors/team.ts +18 -2
- package/src/detectors/token-shape.ts +13 -6
- package/src/document.ts +41 -8
- package/src/eval.ts +1 -1
- package/src/feedback/draft.ts +1 -1
- package/src/feedback/excerpt.ts +2 -2
- package/src/file-header.ts +31 -0
- package/src/files.ts +3 -3
- package/src/genre-check.ts +46 -0
- package/src/genre-load.ts +24 -0
- package/src/genre-parse.ts +126 -0
- package/src/genre-suggest.ts +37 -0
- package/src/genre.ts +25 -5
- package/src/init-choice.ts +47 -0
- package/src/init.ts +20 -19
- package/src/joined-view.ts +96 -0
- package/src/judge.ts +3 -1
- package/src/line-continues.ts +43 -0
- package/src/line-paragraphs.ts +83 -0
- package/src/mask.ts +5 -6
- package/src/measure.ts +9 -1
- package/src/number-name.ts +35 -13
- package/src/orthography.ts +15 -9
- package/src/plain-source.ts +5 -0
- package/src/plugin.ts +27 -2
- package/src/profile/check.ts +34 -0
- package/src/profile/for-file.ts +10 -3
- package/src/profile/load.ts +1 -1
- package/src/profile/select.ts +11 -4
- package/src/quoted-span.ts +39 -0
- package/src/reasons.ts +5 -1
- package/src/render/compact.ts +1 -3
- package/src/render/eval.ts +102 -38
- package/src/render/friendly.ts +21 -6
- package/src/render/genres.ts +31 -0
- package/src/render/plural.ts +6 -0
- package/src/render/rules-json.ts +10 -3
- package/src/render/semantic.ts +117 -34
- package/src/render/summary.ts +4 -3
- package/src/render/suppressions.ts +3 -2
- package/src/render/text.ts +18 -3
- package/src/resolve-genre.ts +20 -0
- package/src/rule-load.ts +6 -1
- package/src/run-semantic.ts +1 -1
- package/src/run.ts +28 -15
- package/src/sentence-shape.ts +3 -7
- package/src/setting-problems.ts +24 -0
- package/src/soft-break.ts +94 -0
- package/src/structure/build.ts +24 -15
- package/src/structure/cite.ts +1 -1
- package/src/structure/date-order.ts +16 -1
- package/src/structure/issues.ts +24 -8
- package/src/structure/listed-tags.ts +7 -0
- package/src/structure/markdown-path.ts +2 -2
- package/src/structure/number-in-sentence.ts +20 -0
- package/src/structure/numbering.ts +1 -1
- package/src/structure/relative-find.ts +1 -1
- package/src/structure/sexp.ts +1 -1
- package/src/structure/unit-word.ts +11 -0
- package/src/structure/unnumbered.ts +2 -2
- package/src/structure/wrapped-tail.ts +22 -0
- package/src/team-names.ts +39 -0
- package/src/ui.ts +7 -0
- package/src/version.ts +1 -1
- package/src/watch.ts +5 -5
package/skills/chaff/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: chaff
|
|
3
|
-
description: "Check prose with chaff (npx chaffjs): run it on Markdown or text, read each finding, fix the text or record why not, and tune chaff.yaml with rules --json and relax --why. Use when asked to lint, proofread or tighten documents, or to set up a team's writing rules."
|
|
3
|
+
description: "Check prose with chaff (npx chaffjs): pick the genre preset for the kind of document (contract, statute, manual, FAQ, paper, fiction, transcript…), run it on Markdown or text, read each finding, fix the text or record why not, and tune chaff.yaml with rules --json and relax --why. Use when asked to lint, proofread or tighten documents, or to set up a team's writing rules."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Checking writing with chaff
|
|
@@ -9,12 +9,30 @@ chaff finds what makes writing hard to read — long sentences, padding, mixed s
|
|
|
9
9
|
contract — and never rewrites anything. Reading the findings and changing the text is your part. Japanese and
|
|
10
10
|
English documents both work; the language is detected per file.
|
|
11
11
|
|
|
12
|
+
## Pick the genre first
|
|
13
|
+
|
|
14
|
+
A genre is a preset for one kind of document: which rules run, where their limits are, and how the document is
|
|
15
|
+
read. A contract checked as a blog post gets wrong findings, so choose the genre before reading any finding.
|
|
16
|
+
|
|
17
|
+
1. Name the kind from the document itself. `npx chaffjs genres` lists every genre with what it is for:
|
|
18
|
+
`legal/contract`, `legal/statute`, `docs/manual`, `docs/faq`, `academic/paper`, `literature/fiction`,
|
|
19
|
+
`speech/transcript`, `business/report`, `technical/spec`, …
|
|
20
|
+
2. Unsure? Run once without `--genre`. When the file looks like another kind, the line under the header says so
|
|
21
|
+
(`Looks like: Contract and terms. Try --genre legal/contract`); use that genre if it fits.
|
|
22
|
+
3. Run with it: `npx chaffjs --genre legal/contract contract.md`. For a folder of one kind,
|
|
23
|
+
`npx chaffjs init --genre legal/contract` writes it into `chaff.yaml` (always pass `--genre`: without it,
|
|
24
|
+
`init` asks at a terminal).
|
|
25
|
+
4. The header's first line shows the genre used and where it came from. Check it before trusting the findings.
|
|
26
|
+
|
|
27
|
+
A rule the genre turns off is listed under "did not run" with the genre as the reason. That is the preset's
|
|
28
|
+
choice, not a gap; turn it back on (`rules:` in `chaff.yaml`) only when the person asks.
|
|
29
|
+
|
|
12
30
|
## Run it
|
|
13
31
|
|
|
14
32
|
```sh
|
|
15
|
-
npx chaffjs <file|dir|glob>...
|
|
16
|
-
npx chaffjs . --compact
|
|
17
|
-
npx chaffjs <file> --experimental
|
|
33
|
+
npx chaffjs --genre <genre> <file|dir|glob>... # human-readable, one block per finding (a folder or glob picks up .md / .markdown / .mdx; name a .txt file directly)
|
|
34
|
+
npx chaffjs . --compact # two lines per finding: line:col severity message, then the rule id
|
|
35
|
+
npx chaffjs <file> --experimental # also the experimental rules
|
|
18
36
|
```
|
|
19
37
|
|
|
20
38
|
A run exits 1 when a finding is an `error`. It also exits 1 when it could not check anything: no file to
|
|
@@ -39,15 +57,16 @@ relaxing a rule.
|
|
|
39
57
|
|
|
40
58
|
## Tune chaff.yaml
|
|
41
59
|
|
|
42
|
-
- `npx chaffjs init
|
|
43
|
-
adds `.chaff-cache/` and `.env*` to `.gitignore`.
|
|
44
|
-
- `npx chaffjs rules --json` prints every rule with its levels, the setting in effect (`now
|
|
45
|
-
off. Read it instead of guessing rule names or limits.
|
|
60
|
+
- `npx chaffjs init --genre <genre>` writes a commented `chaff.yaml` for that genre when there is none (an
|
|
61
|
+
existing one is left as it is), and adds `.chaff-cache/` and `.env*` to `.gitignore`.
|
|
62
|
+
- `npx chaffjs rules --json [--genre <genre>]` prints every rule with its levels, the setting in effect (`now`,
|
|
63
|
+
the genre's preset included) and why a rule is off. Read it instead of guessing rule names or limits.
|
|
46
64
|
- A level is `strict` / `normal` / `relaxed` / `off`; when even `relaxed` is too tight, a positive number is the
|
|
47
65
|
limit itself (`max-sentence-length: 260`).
|
|
48
66
|
- `prefer:` maps spellings to the team's (`e-mail: email`); `preferred-term: normal` turns the rule on.
|
|
49
67
|
- Unknown rule names and unreadable values are reported on stderr — read stderr after editing `chaff.yaml`.
|
|
50
|
-
- `genre:` sets the kind of document
|
|
68
|
+
- `genre:` sets the kind of document; `by_path` sets one per folder; `--genre` overrides both for one run.
|
|
69
|
+
`rules:` wins over the genre's preset.
|
|
51
70
|
|
|
52
71
|
## Structured documents
|
|
53
72
|
|
package/src/address-chain.ts
CHANGED
|
@@ -78,7 +78,7 @@ const chainAt = (patterns: Compiled, text: string, start: number): { readonly sp
|
|
|
78
78
|
const firstEnd = matchEnd(patterns.address, text, start);
|
|
79
79
|
if (firstEnd === undefined) return undefined;
|
|
80
80
|
const { spans, cuts } = readChain(patterns, text, { start, end: firstEnd });
|
|
81
|
-
const cut =
|
|
81
|
+
const cut = cuts.findLast((candidate) => endsHere(patterns, text, candidate.end));
|
|
82
82
|
return cut === undefined ? undefined : { spans: spans.slice(0, cut.addresses), end: cut.end };
|
|
83
83
|
};
|
|
84
84
|
|
|
@@ -102,12 +102,15 @@ export const addressSpans = (text: string, profile: DocumentProfile | undefined)
|
|
|
102
102
|
return spans;
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* 番地を同じ長さの空白にする。空白が連なりを切るので、番地は数えられず、前後は別の連なりになる。
|
|
107
|
+
* 長さを保つので、覆った後の位置は元の文字列の位置のまま。
|
|
108
|
+
*/
|
|
106
109
|
export const maskAddresses = (text: string, profile: DocumentProfile | undefined): string => {
|
|
107
110
|
const spans = addressSpans(text, profile);
|
|
108
111
|
if (spans.length === 0) return text;
|
|
109
112
|
const pieces: string[] = [];
|
|
110
|
-
spans.forEach((span, index) => pieces.push(text.slice(spans[index - 1]?.end ?? 0, span.start), " "));
|
|
113
|
+
spans.forEach((span, index) => pieces.push(text.slice(spans[index - 1]?.end ?? 0, span.start), " ".repeat(span.end - span.start)));
|
|
111
114
|
pieces.push(text.slice(spans.at(-1)?.end ?? 0));
|
|
112
115
|
return pieces.join("");
|
|
113
116
|
};
|
|
@@ -4,6 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import Anthropic from "@anthropic-ai/sdk";
|
|
5
5
|
import { toFailure } from "./types.ts";
|
|
6
6
|
import type { Failure, Judge, Prompt } from "./types.ts";
|
|
7
|
+
import type { Texts } from "../ui.ts";
|
|
7
8
|
|
|
8
9
|
type AnthropicResponse = { readonly content: readonly { readonly type: string; readonly text?: string | undefined }[] };
|
|
9
10
|
|
|
@@ -58,4 +59,7 @@ export const judge =
|
|
|
58
59
|
};
|
|
59
60
|
|
|
60
61
|
/** 認証が無いときに人へ見せる案内。 */
|
|
61
|
-
export const SETUP_HINT =
|
|
62
|
+
export const SETUP_HINT: Texts<string> = {
|
|
63
|
+
ja: "ANTHROPIC_API_KEY を設定するか、ant auth login を実行してください。",
|
|
64
|
+
en: "Set ANTHROPIC_API_KEY, or run ant auth login.",
|
|
65
|
+
};
|
package/src/backends/openai.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
2
|
import { toFailure } from "./types.ts";
|
|
3
3
|
import type { Failure, Judge, Prompt } from "./types.ts";
|
|
4
|
+
import type { Texts } from "../ui.ts";
|
|
4
5
|
|
|
5
6
|
type OpenAIResponse = { readonly choices: readonly { readonly message: { readonly content: string | null } }[] };
|
|
6
7
|
|
|
@@ -43,4 +44,4 @@ export const judge =
|
|
|
43
44
|
return body;
|
|
44
45
|
};
|
|
45
46
|
|
|
46
|
-
export const SETUP_HINT = "OPENAI_API_KEY を設定してください。";
|
|
47
|
+
export const SETUP_HINT: Texts<string> = { ja: "OPENAI_API_KEY を設定してください。", en: "Set OPENAI_API_KEY." };
|
package/src/backends/types.ts
CHANGED
|
@@ -17,11 +17,12 @@ const BACKENDS: readonly BackendName[] = ["anthropic", "openai"];
|
|
|
17
17
|
export const isBackend = (value: unknown): value is BackendName => BACKENDS.some((name) => name === value);
|
|
18
18
|
|
|
19
19
|
/** API が返した失敗。生のスタックトレースを人に見せないための、最小の分類。 */
|
|
20
|
-
|
|
20
|
+
/** message is the provider's own words, undefined when it gave none: the screen says so in the reader's language. */
|
|
21
|
+
export type Failure = { readonly kind: "auth" | "quota" | "api"; readonly status: number | undefined; readonly message: string | undefined };
|
|
21
22
|
|
|
22
23
|
const statusOf = (value: unknown): number | undefined => (typeof value === "number" ? value : undefined);
|
|
23
24
|
|
|
24
|
-
const messageOf = (value: unknown): string => (typeof value === "string" && value.length > 0 ? value :
|
|
25
|
+
const messageOf = (value: unknown): string | undefined => (typeof value === "string" && value.length > 0 ? value : undefined);
|
|
25
26
|
|
|
26
27
|
const kindOf = (status: number | undefined): Failure["kind"] => {
|
|
27
28
|
if (status === 401 || status === 403) return "auth";
|
package/src/baseline.ts
CHANGED
|
@@ -29,7 +29,7 @@ export const readBaseline = (path: string): Baseline | undefined => {
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
export const writeBaseline = (path: string, entries: readonly string[]): void => {
|
|
32
|
-
const sorted =
|
|
32
|
+
const sorted = entries.toSorted((left, right) => left.localeCompare(right, "en"));
|
|
33
33
|
const body = { version: 1, created: new Date().toISOString().slice(0, 10), entries: sorted };
|
|
34
34
|
writeFileSync(path, `${JSON.stringify(body, null, 2)}\n`, "utf8");
|
|
35
35
|
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { digitRunAround } from "./number-name.ts";
|
|
2
|
+
import type { Span, Token } from "./plugin.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 日付・時刻として書かれた数(9月、3時、2026年、10月1日、10時5分)。数を詰めて書くのが決まりで、空け方の好みではない。
|
|
6
|
+
* 長さ(3ヶ月、3時間、3日間)は別の語で書くので、ここには入らない。
|
|
7
|
+
*
|
|
8
|
+
* 単位は語彙表から渡す。chained は日付・時刻の単位を大きい順に(年月日時分秒)。大きい単位の後ろに小さい単位を続けて書けば
|
|
9
|
+
* 一つの日付・時刻になる。「2008年 2年連続」のように同じ単位や大きい単位が続けば、別のものの始まり。
|
|
10
|
+
* positional は数の後ろに置くだけで位置を言う単位(月、時)。year は 4 桁の数の後ろで暦の年を言う単位(年、年度)。
|
|
11
|
+
* 「5日」「5分」「3年」のように、単位だけでは長さと見分けのつかないものは、位置を言う単位と続けて書いたときだけ日付・時刻と読む。
|
|
12
|
+
*/
|
|
13
|
+
export type CalendarUnits = { readonly chained: readonly string[]; readonly positional: ReadonlySet<string>; readonly year: ReadonlySet<string> };
|
|
14
|
+
|
|
15
|
+
/** 日付・時刻の数の並び。first は日付の頭の数、inner は前の単位に続く数(2026年9月の 9)。 */
|
|
16
|
+
export type CalendarRun = { readonly run: Span; readonly place: "first" | "inner" };
|
|
17
|
+
|
|
18
|
+
/** 数の並びと、その後ろの単位の終わり(文の中の位置)。rank は chained の中の単位の順(無ければ -1)、anchored は単位だけで日付・時刻と分かるもの。 */
|
|
19
|
+
type Part = { readonly run: Span; readonly unitEnd: number; readonly rank: number; readonly anchored: boolean };
|
|
20
|
+
|
|
21
|
+
const DIGIT = /\d/u;
|
|
22
|
+
const CALENDAR_YEAR = /^\d{4}$/u;
|
|
23
|
+
|
|
24
|
+
/** text の中の数の並び(数字・小数点・ハイフン)を左から。 */
|
|
25
|
+
const digitRuns = (text: string): Span[] => {
|
|
26
|
+
const runs: Span[] = [];
|
|
27
|
+
let at = 0;
|
|
28
|
+
while (at < text.length) {
|
|
29
|
+
const run = DIGIT.test(text[at] ?? "") ? digitRunAround(text, at) : undefined;
|
|
30
|
+
if (run !== undefined) runs.push(run);
|
|
31
|
+
at = Math.max(run?.end ?? 0, at + 1);
|
|
32
|
+
}
|
|
33
|
+
return runs;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** 空白 1 つを越えた位置。 */
|
|
37
|
+
const pastSpace = (text: string, at: number): number => (text[at] === " " ? at + 1 : at);
|
|
38
|
+
|
|
39
|
+
const isUnit = (surface: string, units: CalendarUnits): boolean => units.chained.includes(surface) || units.positional.has(surface) || units.year.has(surface);
|
|
40
|
+
|
|
41
|
+
const isAnchor = (digits: string, surface: string, units: CalendarUnits): boolean =>
|
|
42
|
+
units.positional.has(surface) || (units.year.has(surface) && CALENDAR_YEAR.test(digits));
|
|
43
|
+
|
|
44
|
+
/** 数のすぐ後ろ(空白 1 つまで)の語が日付・時刻の単位なら、その組。語の切れ目が合わなければ組にしない。 */
|
|
45
|
+
const partOf = (text: string, run: Span, tokens: readonly Token[], base: number, units: CalendarUnits): Part | undefined => {
|
|
46
|
+
const unit = tokens.find((token) => token.span.start === base + pastSpace(text, run.end));
|
|
47
|
+
if (unit === undefined || !isUnit(unit.surface, units)) return undefined;
|
|
48
|
+
const rank = units.chained.indexOf(unit.surface);
|
|
49
|
+
return { run, unitEnd: unit.span.end - base, rank, anchored: isAnchor(text.slice(run.start, run.end), unit.surface, units) };
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** 前の組の単位のすぐ後ろ(空白 1 つまで)から始まり、単位が前より小さい組か。 */
|
|
53
|
+
const continues = (text: string, previous: Part | undefined, part: Part): boolean =>
|
|
54
|
+
previous !== undefined && previous.rank >= 0 && part.rank > previous.rank && part.run.start === pastSpace(text, previous.unitEnd);
|
|
55
|
+
|
|
56
|
+
/** 前の組に続く組を、同じ日付・時刻にまとめる。 */
|
|
57
|
+
const chainsOf = (text: string, parts: readonly Part[]): Part[][] =>
|
|
58
|
+
parts.reduce<Part[][]>((chains, part) => {
|
|
59
|
+
const chain = chains.at(-1);
|
|
60
|
+
if (chain !== undefined && continues(text, chain.at(-1), part)) chain.push(part);
|
|
61
|
+
else chains.push([part]);
|
|
62
|
+
return chains;
|
|
63
|
+
}, []);
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 文 text の中の、日付・時刻の数の並び。tokens は文書全体の座標で、base は text の先頭の位置。
|
|
67
|
+
* 品詞が無ければ判断せず、何も返さない。
|
|
68
|
+
*/
|
|
69
|
+
export const calendarRuns = (text: string, tokens: readonly Token[] | undefined, base: number, units: CalendarUnits): CalendarRun[] => {
|
|
70
|
+
if (tokens === undefined) return [];
|
|
71
|
+
const parts = digitRuns(text).flatMap((run) => partOf(text, run, tokens, base, units) ?? []);
|
|
72
|
+
return chainsOf(text, parts)
|
|
73
|
+
.filter((chain) => chain.some((part) => part.anchored))
|
|
74
|
+
.flatMap((chain) => chain.map((part, index): CalendarRun => ({ run: part.run, place: index === 0 ? "first" : "inner" })));
|
|
75
|
+
};
|
package/src/cli-args.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Options of the commands that take files to check, whose value is the next argument. That value is not a file.
|
|
3
3
|
* `--why` (relax), `--format` and `--language` (tree) belong to commands that read their own arguments.
|
|
4
4
|
*/
|
|
5
|
-
|
|
5
|
+
const VALUE_FLAGS: ReadonlySet<string> = new Set(["--sarif", "--genre", "--rule", "--line"]);
|
|
6
6
|
|
|
7
7
|
/** The arguments that name what to check: everything that is neither an option nor an option's value. */
|
|
8
8
|
export const targetsOf = (args: readonly string[]): string[] =>
|
|
9
|
-
args.filter((arg, index) => !arg.startsWith("--") && !(index > 0 && VALUE_FLAGS.
|
|
9
|
+
args.filter((arg, index) => !arg.startsWith("--") && !(index > 0 && VALUE_FLAGS.has(args[index - 1] ?? "")));
|
package/src/cli-text.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// What the command line says, in Japanese and English. Per-file text follows the file's language; the rest
|
|
2
2
|
// follows hostLanguage (chaff.yaml's language, else the terminal's locale).
|
|
3
3
|
import type { Texts } from "./ui.ts";
|
|
4
|
+
import { counted, formFor } from "./render/plural.ts";
|
|
5
|
+
import type { GenreSetting } from "./genre-check.ts";
|
|
6
|
+
import type { ProfileSetting } from "./profile/check.ts";
|
|
4
7
|
|
|
5
8
|
/** Where a file's genre came from, as the header names it. */
|
|
6
9
|
export type GenreSource = "--genre" | "by_path" | "config" | "default" | "front-matter" | "path" | "content";
|
|
@@ -11,11 +14,11 @@ const USAGE_JA = `chaff — 文章の読みにくいところを見つけます
|
|
|
11
14
|
chaff . この場所の Markdown を全部
|
|
12
15
|
chaff test <file|dir>... 意味を読む検査も動かす(API key が要ります)
|
|
13
16
|
判定役は chaff.yaml の ai_backend で選びます
|
|
14
|
-
chaff init chaff.yaml
|
|
17
|
+
chaff init chaff.yaml を作る(端末ならジャンルを尋ねる。--genre <ジャンル> でも選べる)
|
|
15
18
|
chaff --version chaffjs と言語パッケージの版
|
|
16
19
|
chaff eval <dir> 手元の文書で閾値を測り直す
|
|
17
20
|
chaff explain <rule> そのルールの意図と根拠を読む
|
|
18
|
-
chaff genres
|
|
21
|
+
chaff genres ジャンル(文書の種類)の一覧と、それぞれ何向けか
|
|
19
22
|
chaff tree <file> [--format sexp|json] 文書を番地の付いた木にする(条・項・定義・参照)
|
|
20
23
|
chaff cite <原文> <引用.json> 回答の引用(番地と引用文)が原文にあるかを確かめる
|
|
21
24
|
chaff rules --json いまの設定を JSON で出す(AI に渡す用)
|
|
@@ -45,11 +48,11 @@ const USAGE_EN = `chaff — finds what makes writing hard to read. It never rewr
|
|
|
45
48
|
chaff . every Markdown file here
|
|
46
49
|
chaff test <file|dir>... also run the checks that read meaning (needs an API key)
|
|
47
50
|
the judge is chosen by ai_backend in chaff.yaml
|
|
48
|
-
chaff init create chaff.yaml
|
|
51
|
+
chaff init create chaff.yaml (asks for the genre at a terminal; --genre <genre> chooses it)
|
|
49
52
|
chaff --version the version of chaffjs and its language packages
|
|
50
53
|
chaff eval <dir> re-measure the limits on your own documents
|
|
51
54
|
chaff explain <rule> read what a rule is for and why
|
|
52
|
-
chaff genres list the genres
|
|
55
|
+
chaff genres list the genres (kinds of document) and what each is for
|
|
53
56
|
chaff tree <file> [--format sexp|json] the document as a tree of addresses (sections, clauses, definitions, references)
|
|
54
57
|
chaff cite <source> <quotes.json> check that quoted passages (address and text) are in the source
|
|
55
58
|
chaff rules --json the current settings as JSON (to give to an AI)
|
|
@@ -78,20 +81,25 @@ export type CliText = {
|
|
|
78
81
|
readonly languageName: (language: string) => string;
|
|
79
82
|
readonly genreSource: Readonly<Record<GenreSource, string>>;
|
|
80
83
|
readonly header: (path: string, genre: string, language: string, from: string, shelved: number, hushed: number) => string;
|
|
84
|
+
readonly suggested: (name: string, genre: string) => string;
|
|
85
|
+
readonly suggestedNote: (name: string, genre: string, used: string) => string;
|
|
81
86
|
readonly noMarkdown: (targets: string) => string;
|
|
82
87
|
readonly noMarkdownHere: string;
|
|
83
88
|
readonly noAdapter: (language: string) => string;
|
|
84
|
-
readonly unknownGenre: (genre: string) => string;
|
|
89
|
+
readonly unknownGenre: (genre: string, where: string, known: readonly string[]) => string;
|
|
90
|
+
readonly genreWhere: (where: GenreSetting, files: readonly string[]) => string;
|
|
91
|
+
readonly unreadFrontMatterGenre: (path: string, genre: string, known: readonly string[]) => string;
|
|
92
|
+
readonly unknownProfile: (profile: string, where: string, known: readonly string[]) => string;
|
|
93
|
+
readonly profileWhere: (where: ProfileSetting, files: readonly string[]) => string;
|
|
85
94
|
readonly unknownRule: (id: string) => string;
|
|
86
95
|
readonly unknownRuleWithList: (id: string, list: string) => string;
|
|
87
96
|
readonly unnamed: string;
|
|
88
97
|
readonly sarifWritten: (path: string, count: number) => string;
|
|
89
98
|
readonly unchanged: string;
|
|
90
|
-
readonly findingsUnit: string;
|
|
99
|
+
readonly findingsUnit: (count: number) => string;
|
|
91
100
|
readonly watching: (files: number, findings: number) => string;
|
|
92
101
|
readonly watchHint: string;
|
|
93
102
|
readonly baselineDone: (files: number, entries: number, file: string) => readonly string[];
|
|
94
|
-
readonly genres: (list: readonly string[]) => string;
|
|
95
103
|
readonly unit: (ruleId: string, language: string) => string;
|
|
96
104
|
};
|
|
97
105
|
|
|
@@ -112,16 +120,29 @@ export const CLI_TEXT: Texts<CliText> = {
|
|
|
112
120
|
[`${path} ${genre} · ${language} ジャンルは${from}から`, shelved > 0 ? ` 棚上げ ${shelved} 件` : "", hushed > 0 ? ` stet ${hushed} 件` : ""].join(
|
|
113
121
|
"",
|
|
114
122
|
),
|
|
123
|
+
suggested: (name, genre) => ` ${name}のようです。--genre ${genre} を試せます`,
|
|
124
|
+
suggestedNote: (name, genre, used) =>
|
|
125
|
+
` ジャンルを決めていないので、${used} として見ました。${name}なら、--genre ${genre} でその種類の書き方に合わせて見ます(npx chaffjs genres で一覧)。`,
|
|
115
126
|
noMarkdown: (targets) => `Markdown が 1 つも見つかりませんでした: ${targets}`,
|
|
116
127
|
noMarkdownHere: "Markdown が 1 つも見つかりませんでした。",
|
|
117
128
|
noAdapter: (language) => `言語 "${language}" のアダプタがありません。`,
|
|
118
|
-
unknownGenre: (genre) =>
|
|
129
|
+
unknownGenre: (genre, where, known) =>
|
|
130
|
+
`ジャンル "${genre}" はありません(${where})。使えるのは ${known.join("、")} です。npx chaff genres で一覧が出ます。`,
|
|
131
|
+
genreWhere: (where, files) => {
|
|
132
|
+
if (where === "config") return "chaff.yaml の genre";
|
|
133
|
+
return where === "by_path" ? `chaff.yaml の by_path、files: ${files.join(", ")}` : "--genre";
|
|
134
|
+
},
|
|
135
|
+
unreadFrontMatterGenre: (path, genre, known) =>
|
|
136
|
+
`${path}: front matter の genre "${genre}" はジャンルではないので読みませんでした。使えるのは ${known.join("、")} です。`,
|
|
137
|
+
unknownProfile: (profile, where, known) =>
|
|
138
|
+
`文書の種類 "${profile}" はありません(${where})。使えるのは ${known.join("、")} と、種類を使わない none です。`,
|
|
139
|
+
profileWhere: (where, files) => (where === "config" ? "chaff.yaml の profile" : `chaff.yaml の by_path、files: ${files.join(", ")}`),
|
|
119
140
|
unknownRule: (id) => `${id} というルールはありません。npx chaff rules --json で一覧が出ます。`,
|
|
120
141
|
unknownRuleWithList: (id, list) => `${id} というルールはありません。\n一覧:\n${list}`,
|
|
121
142
|
unnamed: "(名前なし)",
|
|
122
143
|
sarifWritten: (path, count) => `\n SARIF を書きました: ${path}(${count} 件)`,
|
|
123
144
|
unchanged: "変わりませんでした",
|
|
124
|
-
findingsUnit: " 件",
|
|
145
|
+
findingsUnit: () => " 件",
|
|
125
146
|
watching: (files, findings) => `\n ${files} ファイルを見ています。いまの指摘は ${findings} 件です。`,
|
|
126
147
|
watchHint: " 保存するたびに、変わったところだけ出します。止めるには Ctrl-C。\n",
|
|
127
148
|
baselineDone: (files, entries, file) => [
|
|
@@ -134,8 +155,6 @@ export const CLI_TEXT: Texts<CliText> = {
|
|
|
134
155
|
` ${file} を commit してください。`,
|
|
135
156
|
"",
|
|
136
157
|
],
|
|
137
|
-
genres: (list) =>
|
|
138
|
-
["", " 使えるジャンル:", ...list.map((genre) => ` ${genre}`), "", " chaff.yaml の genre に書くか、--genre で指定します。", ""].join("\n"),
|
|
139
158
|
unit: (ruleId, language) => {
|
|
140
159
|
if (ruleId === "bold-density") return "1000 字あたりの箇所数";
|
|
141
160
|
if (ruleId !== "max-sentence-length") return "回";
|
|
@@ -156,29 +175,40 @@ export const CLI_TEXT: Texts<CliText> = {
|
|
|
156
175
|
},
|
|
157
176
|
header: (path, genre, language, from, shelved, hushed) =>
|
|
158
177
|
[`${path} ${genre} · ${language} genre from ${from}`, shelved > 0 ? ` ${shelved} shelved` : "", hushed > 0 ? ` ${hushed} stet` : ""].join(""),
|
|
178
|
+
suggested: (name, genre) => ` Looks like: ${name}. Try --genre ${genre}`,
|
|
179
|
+
suggestedNote: (name, genre, used) =>
|
|
180
|
+
` No genre was set, so this was checked as ${used}. If it is ${name}, --genre ${genre} checks it the way that kind is written (npx chaffjs genres lists them).`,
|
|
159
181
|
noMarkdown: (targets) => `No Markdown files found: ${targets}`,
|
|
160
182
|
noMarkdownHere: "No Markdown files found.",
|
|
161
183
|
noAdapter: (language) => `No language package for "${language}".`,
|
|
162
|
-
unknownGenre: (genre) => `There is no genre "${genre}". npx chaff genres lists them.`,
|
|
184
|
+
unknownGenre: (genre, where, known) => `There is no genre "${genre}" (${where}). The genres are ${known.join(", ")}. npx chaff genres lists them.`,
|
|
185
|
+
genreWhere: (where, files) => {
|
|
186
|
+
if (where === "config") return "genre in chaff.yaml";
|
|
187
|
+
return where === "by_path" ? `by_path in chaff.yaml, files: ${files.join(", ")}` : "--genre";
|
|
188
|
+
},
|
|
189
|
+
unreadFrontMatterGenre: (path, genre, known) =>
|
|
190
|
+
`${path}: the front matter's genre "${genre}" is not a genre, so it was not used. The genres are ${known.join(", ")}.`,
|
|
191
|
+
unknownProfile: (profile, where, known) =>
|
|
192
|
+
`There is no document profile "${profile}" (${where}). The profiles are ${known.join(", ")}; none uses no profile.`,
|
|
193
|
+
profileWhere: (where, files) => (where === "config" ? "profile in chaff.yaml" : `by_path in chaff.yaml, files: ${files.join(", ")}`),
|
|
163
194
|
unknownRule: (id) => `There is no rule named ${id}. npx chaff rules --json lists them.`,
|
|
164
195
|
unknownRuleWithList: (id, list) => `There is no rule named ${id}.\nRules:\n${list}`,
|
|
165
196
|
unnamed: "(no name)",
|
|
166
|
-
sarifWritten: (path, count) => `\n Wrote SARIF: ${path} (${count
|
|
197
|
+
sarifWritten: (path, count) => `\n Wrote SARIF: ${path} (${counted(count, "finding")})`,
|
|
167
198
|
unchanged: "no change",
|
|
168
|
-
findingsUnit: " findings"
|
|
169
|
-
watching: (files, findings) => `\n Watching ${files
|
|
199
|
+
findingsUnit: (count) => ` ${formFor(count, "finding", "findings")}`,
|
|
200
|
+
watching: (files, findings) => `\n Watching ${counted(files, "file")}. ${counted(findings, "finding")} now.`,
|
|
170
201
|
watchHint: " Each save prints only what changed. Ctrl-C to stop.\n",
|
|
171
202
|
baselineDone: (files, entries, file) => [
|
|
172
203
|
"",
|
|
173
|
-
` Checked ${files
|
|
204
|
+
` Checked ${counted(files, "file")}.`,
|
|
174
205
|
"",
|
|
175
|
-
` Recorded ${entries
|
|
206
|
+
` Recorded ${counted(entries, "finding")} in ${file}.`,
|
|
176
207
|
" They will not be reported again; only new ones will.",
|
|
177
208
|
"",
|
|
178
209
|
` Commit ${file}.`,
|
|
179
210
|
"",
|
|
180
211
|
],
|
|
181
|
-
genres: (list) => ["", " Genres:", ...list.map((genre) => ` ${genre}`), "", " Set one with genre in chaff.yaml, or with --genre.", ""].join("\n"),
|
|
182
212
|
unit: (ruleId, language) => {
|
|
183
213
|
if (ruleId === "bold-density") return "places per 1000 characters";
|
|
184
214
|
if (ruleId !== "max-sentence-length") return "times";
|
package/src/cli.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { applyByPath } from "./config/by-path.ts";
|
|
|
7
7
|
import { applyLevel } from "./config/write.ts";
|
|
8
8
|
import { buildDocument, teamRules } from "./document.ts";
|
|
9
9
|
import { guessLanguage } from "./detect.ts";
|
|
10
|
+
import { plainSource } from "./plain-source.ts";
|
|
10
11
|
import { collectTargets } from "./files.ts";
|
|
11
12
|
import { BASELINE_FILE, fingerprint, readBaseline, splitByBaseline, writeBaseline } from "./baseline.ts";
|
|
12
13
|
import { applySuppressions } from "./stet.ts";
|
|
@@ -14,13 +15,18 @@ import { renderSuppressions, type PerFile } from "./render/suppressions.ts";
|
|
|
14
15
|
import { clock, describeChange, snapshotOf, watchPaths, type Snapshot } from "./watch.ts";
|
|
15
16
|
import { runEval } from "./commands/eval.ts";
|
|
16
17
|
import { runTest } from "./commands/test.ts";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
18
|
+
import { GENRES } from "./genre.ts";
|
|
19
|
+
import { resolveGenre } from "./resolve-genre.ts";
|
|
20
|
+
import { runInit } from "./init.ts";
|
|
21
|
+
import { initGenre } from "./commands/init-ask.ts";
|
|
19
22
|
import { targetsOf } from "./cli-args.ts";
|
|
20
23
|
import { loadRules } from "./rule-load.ts";
|
|
21
24
|
import { renderCompact } from "./render/compact.ts";
|
|
22
25
|
import { renderExplain } from "./render/explain.ts";
|
|
23
26
|
import { renderFriendly } from "./render/friendly.ts";
|
|
27
|
+
import { renderGenres } from "./render/genres.ts";
|
|
28
|
+
import { loadGenres, presetLevels } from "./genre-load.ts";
|
|
29
|
+
import { fileHeader } from "./file-header.ts";
|
|
24
30
|
import { rulesJson } from "./render/rules-json.ts";
|
|
25
31
|
import { renderSarif } from "./render/sarif.ts";
|
|
26
32
|
import { VERSION, VERSION_LINES } from "./version.ts";
|
|
@@ -30,29 +36,20 @@ import { runSkill } from "./commands/skill.ts";
|
|
|
30
36
|
import { runFeedback, settingsOf } from "./commands/feedback.ts";
|
|
31
37
|
import { homedir } from "node:os";
|
|
32
38
|
import { ruleProblems } from "./config/rule-problems.ts";
|
|
39
|
+
import { nameProblems } from "./config/name-problems.ts";
|
|
33
40
|
import { renderSummary, type FileOutcome } from "./render/summary.ts";
|
|
34
41
|
import { neededBy, runRules } from "./run.ts";
|
|
35
42
|
import type { Level, RuleDefinition } from "./plugin.ts";
|
|
36
43
|
import { CLI_TEXT, type CliText, type GenreSource } from "./cli-text.ts";
|
|
37
|
-
import { hostLanguage, uiLanguageOf, type UiLanguage } from "./ui.ts";
|
|
44
|
+
import { hostLanguage, sharedLanguage, uiLanguageOf, type UiLanguage } from "./ui.ts";
|
|
38
45
|
import { profileFor } from "./profile/for-file.ts";
|
|
46
|
+
import { settingProblems } from "./setting-problems.ts";
|
|
39
47
|
|
|
40
48
|
/** Text for output that is not about one document. */
|
|
41
49
|
const hostText = (config: Config): CliText => CLI_TEXT[hostLanguage(config.language, process.env)];
|
|
42
50
|
|
|
43
51
|
const readConfig = (): Config => (existsSync(join(process.cwd(), CONFIG_FILE)) ? loadConfig(join(process.cwd(), CONFIG_FILE)) : EMPTY);
|
|
44
52
|
|
|
45
|
-
const resolveGenre = (path: string, source: string, config: Config, cliGenre?: string): { genre: string; from: GenreSource } => {
|
|
46
|
-
// コマンドで指定したものが最優先。その実行だけの指定だから、設定より強い。
|
|
47
|
-
if (cliGenre !== undefined) return { genre: cliGenre, from: "--genre" };
|
|
48
|
-
// パスごとの上書きが次。「全体はこう、ここだけは違う」を書けるようにする。
|
|
49
|
-
const override = applyByPath(config.byPath, config.baseDir, path);
|
|
50
|
-
if (override.genre !== undefined) return { genre: override.genre, from: "by_path" };
|
|
51
|
-
if (config.genre !== undefined) return { genre: config.genre, from: "config" };
|
|
52
|
-
const guess = guessGenre(path, source, frontMatterGenre(source));
|
|
53
|
-
return guess === undefined ? { genre: "blog/tech", from: "default" } : { genre: guess.genre, from: guess.from };
|
|
54
|
-
};
|
|
55
|
-
|
|
56
53
|
/** resolveGenre with this run's --genre, for the commands that take it as a dependency. */
|
|
57
54
|
const genreFrom =
|
|
58
55
|
(argv: readonly string[]) =>
|
|
@@ -90,21 +87,16 @@ type Inspected = {
|
|
|
90
87
|
readonly all: readonly string[];
|
|
91
88
|
};
|
|
92
89
|
|
|
93
|
-
/** The first line for a file, in the file's language. */
|
|
94
|
-
const headerFor = (path: string, genre: string, from: GenreSource, language: string, shelved: number, hushed: number): string => {
|
|
95
|
-
const text = CLI_TEXT[uiLanguageOf(language)];
|
|
96
|
-
return text.header(path, genre, text.languageName(language), text.genreSource[from], shelved, hushed);
|
|
97
|
-
};
|
|
98
|
-
|
|
99
90
|
const inspect = async (path: string, config: Config, argv: readonly string[]): Promise<Inspected> => {
|
|
100
|
-
const source = await readFile(path, "utf8");
|
|
91
|
+
const source = plainSource(await readFile(path, "utf8"));
|
|
101
92
|
const language = applyByPath(config.byPath, config.baseDir, path).language ?? config.language ?? guessLanguage(source).language;
|
|
102
93
|
const adapter = await loadAdapter(language);
|
|
103
|
-
const { genre, from } = resolveGenre(path, source, config, flag(argv, "--genre"));
|
|
94
|
+
const { genre, from, unread } = resolveGenre(path, source, config, flag(argv, "--genre"));
|
|
95
|
+
if (unread !== undefined) console.error(`chaff: ${CLI_TEXT[uiLanguageOf(language)].unreadFrontMatterGenre(path, unread, GENRES)}`);
|
|
104
96
|
const rules = loadRules(language);
|
|
105
97
|
const experimental = config.experimental || argv.includes("--experimental");
|
|
106
98
|
await adapter.prepare?.(neededBy(rules, config.rules, experimental, genre, language));
|
|
107
|
-
const doc = buildDocument(path, source, adapter, teamRules(config), profileFor(config, path, source, language));
|
|
99
|
+
const doc = buildDocument(path, source, adapter, teamRules(config), profileFor(config, path, source, language, genre));
|
|
108
100
|
const raw = runRules(doc, rules, config.rules, experimental, genre, config.limits);
|
|
109
101
|
// 応答は 3 つ。stet で黙らせたものは、ここで落とす。
|
|
110
102
|
const applied = applySuppressions(
|
|
@@ -115,8 +107,8 @@ const inspect = async (path: string, config: Config, argv: readonly string[]): P
|
|
|
115
107
|
const baseline = argv.includes("--show-baseline") ? undefined : readBaseline(join(process.cwd(), BASELINE_FILE));
|
|
116
108
|
const split = splitByBaseline(path, applied.kept, baseline);
|
|
117
109
|
const result = { ...raw, findings: split.fresh };
|
|
118
|
-
const header =
|
|
119
|
-
const text = argv.includes("--compact") ? renderCompact(header, result, rules, language) : renderFriendly(header, result, rules, language);
|
|
110
|
+
const { header, notes } = fileHeader(path, source, language, { genre, from, unread }, { shelved: split.shelved, hushed: applied.suppressed.length });
|
|
111
|
+
const text = argv.includes("--compact") ? renderCompact(header, result, rules, language) : renderFriendly(header, result, rules, language, notes);
|
|
120
112
|
return {
|
|
121
113
|
text,
|
|
122
114
|
rules,
|
|
@@ -146,14 +138,14 @@ const writeSarif = (results: readonly Inspected[], argv: readonly string[]): voi
|
|
|
146
138
|
|
|
147
139
|
/** 効いていない設定は、結果の前に一度だけ言う。標準エラーに出すので、JSON や SARIF の出力は汚さない。 */
|
|
148
140
|
const warnRuleProblems = (config: Config, language: string): void => {
|
|
149
|
-
|
|
141
|
+
const ui = hostLanguage(config.language, process.env);
|
|
142
|
+
[...ruleProblems(config, loadRules(language), ui), ...nameProblems(config, ui)].forEach((problem) => console.error(`chaff: ${problem}`));
|
|
150
143
|
};
|
|
151
144
|
|
|
152
145
|
/** Several files end with one summary: in their language when they share one, else the host's. */
|
|
153
146
|
const summaryLanguage = (results: readonly Inspected[], config: Config): UiLanguage => {
|
|
154
|
-
const languages =
|
|
155
|
-
|
|
156
|
-
return languages.size === 1 && only !== undefined ? only : hostLanguage(config.language, process.env);
|
|
147
|
+
const languages = results.map((result) => result.language);
|
|
148
|
+
return sharedLanguage(languages, hostLanguage(config.language, process.env));
|
|
157
149
|
};
|
|
158
150
|
|
|
159
151
|
const lint = async (targets: readonly string[], argv: readonly string[]): Promise<number> => {
|
|
@@ -215,8 +207,10 @@ const runWatch = async (targets: readonly string[], argv: readonly string[]): Pr
|
|
|
215
207
|
return new Promise(() => undefined);
|
|
216
208
|
};
|
|
217
209
|
|
|
218
|
-
|
|
210
|
+
/** genreFlag: --genre, which wins over chaff.yaml's genre here as it does in a run. */
|
|
211
|
+
const explain = (ruleId: string | undefined, genreFlag: string | undefined): number => {
|
|
219
212
|
const config = readConfig();
|
|
213
|
+
const genre = genreFlag ?? config.genre;
|
|
220
214
|
// The rule's limits differ by language (characters for Japanese, words for English): explain in the one being written.
|
|
221
215
|
const language = config.language ?? hostLanguage(undefined, process.env);
|
|
222
216
|
const text = CLI_TEXT[uiLanguageOf(language)];
|
|
@@ -227,8 +221,9 @@ const explain = (ruleId: string | undefined): number => {
|
|
|
227
221
|
console.error(text.unknownRuleWithList(ruleId ?? text.unnamed, list));
|
|
228
222
|
return 1;
|
|
229
223
|
}
|
|
230
|
-
const
|
|
231
|
-
|
|
224
|
+
const preset = genre === undefined ? {} : presetLevels(genre);
|
|
225
|
+
const current = config.rules[rule.id] ?? preset[rule.id] ?? (rule.status === "experimental" && !config.experimental ? "off" : "normal");
|
|
226
|
+
console.log(renderExplain(rule, current, language, text.unit(rule.id, language), genre));
|
|
232
227
|
return 0;
|
|
233
228
|
};
|
|
234
229
|
|
|
@@ -265,16 +260,16 @@ type Handler = (argv: readonly string[]) => number | Promise<number>;
|
|
|
265
260
|
/** `--` で始まらない引数。対象のパス。 */
|
|
266
261
|
const positional = (argv: readonly string[]): string[] => targetsOf(argv.slice(1));
|
|
267
262
|
|
|
268
|
-
const showRules = (): number => {
|
|
263
|
+
const showRules = (genreFlag: string | undefined): number => {
|
|
269
264
|
const config = readConfig();
|
|
270
265
|
const language = config.language ?? hostLanguage(undefined, process.env);
|
|
271
266
|
warnRuleProblems(config, language);
|
|
272
|
-
console.log(rulesJson(loadRules(language), config, language, config.genre ?? "blog/tech"));
|
|
267
|
+
console.log(rulesJson(loadRules(language), config, language, genreFlag ?? config.genre ?? "blog/tech"));
|
|
273
268
|
return 0;
|
|
274
269
|
};
|
|
275
270
|
|
|
276
271
|
const showGenres = (): number => {
|
|
277
|
-
console.log(
|
|
272
|
+
console.log(renderGenres(loadGenres(), hostLanguage(readConfig().language, process.env)));
|
|
278
273
|
return 0;
|
|
279
274
|
};
|
|
280
275
|
|
|
@@ -283,19 +278,28 @@ const treeContext = (): TreeContext => {
|
|
|
283
278
|
return { config, flag, ui: hostLanguage(config.language, process.env) };
|
|
284
279
|
};
|
|
285
280
|
|
|
281
|
+
/** What eval and test share: the settings, this run's genre, and the language for what is not about one document. */
|
|
282
|
+
const measureContext = (argv: readonly string[]): { config: Config; resolveGenre: ReturnType<typeof genreFrom>; ui: UiLanguage } => {
|
|
283
|
+
const config = readConfig();
|
|
284
|
+
return { config, resolveGenre: genreFrom(argv), ui: hostLanguage(config.language, process.env) };
|
|
285
|
+
};
|
|
286
|
+
|
|
286
287
|
/** 分岐を数珠つなぎにせず表にする。足すときに main を太らせない。 */
|
|
287
288
|
const HANDLERS: Readonly<Record<string, Handler>> = {
|
|
288
|
-
init: (argv) => {
|
|
289
|
-
|
|
290
|
-
|
|
289
|
+
init: async (argv) => {
|
|
290
|
+
const ui = hostLanguage(readConfig().language, process.env);
|
|
291
|
+
const chosen = await initGenre(flag(argv, "--genre"), ui, process.cwd());
|
|
292
|
+
if ("error" in chosen) console.error(chosen.error);
|
|
293
|
+
else runInit(process.cwd(), chosen.genre, ui).forEach((line) => console.log(line));
|
|
294
|
+
return "error" in chosen ? 1 : 0;
|
|
291
295
|
},
|
|
292
296
|
genres: showGenres,
|
|
293
|
-
rules: showRules,
|
|
294
|
-
explain: (argv) => explain(argv[1]),
|
|
295
|
-
eval: (argv) => runEval(positional(argv), argv, {
|
|
297
|
+
rules: (argv) => showRules(flag(argv, "--genre")),
|
|
298
|
+
explain: (argv) => explain(argv[1], flag(argv, "--genre")),
|
|
299
|
+
eval: (argv) => runEval(positional(argv), argv, { ...measureContext(argv), flag }),
|
|
296
300
|
tree: (argv) => runTree(treeTargets(argv), argv, treeContext()),
|
|
297
301
|
cite: (argv) => runCite(citeTargets(argv), argv, treeContext()),
|
|
298
|
-
test: (argv) => runTest(positional(argv), argv, {
|
|
302
|
+
test: (argv) => runTest(positional(argv), argv, { ...measureContext(argv), inspect }),
|
|
299
303
|
baseline: (argv) => runBaseline(positional(argv), argv),
|
|
300
304
|
suppressions: (argv) => runSuppressions(positional(argv), argv),
|
|
301
305
|
relax: (argv) => changeSetting("relaxed", argv[1], flag(argv, "--why")),
|
|
@@ -332,11 +336,11 @@ export const main = async (argv: readonly string[]): Promise<number> => {
|
|
|
332
336
|
console.log(VERSION_LINES.join("\n"));
|
|
333
337
|
return 0;
|
|
334
338
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
339
|
+
// 知らないジャンルではどの rule も当たらず、知らない文書の種類では種類の知識が外れる。どちらも素通りに見えるので、何かする前に止める。
|
|
340
|
+
const config = readConfig();
|
|
341
|
+
const problems = settingProblems(first, flag(argv, "--genre"), config, hostText(config));
|
|
342
|
+
problems.forEach((problem) => console.error(problem));
|
|
343
|
+
if (problems.length > 0) return 1;
|
|
340
344
|
const handler = HANDLERS[first];
|
|
341
345
|
if (handler !== undefined) return handler(argv);
|
|
342
346
|
const targets = targetsOf(first === "lint" ? argv.slice(1) : argv);
|