asciidoc-pubkit 0.1.2 → 0.6.1

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.
@@ -3,48 +3,109 @@ schema_version: 1
3
3
  terms:
4
4
  abstract-reference:
5
5
  terms:
6
- - コスト
7
- - 境界
6
+ # Concepts, evidence, and explanatory frames.
8
7
  - 契約
9
8
  - 観点
10
- - 土台
11
- - 橋渡し
12
- - 入口
13
- - 記述
14
- - 場所
15
9
  - 意図
16
10
  - 役割
17
- - 一続き
18
11
  - 根拠
12
+ - 概念
13
+ - 記述
14
+ # Components, actors, and hierarchical relationships.
15
+ - 構成要素
19
16
  - 部品
20
17
  - 開発者
21
- - 概念
18
+ - 祖先
19
+ - 子孫
20
+ # Scope, separation, and organization.
21
+ - 境界
22
+ - 土台
23
+ - 橋渡し
24
+ - 入口
25
+ - 場所
26
+ - 一続き
27
+ - 区別
28
+ - 単発
29
+ - 別枠
30
+ # Resources and preparation.
31
+ - コスト
32
+ - 予算
33
+ - 準備
34
+ # Review context before choosing a katakana technical term.
35
+ - 木
36
+ - 余白
22
37
  question: Identify the concrete referent, components, or measurable work. Keep
23
- established technical meanings.
38
+ established technical meanings. For 木 and 余白, consider a context-appropriate
39
+ katakana term (such as ツリー or マージン) only when it names the intended concept.
24
40
  weak-predicate:
25
41
  terms:
42
+ # Use, interpretation, and understanding (literal-mode surfaces).
26
43
  - 利用します
27
- - 整理します
28
44
  - 扱います
29
- - 示します
30
- - 変わります
45
+ - 扱いません
46
+ - 意味しません
31
47
  - 把握します
48
+ - 捉えます
49
+ # Writing, representation, and inspection.
50
+ - 記述します
51
+ - 記述できます
52
+ - 書きます
53
+ - 示します
54
+ - 表します
55
+ - 表しません
56
+ - 読みます
57
+ - 調べます
58
+ - 調べられます
59
+ - 確かめます
60
+ - 探します
61
+ - 追えます
62
+ # Construction, preparation, and combination.
63
+ - 用意します
64
+ - 作ります
65
+ - 組み立てられます
66
+ - 組み合わせます
67
+ - 成り立たせています
68
+ # Organization, comparison, and selection.
69
+ - 整理します
70
+ - 分ける
71
+ - 分けて
72
+ - 分けています
73
+ - 分けません
32
74
  - 分けられます
75
+ - 区別します
76
+ - 区別できます
77
+ - 混同しません
78
+ - 混ぜません
33
79
  - そろえます
34
- - まとまっています
35
- - 加えます
36
- - 探します
37
- - 到達しません
38
- - 扱いません
39
80
  - そろいます
81
+ - まとまっています
82
+ - 選びます
40
83
  - 選べます
41
- - 成り立たせています
42
- - 確かめます
43
- - 書き換える
44
84
  - 絞れます
85
+ - 決まります
86
+ # Change, transfer, and retention.
87
+ - 変わります
88
+ - 書き換える
89
+ - 置き換えます
90
+ - 加えます
91
+ - 加わります
92
+ - 付けます
93
+ - 返す
94
+ - 返します
45
95
  - 渡します
96
+ - 送れます
97
+ - 残します
98
+ - 持ちます
99
+ - 消しません
100
+ # Existence, sufficiency, requirements, and completion.
46
101
  - あります
47
- - 意味しません
102
+ - 足りません
103
+ - 求めます
104
+ - 到達しません
105
+ - 避けられます
106
+ - 終えられます
107
+ - 抜けられます
108
+ - 解消する
48
109
  question: Check whether the purpose, operation, or result is clear from the surrounding
49
110
  paragraph. Preserve negation and conditions.
50
111
  vague-degree:
@@ -55,13 +116,57 @@ terms:
55
116
  measurements and established technical meanings.
56
117
  contextual-phrase:
57
118
  terms:
