chaffjs 0.18.0 → 0.19.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.
Files changed (682) hide show
  1. package/README.md +52 -56
  2. package/bin/chaff.js +17 -2
  3. package/dist/acronym-expansions.d.ts +26 -0
  4. package/dist/acronym-expansions.d.ts.map +1 -0
  5. package/dist/acronym-expansions.js +88 -0
  6. package/dist/acronym-expansions.js.map +1 -0
  7. package/dist/announced-count.d.ts.map +1 -1
  8. package/dist/announced-count.js +44 -10
  9. package/dist/announced-count.js.map +1 -1
  10. package/dist/announced-list.d.ts +30 -0
  11. package/dist/announced-list.d.ts.map +1 -0
  12. package/dist/announced-list.js +95 -0
  13. package/dist/announced-list.js.map +1 -0
  14. package/dist/api.d.ts +168 -0
  15. package/dist/api.d.ts.map +1 -0
  16. package/dist/api.js +10 -0
  17. package/dist/api.js.map +1 -0
  18. package/dist/cli-text.d.ts +0 -1
  19. package/dist/cli-text.d.ts.map +1 -1
  20. package/dist/cli-text.js +6 -14
  21. package/dist/cli-text.js.map +1 -1
  22. package/dist/cli.d.ts.map +1 -1
  23. package/dist/cli.js +58 -62
  24. package/dist/cli.js.map +1 -1
  25. package/dist/closing-paragraph.d.ts +7 -0
  26. package/dist/closing-paragraph.d.ts.map +1 -0
  27. package/dist/closing-paragraph.js +22 -0
  28. package/dist/closing-paragraph.js.map +1 -0
  29. package/dist/commands/compare.d.ts +8 -1
  30. package/dist/commands/compare.d.ts.map +1 -1
  31. package/dist/commands/compare.js +9 -17
  32. package/dist/commands/compare.js.map +1 -1
  33. package/dist/commands/document-commands.d.ts +9 -0
  34. package/dist/commands/document-commands.d.ts.map +1 -0
  35. package/dist/commands/document-commands.js +17 -0
  36. package/dist/commands/document-commands.js.map +1 -0
  37. package/dist/commands/eval.d.ts.map +1 -1
  38. package/dist/commands/eval.js +3 -2
  39. package/dist/commands/eval.js.map +1 -1
  40. package/dist/commands/facts.d.ts +8 -0
  41. package/dist/commands/facts.d.ts.map +1 -0
  42. package/dist/commands/facts.js +30 -0
  43. package/dist/commands/facts.js.map +1 -0
  44. package/dist/commands/feedback.d.ts +10 -0
  45. package/dist/commands/feedback.d.ts.map +1 -1
  46. package/dist/commands/feedback.js +25 -1
  47. package/dist/commands/feedback.js.map +1 -1
  48. package/dist/commands/fix-plan.d.ts +13 -0
  49. package/dist/commands/fix-plan.d.ts.map +1 -0
  50. package/dist/commands/fix-plan.js +44 -0
  51. package/dist/commands/fix-plan.js.map +1 -0
  52. package/dist/commands/outline.d.ts +8 -0
  53. package/dist/commands/outline.d.ts.map +1 -0
  54. package/dist/commands/outline.js +43 -0
  55. package/dist/commands/outline.js.map +1 -0
  56. package/dist/commands/read-document.d.ts +14 -0
  57. package/dist/commands/read-document.d.ts.map +1 -0
  58. package/dist/commands/read-document.js +21 -0
  59. package/dist/commands/read-document.js.map +1 -0
  60. package/dist/commands/suppressions.d.ts +7 -0
  61. package/dist/commands/suppressions.d.ts.map +1 -0
  62. package/dist/commands/suppressions.js +9 -0
  63. package/dist/commands/suppressions.js.map +1 -0
  64. package/dist/commands/test.js +1 -1
  65. package/dist/commands/test.js.map +1 -1
  66. package/dist/compare/clock-time.d.ts.map +1 -1
  67. package/dist/compare/clock-time.js +5 -2
  68. package/dist/compare/clock-time.js.map +1 -1
  69. package/dist/compare/distinct.d.ts +9 -0
  70. package/dist/compare/distinct.d.ts.map +1 -0
  71. package/dist/compare/distinct.js +12 -0
  72. package/dist/compare/distinct.js.map +1 -0
  73. package/dist/compare/extract.d.ts.map +1 -1
  74. package/dist/compare/extract.js +7 -5
  75. package/dist/compare/extract.js.map +1 -1
  76. package/dist/compare/fact-key.d.ts +17 -0
  77. package/dist/compare/fact-key.d.ts.map +1 -0
  78. package/dist/compare/fact-key.js +25 -0
  79. package/dist/compare/fact-key.js.map +1 -0
  80. package/dist/compare/fact-text.d.ts +5 -0
  81. package/dist/compare/fact-text.d.ts.map +1 -1
  82. package/dist/compare/fact-text.js +16 -2
  83. package/dist/compare/fact-text.js.map +1 -1
  84. package/dist/compare/facts-render.d.ts +17 -0
  85. package/dist/compare/facts-render.d.ts.map +1 -0
  86. package/dist/compare/facts-render.js +43 -0
  87. package/dist/compare/facts-render.js.map +1 -0
  88. package/dist/compare/facts-text.d.ts +15 -0
  89. package/dist/compare/facts-text.d.ts.map +1 -0
  90. package/dist/compare/facts-text.js +27 -0
  91. package/dist/compare/facts-text.js.map +1 -0
  92. package/dist/compare/match.d.ts +1 -0
  93. package/dist/compare/match.d.ts.map +1 -1
  94. package/dist/compare/match.js +1 -1
  95. package/dist/compare/match.js.map +1 -1
  96. package/dist/compare/outcome.d.ts +4 -1
  97. package/dist/compare/outcome.d.ts.map +1 -1
  98. package/dist/compare/outcome.js +10 -4
  99. package/dist/compare/outcome.js.map +1 -1
  100. package/dist/compare/proper-nouns.d.ts +3 -3
  101. package/dist/compare/proper-nouns.d.ts.map +1 -1
  102. package/dist/compare/proper-nouns.js +2 -3
  103. package/dist/compare/proper-nouns.js.map +1 -1
  104. package/dist/compare/render.d.ts +4 -0
  105. package/dist/compare/render.d.ts.map +1 -1
  106. package/dist/compare/render.js +2 -2
  107. package/dist/compare/render.js.map +1 -1
  108. package/dist/compare/text-atoms.d.ts +5 -2
  109. package/dist/compare/text-atoms.d.ts.map +1 -1
  110. package/dist/compare/text-atoms.js +7 -5
  111. package/dist/compare/text-atoms.js.map +1 -1
  112. package/dist/compare/text.d.ts.map +1 -1
  113. package/dist/compare/text.js +2 -2
  114. package/dist/compare/text.js.map +1 -1
  115. package/dist/config/load.d.ts +8 -0
  116. package/dist/config/load.d.ts.map +1 -1
  117. package/dist/config/load.js +1 -0
  118. package/dist/config/load.js.map +1 -1
  119. package/dist/config/option-problems.d.ts +3 -2
  120. package/dist/config/option-problems.d.ts.map +1 -1
  121. package/dist/config/option-problems.js +4 -3
  122. package/dist/config/option-problems.js.map +1 -1
  123. package/dist/config/style.d.ts +5 -0
  124. package/dist/config/style.d.ts.map +1 -1
  125. package/dist/config/style.js +13 -1
  126. package/dist/config/style.js.map +1 -1
  127. package/dist/custom/load.d.ts +6 -3
  128. package/dist/custom/load.d.ts.map +1 -1
  129. package/dist/custom/load.js +10 -2
  130. package/dist/custom/load.js.map +1 -1
  131. package/dist/custom/module-path.d.ts +11 -0
  132. package/dist/custom/module-path.d.ts.map +1 -0
  133. package/dist/custom/module-path.js +15 -0
  134. package/dist/custom/module-path.js.map +1 -0
  135. package/dist/custom/parse.d.ts +17 -3
  136. package/dist/custom/parse.d.ts.map +1 -1
  137. package/dist/custom/parse.js +57 -14
  138. package/dist/custom/parse.js.map +1 -1
  139. package/dist/custom/problems.d.ts +4 -1
  140. package/dist/custom/problems.d.ts.map +1 -1
  141. package/dist/custom/problems.js +56 -39
  142. package/dist/custom/problems.js.map +1 -1
  143. package/dist/detect-language.js +1 -1
  144. package/dist/detect.d.ts.map +1 -1
  145. package/dist/detect.js +9 -3
  146. package/dist/detect.js.map +1 -1
  147. package/dist/detectors/ai-phrasing.d.ts +4 -1
  148. package/dist/detectors/ai-phrasing.d.ts.map +1 -1
  149. package/dist/detectors/ai-phrasing.js +6 -2
  150. package/dist/detectors/ai-phrasing.js.map +1 -1
  151. package/dist/detectors/bold-label.d.ts +10 -0
  152. package/dist/detectors/bold-label.d.ts.map +1 -0
  153. package/dist/detectors/bold-label.js +44 -0
  154. package/dist/detectors/bold-label.js.map +1 -0
  155. package/dist/detectors/dangling-figure.d.ts +5 -2
  156. package/dist/detectors/dangling-figure.d.ts.map +1 -1
  157. package/dist/detectors/dangling-figure.js +5 -3
  158. package/dist/detectors/dangling-figure.js.map +1 -1
  159. package/dist/detectors/definition-use.d.ts +8 -0
  160. package/dist/detectors/definition-use.d.ts.map +1 -0
  161. package/dist/detectors/definition-use.js +32 -0
  162. package/dist/detectors/definition-use.js.map +1 -0
  163. package/dist/detectors/doubled-punctuation.d.ts.map +1 -1
  164. package/dist/detectors/doubled-punctuation.js +4 -2
  165. package/dist/detectors/doubled-punctuation.js.map +1 -1
  166. package/dist/detectors/duplicate-heading.d.ts +14 -0
  167. package/dist/detectors/duplicate-heading.d.ts.map +1 -0
  168. package/dist/detectors/duplicate-heading.js +39 -0
  169. package/dist/detectors/duplicate-heading.js.map +1 -0
  170. package/dist/detectors/empty-section.d.ts +8 -0
  171. package/dist/detectors/empty-section.d.ts.map +1 -0
  172. package/dist/detectors/empty-section.js +16 -0
  173. package/dist/detectors/empty-section.js.map +1 -0
  174. package/dist/detectors/fullwidth-alnum.d.ts +24 -0
  175. package/dist/detectors/fullwidth-alnum.d.ts.map +1 -0
  176. package/dist/detectors/fullwidth-alnum.js +88 -0
  177. package/dist/detectors/fullwidth-alnum.js.map +1 -0
  178. package/dist/detectors/hankaku-kana.d.ts +9 -0
  179. package/dist/detectors/hankaku-kana.d.ts.map +1 -0
  180. package/dist/detectors/hankaku-kana.js +27 -0
  181. package/dist/detectors/hankaku-kana.js.map +1 -0
  182. package/dist/detectors/heading-code-words.js +2 -2
  183. package/dist/detectors/heading-code-words.js.map +1 -1
  184. package/dist/detectors/index.d.ts.map +1 -1
  185. package/dist/detectors/index.js +36 -0
  186. package/dist/detectors/index.js.map +1 -1
  187. package/dist/detectors/invisible-character.d.ts +20 -0
  188. package/dist/detectors/invisible-character.d.ts.map +1 -0
  189. package/dist/detectors/invisible-character.js +143 -0
  190. package/dist/detectors/invisible-character.js.map +1 -0
  191. package/dist/detectors/kutoten-consistency.js +2 -2
  192. package/dist/detectors/kutoten-consistency.js.map +1 -1
  193. package/dist/detectors/lexicon-match.d.ts +10 -1
  194. package/dist/detectors/lexicon-match.d.ts.map +1 -1
  195. package/dist/detectors/lexicon-match.js +26 -1
  196. package/dist/detectors/lexicon-match.js.map +1 -1
  197. package/dist/detectors/lexicon.d.ts +6 -1
  198. package/dist/detectors/lexicon.d.ts.map +1 -1
  199. package/dist/detectors/lexicon.js +31 -13
  200. package/dist/detectors/lexicon.js.map +1 -1
  201. package/dist/detectors/list-sentence.d.ts +3 -1
  202. package/dist/detectors/list-sentence.d.ts.map +1 -1
  203. package/dist/detectors/list-sentence.js +26 -1
  204. package/dist/detectors/list-sentence.js.map +1 -1
  205. package/dist/detectors/long-vowel.d.ts.map +1 -1
  206. package/dist/detectors/long-vowel.js +18 -4
  207. package/dist/detectors/long-vowel.js.map +1 -1
  208. package/dist/detectors/markup-finding.d.ts +3 -1
  209. package/dist/detectors/markup-finding.d.ts.map +1 -1
  210. package/dist/detectors/markup-finding.js +10 -0
  211. package/dist/detectors/markup-finding.js.map +1 -1
  212. package/dist/detectors/modal-conflict.d.ts +4 -0
  213. package/dist/detectors/modal-conflict.d.ts.map +1 -0
  214. package/dist/detectors/modal-conflict.js +22 -0
  215. package/dist/detectors/modal-conflict.js.map +1 -0
  216. package/dist/detectors/name-variant.d.ts +4 -0
  217. package/dist/detectors/name-variant.d.ts.map +1 -0
  218. package/dist/detectors/name-variant.js +13 -0
  219. package/dist/detectors/name-variant.js.map +1 -0
  220. package/dist/detectors/nominalization.d.ts +11 -0
  221. package/dist/detectors/nominalization.d.ts.map +1 -0
  222. package/dist/detectors/nominalization.js +56 -0
  223. package/dist/detectors/nominalization.js.map +1 -0
  224. package/dist/detectors/orthography.d.ts.map +1 -1
  225. package/dist/detectors/orthography.js +59 -28
  226. package/dist/detectors/orthography.js.map +1 -1
  227. package/dist/detectors/outside-announced-list.d.ts +4 -0
  228. package/dist/detectors/outside-announced-list.d.ts.map +1 -0
  229. package/dist/detectors/outside-announced-list.js +24 -0
  230. package/dist/detectors/outside-announced-list.js.map +1 -0
  231. package/dist/detectors/oxford-comma.d.ts.map +1 -1
  232. package/dist/detectors/oxford-comma.js +1 -0
  233. package/dist/detectors/oxford-comma.js.map +1 -1
  234. package/dist/detectors/phrase-match.d.ts +4 -1
  235. package/dist/detectors/phrase-match.d.ts.map +1 -1
  236. package/dist/detectors/phrase-match.js +26 -7
  237. package/dist/detectors/phrase-match.js.map +1 -1
  238. package/dist/detectors/ra-nuki.d.ts +9 -0
  239. package/dist/detectors/ra-nuki.d.ts.map +1 -0
  240. package/dist/detectors/ra-nuki.js +30 -0
  241. package/dist/detectors/ra-nuki.js.map +1 -0
  242. package/dist/detectors/requirement-modal.d.ts +5 -0
  243. package/dist/detectors/requirement-modal.d.ts.map +1 -0
  244. package/dist/detectors/requirement-modal.js +54 -0
  245. package/dist/detectors/requirement-modal.js.map +1 -0
  246. package/dist/detectors/requirement-smell.d.ts +4 -0
  247. package/dist/detectors/requirement-smell.d.ts.map +1 -0
  248. package/dist/detectors/requirement-smell.js +41 -0
  249. package/dist/detectors/requirement-smell.js.map +1 -0
  250. package/dist/detectors/space-before-punctuation.d.ts +8 -0
  251. package/dist/detectors/space-before-punctuation.d.ts.map +1 -0
  252. package/dist/detectors/space-before-punctuation.js +24 -0
  253. package/dist/detectors/space-before-punctuation.js.map +1 -0
  254. package/dist/detectors/spelling-variety.d.ts +23 -0
  255. package/dist/detectors/spelling-variety.d.ts.map +1 -0
  256. package/dist/detectors/spelling-variety.js +59 -0
  257. package/dist/detectors/spelling-variety.js.map +1 -0
  258. package/dist/detectors/structure-tree.d.ts.map +1 -1
  259. package/dist/detectors/structure-tree.js +3 -1
  260. package/dist/detectors/structure-tree.js.map +1 -1
  261. package/dist/detectors/structure.d.ts.map +1 -1
  262. package/dist/detectors/structure.js +16 -2
  263. package/dist/detectors/structure.js.map +1 -1
  264. package/dist/detectors/superlative-comparison.d.ts +20 -0
  265. package/dist/detectors/superlative-comparison.d.ts.map +1 -0
  266. package/dist/detectors/superlative-comparison.js +32 -0
  267. package/dist/detectors/superlative-comparison.js.map +1 -0
  268. package/dist/detectors/url-run-on.d.ts.map +1 -1
  269. package/dist/detectors/url-run-on.js +6 -1
  270. package/dist/detectors/url-run-on.js.map +1 -1
  271. package/dist/detectors/vague-figure-pointer.d.ts +10 -0
  272. package/dist/detectors/vague-figure-pointer.d.ts.map +1 -0
  273. package/dist/detectors/vague-figure-pointer.js +46 -0
  274. package/dist/detectors/vague-figure-pointer.js.map +1 -0
  275. package/dist/detectors/word-list.d.ts +2 -0
  276. package/dist/detectors/word-list.d.ts.map +1 -1
  277. package/dist/detectors/word-list.js +4 -1
  278. package/dist/detectors/word-list.js.map +1 -1
  279. package/dist/document.d.ts +9 -3
  280. package/dist/document.d.ts.map +1 -1
  281. package/dist/document.js +55 -32
  282. package/dist/document.js.map +1 -1
  283. package/dist/extension/document-view.d.ts +7 -0
  284. package/dist/extension/document-view.d.ts.map +1 -0
  285. package/dist/extension/document-view.js +91 -0
  286. package/dist/extension/document-view.js.map +1 -0
  287. package/dist/extension/failure-text.d.ts +5 -0
  288. package/dist/extension/failure-text.d.ts.map +1 -0
  289. package/dist/extension/failure-text.js +33 -0
  290. package/dist/extension/failure-text.js.map +1 -0
  291. package/dist/extension/import-default.d.ts +7 -0
  292. package/dist/extension/import-default.d.ts.map +1 -0
  293. package/dist/extension/import-default.js +21 -0
  294. package/dist/extension/import-default.js.map +1 -0
  295. package/dist/extension/load.d.ts +28 -0
  296. package/dist/extension/load.d.ts.map +1 -0
  297. package/dist/extension/load.js +51 -0
  298. package/dist/extension/load.js.map +1 -0
  299. package/dist/extension/module-detector.d.ts +21 -0
  300. package/dist/extension/module-detector.d.ts.map +1 -0
  301. package/dist/extension/module-detector.js +49 -0
  302. package/dist/extension/module-detector.js.map +1 -0
  303. package/dist/extension/plugin-load.d.ts +9 -0
  304. package/dist/extension/plugin-load.d.ts.map +1 -0
  305. package/dist/extension/plugin-load.js +68 -0
  306. package/dist/extension/plugin-load.js.map +1 -0
  307. package/dist/extension/plugin-name.d.ts +8 -0
  308. package/dist/extension/plugin-name.d.ts.map +1 -0
  309. package/dist/extension/plugin-name.js +22 -0
  310. package/dist/extension/plugin-name.js.map +1 -0
  311. package/dist/extension/plugin-parse.d.ts +41 -0
  312. package/dist/extension/plugin-parse.d.ts.map +1 -0
  313. package/dist/extension/plugin-parse.js +156 -0
  314. package/dist/extension/plugin-parse.js.map +1 -0
  315. package/dist/extension/problem-text.d.ts +5 -0
  316. package/dist/extension/problem-text.d.ts.map +1 -0
  317. package/dist/extension/problem-text.js +74 -0
  318. package/dist/extension/problem-text.js.map +1 -0
  319. package/dist/extension/real-path.d.ts +3 -0
  320. package/dist/extension/real-path.d.ts.map +1 -0
  321. package/dist/extension/real-path.js +8 -0
  322. package/dist/extension/real-path.js.map +1 -0
  323. package/dist/extension/returned-findings.d.ts +17 -0
  324. package/dist/extension/returned-findings.d.ts.map +1 -0
  325. package/dist/extension/returned-findings.js +48 -0
  326. package/dist/extension/returned-findings.js.map +1 -0
  327. package/dist/extension/rule-export.d.ts +13 -0
  328. package/dist/extension/rule-export.d.ts.map +1 -0
  329. package/dist/extension/rule-export.js +16 -0
  330. package/dist/extension/rule-export.js.map +1 -0
  331. package/dist/feedback/draft.d.ts +2 -0
  332. package/dist/feedback/draft.d.ts.map +1 -1
  333. package/dist/feedback/draft.js +3 -0
  334. package/dist/feedback/draft.js.map +1 -1
  335. package/dist/figure-references.d.ts +11 -2
  336. package/dist/figure-references.d.ts.map +1 -1
  337. package/dist/figure-references.js +50 -13
  338. package/dist/figure-references.js.map +1 -1
  339. package/dist/finding-order.d.ts +9 -0
  340. package/dist/finding-order.d.ts.map +1 -0
  341. package/dist/finding-order.js +3 -0
  342. package/dist/finding-order.js.map +1 -0
  343. package/dist/fix-plan/mode.d.ts +21 -0
  344. package/dist/fix-plan/mode.d.ts.map +1 -0
  345. package/dist/fix-plan/mode.js +16 -0
  346. package/dist/fix-plan/mode.js.map +1 -0
  347. package/dist/fix-plan/plan.d.ts +77 -0
  348. package/dist/fix-plan/plan.d.ts.map +1 -0
  349. package/dist/fix-plan/plan.js +100 -0
  350. package/dist/fix-plan/plan.js.map +1 -0
  351. package/dist/fix-plan/render.d.ts +6 -0
  352. package/dist/fix-plan/render.d.ts.map +1 -0
  353. package/dist/fix-plan/render.js +101 -0
  354. package/dist/fix-plan/render.js.map +1 -0
  355. package/dist/fix-plan/text.d.ts +35 -0
  356. package/dist/fix-plan/text.d.ts.map +1 -0
  357. package/dist/fix-plan/text.js +95 -0
  358. package/dist/fix-plan/text.js.map +1 -0
  359. package/dist/language-sample.d.ts +2 -0
  360. package/dist/language-sample.d.ts.map +1 -0
  361. package/dist/language-sample.js +41 -0
  362. package/dist/language-sample.js.map +1 -0
  363. package/dist/link-line.d.ts +8 -0
  364. package/dist/link-line.d.ts.map +1 -0
  365. package/dist/link-line.js +12 -0
  366. package/dist/link-line.js.map +1 -0
  367. package/dist/long-vowel.d.ts +9 -6
  368. package/dist/long-vowel.d.ts.map +1 -1
  369. package/dist/long-vowel.js +15 -8
  370. package/dist/long-vowel.js.map +1 -1
  371. package/dist/markup.d.ts.map +1 -1
  372. package/dist/markup.js +4 -2
  373. package/dist/markup.js.map +1 -1
  374. package/dist/name-variants.d.ts +25 -0
  375. package/dist/name-variants.d.ts.map +1 -0
  376. package/dist/name-variants.js +181 -0
  377. package/dist/name-variants.js.map +1 -0
  378. package/dist/node-version.d.ts +7 -0
  379. package/dist/node-version.d.ts.map +1 -0
  380. package/dist/node-version.js +19 -0
  381. package/dist/node-version.js.map +1 -0
  382. package/dist/not-run.d.ts +14 -0
  383. package/dist/not-run.d.ts.map +1 -0
  384. package/dist/not-run.js +12 -0
  385. package/dist/not-run.js.map +1 -0
  386. package/dist/orthography.d.ts +8 -1
  387. package/dist/orthography.d.ts.map +1 -1
  388. package/dist/orthography.js +33 -12
  389. package/dist/orthography.js.map +1 -1
  390. package/dist/outline/render.d.ts +18 -0
  391. package/dist/outline/render.d.ts.map +1 -0
  392. package/dist/outline/render.js +60 -0
  393. package/dist/outline/render.js.map +1 -0
  394. package/dist/outline/shape.d.ts +26 -0
  395. package/dist/outline/shape.d.ts.map +1 -0
  396. package/dist/outline/shape.js +37 -0
  397. package/dist/outline/shape.js.map +1 -0
  398. package/dist/outline/text.d.ts +21 -0
  399. package/dist/outline/text.d.ts.map +1 -0
  400. package/dist/outline/text.js +30 -0
  401. package/dist/outline/text.js.map +1 -0
  402. package/dist/plugin.d.ts +24 -2
  403. package/dist/plugin.d.ts.map +1 -1
  404. package/dist/reasons.d.ts +1 -0
  405. package/dist/reasons.d.ts.map +1 -1
  406. package/dist/reasons.js +2 -0
  407. package/dist/reasons.js.map +1 -1
  408. package/dist/render/compact.d.ts +3 -1
  409. package/dist/render/compact.d.ts.map +1 -1
  410. package/dist/render/compact.js +8 -2
  411. package/dist/render/compact.js.map +1 -1
  412. package/dist/render/explain.d.ts +3 -1
  413. package/dist/render/explain.d.ts.map +1 -1
  414. package/dist/render/explain.js +32 -15
  415. package/dist/render/explain.js.map +1 -1
  416. package/dist/render/rules-json.d.ts.map +1 -1
  417. package/dist/render/rules-json.js +16 -12
  418. package/dist/render/rules-json.js.map +1 -1
  419. package/dist/render/suppressions.d.ts +3 -0
  420. package/dist/render/suppressions.d.ts.map +1 -1
  421. package/dist/render/suppressions.js +20 -1
  422. package/dist/render/suppressions.js.map +1 -1
  423. package/dist/rule-guide.d.ts +19 -0
  424. package/dist/rule-guide.d.ts.map +1 -1
  425. package/dist/rule-guide.js +14 -0
  426. package/dist/rule-guide.js.map +1 -1
  427. package/dist/run-semantic.d.ts.map +1 -1
  428. package/dist/run-semantic.js +2 -1
  429. package/dist/run-semantic.js.map +1 -1
  430. package/dist/run.d.ts +5 -1
  431. package/dist/run.d.ts.map +1 -1
  432. package/dist/run.js +31 -14
  433. package/dist/run.js.map +1 -1
  434. package/dist/setting-problems.d.ts.map +1 -1
  435. package/dist/setting-problems.js +3 -1
  436. package/dist/setting-problems.js.map +1 -1
  437. package/dist/spacing-minority.d.ts +16 -0
  438. package/dist/spacing-minority.d.ts.map +1 -0
  439. package/dist/spacing-minority.js +23 -0
  440. package/dist/spacing-minority.js.map +1 -0
  441. package/dist/stet-block.d.ts +5 -0
  442. package/dist/stet-block.d.ts.map +1 -0
  443. package/dist/stet-block.js +59 -0
  444. package/dist/stet-block.js.map +1 -0
  445. package/dist/stet.d.ts +4 -0
  446. package/dist/stet.d.ts.map +1 -1
  447. package/dist/stet.js +31 -11
  448. package/dist/stet.js.map +1 -1
  449. package/dist/structure/date-range.d.ts +10 -0
  450. package/dist/structure/date-range.d.ts.map +1 -1
  451. package/dist/structure/date-range.js +33 -1
  452. package/dist/structure/date-range.js.map +1 -1
  453. package/dist/structure/definition-use.d.ts +32 -0
  454. package/dist/structure/definition-use.d.ts.map +1 -0
  455. package/dist/structure/definition-use.js +102 -0
  456. package/dist/structure/definition-use.js.map +1 -0
  457. package/dist/structure/lazy-tree.d.ts +8 -0
  458. package/dist/structure/lazy-tree.d.ts.map +1 -0
  459. package/dist/structure/lazy-tree.js +14 -0
  460. package/dist/structure/lazy-tree.js.map +1 -0
  461. package/dist/structure/modal-conflict.d.ts +28 -0
  462. package/dist/structure/modal-conflict.d.ts.map +1 -0
  463. package/dist/structure/modal-conflict.js +74 -0
  464. package/dist/structure/modal-conflict.js.map +1 -0
  465. package/dist/structure/numbered-clauses.d.ts +4 -0
  466. package/dist/structure/numbered-clauses.d.ts.map +1 -0
  467. package/dist/structure/numbered-clauses.js +9 -0
  468. package/dist/structure/numbered-clauses.js.map +1 -0
  469. package/dist/structure/universal.d.ts.map +1 -1
  470. package/dist/structure/universal.js +8 -2
  471. package/dist/structure/universal.js.map +1 -1
  472. package/dist/style-parse.d.ts +6 -0
  473. package/dist/style-parse.d.ts.map +1 -1
  474. package/dist/style-parse.js +32 -4
  475. package/dist/style-parse.js.map +1 -1
  476. package/dist/subheading-line.d.ts +19 -2
  477. package/dist/subheading-line.d.ts.map +1 -1
  478. package/dist/subheading-line.js +39 -11
  479. package/dist/subheading-line.js.map +1 -1
  480. package/dist/tag-coverage.d.ts +8 -0
  481. package/dist/tag-coverage.d.ts.map +1 -0
  482. package/dist/tag-coverage.js +7 -0
  483. package/dist/tag-coverage.js.map +1 -0
  484. package/genres.yaml +42 -0
  485. package/package.json +7 -3
  486. package/rules/acronym-expansion-conflict.yaml +82 -0
  487. package/rules/adverb-overuse.yaml +2 -2
  488. package/rules/agentless-passive.yaml +28 -0
  489. package/rules/ai-tell.yaml +34 -0
  490. package/rules/announced-count-mismatch.yaml +2 -2
  491. package/rules/announcing-opener.yaml +31 -0
  492. package/rules/assistant-residue.yaml +30 -0
  493. package/rules/bold-density.yaml +28 -0
  494. package/rules/bold-label-list.yaml +81 -0
  495. package/rules/closing-cliche.yaml +27 -2
  496. package/rules/colon-lead-in.yaml +28 -4
  497. package/rules/contrast-framing.yaml +33 -2
  498. package/rules/cushion-phrase-density.yaml +32 -5
  499. package/rules/dangling-reference.yaml +1 -0
  500. package/rules/date-order.yaml +1 -0
  501. package/rules/date-range-reversed.yaml +2 -2
  502. package/rules/double-negative.yaml +51 -0
  503. package/rules/doubled-punctuation.yaml +2 -2
  504. package/rules/doubled-word.yaml +2 -2
  505. package/rules/duplicate-heading.yaml +78 -0
  506. package/rules/emoji-density.yaml +5 -4
  507. package/rules/empty-intensifier.yaml +25 -0
  508. package/rules/empty-section.yaml +74 -0
  509. package/rules/excessive-hedging.yaml +32 -4
  510. package/rules/fullwidth-alnum-consistency.yaml +53 -0
  511. package/rules/hankaku-kana.yaml +41 -0
  512. package/rules/image-alt-text.yaml +1 -1
  513. package/rules/invisible-character.yaml +63 -0
  514. package/rules/katakana-long-vowel.yaml +10 -9
  515. package/rules/latin-spacing.yaml +12 -5
  516. package/rules/max-kanji-continuous.yaml +1 -0
  517. package/rules/max-paragraph-length.yaml +1 -0
  518. package/rules/max-sentence-length.yaml +26 -0
  519. package/rules/modal-conflict.yaml +106 -0
  520. package/rules/name-variant.yaml +88 -0
  521. package/rules/no-doubled-joshi.yaml +1 -0
  522. package/rules/no-em-dash.yaml +26 -2
  523. package/rules/no-mixed-desumasu.yaml +1 -0
  524. package/rules/nominalization.yaml +89 -0
  525. package/rules/outside-announced-list.yaml +80 -0
  526. package/rules/padded-intro.yaml +25 -0
  527. package/rules/paragraph-length-variance.yaml +26 -2
  528. package/rules/preferred-term.yaml +1 -0
  529. package/rules/proper-noun-density.yaml +14 -5
  530. package/rules/ra-nuki.yaml +43 -0
  531. package/rules/requirement-modal.yaml +94 -0
  532. package/rules/requirement-smell.yaml +70 -0
  533. package/rules/rule-of-three.yaml +46 -0
  534. package/rules/section-length-uniformity.yaml +26 -2
  535. package/rules/sentence-rhythm.yaml +26 -0
  536. package/rules/space-before-punctuation.yaml +41 -0
  537. package/rules/spelling-consistency.yaml +47 -0
  538. package/rules/stock-transition.yaml +33 -2
  539. package/rules/taigen-dome-in-prose.yaml +14 -0
  540. package/rules/total-mismatch.yaml +1 -0
  541. package/rules/undefined-acronym.yaml +1 -0
  542. package/rules/unqualified-superlative.yaml +5 -4
  543. package/rules/unused-definition.yaml +75 -0
  544. package/rules/use-before-definition.yaml +77 -0
  545. package/rules/vague-figure-reference.yaml +66 -0
  546. package/skills/chaff/SKILL.md +108 -8
  547. package/src/acronym-expansions.ts +106 -0
  548. package/src/announced-count.ts +53 -11
  549. package/src/announced-list.ts +120 -0
  550. package/src/api.ts +154 -0
  551. package/src/cli-text.ts +6 -11
  552. package/src/cli.ts +64 -63
  553. package/src/closing-paragraph.ts +24 -0
  554. package/src/commands/compare.ts +11 -18
  555. package/src/commands/document-commands.ts +19 -0
  556. package/src/commands/eval.ts +3 -2
  557. package/src/commands/facts.ts +33 -0
  558. package/src/commands/feedback.ts +33 -1
  559. package/src/commands/fix-plan.ts +53 -0
  560. package/src/commands/outline.ts +49 -0
  561. package/src/commands/read-document.ts +24 -0
  562. package/src/commands/suppressions.ts +19 -0
  563. package/src/commands/test.ts +1 -1
  564. package/src/compare/clock-time.ts +5 -2
  565. package/src/compare/distinct.ts +13 -0
  566. package/src/compare/extract.ts +7 -5
  567. package/src/compare/fact-key.ts +31 -0
  568. package/src/compare/fact-text.ts +21 -2
  569. package/src/compare/facts-render.ts +63 -0
  570. package/src/compare/facts-text.ts +41 -0
  571. package/src/compare/match.ts +1 -1
  572. package/src/compare/outcome.ts +14 -4
  573. package/src/compare/proper-nouns.ts +4 -4
  574. package/src/compare/render.ts +2 -2
  575. package/src/compare/text-atoms.ts +7 -6
  576. package/src/compare/text.ts +3 -2
  577. package/src/config/load.ts +9 -0
  578. package/src/config/option-problems.ts +6 -4
  579. package/src/config/style.ts +14 -1
  580. package/src/custom/load.ts +11 -3
  581. package/src/custom/module-path.ts +23 -0
  582. package/src/custom/parse.ts +67 -18
  583. package/src/custom/problems.ts +63 -41
  584. package/src/detect-language.ts +1 -1
  585. package/src/detect.ts +10 -3
  586. package/src/detectors/ai-phrasing.ts +6 -2
  587. package/src/detectors/bold-label.ts +55 -0
  588. package/src/detectors/dangling-figure.ts +8 -4
  589. package/src/detectors/definition-use.ts +41 -0
  590. package/src/detectors/doubled-punctuation.ts +5 -2
  591. package/src/detectors/duplicate-heading.ts +47 -0
  592. package/src/detectors/empty-section.ts +21 -0
  593. package/src/detectors/fullwidth-alnum.ts +118 -0
  594. package/src/detectors/hankaku-kana.ts +38 -0
  595. package/src/detectors/heading-code-words.ts +2 -2
  596. package/src/detectors/index.ts +36 -0
  597. package/src/detectors/invisible-character.ts +190 -0
  598. package/src/detectors/kutoten-consistency.ts +2 -2
  599. package/src/detectors/lexicon-match.ts +33 -1
  600. package/src/detectors/lexicon.ts +40 -13
  601. package/src/detectors/list-sentence.ts +45 -4
  602. package/src/detectors/long-vowel.ts +20 -4
  603. package/src/detectors/markup-finding.ts +16 -1
  604. package/src/detectors/modal-conflict.ts +26 -0
  605. package/src/detectors/name-variant.ts +15 -0
  606. package/src/detectors/nominalization.ts +73 -0
  607. package/src/detectors/orthography.ts +77 -27
  608. package/src/detectors/outside-announced-list.ts +25 -0
  609. package/src/detectors/oxford-comma.ts +1 -0
  610. package/src/detectors/phrase-match.ts +27 -7
  611. package/src/detectors/ra-nuki.ts +38 -0
  612. package/src/detectors/requirement-modal.ts +67 -0
  613. package/src/detectors/requirement-smell.ts +52 -0
  614. package/src/detectors/space-before-punctuation.ts +32 -0
  615. package/src/detectors/spelling-variety.ts +81 -0
  616. package/src/detectors/structure-tree.ts +3 -1
  617. package/src/detectors/structure.ts +17 -3
  618. package/src/detectors/superlative-comparison.ts +47 -0
  619. package/src/detectors/url-run-on.ts +7 -1
  620. package/src/detectors/vague-figure-pointer.ts +58 -0
  621. package/src/detectors/word-list.ts +6 -1
  622. package/src/document.ts +88 -41
  623. package/src/extension/document-view.ts +109 -0
  624. package/src/extension/failure-text.ts +46 -0
  625. package/src/extension/import-default.ts +13 -0
  626. package/src/extension/load.ts +97 -0
  627. package/src/extension/module-detector.ts +61 -0
  628. package/src/extension/plugin-load.ts +82 -0
  629. package/src/extension/plugin-name.ts +26 -0
  630. package/src/extension/plugin-parse.ts +229 -0
  631. package/src/extension/problem-text.ts +103 -0
  632. package/src/extension/real-path.ts +10 -0
  633. package/src/extension/returned-findings.ts +62 -0
  634. package/src/extension/rule-export.ts +23 -0
  635. package/src/feedback/draft.ts +6 -0
  636. package/src/figure-references.ts +73 -12
  637. package/src/finding-order.ts +3 -0
  638. package/src/fix-plan/mode.ts +34 -0
  639. package/src/fix-plan/plan.ts +181 -0
  640. package/src/fix-plan/render.ts +128 -0
  641. package/src/fix-plan/text.ts +133 -0
  642. package/src/language-sample.ts +48 -0
  643. package/src/link-line.ts +13 -0
  644. package/src/long-vowel.ts +18 -8
  645. package/src/markup.ts +4 -2
  646. package/src/name-variants.ts +217 -0
  647. package/src/node-version.ts +22 -0
  648. package/src/not-run.ts +16 -0
  649. package/src/orthography.ts +40 -11
  650. package/src/outline/render.ts +85 -0
  651. package/src/outline/shape.ts +72 -0
  652. package/src/outline/text.ts +50 -0
  653. package/src/plugin.ts +21 -4
  654. package/src/reasons.ts +3 -0
  655. package/src/render/compact.ts +13 -3
  656. package/src/render/explain.ts +47 -20
  657. package/src/render/rules-json.ts +18 -12
  658. package/src/render/suppressions.ts +30 -2
  659. package/src/rule-guide.ts +38 -0
  660. package/src/run-semantic.ts +2 -1
  661. package/src/run.ts +37 -17
  662. package/src/setting-problems.ts +3 -1
  663. package/src/spacing-minority.ts +27 -0
  664. package/src/stet-block.ts +60 -0
  665. package/src/stet.ts +41 -15
  666. package/src/structure/date-range.ts +49 -1
  667. package/src/structure/definition-use.ts +130 -0
  668. package/src/structure/lazy-tree.ts +15 -0
  669. package/src/structure/modal-conflict.ts +86 -0
  670. package/src/structure/numbered-clauses.ts +11 -0
  671. package/src/structure/universal.ts +10 -2
  672. package/src/style-parse.ts +38 -3
  673. package/src/subheading-line.ts +51 -13
  674. package/src/tag-coverage.ts +13 -0
  675. package/styles/ieice.yaml +5 -4
  676. package/styles/jis-z8301-2011.yaml +5 -2
  677. package/styles/jis-z8301-2019.yaml +31 -0
  678. package/styles/koyobun.yaml +35 -0
  679. package/dist/legal-address.d.ts +0 -2
  680. package/dist/legal-address.d.ts.map +0 -1
  681. package/dist/legal-address.js +0 -16
  682. package/dist/legal-address.js.map +0 -1
