kotonoha 0.1.5 → 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 CHANGED
@@ -7,6 +7,14 @@ 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
+
10
18
  ## [0.1.5] - 2026-09-29
11
19
 
12
20
  ### Added
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 RFP documents. Every supported doctype includes dedicated structure
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kotonoha",
3
- "version": "0.1.5",
3
+ "version": "0.1.6-dev.0",
4
4
  "description": "GitHub Copilot CLI skills for technical writing and PPTX presentation planning.",
5
5
  "license": "MIT",
6
6
  "author": "nahisaho",
@@ -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 — see their doctype reference files for why there's no in-body
224
- '#' title.
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, and suspicious links. Findings
263
- are flags, not mandates — deliberate exceptions can stay; note the reason
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
- Like Zenn, the title lives in YAML frontmatter, not an in-body heading.
4
- Qiita's readership skews toward searching for a specific error message or
5
- task, so apply rule 3 (order matching how the reader looks for
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. No in-body '#' heading for the article title — sections
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 `##` heading**: order by how a reader
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/Qiita specifically, rule 1's "title" lives in YAML frontmatter rather
14
- than an in-body '#' heading — see their doctype reference files for the
15
- platform-specific frontmatter fields — but every other rule (heading
16
- hierarchy starting at '##', code examples, disclosed limitations, etc.)
17
- still applies verbatim.
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 only detects structural problems specific
11
- to technical documents (heading hierarchy, code examples, leftover
12
- placeholders, suspicious links). The two scripts intentionally don't
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