119
+ # Limited exact surfaces for metaphorical operations, not banned verbs.
120
+ - 地味に効く
121
+ - 地味に効きます
122
+ - 地味に効いて
123
+ - 静かに壊れる
124
+ - 静かに壊れます
125
+ - 静かに壊れて
126
+ - 時間を溶かす
127
+ - 時間を溶かします
128
+ - 時間を溶かした
129
+ - 側に倒す
130
+ - 側に倒します
131
+ - 側に倒して
58
132
  - これらを
59
133
  - であることだけでは
60
134
  - あるものとします
135
+ # Retain this specific phrase for existing allow lists and fuller evidence.
61
136
  - わけではありません
62
137
  - 別です
63
- question: Check the referent, assumption, or qualification against the surrounding
64
- explanation. Preserve conditions and negation.
138
+ # Compressed noun relationships; suggestions are not automatic replacements.
139
+ - 明示選択
140
+ - 欠落理由
141
+ # Referents, separation, evaluation, and sufficiency.
142
+ - よいこと
143
+ - おそれ
144
+ - 別の
145
+ - 別に
146
+ - 空の
147
+ - 別々に
148
+ - ことだけで
149
+ - 目安です
150
+ - 必要がありません
151
+ # Shared qualifications cover longer expressions without enumerating them.
152
+ - ではありません
153
+ - にはなりません
154
+ - にはできません
155
+ - にはしません
156
+ - ことはしません
157
+ - 代用にしません
158
+ - でもありません
159
+ - してはいけません
160
+ - 表してはいません
161
+ question: Check whether the phrase adds a condition that changes the reader's
162
+ interpretation or action. If it only denies an overbroad reading already
163
+ excluded by the main claim, revise that claim or remove the qualification.
164
+ Preserve necessary negation, uncertainty, and technical boundaries. For
165
+ 明示選択 and 欠落理由, consider making the relationship explicit (明示的選択 and
166
+ 欠落した理由) if that preserves the meaning.
167
+ For metaphorical operations, identify the actual effect, policy, work, or
168
+ failure state from evidence; do not invent logging behavior, causes, or
169
+ implementation details. Keep literal and established technical meanings.
65
170
  generic-framing:
66
171
  terms:
67
172
  - 重要なのは
@@ -69,8 +174,11 @@ terms:
69
174
  - 本章では
70
175
  - ここでは
71
176
  - まとめると
72
- question: Check whether this framing adds useful scope or information instead
73
- of repeating the explanation.
177
+ - このように
178
+ - 要するに
179
+ question: Check whether this opening or recap adds a claim, scope, or consequence
180
+ beyond the surrounding prose. Keep useful framing; otherwise state the
181
+ substantive claim directly. Do not force a uniform sentence pattern.
74
182
  verbs:
75
183
  扱う:
76
184
  - 扱う
@@ -107,6 +215,56 @@ verbs:
107
215
  - 絞れる
108
216
  渡す:
109
217
  - 渡す
218
+ 決まる:
219
+ - 決まる
220
+ 組み立てる:
221
+ - 組み立てる
222
+ 組み合わせる:
223
+ - 組み合わせる
224
+ 足りる:
225
+ - 足りる
226
+ 置き換える:
227
+ - 置き換える
228
+ 加わる:
229
+ - 加わる
230
+ 返す:
231
+ - 返す
232
+ 避ける:
233
+ - 避ける
234
+ 求める:
235
+ - 求める
236
+ 送る:
237
+ - 送る
238
+ - 送れる
239
+ 読む:
240
+ - 読む
241
+ 付ける:
242
+ - 付ける
243
+ 残す:
244
+ - 残す
245
+ 持つ:
246
+ - 持つ
247
+ 表す:
248
+ - 表す
249
+ 終える:
250
+ - 終える
251
+ 消す:
252
+ - 消す
253
+ 調べる:
254
+ - 調べる
255
+ 作る:
256
+ - 作る
257
+ 抜ける:
258
+ - 抜ける
259
+ 捉える:
260
+ - 捉える
261
+ 書く:
262
+ - 書く
263
+ 混ぜる:
264
+ - 混ぜる
265
+ 追う:
266
+ - 追う
267
+ - 追える
110
268
  ある:
111
269
  - ある
112
270
  sahen:
@@ -115,9 +273,15 @@ sahen:
115
273
  - 把握
116
274
  - 到達
117
275
  - 意味
276
+ - 記述
277
+ - 用意
278
+ - 区別
279
+ - 解消
280
+ - 混同
118
281
  negative_only:
119
282
  - 到達する
120
283
  - 意味する
121
284
  compound_nouns:
122
285
  - 一続き
123
286
  - 開発者
287
+ - 構成要素
@@ -0,0 +1,223 @@
1
+ # Shared criteria for Japanese technical prose
2
+
3
+ ## Contents
4
+
5
+ - Start with the paragraph's technical purpose
6
+ - Replace abstraction with the thing being discussed
7
+ - Give weak predicates a concrete action or consequence
8
+ - Explain metaphorical operations from evidence
9
+ - Distinguish instructions, behavior, and capabilities
10
+ - Make relationships explicit
11
+ - Keep claims within their evidence and purpose
12
+ - Remove generated-prose patterns without flattening the meaning
13
+ - Preserve exact technical terminology
14
+ - Revise the complete paragraph
15
+ - Headings and the relation to figures and code
16
+
17
+ Apply these criteria in context. They identify patterns that deserve inspection,
18
+ not words that must always be removed.
19
+
20
+ ## Start with the paragraph's technical purpose
21
+
22
+ Before rewriting sentences, summarize each paragraph in one sentence:
23
+
24
+ 1. What implementation, behavior, or decision is this paragraph about?
25
+ 2. Who or what performs the action?
26
+ 3. Under what condition does it apply?
27
+ 4. What changes or follows as a result?
28
+ 5. What can an engineer decide or verify from it?
29
+
30
+ If the summary is impossible, repair the paragraph's argument before polishing
31
+ individual phrases. A short sentence is not automatically clear; omitted
32
+ subjects, objects, complements, and referents must still be recoverable from the
33
+ adjacent sentences.
34
+
35
+ ## Replace abstraction with the thing being discussed
36
+
37
+ Abstract terms are valid when their referent is stated. Inspect terms such as:
38
+
39
+ | Term | What must be identifiable |
40
+ | --- | --- |
41
+ | 費用・コスト | investigation time, maintenance work, CPU time, memory, network transfer, or money |
42
+ | 契約 | accepted inputs, returned values and errors, compatibility, ownership, concurrency, or another promised behavior |
43
+ | 境界 | the two components and the call, data transfer, or ownership transfer between them |
44
+ | 観点 | the actual comparison axis, such as type safety, shutdown behavior, allocation count, or supported OS |
45
+ | 書き方 | syntax, implementation technique, API shape, or document notation |
46
+ | 範囲・射程 | supported readers, covered subjects, applicable versions, or behavior guaranteed by an API |
47
+
48
+ Do not improve a sentence by replacing one abstraction with another. For
49
+ example, changing `費用` to `コスト` says nothing unless the sentence identifies
50
+ what increases.
51
+
52
+ ## Give weak predicates a concrete action or consequence
53
+
54
+ Predicates such as `利用します`, `決めます`, `示します`, `扱います`, `整理します`,
55
+ `表します`, `保てます`, `異なります`, `同じではありません`, `残します`, and
56
+ `評価します` often omit the important information. Ask what is used for what,
57
+ which value or policy is decided, what differs, where evidence is stored, and
58
+ which result drives the decision.
59
+
60
+ Prefer a precise action or consequence:
61
+
62
+ - `検証結果を利用します` becomes `検証結果をAPIの見直し材料にします` when that
63
+ is the actual purpose.
64
+ - `採用する機能を決めます` becomes `サポートするバージョンと互換性を基に採用を
65
+ 判断します` when those are the criteria.
66
+ - `結果を残します` becomes `実行したコマンドと結果をプルリクエストへ記録します`
67
+ when that is the storage location.
68
+
69
+ Do not choose a more impressive synonym merely to vary sentence endings. Keep
70
+ the technically correct verb when it names the real operation.
71
+
72
+ ## Explain metaphorical operations from evidence
73
+
74
+ Inspect expressions such as `地味に効く`, `側に倒す`, `時間を溶かす`, and
75
+ `静かに壊れる`. Identify the actual effect, selection policy, work that took
76
+ time, or failure state before revising the paragraph. A system can be a valid
77
+ subject: `サーバーが応答を返します` describes observable behavior. Repair
78
+ personification only when it conceals who acts or what happens.
79
+
80
+ Concrete wording must not invent facts. `静かに壊れる` does not by itself establish
81
+ whether an exception, warning, or log is emitted. `地味に効く` does not establish
82
+ which failure a setting prevents. Check the surrounding explanation, code, or
83
+ primary documentation; when evidence is missing, record what needs confirmation
84
+ instead of supplying a plausible implementation or effect.
85
+
86
+ For example, `採否を判断できない項目は除外する側に倒します` can become
87
+ `採否を判断できない項目は除外します` if that is the stated policy. Retain its
88
+ condition and action. Keep literal meanings and established technical terms;
89
+ `解像度`, `触媒`, and `正本` are not inherently defects.
90
+
91
+ ## Distinguish instructions, behavior, and capabilities
92
+
93
+ Identify whether a sentence asks the reader to act, describes an actual system
94
+ operation, or states an available capability. Use `〜してください` for an
95
+ instruction when that fits the document's voice. Do not change `〜します` to
96
+ `〜できます` merely to make the subject clearer: automatic execution and an
97
+ optional capability are different claims. Name the actor when context does not
98
+ identify it, without repeating an already clear subject in every sentence.
99
+
100
+ ## Make relationships explicit
101
+
102
+ Inspect a sentence when it:
103
+
104
+ - begins a new topic without connecting it to the preceding paragraph;
105
+ - says two things differ without naming the differing property;
106
+ - introduces a comparison without naming the alternatives and comparison axis;
107
+ - uses `これ`, `それ`, `前者`, or `後者` when the referent requires rereading;
108
+ - strings three or more abstract nouns together without explaining their
109
+ relationship;
110
+ - uses an abrupt `読者は` even though the effect can be stated directly;
111
+ - attaches `へ` or `から` to an abstract noun in phrases such as `判断へ戻す`;
112
+ - uses a literal translation that is not an established term in the technical
113
+ domain.
114
+
115
+ When a standalone sentence cannot identify its subject or object, use the
116
+ surrounding paragraph to supply the missing noun. Do not repeat the subject in
117
+ every sentence when it is already unambiguous.
118
+ Use prose for a sequence in which one event causes or conditions the next; use
119
+ a list when the items are genuinely parallel. Do not turn a causal explanation
120
+ into disconnected bullets or impose an order on independent items.
121
+
122
+ ## Keep claims within their evidence and purpose
123
+
124
+ State whether a claim comes from an implementation, a test, a cited study, or
125
+ the author's inference. Match its scope to that evidence: a single example or
126
+ test establishes what happened under its stated conditions, not a universal
127
+ result. Preserve uncertainty when evidence does not settle the claim; do not
128
+ weaken an established fact merely to sound cautious.
129
+
130
+ Before adding a qualification, ask what decision or interpretation it changes.
131
+ Correct an overbroad main claim at its source. Remove a following disclaimer if
132
+ the preceding explanation already excludes that reading. When a limitation
133
+ affects use, implementation, or verification, state the concrete condition and
134
+ connect it to the relevant action or check. Do not delete a technical boundary
135
+ merely because it uses negative wording.
136
+
137
+ ## Remove generated-prose patterns without flattening the meaning
138
+
139
+ Common patterns that merit paragraph-level review include:
140
+
141
+ - repeated `AではなくBです` or `AだけでなくBも` contrasts;
142
+ - paired short slogans that omit the actor, object, condition, or consequence;
143
+ - repeated endings such as `扱います`, `示します`, or `選びます`;
144
+ - `重要なのは`, `ポイントは`, or `〜することです` standing in for the actual
145
+ technical fact;
146
+ - a section opening that only announces what the section will discuss;
147
+ - a section ending that only says the material was explained or organized;
148
+ - metaphors such as `土台`, `器`, `橋渡し`, or `輪が閉じる` used instead of a
149
+ concrete technical operation;
150
+ - a standalone qualification that only denies an overbroad reading already
151
+ excluded by the preceding explanation, such as a redundant
152
+ `という意味ではありません` or `保証ではありません`;
153
+ - generic praise, warnings, or recommendations without the condition that makes
154
+ them true.
155
+
156
+ Keep a contrast when the two alternatives and the difference that affects the
157
+ decision are explicit. Keep necessary repetition when it improves technical
158
+ accuracy.
159
+ Do not shorten sentences or vary endings mechanically. Read the sequence aloud
160
+ and repair the missing relationship rather than replacing one stock pattern
161
+ with another.
162
+
163
+ ## Preserve exact technical terminology
164
+
165
+ Check terminology against identifiers, surrounding code, the project's
166
+ glossary, and primary documentation before changing it.
167
+
168
+ - Distinguish an API from a function, method, interface, protocol, or command.
169
+ - Distinguish an error value, operation failure, incident, and defect.
170
+ - Distinguish resource ownership from the responsibility to close or stop it.
171
+ - Use the project's chosen forms consistently, such as `goroutine` versus
172
+ `ゴルーチン`, or `reflection` versus `リフレクション`.
173
+ - Replace unnecessary English words with established Japanese terms, but retain
174
+ identifiers and official names.
175
+ - Do not describe a language operation with a loose everyday verb when the
176
+ domain has a precise term, such as name shadowing, method promotion, parsing,
177
+ serialization, cancellation, or resource release.
178
+
179
+ Natural Japanese must not erase a technical distinction. If the original claim
180
+ is ambiguous, inspect the code or authoritative documentation before editing.
181
+ Preserve numerical values, versions, settings, responsibility, causal claims,
182
+ and implementation requirements. Do not add an actor, cause, mechanism, or
183
+ effect that the source evidence does not establish. Making prose more specific
184
+ requires evidence, not a guess.
185
+
186
+ ## Revise the complete paragraph
187
+
188
+ Use a finding to understand the intended claim, then rewrite the paragraph so
189
+ that every sentence connects to the next. Do not paste an isolated suggested
190
+ sentence into the manuscript without adjusting its context. After editing,
191
+ read the paragraph as continuous prose and verify subject, object, modifier,
192
+ condition, cause, and effect.
193
+
194
+ Assess readability and meaning preservation separately. Compare the source and
195
+ revision for both missing information and unsupported additions, including
196
+ conditions, negation, numbers, actors, causes, and implementation requirements.
197
+ For example, removing a unique constraint from a duplicate-message check can
198
+ lose a concurrency requirement even when the revised paragraph sounds clearer.
199
+ Record unresolved evidence and keep necessary repetition or qualifications.
200
+ No candidate count or mechanical verification result proves semantic correctness.
201
+ Sentence length, punctuation, and list density can help locate passages to read;
202
+ they are not fixed acceptance thresholds. Keep lists for parallel items and
203
+ ordered procedures, and prose for connected explanations.
204
+
205
+
206
+ ## Headings and the relation to figures and code
207
+
208
+ Headings identify the subject, operation, or comparison actually explained.
209
+ Do not promise a broader comparison than the text supports or replace the
210
+ subject with an unexplained metaphor. Heading length and required section
211
+ names belong to the book's format, not to universal prose quality.
212
+ Do not force every heading into a conclusion sentence or the same template;
213
+ make the section's content identifiable in the form the book uses.
214
+
215
+ Introduce tables and figures with enough context to explain what is compared
216
+ or traced, and explain what the reader can establish from them. Keep identifiers,
217
+ values, ordering, and conditions consistent with the code or clearly label a
218
+ simplified conceptual representation. Do not repeat every label as prose.
219
+
220
+ File paths and type names belong where they help explain a specific operation;
221
+ avoid a list of identifiers before the reader knows their roles. Bibliographic
222
+ and implementation-file descriptions introduce the referenced material, rather
223
+ than narrating the author's research activity.
@@ -3,10 +3,13 @@
3
3
  module AsciidocPubkit