package/src/cli-text.ts CHANGED
@@ -22,6 +22,9 @@ const USAGE_JA = `chaff — 文章の読みにくいところを見つけます
22
22
  chaff tree <file> [--format sexp|json] 文書を番地の付いた木にする(条・項・定義・参照)
23
23
  chaff cite <原文> <引用.json> 回答の引用(番地と引用文)が原文にあるかを確かめる
24
24
  chaff compare <前> <後> 書き換えで事実(数・日付・URL・コード・名前・引用…)が落ちても足されてもいないかを確かめる
25
+ chaff facts <file> compare が照合する事実を、書き直す前の控えとして一覧にする
26
+ chaff outline <file> [<後>] 見出しの構成と形(見出しの数・節の平均の長さ・箇条書きの割合・太字)を測る。2 つなら前と後を並べる
27
+ chaff fix-plan <file> [--json] 指摘をルールごとにまとめ、直す方向と確かめのコマンドを、書き直す人や AI 向けの計画にする(何も送らない)
25
28
  chaff rules ルールの一覧を、グループごとに表で出す(いまの段階つき)
26
29
  chaff rules --json いまの設定とルールの説明を JSON で出す(AI に渡す用)
27
30
  chaff baseline <dir> いまある指摘を棚上げする(既存の repo に入れるとき)
@@ -58,6 +61,9 @@ const USAGE_EN = `chaff — finds what makes writing hard to read. It never rewr
58
61
  chaff tree <file> [--format sexp|json] the document as a tree of addresses (sections, clauses, definitions, references)
