asciidoc-pubkit 1.0.2 → 1.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +43 -0
- data/README.md +28 -5
- data/data/heading-rules.ja.yml +4 -0
- data/data/review-rules.ja.yml +5 -1
- data/data/writing/ja/criteria.md +67 -0
- data/data/writing/ja/headings.md +11 -0
- data/data/writing/ja/outline.md +23 -0
- data/docs/agent-review-tutorial.md +258 -0
- data/docs/configuration.md +6 -0
- data/docs/document-commands.md +6 -1
- data/docs/installation.md +2 -2
- data/docs/outline-review.md +110 -0
- data/docs/prose-evaluation.md +25 -7
- data/docs/references.md +6 -0
- data/docs/review-workflow.md +39 -1
- data/docs/rules-and-coverage.md +2 -2
- data/docs/skill-integration.md +131 -0
- data/docs/writing.md +16 -1
- data/examples/skills/review-asciidoc-manuscript/SKILL.md +48 -0
- data/lib/asciidoc_pubkit/cli.rb +8 -1
- data/lib/asciidoc_pubkit/document.rb +11 -2
- data/lib/asciidoc_pubkit/document_commands.rb +32 -2
- data/lib/asciidoc_pubkit/heading_rules.rb +1 -0
- data/lib/asciidoc_pubkit/rules.rb +14 -0
- data/lib/asciidoc_pubkit/session.rb +43 -3
- data/lib/asciidoc_pubkit/settings.rb +12 -1
- data/lib/asciidoc_pubkit/writing.rb +5 -0
- data/lib/asciidoc_pubkit.rb +1 -1
- metadata +6 -1
|
@@ -113,6 +113,7 @@ module AsciidocPubkit
|
|
|
113
113
|
tokens.each_with_index do |token, index|
|
|
114
114
|
next if token['unknown'] || phrase_ranges.any? { |range| range.cover?(token['offset']) }
|
|
115
115
|
lemma = token['lemma']
|
|
116
|
+
next if lemma == '人' && !standalone_human?(text, token['offset'])
|
|
116
117
|
rule = nil
|
|
117
118
|
finish_index = index
|
|
118
119
|
compound = rules.fetch('compound_nouns').find do |term|
|
|
@@ -185,6 +186,7 @@ module AsciidocPubkit
|
|
|
185
186
|
surfaces.each do |surface|
|
|
186
187
|
text.to_enum(:scan, Regexp.new(Regexp.escape(surface))).each do
|
|
187
188
|
match = Regexp.last_match
|
|
189
|
+
next if surface == '人' && !standalone_human?(text, match.begin(0))
|
|
188
190
|
next if contextual.any? { |start, finish, _, candidate_rule, *_| start == match.begin(0) && finish == match.end(0) && candidate_rule == rule }
|
|
189
191
|
candidates << [match.begin(0), match.end(0), surface, rule, question]
|
|
190
192
|
end
|
|
@@ -200,6 +202,18 @@ module AsciidocPubkit
|
|
|
200
202
|
end
|
|
201
203
|
end
|
|
202
204
|
|
|
205
|
+
# Exclude compound words, identifiers and counts in both tokenizer modes.
|
|
206
|
+
# Hiragana context such as その人 or 人による remains a review cue.
|
|
207
|
+
def self.standalone_human?(text, offset)
|
|
208
|
+
compound = /[\p{Han}\p{N}\p{Latin}\p{Katakana}_]/
|
|
209
|
+
previous = offset.positive? ? text[offset - 1] : nil
|
|
210
|
+
following = text[offset + 1]
|
|
211
|
+
return false if previous == 'ー' || previous&.match?(compound)
|
|
212
|
+
return true if text[(offset + 1)..].start_with?('向け')
|
|
213
|
+
|
|
214
|
+
following != 'ー' && !following&.match?(compound)
|
|
215
|
+
end
|
|
216
|
+
|
|
203
217
|
def self.find_term(findings, paragraph, text, term, rule, severity, question)
|
|
204
218
|
text.to_enum(:scan, Regexp.new(Regexp.escape(term))).each do
|
|
205
219
|
match = Regexp.last_match
|
|
@@ -51,6 +51,7 @@ module AsciidocPubkit
|
|
|
51
51
|
'protected' => protected_content(document),
|
|
52
52
|
'numeric_tokens' => numeric_tokens(document)
|
|
53
53
|
}
|
|
54
|
+
manifest['outline_criteria'] = Writing.outline_criteria(settings.data['language']) if settings.data['scope'] == 'headings'
|
|
54
55
|
write_json(File.join(staging, 'document.json'), document.to_h)
|
|
55
56
|
write_json(File.join(staging, 'findings.json'), findings)
|
|
56
57
|
manifest['artifacts'] = %w[document.json findings.json].to_h do |name|
|
|
@@ -157,14 +158,21 @@ module AsciidocPubkit
|
|
|
157
158
|
@manifest.fetch('settings').fetch('language')
|
|
158
159
|
end
|
|
159
160
|
|
|
160
|
-
def prompt(mode)
|
|
161
|
+
def prompt(mode, view: 'full')
|
|
162
|
+
raise Error, 'view must be full or outline.' unless %w[full outline].include?(view)
|
|
163
|
+
if view == 'outline' && @manifest.fetch('settings').fetch('scope', 'prose') != 'headings'
|
|
164
|
+
raise Error, 'Outline view requires a heading session.'
|
|
165
|
+
end
|
|
166
|
+
if view == 'outline' && !@manifest['outline_criteria'].is_a?(String)
|
|
167
|
+
raise Error, 'Outline view requires saved outline criteria. Create a new heading session without replacing the existing baseline.'
|
|
168
|
+
end
|
|
161
169
|
raise Error, 'mode must be revise or diagnose.' unless %w[revise diagnose].include?(mode)
|
|
162
170
|
stale = @manifest['sources'].select do |source|
|
|
163
171
|
!File.file?(source['path']) || Digest::SHA256.file(source['path']).hexdigest != source['sha256']
|
|
164
172
|
end
|
|
165
173
|
raise Error, 'Sources have changed since scanning. Create a new scan before generating a prompt.' unless stale.empty?
|
|
166
174
|
language = Language.validate!(@manifest.fetch('settings').fetch('language'), operation: 'review')
|
|
167
|
-
return heading_prompt(mode, language) if @manifest.fetch('settings').fetch('scope', 'prose') == 'headings'
|
|
175
|
+
return heading_prompt(mode, language, view: view) if @manifest.fetch('settings').fetch('scope', 'prose') == 'headings'
|
|
168
176
|
list_scope = @manifest['settings']['scope'] == 'lists'
|
|
169
177
|
criteria = @manifest.fetch('writing_criteria')
|
|
170
178
|
instructions = <<~TEXT
|
|
@@ -332,7 +340,7 @@ module AsciidocPubkit
|
|
|
332
340
|
end
|
|
333
341
|
end
|
|
334
342
|
|
|
335
|
-
def heading_prompt(mode, language)
|
|
343
|
+
def heading_prompt(mode, language, view: 'full')
|
|
336
344
|
text = <<~TEXT
|
|
337
345
|
# Japanese heading review
|
|
338
346
|
|
|
@@ -399,6 +407,14 @@ module AsciidocPubkit
|
|
|
399
407
|
## Selected headings
|
|
400
408
|
|
|
401
409
|
TEXT
|
|
410
|
+
if view == 'outline'
|
|
411
|
+
text.sub!('# Japanese heading review', '# Japanese outline review')
|
|
412
|
+
text.sub!(prompt_data(JSON.generate(@document.fetch('outline')), 'json'), outline_view_data)
|
|
413
|
+
text.sub!('## Outline (reference data)', outline_review_guidance + "\n## Outline (reference data)")
|
|
414
|
+
end
|
|
415
|
+
if (depth = @manifest['settings']['heading_depth'])
|
|
416
|
+
text.sub!('## Selected headings', "Selected heading depth: #{depth} (maximum Asciidoctor section level).\nDeeper titles are reference data only and remain protected.\n\n## Selected headings")
|
|
417
|
+
end
|
|
402
418
|
grouped = @findings.group_by { |finding| finding['heading_id'] }
|
|
403
419
|
@document.fetch('headings').each do |heading|
|
|
404
420
|
text << "### Heading #{heading['id']}\n\n"
|
|
@@ -415,6 +431,28 @@ module AsciidocPubkit
|
|
|
415
431
|
text
|
|
416
432
|
end
|
|
417
433
|
|
|
434
|
+
def outline_review_guidance
|
|
435
|
+
@manifest.fetch('outline_criteria')
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def outline_view_data
|
|
439
|
+
selected = @document.fetch('headings').to_h { |h| [h['index'], h] }
|
|
440
|
+
numbers = {}
|
|
441
|
+
siblings = Hash.new(0)
|
|
442
|
+
rows = @document.fetch('outline').map do |heading|
|
|
443
|
+
parent = heading['parent_index']
|
|
444
|
+
siblings[parent] += 1
|
|
445
|
+
number = (numbers[parent] || []) + [siblings[parent]]
|
|
446
|
+
numbers[heading['index']] = number
|
|
447
|
+
source = selected[heading['index']] || heading
|
|
448
|
+
location = source['file'] ? "#{source['file']}:#{source['line'] || '?'}" : 'source position unavailable'
|
|
449
|
+
status = selected.key?(heading['index']) ? 'editable' : 'reference-only'
|
|
450
|
+
"#{' ' * (number.length - 1)}#{number.join('-')}. #{heading['text']} " \
|
|
451
|
+
"[#{status}; level #{heading['level']}; section_id #{heading['section_id']}; #{location}]"
|
|
452
|
+
end
|
|
453
|
+
prompt_data(rows.join("\n"), 'text')
|
|
454
|
+
end
|
|
455
|
+
|
|
418
456
|
def review_process(unit, mode)
|
|
419
457
|
<<~TEXT
|
|
420
458
|
## Review decisions and completion
|
|
@@ -427,6 +465,8 @@ module AsciidocPubkit
|
|
|
427
465
|
Review #{unit}s without candidates against the shared criteria as well. For concerns outside the editable scope, report the location and a separate out-of-scope proposal; do not count protection as an editorial keep decision or edit protected content.
|
|
428
466
|
#{mode == 'revise' ? 'Compare the original and edited passages, check both information loss and unsupported additions, and reconcile each recorded decision with the actual diff. After verification, individually inspect newly detected or remaining candidates; retain the original candidate IDs in the review record.' : 'Compare each proposed revision with the original passage for information loss and unsupported additions. A revise decision means a proposed revision, not an applied edit.'}
|
|
429
467
|
Report mechanical preservation, candidate decisions, and #{unit}-level quality review separately. State selected and reviewed #{unit} counts, candidate decision counts, pending items, needs-evidence items, out-of-scope concerns, and evidence used. Recheck retained role metaphors by their actual roles and cite the individual decisions; listing remaining words is not enough. Out-of-scope concerns cover only evidence actually inspected; the supplied prose/body context omits lists and other excluded blocks, so do not claim those were reviewed. If any selected item or candidate remains pending, report the review as incomplete.
|
|
468
|
+
For an explicitly task-limited review (such as human-role terminology), state the objective and relevant entry IDs separately from the total scanned entries. Report task-specific decisions and full-quality review counts separately. Scanning the whole book or completing a terminology pass does not establish full editorial review; unrelated unreviewed entries and candidates remain pending, not automatic keep decisions. This reporting distinction does not change saved edit permissions or add a selection filter.
|
|
469
|
+
Check related tables, figures and alternative text when terminology changes affect them. Report updates outside this session's edit scope separately; this prompt does not authorize those edits. Preserve the original baseline and phase-end verification when separately authorized synchronization follows.
|
|
430
470
|
Mechanical verification does not read or validate these decision records. Its passed result and candidate counts do not establish review completion or writing quality; meaning_verified remains false. Do not claim semantic correctness from those checks.
|
|
431
471
|
TEXT
|
|
432
472
|
end
|
|
@@ -23,7 +23,7 @@ module AsciidocPubkit
|
|
|
23
23
|
reject_keys(config, ['review'], 'configuration')
|
|
24
24
|
review = config.fetch('review', {})
|
|
25
25
|
raise Error, 'review must be a mapping.' unless review.is_a?(Hash)
|
|
26
|
-
reject_keys(review, %w[language style glossary exclude allows attributes base_dir tokenizer mecab_command mecab_dictionary rules heading_rules], 'review')
|
|
26
|
+
reject_keys(review, %w[language style glossary exclude allows attributes base_dir tokenizer mecab_command mecab_dictionary rules heading_rules heading_depth], 'review')
|
|
27
27
|
raise Error, 'attributes must be a mapping.' unless review.fetch('attributes', {}).is_a?(Hash)
|
|
28
28
|
%w[base_dir glossary mecab_command mecab_dictionary rules heading_rules].each do |key|
|
|
29
29
|
raise Error, "#{key} must be a nonempty path string." if review.key?(key) && (!review[key].is_a?(String) || review[key].empty?)
|
|
@@ -38,6 +38,16 @@ module AsciidocPubkit
|
|
|
38
38
|
if scope != 'headings' && options[:heading_rules]
|
|
39
39
|
raise Error, '--heading-rules requires --scope headings.'
|
|
40
40
|
end
|
|
41
|
+
if options.key?(:depth) && scope != 'headings'
|
|
42
|
+
raise Error, '--depth requires --scope headings.'
|
|
43
|
+
end
|
|
44
|
+
if review.key?('heading_depth') && (!review['heading_depth'].is_a?(Integer) || review['heading_depth'] < 1)
|
|
45
|
+
raise Error, 'heading_depth must be a positive integer.'
|
|
46
|
+
end
|
|
47
|
+
depth = options.fetch(:depth, review['heading_depth']) if scope == 'headings'
|
|
48
|
+
if depth && (!depth.is_a?(Integer) || depth < 1)
|
|
49
|
+
raise Error, 'Depth must be a positive integer.'
|
|
50
|
+
end
|
|
41
51
|
heading_rules_path = options[:heading_rules] ? File.expand_path(options[:heading_rules]) : (review['heading_rules'] ? File.expand_path(review['heading_rules'], base) : HeadingRuleSet.default_path(language))
|
|
42
52
|
rules_path = options[:replacement] ? RuleSet.default_path(language) : options[:rules] ? File.expand_path(options[:rules]) : (review['rules'] ? File.expand_path(review['rules'], base) : RuleSet.default_path(language))
|
|
43
53
|
@data = {
|
|
@@ -58,6 +68,7 @@ module AsciidocPubkit
|
|
|
58
68
|
'base_dir' => File.expand_path(options[:base_dir] || review.fetch('base_dir', base), options[:base_dir] ? Dir.pwd : base),
|
|
59
69
|
'glossary' => {}
|
|
60
70
|
}
|
|
71
|
+
@data['heading_depth'] = depth if depth
|
|
61
72
|
raise Error, 'tokenizer must be mecab or literal.' unless %w[mecab literal].include?(@data['tokenizer'])
|
|
62
73
|
raise Error, 'style must be preserve, desu-masu, or dearu.' unless %w[preserve desu-masu dearu].include?(@data['style'])
|
|
63
74
|
%w[exclude allows].each do |key|
|
|
@@ -18,6 +18,11 @@ module AsciidocPubkit
|
|
|
18
18
|
AsciidocPubkit.read_text(File.join(DATA_ROOT, language, 'headings.md'))
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
def self.outline_criteria(language = Language::DEFAULT)
|
|
22
|
+
Language.validate!(language, operation: 'review')
|
|
23
|
+
AsciidocPubkit.read_text(File.join(DATA_ROOT, language, 'outline.md'))
|
|
24
|
+
end
|
|
25
|
+
|
|
21
26
|
def self.prompt(language = Language::DEFAULT)
|
|
22
27
|
guide = prompt_criteria(language)
|
|
23
28
|
<<~TEXT
|
data/lib/asciidoc_pubkit.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: asciidoc-pubkit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- CyberGarage
|
|
@@ -53,6 +53,8 @@ files:
|
|
|
53
53
|
- data/review-rules.ja.yml
|
|
54
54
|
- data/writing/ja/criteria.md
|
|
55
55
|
- data/writing/ja/headings.md
|
|
56
|
+
- data/writing/ja/outline.md
|
|
57
|
+
- docs/agent-review-tutorial.md
|
|
56
58
|
- docs/architecture.md
|
|
57
59
|
- docs/configuration.md
|
|
58
60
|
- docs/custom-rules.md
|
|
@@ -61,15 +63,18 @@ files:
|
|
|
61
63
|
- docs/english-review-research.md
|
|
62
64
|
- docs/installation.md
|
|
63
65
|
- docs/list-review.md
|
|
66
|
+
- docs/outline-review.md
|
|
64
67
|
- docs/prose-evaluation.md
|
|
65
68
|
- docs/references.md
|
|
66
69
|
- docs/replacements.md
|
|
67
70
|
- docs/review-workflow.md
|
|
68
71
|
- docs/rules-and-coverage.md
|
|
69
72
|
- docs/scoring.md
|
|
73
|
+
- docs/skill-integration.md
|
|
70
74
|
- docs/writing.md
|
|
71
75
|
- examples/book.adoc
|
|
72
76
|
- examples/replacements.yml
|
|
77
|
+
- examples/skills/review-asciidoc-manuscript/SKILL.md
|
|
73
78
|
- exe/asciidoc-pubkit
|
|
74
79
|
- lib/asciidoc_pubkit.rb
|
|
75
80
|
- lib/asciidoc_pubkit/cli.rb
|