4
4
  class CLI
5
5
  HELP = <<~TEXT
6
- Usage: asciidoc-pubkit review <command> [options] <input>
6
+ Usage: asciidoc-pubkit <group> <command> [options] [input]
7
7
 
8
8
  Commands:
9
+ writing criteria Print the shared Japanese prose criteria
10
+ writing prompt Generate a Japanese technical writing prompt
9
11
  review scan FILE Collect Japanese prose and review candidates
12
+ review score FILE Score Japanese prose; optionally use a local AI CLI
10
13
  review prompt SESSION Generate an English review prompt with Japanese source excerpts
11
14
  review verify SESSION Compare edited sources with the saved baseline
12
15
 
@@ -14,31 +17,46 @@ module AsciidocPubkit
14
17
  --help Show help (also supported for each command)
15
18
  --version Show the version
16
19
 
17
- No command edits manuscript files or invokes an AI service.
20
+ Use --lang ja with writing commands and review commands. Other review and
21
+ writing languages are not supported yet and return an error.
22
+ No command edits manuscripts. review score --agent explicitly invokes a
23
+ local Codex or Claude CLI, which may connect to its configured AI service.
18
24
  TEXT
19
25
 
20
- def self.run(arguments, out: $stdout, err: $stderr)
26
+ def self.run(arguments, out: $stdout, err: $stderr, input: $stdin)
21
27
  args = arguments.dup