59
62
  chaff cite <source> <quotes.json> check that quoted passages (address and text) are in the source
60
63
  chaff compare <before> <after> check that a rewrite dropped no fact and added none (numbers, dates, URLs, code, names, quotations…)
64
+ chaff facts <file> list the facts compare checks, as an inventory to keep before a rewrite
65
+ chaff outline <file> [<after>] measure the outline and its shape (headings, average section length, text in lists, bold); two files side by side
66
+ chaff fix-plan <file> [--json] a plan for whoever rewrites the file, a person or an AI: findings by rule, how to rewrite each, the checks to run after (sends nothing)
61
67
  chaff rules the rules as a table, by group, with the level each runs at now
62
68
  chaff rules --json the current settings and what each rule is, as JSON (to give to an AI)
63
69
  chaff baseline <dir> shelve today's findings (when adding chaff to an existing repository)
@@ -104,7 +110,6 @@ export type CliText = {
104
110
  readonly watching: (files: number, findings: number) => string;
105
111
  readonly watchHint: string;
106
112
  readonly baselineDone: (files: number, entries: number, file: string) => readonly string[];
107
- readonly unit: (ruleId: string, language: string) => string;
108
113
  };
109
114
 
110
115
  export const CLI_TEXT: Texts<CliText> = {
@@ -159,11 +164,6 @@ export const CLI_TEXT: Texts<CliText> = {
159
164
  ` ${file} を commit してください。`,
160
165
  "",
161
166
  ],
162
- unit: (ruleId, language) => {
163
- if (ruleId === "bold-density") return "1000 字あたりの箇所数";
164
- if (ruleId !== "max-sentence-length") return "回";
165
- return language === "ja" ? "文字" : "語";
166
- },
167
167
  },
