chaffjs 0.15.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.js +1 -1
- 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 +6 -2
- package/dist/cli-text.d.ts.map +1 -1
- package/dist/cli-text.js +19 -12
- package/dist/cli-text.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +43 -37
- 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.js +1 -1
- package/dist/commands/eval.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 +70 -38
- 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 +7 -6
- 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/char-shape.d.ts.map +1 -1
- package/dist/detectors/char-shape.js +16 -6
- package/dist/detectors/char-shape.js.map +1 -1
- package/dist/detectors/en-shape.d.ts.map +1 -1
- package/dist/detectors/en-shape.js +101 -7
- 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.js +1 -1
- package/dist/detectors/gram-place.js.map +1 -1
- package/dist/detectors/heading-case.d.ts.map +1 -1
- package/dist/detectors/heading-case.js +3 -1
- package/dist/detectors/heading-case.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 +46 -3
- package/dist/detectors/lexicon.js.map +1 -1
- package/dist/detectors/orthography.d.ts.map +1 -1
- package/dist/detectors/orthography.js +24 -6
- 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/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/signals.d.ts.map +1 -1
- package/dist/detectors/signals.js +16 -6
- 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/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-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 +8 -6
- 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 +18 -3
- 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.js +1 -1
- package/dist/genre-check.js.map +1 -1
- 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 +1 -1
- package/dist/genre.d.ts.map +1 -1
- package/dist/genre.js +3 -13
- 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.map +1 -1
- package/dist/init.js +16 -5
- package/dist/init.js.map +1 -1
- 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-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/plugin.d.ts +6 -0
- 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 +15 -3
- package/dist/quoted-span.d.ts.map +1 -1
- package/dist/quoted-span.js +16 -11
- package/dist/quoted-span.js.map +1 -1
- 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.map +1 -1
- package/dist/render/eval.js +4 -3
- 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/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/structure/build.js +5 -5
- 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/issues.d.ts.map +1 -1
- package/dist/structure/issues.js +5 -5
- package/dist/structure/issues.js.map +1 -1
- 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/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/unnumbered.js +2 -2
- package/dist/structure/unnumbered.js.map +1 -1
- 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 +1 -1
- package/rules/date-order.yaml +1 -1
- 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 +5 -4
- package/rules/max-kanji-continuous.yaml +4 -3
- 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 +2 -2
- 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 +1 -1
- package/rules/total-mismatch.yaml +1 -1
- package/rules/undefined-acronym.yaml +4 -4
- package/rules/unqualified-superlative.yaml +2 -2
- package/skills/chaff/SKILL.md +28 -9
- package/src/address-chain.ts +1 -1
- 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 +29 -15
- package/src/cli.ts +42 -41
- package/src/commands/cite.ts +4 -4
- package/src/commands/eval.ts +1 -1
- package/src/commands/init-ask.ts +53 -0
- package/src/commands/test.ts +102 -45
- package/src/commands/tree.ts +7 -6
- 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/char-shape.ts +21 -9
- package/src/detectors/en-shape.ts +106 -8
- package/src/detectors/enumerated-runs.ts +3 -2
- package/src/detectors/gram-place.ts +1 -1
- package/src/detectors/heading-case.ts +4 -1
- package/src/detectors/index.ts +4 -3
- package/src/detectors/lettered-index.ts +78 -0
- package/src/detectors/lexicon.ts +63 -6
- package/src/detectors/orthography.ts +31 -7
- package/src/detectors/phrase-match.ts +23 -12
- package/src/detectors/repeated-head.ts +3 -4
- package/src/detectors/signals.ts +15 -6
- package/src/detectors/stacked-hedge.ts +93 -0
- package/src/detectors/superlative-amount.ts +24 -0
- package/src/detectors/superlative-clause.ts +82 -0
- package/src/detectors/superlative-scope.ts +3 -3
- package/src/detectors/team.ts +18 -2
- package/src/detectors/token-shape.ts +8 -10
- package/src/document.ts +29 -3
- 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 +1 -1
- 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 +4 -13
- package/src/init-choice.ts +47 -0
- package/src/init.ts +20 -6
- package/src/judge.ts +3 -1
- package/src/line-paragraphs.ts +83 -0
- package/src/mask.ts +5 -6
- package/src/plugin.ts +6 -0
- 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 +26 -12
- package/src/reasons.ts +5 -1
- package/src/render/compact.ts +1 -3
- package/src/render/eval.ts +4 -3
- 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/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/structure/build.ts +5 -5
- package/src/structure/cite.ts +1 -1
- package/src/structure/issues.ts +6 -5
- package/src/structure/markdown-path.ts +2 -2
- 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/unnumbered.ts +2 -2
- 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/rules/latin-spacing.yaml
CHANGED
|
@@ -7,15 +7,16 @@ severity: warning
|
|
|
7
7
|
# 番号・識別子として書かれた数を見分けるのに、数の後ろの語の品詞を使う。無ければ全部数える。
|
|
8
8
|
uses: [pos]
|
|
9
9
|
# 番地(千代田区紀尾井町1-3)を、都道府県までの地名の後ろの範囲(東京都2-3 営業日)と分ける。
|
|
10
|
-
|
|
10
|
+
# 日付・時刻(9月、2026年、10時5分)を、長さ(3ヶ月、3 時間)と分ける。
|
|
11
|
+
extra_word_lists: [prefecture-unit, date-time-unit, calendar-unit, calendar-year-unit]
|
|
11
12
|
|
|
12
13
|
name:
|
|
13
14
|
ja: 英数字の前後の空白がそろっていない
|
|
14
15
|
en: Spacing around Latin text is inconsistent
|
|
15
16
|
|
|
16
17
|
why:
|
|
17
|
-
ja: 日本語と英字・数字のあいだを空けるか詰めるかは、どちらでも読めます。困るのは一つの文書の中で混ざることで、読み手には違いに意味があるように見えます。chaff はどちらが正しいかを決めず、文書の中で少ないほうを指摘します。英字と、数字の前、数字の後ろは別々に数えます。「を 3回」のように、数字の前は空けて助数詞とは詰める書き方がふつうにあるからです。「3GBの」のように数字に続く単位は、英字ではなく数字の側として数えます。逆に「H30 等」「EC2 で」「Phase 1 は」のように英字で始まって数字で終わる名前は、英字の側として数えます。「073-489-5909」「2026-06-02
|
|
18
|
-
en: Whether to put a space between Japanese and Latin letters or digits is a choice either way. What reads badly is mixing both in one document, which suggests the difference means something. chaff does not decide which is right; it points at whichever the document uses less. Letters, the space before a digit and the space after one are counted separately, since spacing before a number but not before its counter is common. A unit after a number (3GB) counts with the number; a name that starts with a letter and ends in a digit (H30, EC2, Phase 1) counts with the letters. A code such as a phone number or a date (073-489-5909, 2026-06-02) is not a quantity and is not counted. Neither is a number written as the label of a title or a note (a section number such as 3.1 before its title, a note number after ※, the numbers of notes listed one per line in sequence), a code with a part starting with 0 (102-0094), a postal code after 〒 (〒100-8916), or a hyphenated address number right after a city, ward or town (千代田区紀尾井町1-3), since the space after it separates the label from its title or the next item. Nor is the spacing inside a title or a quotation in 「」 or 『』, which is the source's, not the writer's.
|
|
18
|
+
ja: 日本語と英字・数字のあいだを空けるか詰めるかは、どちらでも読めます。困るのは一つの文書の中で混ざることで、読み手には違いに意味があるように見えます。chaff はどちらが正しいかを決めず、文書の中で少ないほうを指摘します。英字と、数字の前、数字の後ろは別々に数えます。「を 3回」のように、数字の前は空けて助数詞とは詰める書き方がふつうにあるからです。「3GBの」のように数字に続く単位は、英字ではなく数字の側として数えます。逆に「H30 等」「EC2 で」「Phase 1 は」のように英字で始まって数字で終わる名前は、英字の側として数えます。「073-489-5909」「2026-06-02」のような番号は数量ではないので数えません。日付・時刻(「9月」「2026年」「10月1日」「3時」「10時5分」)も、数と単位を詰めて書く決まりなので、その中の境目は数えません(日付の前の空白は数えます)。長さ(「3ヶ月」「3 時間」「5日で」「3年」)は数量として数えます。題や注の番号として書かれた数(「3.1 リサーチの原則」の節番号、※1 の注番号、行の頭に続き番号で並べた注の番号)、0 で始まる組を含む番号(102-0094)、〒の後ろの郵便番号(〒100-8916)、市・区・町まで下りた地名のすぐ後ろの番地(千代田区紀尾井町1-3)も、後ろの空白が番号と題・項目の区切りなので数えません。鉤括弧(「」『』)で引いた題名や発言の中も、空け方は引いた元のものなので数えません。
|
|
19
|
+
en: Whether to put a space between Japanese and Latin letters or digits is a choice either way. What reads badly is mixing both in one document, which suggests the difference means something. chaff does not decide which is right; it points at whichever the document uses less. Letters, the space before a digit and the space after one are counted separately, since spacing before a number but not before its counter is common. A unit after a number (3GB) counts with the number; a name that starts with a letter and ends in a digit (H30, EC2, Phase 1) counts with the letters. A code such as a phone number or a date (073-489-5909, 2026-06-02) is not a quantity and is not counted. Nor is the inside of a date or a time written with units (9月, 2026年, 10月1日, 3時, 10時5分), which is packed by convention; the space before it still counts. A length of time (3ヶ月, 3 時間, 5日で, 3年) is a quantity and counts. Neither is a number written as the label of a title or a note (a section number such as 3.1 before its title, a note number after ※, the numbers of notes listed one per line in sequence), a code with a part starting with 0 (102-0094), a postal code after 〒 (〒100-8916), or a hyphenated address number right after a city, ward or town (千代田区紀尾井町1-3), since the space after it separates the label from its title or the next item. Nor is the spacing inside a title or a quotation in 「」 or 『』, which is the source's, not the writer's.
|
|
19
20
|
|
|
20
21
|
message:
|
|
21
22
|
ja: "日本語と{kind}のあいだを{style}(この文書はふつう{usual}。{of} 箇所のうち {count} 箇所が違う)"
|
|
@@ -28,4 +29,4 @@ how_to_fix:
|
|
|
28
29
|
# 何箇所違えば指摘するか。1 は「1 箇所でも混ざっていれば」。
|
|
29
30
|
levels: { strict: 1, normal: 1, relaxed: 3 }
|
|
30
31
|
|
|
31
|
-
use_for: [business, blog, technical]
|
|
32
|
+
use_for: [business, blog, technical, legal, docs, academic, literature, speech]
|
|
@@ -4,6 +4,7 @@ status: experimental
|
|
|
4
4
|
how_to_find: kanji-run
|
|
5
5
|
# 品詞があれば、住所(地名と数だけでできた連なり)と 1 つの名前(辞書の固有名詞 1 語、人の姓と名)を数えない。無くても文字だけで動く。
|
|
6
6
|
uses: [pos]
|
|
7
|
+
# chaff.yaml の names に並べた名前は連なりを切る。鉤括弧の中身がまるごと 1 つの連なりなら数えない(書き手が 1 つの名前と示している)。
|
|
7
8
|
# 住所の先頭にしか来ない単位(都道府県)。地名の後ろに付けば、割られた町名ではなく地名の並び。
|
|
8
9
|
extra_word_lists: [prefecture-unit]
|
|
9
10
|
languages: [ja]
|
|
@@ -22,9 +23,9 @@ message:
|
|
|
22
23
|
en: '"{word}" runs {count} kanji together (limit {limit})'
|
|
23
24
|
|
|
24
25
|
how_to_fix:
|
|
25
|
-
ja: ひらがなを 1
|
|
26
|
-
en: Break it with a particle or kana.
|
|
26
|
+
ja: ひらがなを 1 つ挟むか、助詞で割ってください。「情報処理推進機構の認定試験」に。割れない正式名称なら、chaff.yaml の names に並べると数えません。鉤括弧でまるごとくくった名前(「英国大使館別荘記念公園」)も数えません。
|
|
27
|
+
en: Break it with a particle or kana. If it is an official name that cannot be broken, list it under names in chaff.yaml and it is not counted. A name that fills a 「」 quotation exactly is not counted either.
|
|
27
28
|
|
|
28
29
|
levels: { strict: 6, normal: 8, relaxed: 12 }
|
|
29
30
|
|
|
30
|
-
use_for: [blog, business, technical]
|
|
31
|
+
use_for: [blog, business, technical, legal, docs, academic, literature, speech]
|
|
@@ -14,7 +14,7 @@ why:
|
|
|
14
14
|
|
|
15
15
|
message:
|
|
16
16
|
ja: この段落は {count} 文あります({limit} 文まで)
|
|
17
|
-
en: This paragraph runs {count} sentences (limit {limit})
|
|
17
|
+
en: This paragraph runs {count} {count|sentence|sentences} (limit {limit})
|
|
18
18
|
|
|
19
19
|
how_to_fix:
|
|
20
20
|
ja: 話題の変わり目で段落を割ってください。
|
|
@@ -22,4 +22,11 @@ how_to_fix:
|
|
|
22
22
|
|
|
23
23
|
levels: { strict: 3, normal: 5, relaxed: 8 }
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
# 条文・判決・論文の段落は、一つの規定や論点をまとめて書く。corpus のその種類の文書で、normal が最も長い一握りの段落だけを指すように測って置いた。
|
|
26
|
+
by_genre:
|
|
27
|
+
legal: { strict: 5, normal: 8, relaxed: 12 }
|
|
28
|
+
legal/statute: { strict: 8, normal: 12, relaxed: 16 }
|
|
29
|
+
legal/judgment: { strict: 8, normal: 12, relaxed: 16 }
|
|
30
|
+
academic: { strict: 5, normal: 9, relaxed: 12 }
|
|
31
|
+
|
|
32
|
+
use_for: [blog, business, technical, legal, docs, academic, literature, speech]
|
|
@@ -14,7 +14,7 @@ why:
|
|
|
14
14
|
|
|
15
15
|
message:
|
|
16
16
|
ja: この文は {count} 文字あります({limit} 文字まで)
|
|
17
|
-
en: "This sentence runs {count} words (limit {limit})"
|
|
17
|
+
en: "This sentence runs {count} {count|word|words} (limit {limit})"
|
|
18
18
|
|
|
19
19
|
how_to_fix:
|
|
20
20
|
ja: 接続助詞のところで 2 文に割ってください。それだけで読めるようになります。
|
|
@@ -46,5 +46,17 @@ by_genre:
|
|
|
46
46
|
blog/owned-media:
|
|
47
47
|
ja: { strict: 65, normal: 90, relaxed: 125 }
|
|
48
48
|
en: { strict: 16, normal: 22, relaxed: 30 }
|
|
49
|
+
# 法令・契約・判決・論文は、長い一文がその種類の書き方。corpus のその種類の文書で、normal が最も長い一握りの文だけを指すように測って置いた。
|
|
50
|
+
legal:
|
|
51
|
+
ja: { strict: 120, normal: 200, relaxed: 300 }
|
|
52
|
+
en: { strict: 40, normal: 60, relaxed: 80 }
|
|
53
|
+
legal/statute:
|
|
54
|
+
ja: { strict: 200, normal: 300, relaxed: 450 }
|
|
55
|
+
en: { strict: 80, normal: 130, relaxed: 180 }
|
|
56
|
+
legal/judgment:
|
|
57
|
+
ja: { strict: 250, normal: 350, relaxed: 450 }
|
|
58
|
+
academic:
|
|
59
|
+
ja: { strict: 100, normal: 140, relaxed: 180 }
|
|
60
|
+
en: { strict: 35, normal: 50, relaxed: 65 }
|
|
49
61
|
|
|
50
|
-
use_for: [blog, business, technical]
|
|
62
|
+
use_for: [blog, business, technical, legal, docs, academic, literature, speech]
|
|
@@ -16,12 +16,12 @@ why:
|
|
|
16
16
|
|
|
17
17
|
message:
|
|
18
18
|
ja: 「{word}」が {count} 回出てきます({limit} 回まで)
|
|
19
|
-
en: '"{word}" appears {count} times (limit {limit})'
|
|
19
|
+
en: '"{word}" appears {count} {count|time|times} (limit {limit})'
|
|
20
20
|
|
|
21
21
|
how_to_fix:
|
|
22
|
-
ja: 2 回目以降を言い換えるか、まとめて 1
|
|
23
|
-
en: Rephrase the repeats, or say it once.
|
|
22
|
+
ja: 2 回目以降を言い換えるか、まとめて 1 度にしてください。繰り返しているのが組織名や製品名なら、chaff.yaml の names に並べると数えません。
|
|
23
|
+
en: Rephrase the repeats, or say it once. If what repeats is an organisation or product name, list it under names in chaff.yaml and it is not counted.
|
|
24
24
|
|
|
25
25
|
levels: { strict: 3, normal: 5, relaxed: 9 }
|
|
26
26
|
|
|
27
|
-
use_for: [blog, business, technical]
|
|
27
|
+
use_for: [blog, business, technical, legal, docs, academic, literature, speech]
|
|
@@ -17,7 +17,7 @@ why:
|
|
|
17
17
|
|
|
18
18
|
message:
|
|
19
19
|
ja: 「{word}」が読点を挟まずに {count} 回続いています({limit} 回まで)
|
|
20
|
-
en: '"{word}" repeats {count} times with no break (limit {limit})'
|
|
20
|
+
en: '"{word}" repeats {count} {count|time|times} with no break (limit {limit})'
|
|
21
21
|
|
|
22
22
|
how_to_fix:
|
|
23
23
|
ja: 文を割るか、係り先を言い換えてください。「弊社の新製品の販売の計画」を「弊社が新製品を販売する計画」に。
|
|
@@ -25,4 +25,4 @@ how_to_fix:
|
|
|
25
25
|
|
|
26
26
|
levels: { strict: 1, normal: 2, relaxed: 3 }
|
|
27
27
|
|
|
28
|
-
use_for: [blog, business, technical]
|
|
28
|
+
use_for: [blog, business, technical, legal, docs, academic, literature, speech]
|
package/rules/no-em-dash.yaml
CHANGED
|
@@ -18,7 +18,7 @@ why:
|
|
|
18
18
|
|
|
19
19
|
message:
|
|
20
20
|
ja: 1000 字あたり {density} 個のダッシュがあります({limit} 個まで)
|
|
21
|
-
en: "{density} dashes per 1000 words (limit {limit})"
|
|
21
|
+
en: "{density} {density|dash|dashes} per 1000 words (limit {limit})"
|
|
22
22
|
|
|
23
23
|
how_to_fix:
|
|
24
24
|
ja: 読点か括弧に置き換えてください。挟み込みなら括弧、言い換えなら読点が読みやすいです。
|
|
@@ -29,4 +29,4 @@ levels:
|
|
|
29
29
|
ja: { strict: 1, normal: 3, relaxed: 8 }
|
|
30
30
|
en: { strict: 3, normal: 8, relaxed: 20 }
|
|
31
31
|
|
|
32
|
-
use_for: [blog, business, technical]
|
|
32
|
+
use_for: [blog, business, technical, legal, docs, academic, literature, speech]
|
|
@@ -15,7 +15,7 @@ why:
|
|
|
15
15
|
|
|
16
16
|
message:
|
|
17
17
|
ja: この文に中黒が {count} 個あります({limit} 個まで)
|
|
18
|
-
en: "{count} middle dots in this sentence (limit {limit})"
|
|
18
|
+
en: "{count} middle {count|dot|dots} in this sentence (limit {limit})"
|
|
19
19
|
|
|
20
20
|
how_to_fix:
|
|
21
21
|
ja: 文を割るか、箇条書きにしてください。組の切れ目がはっきりします。
|
|
@@ -24,4 +24,4 @@ how_to_fix:
|
|
|
24
24
|
# 実文書の分布から。1 文 4 個までは読める。
|
|
25
25
|
levels: { strict: 3, normal: 5, relaxed: 8 }
|
|
26
26
|
|
|
27
|
-
use_for: [blog, business, technical]
|
|
27
|
+
use_for: [blog, business, technical, legal, docs, academic, literature, speech]
|
package/rules/numbering-gap.yaml
CHANGED
|
@@ -3,6 +3,7 @@ layer: L3
|
|
|
3
3
|
status: experimental
|
|
4
4
|
how_to_find: oxford-comma
|
|
5
5
|
languages: [en]
|
|
6
|
+
extra_word_lists: [participle-word]
|
|
6
7
|
severity: info
|
|
7
8
|
|
|
8
9
|
name:
|
|
@@ -23,4 +24,4 @@ how_to_fix:
|
|
|
23
24
|
|
|
24
25
|
levels: { strict: 3, normal: 2, relaxed: 1 }
|
|
25
26
|
|
|
26
|
-
use_for: [blog, business, technical]
|
|
27
|
+
use_for: [blog, business, technical, legal, docs, academic, literature, speech]
|
package/rules/padded-intro.yaml
CHANGED
|
@@ -14,7 +14,7 @@ why:
|
|
|
14
14
|
|
|
15
15
|
message:
|
|
16
16
|
ja: 最初の見出しまでに {count} 段落あります({limit} 段落まで)
|
|
17
|
-
en: "{count} paragraphs before the first heading (limit {limit})"
|
|
17
|
+
en: "{count} {count|paragraph|paragraphs} before the first heading (limit {limit})"
|
|
18
18
|
|
|
19
19
|
how_to_fix:
|
|
20
20
|
ja: 結論を先に書くか、前置きを見出しの下へ移してください。
|
|
@@ -22,4 +22,4 @@ how_to_fix:
|
|
|
22
22
|
|
|
23
23
|
levels: { strict: 1, normal: 2, relaxed: 4 }
|
|
24
24
|
|
|
25
|
-
use_for: [business]
|
|
25
|
+
use_for: [business, legal, docs, academic, literature, speech]
|
|
@@ -3,6 +3,7 @@ layer: L3
|
|
|
3
3
|
status: experimental
|
|
4
4
|
how_to_find: proper-noun-density
|
|
5
5
|
requires: [pos]
|
|
6
|
+
# chaff.yaml の names に並べた名前は、何語に割れても 1 つと数える。名前そのものは数える(読み手が知らないことは変わらない)。
|
|
6
7
|
severity: info
|
|
7
8
|
|
|
8
9
|
name:
|
|
@@ -15,7 +16,7 @@ why:
|
|
|
15
16
|
|
|
16
17
|
message:
|
|
17
18
|
ja: 1000 語あたり {density} 個の固有名詞があります({limit} 個まで)
|
|
18
|
-
en: "{density} proper nouns per 1000 words (limit {limit})"
|
|
19
|
+
en: "{density} proper {density|noun|nouns} per 1000 words (limit {limit})"
|
|
19
20
|
|
|
20
21
|
how_to_fix:
|
|
21
22
|
ja: 2 度目からは「その製品」「そのチーム」のように受けてください。初出で何者かを一言添えると、知らない人も読めます。
|
|
@@ -23,4 +24,4 @@ how_to_fix:
|
|
|
23
24
|
|
|
24
25
|
levels: { strict: 20, normal: 40, relaxed: 80 }
|
|
25
26
|
|
|
26
|
-
use_for: [blog, business, technical]
|
|
27
|
+
use_for: [blog, business, technical, legal, docs, academic, literature, speech]
|
|
@@ -16,7 +16,7 @@ why:
|
|
|
16
16
|
|
|
17
17
|
message:
|
|
18
18
|
ja: 接続詞で始まる段落が {count} 続いています({limit} まで)
|
|
19
|
-
en: "{count} paragraphs in a row open with a conjunction (limit {limit})"
|
|
19
|
+
en: "{count} {count|paragraph|paragraphs} in a row {count|opens|open} with a conjunction (limit {limit})"
|
|
20
20
|
|
|
21
21
|
how_to_fix:
|
|
22
22
|
ja: どれか 1 つを残して、他は言いたいことから始めてください。
|
|
@@ -24,4 +24,4 @@ how_to_fix:
|
|
|
24
24
|
|
|
25
25
|
levels: { strict: 1, normal: 2, relaxed: 4 }
|
|
26
26
|
|
|
27
|
-
use_for: [business, blog, technical]
|
|
27
|
+
use_for: [business, blog, technical, legal, docs, academic, literature, speech]
|
|
@@ -14,7 +14,7 @@ why:
|
|
|
14
14
|
|
|
15
15
|
message:
|
|
16
16
|
ja: 「{head}」で始まる文が {count} 続いています({limit} まで)
|
|
17
|
-
en: '{count} sentences in a row open with "{head}" (limit {limit})'
|
|
17
|
+
en: '{count} {count|sentence|sentences} in a row {count|opens|open} with "{head}" (limit {limit})'
|
|
18
18
|
|
|
19
19
|
how_to_fix:
|
|
20
20
|
ja: 語順を変えるか、1 文を前の文に繋げてください。
|
|
@@ -25,4 +25,4 @@ levels:
|
|
|
25
25
|
normal: 3
|
|
26
26
|
relaxed: 4
|
|
27
27
|
|
|
28
|
-
use_for: [blog, business, technical]
|
|
28
|
+
use_for: [blog, business, technical, legal, docs, academic, literature, speech]
|
package/rules/rule-of-three.yaml
CHANGED
|
@@ -14,7 +14,7 @@ why:
|
|
|
14
14
|
|
|
15
15
|
message:
|
|
16
16
|
ja: 箇条書き {total} 個のうち {count} 個が 3 項目です({limit}% まで)
|
|
17
|
-
en: "{count} of {total} lists have exactly three items ({limit}% allowed)"
|
|
17
|
+
en: "{count} of {total} {total|list|lists} {count|has|have} exactly three items ({limit}% allowed)"
|
|
18
18
|
|
|
19
19
|
how_to_fix:
|
|
20
20
|
ja: 数を合わせるために足した項目を消してください。2 つで足りるなら 2 つで。
|
|
@@ -22,4 +22,4 @@ how_to_fix:
|
|
|
22
22
|
|
|
23
23
|
levels: { strict: 50, normal: 70, relaxed: 90 }
|
|
24
24
|
|
|
25
|
-
use_for: [blog]
|
|
25
|
+
use_for: [blog, legal, docs, academic, literature, speech]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
id: sasete-itadaku
|
|
2
2
|
layer: L3
|
|
3
3
|
status: experimental
|
|
4
|
-
how_to_find: phrase-
|
|
4
|
+
how_to_find: phrase-count
|
|
5
5
|
word_list: sasete-itadaku
|
|
6
6
|
uses: [pos]
|
|
7
7
|
languages: [ja]
|
|
@@ -17,7 +17,7 @@ why:
|
|
|
17
17
|
|
|
18
18
|
message:
|
|
19
19
|
ja: 「{matched}」が文書内に {count} 回あります({limit} 回まで)
|
|
20
|
-
en: '"{matched}" appears {count} times in this document (limit {limit})'
|
|
20
|
+
en: '"{matched}" appears {count} {count|time|times} in this document (limit {limit})'
|
|
21
21
|
|
|
22
22
|
how_to_fix:
|
|
23
23
|
ja: 主語を立てて言い切ってください。「検討させていただきます」を「来週までに結論を出します」に。
|
|
@@ -25,4 +25,4 @@ how_to_fix:
|
|
|
25
25
|
|
|
26
26
|
levels: { strict: 1, normal: 3, relaxed: 6 }
|
|
27
27
|
|
|
28
|
-
use_for: [business, blog]
|
|
28
|
+
use_for: [business, blog, legal, docs, academic, literature, speech]
|
|
@@ -16,7 +16,7 @@ why:
|
|
|
16
16
|
|
|
17
17
|
message:
|
|
18
18
|
ja: 接続詞で始まる文が {count} 文続いています({limit} 文まで)
|
|
19
|
-
en: "{count} sentences in a row open with a conjunction (limit {limit})"
|
|
19
|
+
en: "{count} {count|sentence|sentences} in a row {count|opens|open} with a conjunction (limit {limit})"
|
|
20
20
|
|
|
21
21
|
how_to_fix:
|
|
22
22
|
ja: どれか 1 つを残して、他は前の文に繋ぐか独立させてください。
|
|
@@ -24,4 +24,4 @@ how_to_fix:
|
|
|
24
24
|
|
|
25
25
|
levels: { strict: 1, normal: 2, relaxed: 4 }
|
|
26
26
|
|
|
27
|
-
use_for: [blog, business]
|
|
27
|
+
use_for: [blog, business, legal, docs, academic, literature, speech]
|
|
@@ -16,7 +16,7 @@ why:
|
|
|
16
16
|
|
|
17
17
|
message:
|
|
18
18
|
ja: 本文に体言止めが {count} 文あります({limit} 文まで)
|
|
19
|
-
en: "{count} sentences end on a noun in the body text (limit {limit})"
|
|
19
|
+
en: "{count} {count|sentence|sentences} {count|ends|end} on a noun in the body text (limit {limit})"
|
|
20
20
|
|
|
21
21
|
how_to_fix:
|
|
22
22
|
ja: 述語を足してください。「〜が課題。」を「〜が課題です。」に。
|
|
@@ -24,4 +24,4 @@ how_to_fix:
|
|
|
24
24
|
|
|
25
25
|
levels: { strict: 2, normal: 4, relaxed: 8 }
|
|
26
26
|
|
|
27
|
-
use_for: [blog, business, technical]
|
|
27
|
+
use_for: [blog, business, technical, legal, docs, academic, literature, speech]
|
|
@@ -30,12 +30,12 @@ why:
|
|
|
30
30
|
|
|
31
31
|
message:
|
|
32
32
|
ja: 「{word}」が説明なしで使われています(説明のない略語 {count} 個)
|
|
33
|
-
en: '"{word}" is used without an expansion ({count} such acronyms)'
|
|
33
|
+
en: '"{word}" is used without an expansion ({count} such {count|acronym|acronyms})'
|
|
34
34
|
|
|
35
35
|
how_to_fix:
|
|
36
|
-
ja: 初出のところで括弧に入れて展開してください。「CI
|
|
37
|
-
en: Expand it in brackets at first use.
|
|
36
|
+
ja: 初出のところで括弧に入れて展開してください。「CI(継続的インテグレーション)」のように。読み手が名前として知っている略語(社名など)なら、chaff.yaml の names に並べると求めません。
|
|
37
|
+
en: Expand it in brackets at first use. If your readers know it as a name (a company, say), list it under names in chaff.yaml and it is not asked for.
|
|
38
38
|
|
|
39
39
|
levels: { strict: 1, normal: 3, relaxed: 6 }
|
|
40
40
|
|
|
41
|
-
use_for: [business, technical]
|
|
41
|
+
use_for: [business, technical, legal, docs, academic, literature, speech]
|
|
@@ -17,7 +17,7 @@ why:
|
|
|
17
17
|
|
|
18
18
|
message:
|
|
19
19
|
ja: 「{matched}」に比べる相手がありません(そういう言い方が {count} 箇所)
|
|
20
|
-
en: '"{matched}" has nothing to compare against ({count} such claims)'
|
|
20
|
+
en: '"{matched}" has nothing to compare against ({count} such {count|claim|claims})'
|
|
21
21
|
|
|
22
22
|
how_to_fix:
|
|
23
23
|
ja: 比較対象か条件を添えてください。「社内の 3 案のうち最も速い」のように。
|
|
@@ -25,4 +25,4 @@ how_to_fix:
|
|
|
25
25
|
|
|
26
26
|
levels: { strict: 1, normal: 2, relaxed: 4 }
|
|
27
27
|
|
|
28
|
-
use_for: [business, blog]
|
|
28
|
+
use_for: [business, blog, legal, docs, academic, literature, speech]
|
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
|
|
|
@@ -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
|
};
|