22
28
  if args == ['--version']
23
29
  out.puts VERSION
24
30
  return 0
25
31
  end
26
- if args.empty? || args == ['--help'] || args == ['-h'] || args == ['review', '--help']
32
+ if args.empty? || args == ['--help'] || args == ['-h'] || %w[review writing].any? { |group| args == [group, '--help'] }
27
33
  out.puts HELP
28
34
  return 0
29
35
  end
30
36
  group, command = args.shift(2)
31
- raise Error, 'Expected review scan, review prompt, or review verify. Use --help.' unless group == 'review' && %w[scan prompt verify].include?(command)
37
+ valid = (group == 'review' && %w[scan score prompt verify].include?(command)) ||
38
+ (group == 'writing' && %w[criteria prompt].include?(command))
39
+ raise Error, 'Unknown command. Use --help.' unless valid
32
40
  options = { attributes: {} }
33
41
  parser = OptionParser.new do |opts|
34
- opts.banner = "Usage: asciidoc-pubkit review #{command} [options] #{command == 'scan' ? 'FILE' : 'SESSION'}"
35
- opts.on('-o', '--output PATH', command == 'scan' ? 'New session directory (default: .pubkit/review)' : 'New output file (default: standard output)') { |v| options[:output] = v }
36
- if command == 'scan'
42
+ input_name = group == 'writing' ? '' : (%w[scan score].include?(command) ? ' FILE' : ' SESSION')
43
+ opts.banner = "Usage: asciidoc-pubkit #{group} #{command} [options]#{input_name}"
44
+ opts.on('-o', '--output PATH', group == 'review' && command == 'scan' ? 'New session directory (default: .pubkit/review)' : 'New output file (default: standard output)') { |v| options[:output] = v }
45
+ opts.on('--lang LANG', 'Language (ja only in this release)') { |v| options[:language] = v }
46
+ if group == 'review' && %w[scan score].include?(command)
47
+ if command == 'scan'
48
+ opts.on('-y', '--yes', 'Answer yes to replacement confirmation') { options[:yes] = true }
49
+ opts.on('--no-input', 'Never prompt; fail on existing output unless --yes') { options[:no_input] = true }
50
+ else
51
+ opts.on('--json', 'Print the complete score report as JSON') { options[:json] = true }
52
+ opts.on('--agent NAME', 'Invoke local codex or claude for readability scoring') { |v| options[:agent] = v }
53
+ opts.on('--model NAME', 'Local evaluator model (default: CLI default)') { |v| options[:model] = v }
54
+ opts.on('--timeout SECONDS', Integer, 'Local evaluator timeout (default: 300)') { |v| options[:timeout] = v }
55
+ end
37
56
  opts.on('--only FILE', 'Review one included file in the book context') { |v| options[:only] = v }