168
168
  en: {
169
169
  usage: USAGE_EN,
@@ -213,10 +213,5 @@ export const CLI_TEXT: Texts<CliText> = {
213
213
  ` Commit ${file}.`,
214
214
  "",
215
215
  ],
216
- unit: (ruleId, language) => {
217
- if (ruleId === "bold-density") return "places per 1000 characters";
218
- if (ruleId !== "max-sentence-length") return "times";
219
- return language === "ja" ? "characters" : "words";
220
- },
221
216
  },
222
217
  };
package/src/cli.ts CHANGED
@@ -3,13 +3,15 @@ import { dirname, join } from "node:path";
3
3
  import { loadAdapter, packageFor } from "./adapter-load.ts";
4
4
  import { CONFIG_FILE, type Config } from "./config/load.ts";
5
5
  import { applyByPath } from "./config/by-path.ts";
6
+ import { limitsFor } from "./config/style.ts";
6
7
  import { applyLevel } from "./config/write.ts";
7
8
  import { buildDocument, teamRules } from "./document.ts";
8
9
  import { guessLanguage } from "./detect.ts";
9
10
  import { collectTargets, readDocumentFile } from "./files.ts";
10
11
  import { BASELINE_FILE, fingerprints, readBaseline, splitByBaseline, writeBaseline } from "./baseline.ts";
