kotonoha 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README-ja.md +246 -0
- package/README.md +76 -49
- package/package.json +7 -3
- package/skills/japanese-prose/NOTICE.md +17 -0
- package/skills/japanese-prose/SKILL.md +108 -0
- package/skills/japanese-prose/references/review-workflow.md +50 -0
- package/skills/japanese-prose/references/scoring.md +24 -0
- package/skills/japanese-prose/references/writing-guidelines.md +60 -0
- package/skills/japanese-prose/scripts/core.py +192 -0
- package/skills/japanese-prose/scripts/fixtures/natural.md +5 -0
- package/skills/japanese-prose/scripts/fixtures/unnatural.md +5 -0
- package/skills/japanese-prose/scripts/lint.py +378 -0
- package/skills/japanese-prose/scripts/outline.py +68 -0
- package/skills/japanese-prose/scripts/terms.py +112 -0
- package/skills/japanese-prose/scripts/test_engine.py +117 -0
- package/skills/tech-writer/SKILL.md +38 -38
- package/skills/tech-writer/assets/templates/qiita.md +44 -0
- package/skills/tech-writer/references/doctypes/qiita.md +15 -8
- package/skills/tech-writer/references/japanese-prose-optimization.md +22 -21
- package/skills/tech-writer/references/style-constitution.md +14 -6
- package/skills/tech-writer/scripts/lint.py +41 -5
|
@@ -27,10 +27,9 @@ description: >-
|
|
|
27
27
|
question-at-a-time intake before writing. Supports both Japanese and
|
|
28
28
|
English documents, with Japanese as the primary target. Owns document
|
|
29
29
|
structure, information completeness, and reader fit; for living Japanese
|
|
30
|
-
documents, it
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
skill.
|
|
30
|
+
documents, it invokes kotonoha's original bundled `japanese-prose` skill,
|
|
31
|
+
which uses GiNZA for sentence-level naturalness, word choice, rhythm, and
|
|
32
|
+
repeated AI-like pattern analysis.
|
|
34
33
|
license: MIT
|
|
35
34
|
argument-hint: "[write|review|score] [doctype] <target file or request>"
|
|
36
35
|
---
|
|
@@ -57,13 +56,12 @@ reference files.
|
|
|
57
56
|
|
|
58
57
|
Document quality splits into two layers: "is the structure right" and "is
|
|
59
58
|
the prose natural and readable". This skill owns the first layer (structure,
|
|
60
|
-
document type conventions, completeness, reader fit).
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
tech-writer
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
the final rubber-duck review.
|
|
59
|
+
document type conventions, completeness, reader fit). The bundled
|
|
60
|
+
`japanese-prose` skill owns the second layer (sentence-level naturalness,
|
|
61
|
+
reading load, terminology, and repeated-pattern analysis). Kotonoha packages
|
|
62
|
+
both layers: tech-writer locks down the structure and orchestrates the §5
|
|
63
|
+
handoff to japanese-prose during `write` mode before the final rubber-duck
|
|
64
|
+
review.
|
|
67
65
|
|
|
68
66
|
- Rule of thumb: "does removing a heading still make sense?" tests structure
|
|
69
67
|
(this skill's job). "Does rereading a single sentence change its meaning?"
|
|
@@ -220,8 +218,14 @@ omissions explicitly; disclose known limitations and unsupported cases
|
|
|
220
218
|
instead of hiding them; and keep a last-updated date or target version
|
|
221
219
|
where staleness is a real risk. For Zenn/Qiita, rule 1's "first three
|
|
222
220
|
lines" maps to the frontmatter `title` plus the lead paragraph right
|
|
223
|
-
after it
|
|
224
|
-
|
|
221
|
+
after it. Zenn body sections start at `##`; Qiita body sections start at
|
|
222
|
+
`#` and use `##` for subsections.
|
|
223
|
+
|
|
224
|
+
For every Markdown doctype, surround `**strong emphasis**` with half-width
|
|
225
|
+
spaces when the delimiters would otherwise touch prose. For example, write
|
|
226
|
+
`これは **強調** になる`, not `これは**強調**にならない`. Spaces are not
|
|
227
|
+
required at line boundaries or next to punctuation, and must not be placed
|
|
228
|
+
inside the `**` delimiters.
|
|
225
229
|
|
|
226
230
|
For atomic artifacts (commit messages, PR descriptions, issue reports,
|
|
227
231
|
code comments/docstrings, a single entry appended to an existing
|
|
@@ -259,30 +263,29 @@ step 2 as the primary check for those.
|
|
|
259
263
|
(add `--atomic` for a commit message, PR description, issue report,
|
|
260
264
|
code comment/docstring, or a single release-notes entry) to
|
|
261
265
|
mechanically catch heading-level skips, code blocks missing a
|
|
262
|
-
language tag, leftover placeholders,
|
|
263
|
-
are flags, not mandates —
|
|
264
|
-
briefly.
|
|
266
|
+
language tag, leftover placeholders, suspicious links, and strong-emphasis
|
|
267
|
+
delimiters that directly touch prose. Findings are flags, not mandates —
|
|
268
|
+
deliberate exceptions can stay; note the reason briefly.
|
|
265
269
|
4. **Reader-goal recheck**: confirm the "what the reader can do after
|
|
266
270
|
reading" outcome from §1 is actually achievable from this document alone.
|
|
267
271
|
|
|
268
272
|
## 5. Optimize Japanese prose without changing the structure — write mode only
|
|
269
273
|
|
|
270
274
|
For a living, multi-section document whose requested final language is
|
|
271
|
-
Japanese, read
|
|
272
|
-
`
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
the optimizer cannot start.
|
|
275
|
+
Japanese, read `references/japanese-prose-optimization.md` and hand the
|
|
276
|
+
completed draft to kotonoha's bundled `japanese-prose` skill. This pass is
|
|
277
|
+
mandatory for an in-scope document.
|
|
278
|
+
|
|
279
|
+
Load `japanese-prose` through the host's skill-loading mechanism. When only
|
|
280
|
+
installed skill files are exposed, resolve the sibling path
|
|
281
|
+
`<tech-writer-dir>/../japanese-prose/SKILL.md` first, then check
|
|
282
|
+
`.github/skills/japanese-prose/SKILL.md`,
|
|
283
|
+
`.copilot/skills/japanese-prose/SKILL.md`, and
|
|
284
|
+
`$HOME/.copilot/skills/japanese-prose/SKILL.md`. Load the discovered
|
|
285
|
+
`SKILL.md` and follow its quick or full workflow. A standard
|
|
286
|
+
`npx kotonoha install` installs this sibling skill automatically. Treat the
|
|
287
|
+
pass as unavailable only when someone copied or installed tech-writer without
|
|
288
|
+
its bundled sibling, or when the optimizer cannot start.
|
|
286
289
|
|
|
287
290
|
Freeze heading hierarchy, section order, identifiers, numeric facts,
|
|
288
291
|
citations, normative language, tables, code, commands, acceptance criteria,
|
|
@@ -388,10 +391,7 @@ of that. Common items to confirm:
|
|
|
388
391
|
|
|
389
392
|
## Acknowledgment
|
|
390
393
|
|
|
391
|
-
The
|
|
392
|
-
[
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
that separation by owning technical-document structure while orchestrating,
|
|
396
|
-
but not reimplementing, the registered prose-polishing skill responsible for
|
|
397
|
-
sentence-level naturalness.
|
|
394
|
+
The bundled prose optimizer is an original kotonoha implementation built on
|
|
395
|
+
[GiNZA](https://github.com/megagonlabs/ginza) (MIT License). Tech-writer owns
|
|
396
|
+
technical-document structure while japanese-prose uses GiNZA's morphology,
|
|
397
|
+
dependency, and named-entity analysis for sentence-level review.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: <記事タイトル>
|
|
3
|
+
tags:
|
|
4
|
+
- <tag1>
|
|
5
|
+
- <tag2>
|
|
6
|
+
private: false
|
|
7
|
+
organization_url_name: null
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<この記事が解決する具体的な課題と、読了後にできるようになることを記載する。>
|
|
11
|
+
|
|
12
|
+
# 検証した環境
|
|
13
|
+
|
|
14
|
+
| 項目 | バージョン・値 |
|
|
15
|
+
|---|---|
|
|
16
|
+
| OS | <OSとバージョン> |
|
|
17
|
+
| 言語・ランタイム | <名称とバージョン> |
|
|
18
|
+
| フレームワーク・ライブラリ | <名称とバージョン> |
|
|
19
|
+
|
|
20
|
+
# <最初に読者へ示す結論・解決方法>
|
|
21
|
+
|
|
22
|
+
<検索から到達した読者が最短で解決できるよう、結論または実行手順を先に記載する。>
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
<そのまま実行できる最小のコマンド例>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## <手順の前提または補足>
|
|
29
|
+
|
|
30
|
+
<必要な権限、設定、注意点を記載する。>
|
|
31
|
+
|
|
32
|
+
# <仕組み・原因・技術的な説明>
|
|
33
|
+
|
|
34
|
+
<なぜこの方法で解決するのかを、図・コード・具体例を使って説明する。>
|
|
35
|
+
|
|
36
|
+
# 制約と未対応事項
|
|
37
|
+
|
|
38
|
+
- <確認できていない環境>
|
|
39
|
+
- <適用できない条件>
|
|
40
|
+
- <既知の制約>
|
|
41
|
+
|
|
42
|
+
# 参考資料
|
|
43
|
+
|
|
44
|
+
- [<公式ドキュメント名>](<URL>)
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
# Qiita article type
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
information) especially strictly here.
|
|
3
|
+
The article title lives in YAML frontmatter. Qiita's readership skews toward
|
|
4
|
+
searching for a specific error message or task, so apply rule 3 (order
|
|
5
|
+
matching how the reader looks for information) especially strictly here.
|
|
7
6
|
|
|
8
7
|
## Target reader
|
|
9
8
|
|
|
@@ -13,8 +12,8 @@ is scanning to confirm this article addresses their exact situation.
|
|
|
13
12
|
## Format
|
|
14
13
|
|
|
15
14
|
Markdown, with Qiita's frontmatter and a few platform-specific extensions
|
|
16
|
-
on top of GFM.
|
|
17
|
-
start at
|
|
15
|
+
on top of GFM. The frontmatter `title` is the article title; body sections
|
|
16
|
+
start at `#`, with `##` used for subsections.
|
|
18
17
|
|
|
19
18
|
## Recommended frontmatter
|
|
20
19
|
|
|
@@ -36,6 +35,11 @@ organization_url_name: null # only if publishing under an Organization
|
|
|
36
35
|
automatically on publish/update — don't hand-edit those unless you know
|
|
37
36
|
why.
|
|
38
37
|
|
|
38
|
+
## Template
|
|
39
|
+
|
|
40
|
+
Start from `assets/templates/qiita.md`.
|
|
41
|
+
Use `#` for the highest-level body sections and `##` for their subsections.
|
|
42
|
+
|
|
39
43
|
## Recommended skeleton
|
|
40
44
|
|
|
41
45
|
1. **Lead paragraph (right after frontmatter, before any heading)**: the
|
|
@@ -45,9 +49,10 @@ organization_url_name: null # only if publishing under an Organization
|
|
|
45
49
|
2. **Environment / versions**: state the exact versions (language,
|
|
46
50
|
framework, OS) the article was verified against, before any steps —
|
|
47
51
|
Qiita readers frequently hit version-specific breakage.
|
|
48
|
-
3. **Body sections, one concern per
|
|
52
|
+
3. **Body sections, one concern per `#` heading**: order by how a reader
|
|
49
53
|
arriving via search would scan (rule 3) — put the fix/answer before
|
|
50
|
-
background explanation if the article is troubleshooting-oriented.
|
|
54
|
+
background explanation if the article is troubleshooting-oriented. Use
|
|
55
|
+
`##` only for subsections within one concern.
|
|
51
56
|
4. **Code blocks with both a language tag and, where relevant, a filename**:
|
|
52
57
|
Qiita supports ` ```js:example.js ` — prefer this over a bare language
|
|
53
58
|
tag when the file identity matters to the reader.
|
|
@@ -60,6 +65,8 @@ organization_url_name: null # only if publishing under an Organization
|
|
|
60
65
|
rather than an unconsidered default?
|
|
61
66
|
- [ ] Does the lead paragraph right after frontmatter state the specific
|
|
62
67
|
problem/task addressed, since there's no in-body title to do that?
|
|
68
|
+
- [ ] Do highest-level body sections use `#`, with `##` reserved for
|
|
69
|
+
subsections?
|
|
63
70
|
- [ ] Are the exact versions/environment stated before the steps?
|
|
64
71
|
- [ ] For troubleshooting-oriented articles, does the fix appear before
|
|
65
72
|
background explanation, matching how a reader arriving via search
|
|
@@ -5,11 +5,10 @@ are complete. The goal is to remove mechanical or translated-sounding prose
|
|
|
5
5
|
without changing the document's approved meaning, obligations, evidence, or
|
|
6
6
|
traceability.
|
|
7
7
|
|
|
8
|
-
The
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
repeated AI-like phrasing.
|
|
8
|
+
The optimizer is kotonoha's original bundled `japanese-prose` skill.
|
|
9
|
+
Kotonoha owns document structure and completeness; `japanese-prose` owns
|
|
10
|
+
sentence-level clarity, rhythm, word choice, reading load, terminology, and
|
|
11
|
+
detection of repeated AI-like phrasing through GiNZA analysis.
|
|
13
12
|
|
|
14
13
|
## Run this pass when
|
|
15
14
|
|
|
@@ -17,8 +16,7 @@ repeated AI-like phrasing.
|
|
|
17
16
|
whose audience-facing prose is primarily Japanese.
|
|
18
17
|
- The task is `write` mode.
|
|
19
18
|
- The artifact is a living, multi-section document.
|
|
20
|
-
-
|
|
21
|
-
skill is available.
|
|
19
|
+
- The bundled `japanese-prose` skill is readable.
|
|
22
20
|
|
|
23
21
|
Skip it for English documents, generated machine-readable files, and source
|
|
24
22
|
code. Also skip atomic artifacts such as commit messages, PR descriptions,
|
|
@@ -38,6 +36,8 @@ outcome. Require it to preserve:
|
|
|
38
36
|
- Normative force such as Must / Should / May, approval states, and
|
|
39
37
|
acceptance criteria
|
|
40
38
|
- Tables, code blocks, commands, schemas, frontmatter keys, and placeholders
|
|
39
|
+
- Markdown emphasis spacing: keep half-width spaces between surrounding prose
|
|
40
|
+
and `**strong emphasis**` delimiters
|
|
41
41
|
- Explicit assumptions, limitations, residual risks, and unresolved items
|
|
42
42
|
|
|
43
43
|
Heading wording may improve only when its meaning and hierarchy stay intact.
|
|
@@ -45,25 +45,25 @@ Never trade technical precision for conversational phrasing.
|
|
|
45
45
|
|
|
46
46
|
## Optimization loop
|
|
47
47
|
|
|
48
|
-
1.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
`.github/skills/
|
|
52
|
-
`.copilot/skills/
|
|
53
|
-
`$HOME/.copilot/skills/
|
|
48
|
+
1. Invoke kotonoha's bundled `japanese-prose` through the host's
|
|
49
|
+
skill-loading mechanism. If the host exposes only skill files, resolve
|
|
50
|
+
`<tech-writer-dir>/../japanese-prose/SKILL.md` first, then check
|
|
51
|
+
`.github/skills/japanese-prose/SKILL.md`,
|
|
52
|
+
`.copilot/skills/japanese-prose/SKILL.md`, and
|
|
53
|
+
`$HOME/.copilot/skills/japanese-prose/SKILL.md`; expand `$HOME`, load the
|
|
54
54
|
discovered skill,
|
|
55
55
|
and follow its workflow.
|
|
56
56
|
2. Ask the loaded skill to review and rewrite only the Japanese prose under
|
|
57
57
|
the frozen invariants.
|
|
58
|
-
3. Let `<
|
|
58
|
+
3. Let `<japanese-prose-dir>` be the directory containing the loaded
|
|
59
59
|
optimizer's `SKILL.md`. When its diagnostics are available, run the
|
|
60
60
|
optimizer's scripts with their qualified paths:
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
|
-
uv run <
|
|
64
|
-
uv run <
|
|
65
|
-
uv run <
|
|
66
|
-
uv run <
|
|
63
|
+
uv run <japanese-prose-dir>/scripts/lint.py <target-file> --genre tech --json > <workdir>/prose-baseline.json
|
|
64
|
+
uv run <japanese-prose-dir>/scripts/lint.py <target-file> --genre tech --reading-load
|
|
65
|
+
uv run <japanese-prose-dir>/scripts/outline.py <target-file>
|
|
66
|
+
uv run <japanese-prose-dir>/scripts/terms.py <target-file>
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
Do not substitute `skills/tech-writer/scripts/lint.py`; kotonoha's script
|
|
@@ -93,9 +93,10 @@ final language is Japanese, in `write` mode), report exactly one status:
|
|
|
93
93
|
no actionable prose findings remain. Run diagnostics when available; if
|
|
94
94
|
`uv` or a diagnostic script is unavailable, note that limitation without
|
|
95
95
|
changing this status.
|
|
96
|
-
- `Japanese prose optimization not performed`:
|
|
97
|
-
|
|
98
|
-
concrete reason.
|
|
96
|
+
- `Japanese prose optimization not performed`: the bundled optimizer was
|
|
97
|
+
missing or unreadable, so no prose review or rewrite ran; include the
|
|
98
|
+
concrete reason. A standard `npx kotonoha install` should prevent this
|
|
99
|
+
state.
|
|
99
100
|
- `Japanese prose optimization did not converge`: three rounds completed with
|
|
100
101
|
unresolved actionable findings or invariant violations, or the optimizer
|
|
101
102
|
started but failed before producing a valid result, a required
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Constraints to fix a technical document's *structure* before writing.
|
|
4
4
|
Sentence-level naturalness, vocabulary, and rhythm are out of scope here
|
|
5
|
-
(→ the domain of
|
|
5
|
+
(→ the domain of kotonoha's bundled `japanese-prose` skill).
|
|
6
6
|
|
|
7
7
|
## Scope: living documents vs. atomic artifacts
|
|
8
8
|
|
|
@@ -10,11 +10,11 @@ These 8 rules assume a **living, multi-section reference document** —
|
|
|
10
10
|
README, design doc/ADR, API reference, release notes/CHANGELOG file as a
|
|
11
11
|
whole, user manual, technical proposal, RFI/RFP (including a supplier's RFP
|
|
12
12
|
response), or Zenn/Qiita article. Apply all 8 rules to those doctypes. For
|
|
13
|
-
Zenn
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
Zenn and Qiita, rule 1's article title lives in YAML frontmatter — see their
|
|
14
|
+
doctype reference files for the platform-specific fields. Zenn body sections
|
|
15
|
+
start at `##`; Qiita body sections start at `#` and use `##` for subsections.
|
|
16
|
+
Every other rule (code examples, disclosed limitations, etc.) still applies
|
|
17
|
+
verbatim.
|
|
18
18
|
|
|
19
19
|
**Atomic, single-purpose artifacts** — commit messages, PR descriptions,
|
|
20
20
|
issue reports, code comments/docstrings, and a single new entry appended
|
|
@@ -28,6 +28,14 @@ a title-plus-paragraph). Follow that doctype's own skeleton for those
|
|
|
28
28
|
instead of applying the heading-hierarchy and document-metadata rules
|
|
29
29
|
below verbatim; where the two disagree, the doctype reference wins.
|
|
30
30
|
|
|
31
|
+
## Markdown emphasis spacing
|
|
32
|
+
|
|
33
|
+
In generated Markdown, add half-width spaces outside strong-emphasis
|
|
34
|
+
delimiters when they touch surrounding prose. Write
|
|
35
|
+
`これは **強調** になる` rather than `これは**強調**にならない`.
|
|
36
|
+
The spaces are unnecessary at a line boundary or next to punctuation, for
|
|
37
|
+
example `**重要**: 設定を確認する`. Never put spaces inside the delimiters.
|
|
38
|
+
|
|
31
39
|
## 1. Say "what this is" and "the outcome" in the first three lines
|
|
32
40
|
|
|
33
41
|
Readers decide "is this relevant to me" within the first three lines. Don't
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
# dependencies = []
|
|
5
5
|
# ///
|
|
6
6
|
"""tech-writer skill: a lint script that mechanically checks a technical
|
|
7
|
-
document's *structure
|
|
7
|
+
document's *structure* and Markdown rendering safety.
|
|
8
8
|
|
|
9
|
-
Where
|
|
10
|
-
(vocabulary, rhythm), this script
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
Where japanese-prose's GiNZA lint detects sentence-level naturalness
|
|
10
|
+
(vocabulary, rhythm), this script detects structural problems specific to
|
|
11
|
+
technical documents plus Markdown syntax patterns that render inconsistently
|
|
12
|
+
(heading hierarchy, code examples, leftover placeholders, suspicious links,
|
|
13
|
+
and bold delimiters touching prose). The two scripts intentionally don't
|
|
13
14
|
overlap in scope.
|
|
14
15
|
|
|
15
16
|
Findings are flags, not mandates: exit code is always 0 regardless of the
|
|
@@ -73,6 +74,7 @@ FENCE_RE = re.compile(r"^(`{3,}|~{3,})(.*)$")
|
|
|
73
74
|
PLACEHOLDER_RE = re.compile(r"\b(TODO|FIXME|TBD|XXX)\b", re.IGNORECASE)
|
|
74
75
|
MD_LINK_RE = re.compile(r"\[([^\]]*)\]\(([^)]+)\)")
|
|
75
76
|
INLINE_CODE_RE = re.compile(r"`[^`\n]+`")
|
|
77
|
+
BOLD_RE = re.compile(r"(?<![\\*])\*\*(?!\s)(.+?)(?<!\s)\*\*(?!\*)")
|
|
76
78
|
NON_PARAGRAPH_RE = re.compile(
|
|
77
79
|
r"^(?:[-*+]\s|\d+[.)]\s|>|<!--|\|)|^(?:-{3,}|\*{3,}|_{3,})$"
|
|
78
80
|
)
|
|
@@ -243,6 +245,39 @@ def check_links(lines: list, fence_mask: list) -> list:
|
|
|
243
245
|
return findings
|
|
244
246
|
|
|
245
247
|
|
|
248
|
+
def check_bold_spacing(lines: list, fence_mask: list) -> list:
|
|
249
|
+
"""Flag bold delimiters that directly touch surrounding prose.
|
|
250
|
+
|
|
251
|
+
Some Markdown renderers fail to recognize strong emphasis when `**`
|
|
252
|
+
directly adjoins Japanese or other word characters. Punctuation and
|
|
253
|
+
line boundaries do not need padding.
|
|
254
|
+
"""
|
|
255
|
+
findings = []
|
|
256
|
+
for i, line in enumerate(lines):
|
|
257
|
+
if fence_mask[i]:
|
|
258
|
+
continue
|
|
259
|
+
checked = strip_inline_code(line)
|
|
260
|
+
for match in BOLD_RE.finditer(checked):
|
|
261
|
+
before = checked[match.start() - 1] if match.start() else ""
|
|
262
|
+
after = checked[match.end()] if match.end() < len(checked) else ""
|
|
263
|
+
if (before and (before.isalnum() or before == "_")) or (
|
|
264
|
+
after and (after.isalnum() or after == "_")
|
|
265
|
+
):
|
|
266
|
+
findings.append(
|
|
267
|
+
Finding(
|
|
268
|
+
line=i + 1,
|
|
269
|
+
category="bold_spacing",
|
|
270
|
+
message=(
|
|
271
|
+
"Add half-width spaces outside '**...**' when it "
|
|
272
|
+
"touches surrounding prose so strong emphasis "
|
|
273
|
+
"renders consistently."
|
|
274
|
+
),
|
|
275
|
+
snippet=line.strip(),
|
|
276
|
+
)
|
|
277
|
+
)
|
|
278
|
+
return findings
|
|
279
|
+
|
|
280
|
+
|
|
246
281
|
def check_intro_paragraph(lines: list, fence_mask: list) -> list:
|
|
247
282
|
"""Check that the document opens with a non-empty H1 title immediately
|
|
248
283
|
followed by a genuine body paragraph.
|
|
@@ -315,6 +350,7 @@ def run_lint(path: Path, atomic: bool = False) -> LintResult:
|
|
|
315
350
|
result.findings.extend(check_heading_hierarchy(lines, fence_mask))
|
|
316
351
|
result.findings.extend(check_placeholders(lines, fence_mask))
|
|
317
352
|
result.findings.extend(check_links(lines, fence_mask))
|
|
353
|
+
result.findings.extend(check_bold_spacing(lines, fence_mask))
|
|
318
354
|
if not atomic:
|
|
319
355
|
# check_intro_paragraph assumes a living, multi-section document
|
|
320
356
|
# (H1 title + opening paragraph); atomic artifacts like a PR
|