38
57
  opts.on('--config FILE', 'Use an explicit YAML configuration') { |v| options[:config] = v }
39
58
  opts.on('--rules FILE', 'Replace default review rules with a YAML rule set') { |v| options[:rules] = v }
40
59
  opts.on('--base-dir DIR', 'Set the Asciidoctor base directory') { |v| options[:base_dir] = v }
41
- opts.on('--lang LANG', 'Prose language (ja only)') { |v| options[:language] = v }
42
60
  opts.on('--tokenizer NAME', 'mecab (default) or literal (limited phrase matching)') { |v| options[:tokenizer] = v }
43
61
  opts.on('--style STYLE', 'preserve (default), desu-masu, or dearu') { |v| options[:style] = v }
44
62
  opts.on('-a', '--attribute NAME=VALUE', 'Set an Asciidoctor attribute; repeat as needed') do |v|
@@ -46,7 +64,7 @@ module AsciidocPubkit
46
64
  raise Error, 'Attribute name cannot be empty.' if key.nil? || key.empty?
47
65
  options[:attributes][key] = value || ''
48
66
  end
49
- elsif command == 'prompt'
67
+ elsif group == 'review' && command == 'prompt'
50
68
  opts.on('--mode MODE', 'revise (default) or diagnose') { |v| options[:mode] = v }