11
12
  import { applySuppressions } from "./stet.ts";
12
- import { renderSuppressions, type PerFile } from "./render/suppressions.ts";
13
+ import type { PerFile } from "./render/suppressions.ts";
14
+ import { runSuppressions } from "./commands/suppressions.ts";
13
15
  import { clock, describeChange, snapshotOf, watchPaths, type Snapshot } from "./watch.ts";
14
16
  import { runEval } from "./commands/eval.ts";
15
17
  import { runTest } from "./commands/test.ts";
@@ -29,11 +31,11 @@ import { rulesJson } from "./render/rules-json.ts";
29
31
  import { rulesTable } from "./render/rules-table.ts";
30
32
  import { renderSarif } from "./render/sarif.ts";
31
33
  import { VERSION, VERSION_LINES } from "./version.ts";
32
- import { runTree, treeTargets, type TreeContext } from "./commands/tree.ts";
33
- import { citeTargets, runCite } from "./commands/cite.ts";
34
- import { compareTargets, runCompare } from "./commands/compare.ts";
34
+ import type { TreeContext } from "./commands/tree.ts";
35
+ import { DOCUMENT_COMMANDS } from "./commands/document-commands.ts";
35
36
  import { runSkill } from "./commands/skill.ts";
36
- import { runFeedback, settingsOf } from "./commands/feedback.ts";
37
+ import { fixPlanTargets, runFixPlan } from "./commands/fix-plan.ts";
38
+ import { runConditions, runFeedback, settingsOf, type Checked } from "./commands/feedback.ts";
37
39
  import { homedir } from "node:os";
38
40
  import { settingWarnings } from "./config/warnings.ts";
39
41
  import { readConfigIn } from "./config/read.ts";
@@ -44,7 +46,9 @@ import type { Finding, Level, RuleDefinition } from "./plugin.ts";
44
46
  import { CLI_TEXT, type CliText, type GenreSource } from "./cli-text.ts";
45
47
  import { hostLanguage, sharedLanguage, uiLanguageOf, type UiLanguage } from "./ui.ts";
46
48
  import { profileFor } from "./profile/for-file.ts";
49
+ import { notRunAmong } from "./not-run.ts";
47
50
  import { settingProblems } from "./setting-problems.ts";
51
+ import { withExtensions } from "./extension/load.ts";
48
52
 
49
53
  /** Text for output that is not about one document. */
50
54
  const hostText = (config: Config): CliText => CLI_TEXT[hostLanguage(config.language, process.env)];
@@ -59,8 +63,7 @@ const genreFrom =
59
63
 
60
64
  const findRule = (rules: readonly RuleDefinition[], id: string | undefined): RuleDefinition | undefined => rules.find((rule) => rule.id === id);
61
65
 
