kotonoha 0.1.5-dev.1 → 0.1.6-dev.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 +12 -1
- package/README.md +2 -2
- package/package.json +1 -1
- package/skills/tech-writer/SKILL.md +11 -5
- 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 +2 -0
- package/skills/tech-writer/references/style-constitution.md +13 -5
- package/skills/tech-writer/scripts/lint.py +40 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Generate Markdown with half-width spaces around `**strong emphasis**`
|
|
13
|
+
whenever the delimiters would otherwise touch surrounding prose, and flag
|
|
14
|
+
violations in the structural lint.
|
|
15
|
+
- Add a Qiita article template whose highest-level body sections use `#` and
|
|
16
|
+
subsections use `##`.
|
|
17
|
+
|
|
18
|
+
## [0.1.5] - 2026-09-29
|
|
19
|
+
|
|
10
20
|
### Added
|
|
11
21
|
|
|
12
22
|
- Add a guarded Japanese prose-optimization handoff to `natural-japanese` or
|
|
@@ -80,4 +90,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
80
90
|
[0.1.1]: https://github.com/nahisaho/kotonoha/compare/v0.1.0...v0.1.1
|
|
81
91
|
[0.1.0]: https://github.com/nahisaho/kotonoha/releases/tag/v0.1.0
|
|
82
92
|
[0.1.4]: https://github.com/nahisaho/kotonoha/compare/v0.1.2...v0.1.4
|
|
83
|
-
[
|
|
93
|
+
[0.1.5]: https://github.com/nahisaho/kotonoha/compare/v0.1.4...v0.1.5
|
|
94
|
+
[Unreleased]: https://github.com/nahisaho/kotonoha/compare/v0.1.5...HEAD
|
package/README.md
CHANGED
|
@@ -72,8 +72,8 @@ Japanese prose pass, then rechecks the structure before rubber-duck review.
|
|
|
72
72
|
Reusable templates are included for README, design decisions, user manuals,
|
|
73
73
|
PR descriptions, requirements definitions, system designs, test plans,
|
|
74
74
|
operations runbooks, migration plans, security designs, technical proposals,
|
|
75
|
-
RFI, and
|
|
76
|
-
guidance and a review checklist under
|
|
75
|
+
RFI, RFP, and Qiita articles. Every supported doctype includes dedicated
|
|
76
|
+
structure guidance and a review checklist under
|
|
77
77
|
`skills/tech-writer/references/doctypes/`.
|
|
78
78
|
|
|
79
79
|
## What presentation-planner does
|
package/package.json
CHANGED
|
@@ -220,8 +220,14 @@ omissions explicitly; disclose known limitations and unsupported cases
|
|
|
220
220
|
instead of hiding them; and keep a last-updated date or target version
|
|
221
221
|
where staleness is a real risk. For Zenn/Qiita, rule 1's "first three
|
|
222
222
|
lines" maps to the frontmatter `title` plus the lead paragraph right
|
|
223
|
-
after it
|
|
224
|
-
|
|
223
|
+
after it. Zenn body sections start at `##`; Qiita body sections start at
|
|
224
|
+
`#` and use `##` for subsections.
|
|
225
|
+
|
|
226
|
+
For every Markdown doctype, surround `**strong emphasis**` with half-width
|
|
227
|
+
spaces when the delimiters would otherwise touch prose. For example, write
|
|
228
|
+
`これは **強調** になる`, not `これは**強調**にならない`. Spaces are not
|
|
229
|
+
required at line boundaries or next to punctuation, and must not be placed
|
|
230
|
+
inside the `**` delimiters.
|
|
225
231
|
|
|
226
232
|
For atomic artifacts (commit messages, PR descriptions, issue reports,
|
|
227
233
|
code comments/docstrings, a single entry appended to an existing
|
|
@@ -259,9 +265,9 @@ step 2 as the primary check for those.
|
|
|
259
265
|
(add `--atomic` for a commit message, PR description, issue report,
|
|
260
266
|
code comment/docstring, or a single release-notes entry) to
|
|
261
267
|
mechanically catch heading-level skips, code blocks missing a
|
|
262
|
-
language tag, leftover placeholders,
|
|
263
|
-
are flags, not mandates —
|
|
264
|
-
briefly.
|
|
268
|
+
language tag, leftover placeholders, suspicious links, and strong-emphasis
|
|
269
|
+
delimiters that directly touch prose. Findings are flags, not mandates —
|
|
270
|
+
deliberate exceptions can stay; note the reason briefly.
|
|
265
271
|
4. **Reader-goal recheck**: confirm the "what the reader can do after
|
|
266
272
|
reading" outcome from §1 is actually achievable from this document alone.
|
|
267
273
|
|
|
@@ -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
|
|
@@ -38,6 +38,8 @@ outcome. Require it to preserve:
|
|
|
38
38
|
- Normative force such as Must / Should / May, approval states, and
|
|
39
39
|
acceptance criteria
|
|
40
40
|
- Tables, code blocks, commands, schemas, frontmatter keys, and placeholders
|
|
41
|
+
- Markdown emphasis spacing: keep half-width spaces between surrounding prose
|
|
42
|
+
and `**strong emphasis**` delimiters
|
|
41
43
|
- Explicit assumptions, limitations, residual risks, and unresolved items
|
|
42
44
|
|
|
43
45
|
Heading wording may improve only when its meaning and hierarchy stay intact.
|
|
@@ -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
9
|
Where natural-japanese's lint.py detects sentence-level naturalness
|
|
10
|
-
(vocabulary, rhythm), this script
|
|
11
|
-
|
|
12
|
-
|
|
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
|