51
69
  end
52
70
  opts.on('-h', '--help', 'Show command help') { options[:help] = true }
@@ -56,9 +74,38 @@ module AsciidocPubkit
56
74
  out.puts parser
57
75
  return 0
58
76
  end
77
+ if group == 'writing'
78
+ raise Error, 'Writing commands do not accept an input file. Use --help.' unless args.empty?
79
+ language = Language.validate!(options.fetch(:language, Language::DEFAULT), operation: 'writing')
80
+ content = command == 'criteria' ? Writing.criteria(language) : Writing.prompt(language)
81
+ output(content, options[:output], out)
82
+ return 0
83
+ end
59
84
  raise Error, 'Exactly one input is required. Use --help.' unless args.length == 1
85
+ Language.validate!(options[:language], operation: 'review') if options[:language]
86
+ if command == 'score'
87
+ if options[:output] && (File.exist?(options[:output]) || File.symlink?(options[:output]))
88
+ raise Error, "Output already exists: #{options[:output]}"
89
+ end
90
+ report = Score.run(args.first, options)
91
+ text = options[:json] ? JSON.pretty_generate(report) + "\n" : Score.format(report)
92
+ output(text, options[:output], out)
93
+ return 0
94
+ end
60
95
  if command == 'scan'
61
- result = Session.scan(args.first, options)
96
+ result = Session.scan(args.first, options) do |destination|
97
+ if options[:yes]
98
+ true
99
+ elsif options[:no_input] || !input.tty?
100
+ raise Error, "Output already exists: #{destination}. Use --yes to replace it or --output for a new session."
101
+ else
102
+ err.print "Output already exists: #{destination}. Replace it? [y/N] "
103
+ err.flush
104
+ answer = input.gets
105
+ raise Error, 'Scan cancelled; existing session was kept.' unless answer && %w[y yes].include?(answer.strip.downcase)
106
+ true
107
+ end
108
+ end
62
109
  out.puts "Scanned #{result['paragraphs']} paragraphs; found #{result['findings']} review candidates."
63
110
  out.puts "Tokenizer: #{result['tokenizer']}#{result['tokenizer'] == 'literal' ? ' (limited phrase matching; no morphological analysis)' : ' (UTF-8 IPADIC)'}"
64
111
  out.puts "Coverage notices: #{result['coverage_notices']}. See document.json for limitations."
@@ -66,6 +113,10 @@ module AsciidocPubkit
66
113
  return 0
67
114
  end
68
115
  session = Session.new(args.first)
116
+ saved_language = Language.validate!(session.language, operation: 'review')
117
+ if options[:language] && options[:language] != saved_language
118
+ raise Error, "Requested language #{options[:language].inspect} does not match session language #{saved_language.inspect}."
119
+ end
69
120
  if command == 'prompt'
70
121
  output(session.prompt(options.fetch(:mode, 'revise')), options[:output], out)
71
122
  return 0
@@ -26,6 +26,11 @@ module AsciidocPubkit
26
26
  super(*args)
27
27
  end
28
28
  doc.parse
29
+ document_language = doc.attr('lang')
30
+ if document_language && document_language != settings.fetch('language')
31
+ Language.validate!(document_language, operation: 'review')
32
+ raise Error, "Document language #{document_language.inspect} does not match review language #{settings.fetch('language').inspect}."
33
+ end
29
34
  @line_index = Hash.new { |hash, key| hash[key] = [] }
30
35
  @source_lines = @sources.to_h do |path, raw|
31
36
  lines = raw.lines.map { |line| line.delete_suffix("\n").delete_suffix("\r") }
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AsciidocPubkit
4
+ module Language
5
+ WRITING = %w[ja].freeze
6
+ REVIEW = %w[ja].freeze
7
+ DEFAULT = 'ja'
8
+
9
+ def self.validate!(value, operation:)
10
+ supported = { 'writing' => WRITING, 'review' => REVIEW }.fetch(operation)
11
+ return value if supported.include?(value)
12
+
13
+ raise Error, "Unsupported #{operation} language #{value.inspect}; supported: #{supported.join(', ')}."
14
+ end
15
+ end
16
+ end