62
- const changeSetting = (command: Level, ruleId: string | undefined, why: string | undefined): number => {
63
- const config = readConfig();
66
+ const changeSetting = (config: Config, command: Level, ruleId: string | undefined, why: string | undefined): number => {
64
67
  const language = config.language ?? hostLanguage(undefined, process.env);
65
68
  const rule = findRule(rulesOf(language, config), ruleId);
66
69
  if (rule === undefined) {
@@ -85,6 +88,7 @@ type Inspected = {
85
88
  readonly genre: string;
86
89
  readonly outcome: FileOutcome;
87
90
  readonly perFile: PerFile;
91
+ readonly checked: Omit<Checked, "conditions">;
88
92
  /** stet で黙らせたものを除いた、baseline で棚上げする前の指摘。fingerprint は baseline を書くときだけ作る。 */
89
93
  readonly kept: readonly Finding[];
90
94
  };
@@ -98,8 +102,15 @@ const inspect = async (path: string, config: Config, argv: readonly string[]): P
98
102
  const rules = rulesOf(language, config);
99
103
  const experimental = config.experimental || argv.includes("--experimental");
100
104
  await adapter.prepare?.(neededBy(rules, config.rules, experimental, genre, language));
101
- const doc = buildDocument(path, source, adapter, teamRules(config), profileFor(config, path, source, language, genre));
102
- const raw = runRulesWith(doc, rules, { settings: config.rules, experimental, genre, limits: config.limits, optionLayers: optionLayersOf(config) });
105
+ const doc = buildDocument(path, source, adapter, teamRules(config, language), profileFor(config, path, source, language, genre));
106
+ const raw = runRulesWith(doc, rules, {
107
+ settings: config.rules,
108
+ experimental,
109
+ genre,
110
+ limits: limitsFor(config, language),
111
+ optionLayers: optionLayersOf(config),
112
+ detectors: config.extensions?.detectors ?? {},
113
+ });
103
114
  // 応答は 3 つ。stet で黙らせたものは、ここで落とす。
104
115
  const applied = applySuppressions(
105
116
  source,
@@ -110,6 +121,7 @@ const inspect = async (path: string, config: Config, argv: readonly string[]): P
110
121
  const split = splitByBaseline(path, applied.kept, baseline);
111
122
  const result = { ...raw, findings: split.fresh };
112
123
  const { header, notes } = fileHeader(path, source, language, { genre, from, unread }, { shelved: split.shelved, hushed: applied.suppressed.length });
124
+ const notRun = notRunAmong(applied.named, raw.skipped);
113
125
  const text = argv.includes("--compact") ? renderCompact(header, result, rules, language) : renderFriendly(header, result, rules, language, notes);
114
126
  return {
115
127
  text,
@@ -117,7 +129,8 @@ const inspect = async (path: string, config: Config, argv: readonly string[]): P
117
129
  language,
118
130
  genre,
119
131
  outcome: { path, findings: split.fresh, notRun: raw.skipped.length },
120
- perFile: { path, suppressed: applied.suppressed, reasonless: applied.unusedReasonless },
132
+ perFile: { path, suppressed: applied.suppressed, reasonless: applied.unusedReasonless, notRun },
133
+ checked: { findings: split.fresh, rules, language, genre, skipped: raw.skipped },
121
134
  kept: applied.kept,
122
135
  };
123
136
  };
@@ -126,7 +139,7 @@ const inspect = async (path: string, config: Config, argv: readonly string[]): P
126
139
  * 指摘を PR の変更行に出すための出口。--sarif <path> を書いたときだけ作る。
127
140
  * 端末の出力は変えない。CI で上げるためのファイルが増えるだけ。
128
141
  */
129
- const writeSarif = (results: readonly Inspected[], argv: readonly string[]): void => {
142
+ const writeSarif = (results: readonly Inspected[], argv: readonly string[], config: Config): void => {
130
143
  const path = flag(argv, "--sarif");
131
144
  if (path === undefined) return;
132
145
  // 文言はファイルの言語で描く。by_path で 1 つの repo に 2 言語が混ざるため。
@@ -135,7 +148,7 @@ const writeSarif = (results: readonly Inspected[], argv: readonly string[]): voi
135
148
  );
136
149
  mkdirSync(dirname(path), { recursive: true });
137
150
  writeFileSync(path, renderSarif(located, VERSION), "utf8");
138
- console.log(hostText(readConfig()).sarifWritten(path, located.length));
151
+ console.log(hostText(config).sarifWritten(path, located.length));
139
152
  };
140
153
 
141
154
  /** 効いていない設定は、結果の前に一度だけ言う。標準エラーに出すので、JSON や SARIF の出力は汚さない。 */
@@ -148,9 +161,8 @@ const summaryLanguage = (results: readonly Inspected[], config: Config): UiLangu
148
161
  return sharedLanguage(languages, hostLanguage(config.language, process.env));
149
162
  };
150
163
 
151
- const lint = async (targets: readonly string[], argv: readonly string[]): Promise<number> => {
164
+ const lint = async (targets: readonly string[], argv: readonly string[], config: Config): Promise<number> => {
152
165
  const paths = collectTargets(targets);
153
- const config = readConfig();
154
166
  if (paths.length === 0) {
155
167
  // 0 件を成功にすると「CI は通っているが何も検証していない」状態が続く。§14。
156
168
  console.error(hostText(config).noMarkdown(targets.join(", ")));
@@ -163,7 +175,7 @@ const lint = async (targets: readonly string[], argv: readonly string[]): Promis
163
175
  }
164
176
  warnRuleProblems(config, language);
165
177
  const results = await Promise.all(paths.map((path) => inspect(path, config, argv)));
166
- writeSarif(results, argv);
178
+ writeSarif(results, argv, config);
167
179
  results.filter((result) => result.outcome.findings.length > 0 || paths.length === 1).forEach((result) => console.log(result.text));
168
180
  renderSummary(
169
181
  results.map((result) => result.outcome),
@@ -176,9 +188,8 @@ const lint = async (targets: readonly string[], argv: readonly string[]): Promis
176
188
  * 書いている最中は、全件を出し直されても何が変わったのか分からない。
177
189
  * 差分だけを出す。workflow spec §11。
178
190
  */
179
- const runWatch = async (targets: readonly string[], argv: readonly string[]): Promise<number> => {
191
+ const runWatch = async (targets: readonly string[], argv: readonly string[], config: Config): Promise<number> => {
180
192
  const paths = collectTargets(targets);
181
- const config = readConfig();
182
193
  const text = hostText(config);
183
194
  if (paths.length === 0) {
184
195
  console.error(text.noMarkdown(targets.join(", ")));
@@ -208,8 +219,7 @@ const runWatch = async (targets: readonly string[], argv: readonly string[]): Pr
208
219
  };
209
220
 
210
221
  /** 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 => {
212
- const config = readConfig();
222
+ const explain = (config: Config, ruleId: string | undefined, genreFlag: string | undefined): number => {
213
223
  const genre = genreFlag ?? config.genre;
214
224
  // The rule's limits differ by language (characters for Japanese, words for English): explain in the one being written.
215
225
  const language = config.language ?? hostLanguage(undefined, process.env);
@@ -223,13 +233,12 @@ const explain = (ruleId: string | undefined, genreFlag: string | undefined): num
223
233
  }
224
234
  const preset = genre === undefined ? {} : presetLevels(genre);
225
235
  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, settingSourcesOf(config, rule.id)));
236
+ console.log(renderExplain(rule, current, language, genre, settingSourcesOf(config, rule.id, language)));
227
237
  return 0;
228
238
  };
229
239
 
230
- const runBaseline = async (targets: readonly string[], argv: readonly string[]): Promise<number> => {
240
+ const runBaseline = async (targets: readonly string[], argv: readonly string[], config: Config): Promise<number> => {
231
241
  const paths = collectTargets(targets.length > 0 ? targets : ["."]);
232
- const config = readConfig();
233
242
  if (paths.length === 0) {
234
243
  console.error(hostText(config).noMarkdownHere);
235
244
  return 1;
@@ -242,23 +251,18 @@ const runBaseline = async (targets: readonly string[], argv: readonly string[]):
242
251
  return 0;
243
252
  };
244
253
 
245
- const runSuppressions = async (targets: readonly string[], argv: readonly string[]): Promise<number> => {
246
- const paths = collectTargets(targets.length > 0 ? targets : ["."]);
247
- const config = readConfig();
248
- const results = await Promise.all(paths.map((path) => inspect(path, config, [...argv, "--show-baseline"])));
249
- const perFile = results.map((result) => result.perFile);
250
- console.log(renderSuppressions(perFile, hostLanguage(config.language, process.env)));
251
- return 0;
252
- };
254
+ /** A file's findings, shelved ones included: commands that look findings up are not asking what is new. */
255
+ const inspectAll = (config: Config, argv: readonly string[]) => (path: string) => inspect(path, config, [...argv, "--show-baseline"]);
253
256
 
254
- type Handler = (argv: readonly string[]) => number | Promise<number>;
257
+ /** A subcommand, given the command line and chaff.yaml as read once, with the code it names loaded. */
258
+ type Handler = (argv: readonly string[], config: Config) => number | Promise<number>;
255
259
 
256
260
  /** `--` で始まらない引数。対象のパス。 */
257
261
  const positional = (argv: readonly string[]): string[] => targetsOf(argv.slice(1));
258
262
 
259
263
  /** `rules --json` for an AI to read; `rules` alone, a table for a person. */
260
- const showRules = (argv: readonly string[]): number => {
261
- const config = withExperimental(readConfig(), argv);
264
+ const showRules = (argv: readonly string[], written: Config): number => {
265
+ const config = withExperimental(written, argv);
262
266
  const language = config.language ?? hostLanguage(undefined, process.env);
263
267
  warnRuleProblems(config, language);
264
268
  const genre = flag(argv, "--genre") ?? config.genre ?? "blog/tech";
@@ -267,46 +271,43 @@ const showRules = (argv: readonly string[]): number => {
267
271
  return 0;
268
272
  };
269
273
 
270
- const showGenres = (): number => {
271
- console.log(renderGenres(loadGenres(), hostLanguage(readConfig().language, process.env)));
274
+ const showGenres = (config: Config): number => {
275
+ console.log(renderGenres(loadGenres(), hostLanguage(config.language, process.env)));
272
276
  return 0;
273
277
  };
274
278
 
275
- const treeContext = (): TreeContext => {
276
- const config = readConfig();
279
+ const treeContext = (config: Config): TreeContext => {
277
280
  return { config, flag, ui: hostLanguage(config.language, process.env) };
278
281
  };
279
282
 
280
283
  /** What eval and test share: the settings, this run's genre, and the language for what is not about one document. */
281
- const measureContext = (argv: readonly string[]): { config: Config; resolveGenre: ReturnType<typeof genreFrom>; ui: UiLanguage } => {
282
- const config = readConfig();
284
+ const measureContext = (argv: readonly string[], config: Config): { config: Config; resolveGenre: ReturnType<typeof genreFrom>; ui: UiLanguage } => {
283
285
  return { config, resolveGenre: genreFrom(argv), ui: hostLanguage(config.language, process.env) };
284
286
  };
285
287
 
286
288
  /** 分岐を数珠つなぎにせず表にする。足すときに main を太らせない。 */
287
289
  const HANDLERS: Readonly<Record<string, Handler>> = {
288
- init: async (argv) => {
289
- const ui = hostLanguage(readConfig().language, process.env);
290
+ init: async (argv, config) => {
291
+ const ui = hostLanguage(config.language, process.env);
290
292
  const chosen = await initGenre(flag(argv, "--genre"), ui, process.cwd());
291
293
  if ("error" in chosen) console.error(chosen.error);
292
294
  else runInit(process.cwd(), chosen.genre, ui).forEach((line) => console.log(line));
293
295
  return "error" in chosen ? 1 : 0;
294
296
  },
295
- genres: showGenres,
297
+ genres: (_argv, config) => showGenres(config),
296
298
  rules: showRules,
297
- explain: (argv) => explain(argv[1], flag(argv, "--genre")),
298
- eval: (argv) => runEval(positional(argv), argv, { ...measureContext(argv), flag }),
299
- tree: (argv) => runTree(treeTargets(argv), argv, treeContext()),
300
- cite: (argv) => runCite(citeTargets(argv), argv, treeContext()),
301
- compare: (argv) => runCompare(compareTargets(argv), argv, treeContext()),
302
- test: (argv) => runTest(positional(argv), argv, { ...measureContext(argv), inspect }),
303
- baseline: (argv) => runBaseline(positional(argv), argv),
304
- suppressions: (argv) => runSuppressions(positional(argv), argv),
305
- relax: (argv) => changeSetting("relaxed", argv[1], flag(argv, "--why")),
306
- strict: (argv) => changeSetting("strict", argv[1], flag(argv, "--why")),
307
- off: (argv) => changeSetting("off", argv[1], flag(argv, "--why")),
308
- feedback: (argv) => {
309
- const config = readConfig();
299
+ explain: (argv, config) => explain(config, argv[1], flag(argv, "--genre")),
300
+ eval: (argv, config) => runEval(positional(argv), argv, { ...measureContext(argv, config), flag }),
301
+ ...Object.fromEntries(Object.entries(DOCUMENT_COMMANDS).map(([name, run]): [string, Handler] => [name, (argv, config) => run(argv, treeContext(config))])),
302
+ test: (argv, config) => runTest(positional(argv), argv, { ...measureContext(argv, config), inspect }),
303
+ "fix-plan": (argv, config) =>
304
+ runFixPlan(fixPlanTargets(argv), argv, { ...treeContext(config), check: async (path) => (await inspectAll(config, argv)(path)).checked }),
305
+ baseline: (argv, config) => runBaseline(positional(argv), argv, config),
306
+ suppressions: (argv, config) => runSuppressions(positional(argv), inspectAll(config, argv), hostLanguage(config.language, process.env)),
307
+ relax: (argv, config) => changeSetting(config, "relaxed", argv[1], flag(argv, "--why")),
308
+ strict: (argv, config) => changeSetting(config, "strict", argv[1], flag(argv, "--why")),
309
+ off: (argv, config) => changeSetting(config, "off", argv[1], flag(argv, "--why")),
310
+ feedback: (argv, config) => {
310
311
  return runFeedback(positional(argv), argv, {
311
312
  cwd: process.cwd(),
312
313
  ui: hostLanguage(config.language, process.env),
@@ -314,13 +315,13 @@ const HANDLERS: Readonly<Record<string, Handler>> = {
314
315
  runtime: `Node ${process.version} · ${process.platform} ${process.arch}`,
315
316
  flag,
316
317
  settingsOf: (ruleIds) => settingsOf(config, ruleIds),
317
- check: async (path) => {
318
- const inspected = await inspect(path, config, [...argv, "--show-baseline"]);
319
- return { findings: inspected.outcome.findings, rules: inspected.rules, language: inspected.language, genre: inspected.genre };
320
- },
318
+ check: async (path) => ({
319
+ ...(await inspectAll(config, argv)(path)).checked,
320
+ conditions: runConditions(argv, flag(argv, "--genre"), config.experimental),
321
+ }),
321
322
  });
322
323
  },
323
- skill: (argv) => runSkill(argv, { cwd: process.cwd(), home: homedir(), ui: hostLanguage(readConfig().language, process.env) }),
324
+ skill: (argv, config) => runSkill(argv, { cwd: process.cwd(), home: homedir(), ui: hostLanguage(config.language, process.env) }),
324
325
  };
325
326
 
326
327
  /** Every subcommand. Anything else on the command line is a file to check. */
@@ -337,12 +338,12 @@ export const main = async (argv: readonly string[]): Promise<number> => {
337
338
  return 0;
338
339
  }
339
340
  // 知らないジャンルではどの rule も当たらず、知らない文書の種類では種類の知識が外れる。どちらも素通りに見えるので、何かする前に止める。
340
- const config = readConfig();
341
+ const config = await withExtensions(readConfig());
341
342
  const problems = settingProblems(first, flag(argv, "--genre"), config, hostText(config), hostLanguage(config.language, process.env));
342
343
  problems.forEach((problem) => console.error(problem));
343
344
  if (problems.length > 0) return 1;
344
345
  const handler = HANDLERS[first];
345
- if (handler !== undefined) return handler(argv);
346
+ if (handler !== undefined) return handler(argv, config);
346
347
  const targets = targetsOf(first === "lint" ? argv.slice(1) : argv);
347
- return argv.includes("--watch") ? runWatch(targets, argv) : lint(targets, argv);
348
+ return argv.includes("--watch") ? runWatch(targets, argv, config) : lint(targets, argv, config);
348
349
  };
@@ -0,0 +1,24 @@
1
+ import type { Paragraph, Section, Sentence } from "./plugin.ts";
2
+
3
+ /**
4
+ * 結びとして読む、最後の節の終わりの段落の数。決まり文句の後ろに一言や追記が続く結び(「いかがでしたか。」→ 感想 → 「よいお年を!」)は
5
+ * 人の書いた記事に多く、最後の段落だけでは取りこぼす。
6
+ */
7
+ const CLOSING_PARAGRAPHS = 4;
8
+
9
+ /** 文書の最初の段落は書き出しで、結びではない(冒頭の「簡単にまとめると、」)。段落が一つだけなら、それが結びでもある。 */
10
+ const isOpening = (paragraphs: readonly Paragraph[], paragraph: Paragraph): boolean => paragraphs.length > 1 && paragraphs[0] === paragraph;
11
+
12
+ /**
13
+ * 結びの文。最後の節の終わりの段落で、書き出しの段落でないもの。見出しの無い文書は本文全体が一つの節なので、節だけで切ると冒頭の
14
+ * 「まとめると」や、長い書き起こしの途中の問いかけまで結びになる。箇条書きは段落に数えない。
15
+ */
16
+ export const closingSentences = (sections: readonly Pick<Section, "span">[], paragraphs: readonly Paragraph[]): readonly Sentence[] => {
17
+ const last = sections.at(-1);
18
+ if (last === undefined) return [];
19
+ return paragraphs
20
+ .filter((paragraph) => paragraph.span.start >= last.span.start && paragraph.span.start < last.span.end)
21
+ .slice(-CLOSING_PARAGRAPHS)
22
+ .filter((paragraph) => !isOpening(paragraphs, paragraph))
23
+ .flatMap((paragraph) => paragraph.sentences);
24
+ };
@@ -1,8 +1,4 @@
1
- import { loadAdapter } from "../adapter-load.ts";
2
- import { buildDocument, teamRules } from "../document.ts";
3
1
  import { readMarkdown } from "../markdown-read.ts";
4
- import { profileFor } from "../profile/for-file.ts";
5
- import { resolveGenre } from "../resolve-genre.ts";
6
2
  import { isMarkdownPath } from "../structure/markdown-path.ts";
7
3
  import { uiLanguageOf } from "../ui.ts";
8
4
  import { ATOM_KINDS, isAtomKind, type AtomKind } from "../compare/atom.ts";
@@ -10,7 +6,8 @@ import { extractFacts } from "../compare/extract.ts";
10
6
  import { outcomeOf, type Allowed, type Compared } from "../compare/outcome.ts";
11
7
  import { renderCompact, renderFriendly, renderJson } from "../compare/render.ts";
12
8
  import { COMPARE_TEXT } from "../compare/text.ts";
13
- import { readSource, treeLanguage, type TreeContext } from "./tree.ts";
9
+ import { readDocument } from "./read-document.ts";
10
+ import type { TreeContext } from "./tree.ts";
14
11
 
15
12
  /** Options whose value is the next argument. That value is not a file. */
16
13
  const VALUED: ReadonlySet<string> = new Set(["--language", "--genre", "--allow-dropped", "--allow-added"]);
@@ -34,20 +31,15 @@ const parseAllowed = (argv: readonly string[]): ParsedAllowed => {
34
31
  return { allowed: { dropped: kinds(dropped), added: kinds(added) } };
35
32
  };
36
33
 
37
- type Read = Compared & { readonly language: string };
34
+ export type DocumentFacts = Compared & { readonly language: string };
38
35
 
39
36
  /** One document's facts, read as lint reads it: its language, the team's names, and the parts of speech. */
40
- const readFacts = async (path: string, argv: readonly string[], context: TreeContext): Promise<Read | undefined> => {
41
- const source = await readSource(path, context);
42
- if (source === undefined) return undefined;
43
- const language = treeLanguage(path, source, argv, context);
44
- const adapter = await loadAdapter(language);
45
- await adapter.prepare?.({ pos: true });
46
- const genre = resolveGenre(path, source, context.config, context.flag(argv, "--genre")).genre;
47
- const doc = buildDocument(path, source, adapter, teamRules(context.config), profileFor(context.config, path, source, language, genre));
48
- const root = isMarkdownPath(path) ? readMarkdown(doc.source).root : undefined;
49
- const extraction = extractFacts({ doc, root, structure: adapter.structure, names: context.config.names ?? [] });
50
- return { path, extraction, language };
37
+ export const readFacts = async (path: string, argv: readonly string[], context: TreeContext): Promise<DocumentFacts | undefined> => {
38
+ const prose = await readDocument(path, argv, context, true);
39
+ if (prose === undefined) return undefined;
40
+ const root = isMarkdownPath(path) ? readMarkdown(prose.doc.source).root : undefined;
41
+ const extraction = extractFacts({ doc: prose.doc, root, structure: prose.structure, names: context.config.names ?? [] });
42
+ return { path, extraction, language: prose.language };
51
43
  };
52
44
 
53
45
  const renderFor = (argv: readonly string[]): typeof renderFriendly => {
@@ -58,6 +50,7 @@ const renderFor = (argv: readonly string[]): typeof renderFriendly => {
58
50
  /**
59
51
  * Whether a rewrite kept every fact: numbers, dates, URLs, code, names, quotations, headings, references and footnotes.
60
52
  * Compares; never rewrites. Ends with 1 when a fact was dropped or added, so a rewrite can be checked like a test.
53
+ * --distinct counts a fact as kept when the other document states it at least once.
61
54
  */
62
55
  export const runCompare = async (targets: readonly string[], argv: readonly string[], context: TreeContext): Promise<number> => {
63
56
  const host = COMPARE_TEXT[context.ui ?? "ja"];
@@ -74,7 +67,7 @@ export const runCompare = async (targets: readonly string[], argv: readonly stri
74
67
  const before = await readFacts(beforePath, argv, context);
75
68
  const after = before === undefined ? undefined : await readFacts(afterPath, argv, context);
76
69
  if (before === undefined || after === undefined) return 1;
77
- const outcome = outcomeOf(before, after, parsed.allowed);
70
+ const outcome = outcomeOf(before, after, parsed.allowed, argv.includes("--distinct") ? "distinct" : "as-stated");
78
71
  console.log(renderFor(argv)(outcome, COMPARE_TEXT[uiLanguageOf(before.language)]));
79
72
  return outcome.ok ? 0 : 1;
80
73
  };
@@ -0,0 +1,19 @@
1
+ import { citeTargets, runCite } from "./cite.ts";
2
+ import { compareTargets, runCompare } from "./compare.ts";
3
+ import { factsTargets, runFacts } from "./facts.ts";
4
+ import { outlineTargets, runOutline } from "./outline.ts";
5
+ import { runTree, treeTargets, type TreeContext } from "./tree.ts";
6
+
7
+ type DocumentCommand = (argv: readonly string[], context: TreeContext) => Promise<number>;
8
+
9
+ /**
10
+ * The commands that read one or two documents and report on them without linting: the tree, quotations, facts and
11
+ * outline. Each is given the settings the command line read once.
12
+ */
13
+ export const DOCUMENT_COMMANDS: Readonly<Record<string, DocumentCommand>> = {
14
+ tree: (argv, context) => runTree(treeTargets(argv), argv, context),
15
+ cite: (argv, context) => runCite(citeTargets(argv), argv, context),
16
+ compare: (argv, context) => runCompare(compareTargets(argv), argv, context),
17
+ facts: (argv, context) => runFacts(factsTargets(argv), argv, context),
18
+ outline: (argv, context) => runOutline(outlineTargets(argv), argv, context),
19
+ };
@@ -9,6 +9,7 @@ import { renderEval } from "../render/eval.ts";
9
9
  import { neededBy, tokenFeaturesOf, wantsTags } from "../run.ts";
10
10
  import type { AdapterNeeds, RuleDefinition } from "../plugin.ts";
11
11
  import { optionLayersOf } from "../config/option-problems.ts";
12
+ import { limitsFor } from "../config/style.ts";
12
13
  import type { Config } from "../config/load.ts";
13
14
  import { profileFor } from "../profile/for-file.ts";
14
15
  import { CLI_TEXT } from "../cli-text.ts";
@@ -66,7 +67,7 @@ export const runEval = async (targets: readonly string[], argv: readonly string[
66
67
  const adapter = await loadAdapter(language);
67
68
  const { genre } = resolveGenre(path, source, config);
68
69
  await adapter.prepare?.(evalNeeds(rulesOf(language, config), only, config, genre, language));
69
- return { doc: buildDocument(path, source, adapter, teamRules(config), profileFor(config, path, source, language, genre)), language, genre };
70
+ return { doc: buildDocument(path, source, adapter, teamRules(config, language), profileFor(config, path, source, language, genre)), language, genre };
70
71
  }),
71
72
  );
72
73
  const language = docs[0]?.language ?? "ja";
@@ -89,7 +90,7 @@ export const runEval = async (targets: readonly string[], argv: readonly string[
89
90
  rules,
90
91
  genre,
91
92
  language,
92
- config.limits,
93
+ limitsFor(config, language),
93
94
  optionLayersOf(config),
94
95
  ),
95
96
  docs.length,
@@ -0,0 +1,33 @@
1
+ import { uiLanguageOf } from "../ui.ts";
2
+ import { FACTS_TEXT } from "../compare/facts-text.ts";
3
+ import { renderFactsCompact, renderFactsFriendly, renderFactsJson, type FactList } from "../compare/facts-render.ts";
4
+ import { readFacts } from "./compare.ts";
5
+ import type { TreeContext } from "./tree.ts";
6
+
7
+ /** Options whose value is the next argument. That value is not a file. */
8
+ const VALUED: ReadonlySet<string> = new Set(["--language", "--genre"]);
9
+
10
+ export const factsTargets = (argv: readonly string[]): string[] =>
11
+ argv.slice(1).filter((arg, index, all) => !arg.startsWith("--") && !VALUED.has(all[index - 1] ?? ""));
12
+
13
+ const renderFor = (argv: readonly string[], list: FactList): string => {
14
+ const text = FACTS_TEXT[uiLanguageOf(list.language)];
15
+ if (argv.includes("--json")) return renderFactsJson(list);
16
+ return argv.includes("--compact") ? renderFactsCompact(list, text) : renderFactsFriendly(list, text);
17
+ };
18
+
19
+ /**
20
+ * The facts `chaff compare` would check in one document, listed before a rewrite: the inventory a full rewrite
21
+ * starts from. Read by compare's own extractor, so the list is exactly what compare holds the rewrite to.
22
+ */
23
+ export const runFacts = async (targets: readonly string[], argv: readonly string[], context: TreeContext): Promise<number> => {
24
+ const [path] = targets;
25
+ if (path === undefined || targets.length !== 1) {
26
+ console.error(FACTS_TEXT[context.ui ?? "ja"].usage);
27
+ return 1;
28
+ }
29
+ const documentFacts = await readFacts(path, argv, context);
30
+ if (documentFacts === undefined) return 1;
31
+ console.log(renderFor(argv, documentFacts));
32
+ return 0;
33
+ };
@@ -7,6 +7,8 @@ import { messageOf } from "../render/text.ts";
7
7
  import type { Texts, UiLanguage } from "../ui.ts";
8
8
  import { excerptsAround } from "../feedback/excerpt.ts";
9
9
  import { feedbackDraft, type FeedbackDraft, type FeedbackKind, type ReportedFinding } from "../feedback/draft.ts";
10
+ import { notRunAmong } from "../not-run.ts";
11
+ import type { Skipped } from "../run.ts";
10
12
 
11
13
  export const FEEDBACK_FILE = ".chaff-feedback.md";
12
14
  const NEW_ISSUE = "https://github.com/isamu/lab/issues/new";
@@ -18,6 +20,10 @@ export type Checked = {
18
20
  readonly rules: readonly RuleDefinition[];
19
21
  readonly language: string;
20
22
  readonly genre: string;
23
+ /** The rules this check did not run, and why. */
24
+ readonly skipped: readonly Skipped[];
25
+ /** How the check was run, as the reader of a report would repeat it (runConditions). */
26
+ readonly conditions: readonly string[];
21
27
  };
22
28
 
23
29
  export type FeedbackContext = {
@@ -36,6 +42,8 @@ const TEXT: Texts<{
36
42
  readonly notFound: (path: string) => string;
37
43
  readonly badLine: (lines: number) => string;
38
44
  readonly noMatch: (list: string) => string;
45
+ readonly notRun: (rule: string, why: string) => string;
46
+ readonly runExperimental: string;
39
47
  readonly several: (list: string) => string;
40
48
  readonly none: string;
41
49
  readonly written: (file: string, lines: string) => string;
@@ -51,6 +59,8 @@ const TEXT: Texts<{
51
59
  notFound: (path) => `${path} がありません。`,
52
60
  badLine: (lines) => `--line には 1〜${String(lines)} の行番号を書いてください。`,
53
61
  noMatch: (list) => `その指摘が見つかりません。この文書の指摘:\n${list}`,
62
+ notRun: (rule, why) => `${rule} は今回動いていません(${why})。`,
63
+ runExperimental: "試験中の rule です。--experimental を付けてかけ直してください。",
54
64
  several: (list) => `その rule の指摘がいくつもあります。--line で一つ選んでください:\n${list}`,
55
65
  none: " (指摘はありません)",
56
66
  written: (file, lines) => `報告の下書きを ${file} に書きました。文書から載せたのは ${lines} だけです。送る前に読んで、要らない所は消してください。`,
@@ -66,6 +76,8 @@ const TEXT: Texts<{
66
76
  notFound: (path) => `${path} does not exist.`,
67
77
  badLine: (lines) => `--line must be a line number from 1 to ${String(lines)}.`,
68
78
  noMatch: (list) => `No such finding. The findings in this document:\n${list}`,
79
+ notRun: (rule, why) => `${rule} did not run in this check (${why}).`,
80
+ runExperimental: "It is experimental: run again with --experimental.",
69
81
  several: (list) => `That rule has several findings here. Pick one with --line:\n${list}`,
70
82
  none: " (no findings)",
71
83
  written: (file, lines) =>
@@ -157,13 +169,32 @@ const sendInstructions = (title: string, lines: readonly number[], ui: UiLanguag
157
169
  ].join("\n");
158
170
  };
159
171
 
172
+ /**
173
+ * Pure: how a check was run, in the words a reader would type to run it again: the flags that change what is found.
174
+ * Experimental rules switched on in chaff.yaml count too, said as chaff.yaml says it.
175
+ */
176
+ export const runConditions = (argv: readonly string[], genreFlag: string | undefined, experimentalInConfig: boolean): string[] => {
177
+ const experimental = argv.includes("--experimental") ? ["--experimental"] : [];
178
+ const fromConfig = experimental.length === 0 && experimentalInConfig ? ["experimental: true (chaff.yaml)"] : [];
179
+ return [...experimental, ...fromConfig, ...(genreFlag === undefined ? [] : [`--genre ${genreFlag}`])];
180
+ };
181
+
182
+ /** No finding matched: say so with the findings there are, and first why the asked-for rule did not run, if it did not. */
183
+ const noMatch = (request: Request, checked: Checked, ui: UiLanguage): string => {
184
+ const text = TEXT[ui];
185
+ const list = text.noMatch(describe(checked.findings, text.none));
186
+ const [notRun] = request.rule === undefined ? [] : notRunAmong([request.rule], checked.skipped);
187
+ if (notRun === undefined) return list;
188
+ return [text.notRun(notRun.rule, notRun.why), ...(notRun.needsExperimental ? [text.runExperimental] : []), list].join("\n");
189
+ };
190
+
160
191
  /** Why the request cannot be drafted, or the findings to report. */
161
192
  const pick = (request: Request, checked: Checked, lineCount: number, ui: UiLanguage): { readonly error: string } | { readonly findings: Finding[] } => {
162
193
  const text = TEXT[ui];
163
194
  if (request.line !== undefined && (request.line < 1 || request.line > lineCount)) return { error: text.badLine(lineCount) };
164
195
  const picked = chosen(checked.findings, request.rule, request.line);
165
196
  if (request.kind === "missed") return { findings: picked };
166
- if (picked.length === 0) return { error: text.noMatch(describe(checked.findings, text.none)) };
197
+ if (picked.length === 0) return { error: noMatch(request, checked, ui) };
167
198
  // One finding per report: a rule picked alone could otherwise pull excerpts from all over the document.
168
199
  if (picked.length > 1) return { error: text.several(describe(picked, text.none)) };
169
200
  return { findings: picked };
@@ -204,6 +235,7 @@ export const runFeedback = async (targets: readonly string[], argv: readonly str
204
235
  fileName: basename(request.path),
205
236
  language: checked.language,
206
237
  genre: checked.genre,
238
+ conditions: checked.conditions,
207
239
  findings,
208
240
  line: lines[0] ?? 0,
209
241
  excerpts: excerptsAround(source, lines),