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.
@@ -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 orchestrates a registered prose-polishing skill such as
31
- https://github.com/coji/natural-japanese for sentence-level naturalness,
32
- word choice, rhythm, and "AI smell" removal without reimplementing that
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). A prose-polishing skill
61
- such as [natural-japanese](https://github.com/coji/natural-japanese) owns the
62
- second layer (sentence-level naturalness, removing "AI smell", rhythm);
63
- tech-writer orchestrates that skill without duplicating its rules or scripts.
64
- When both skills are installed, this skill locks down the structure and
65
- orchestrates the §5 handoff to natural-japanese during `write` mode before
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 — see their doctype reference files for why there's no in-body
224
- '#' title.
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, and suspicious links. Findings
263
- are flags, not mandates — deliberate exceptions can stay; note the reason
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
- `references/japanese-prose-optimization.md` and hand the completed draft to a
273
- registered `natural-japanese` or equivalent Japanese prose-polishing skill.
274
- This pass is mandatory when such a skill is available.
275
-
276
- Treat the optimizer as available when the host's registered skill list
277
- contains `natural-japanese` or a compatible Japanese prose-polishing skill.
278
- Invoke it through the host's skill-loading mechanism; when only installed
279
- skill files are exposed, check
280
- `.github/skills/natural-japanese/SKILL.md`,
281
- `.copilot/skills/natural-japanese/SKILL.md`, and
282
- `$HOME/.copilot/skills/natural-japanese/SKILL.md`, then load the discovered
283
- `SKILL.md` and follow that workflow. Treat the pass as unavailable only when
284
- no compatible registration or readable skill installation exists, or when
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 idea of separating structure from prose comes from
392
- [natural-japanese](https://github.com/coji/natural-japanese) (MIT License)
393
- and its design principle "machines detect, humans (or agents) judge" and
394
- "prevent at generation time rather than fix afterward". This skill extends
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
- 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
@@ -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 preferred optimizer is the
9
- [natural-japanese prose-polishing skill](https://github.com/coji/natural-japanese).
10
- Kotonoha owns document structure and completeness; `natural-japanese` owns
11
- sentence-level clarity, rhythm, word choice, reading load, and detection of
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
- - A registered `natural-japanese` or equivalent Japanese prose-polishing
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. Inspect the host's registered skill list for `natural-japanese` or a
49
- compatible Japanese prose-polishing skill. Invoke it through the host's
50
- skill-loading mechanism. If the host exposes only skill files, check
51
- `.github/skills/natural-japanese/SKILL.md`,
52
- `.copilot/skills/natural-japanese/SKILL.md`, and
53
- `$HOME/.copilot/skills/natural-japanese/SKILL.md`; expand `$HOME`, load the
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 `<natural-japanese-dir>` be the directory containing the loaded
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 <natural-japanese-dir>/scripts/lint.py <target-file> --genre tech
64
- uv run <natural-japanese-dir>/scripts/lint.py <target-file> --reading-load
65
- uv run <natural-japanese-dir>/scripts/outline.py <target-file>
66
- uv run <natural-japanese-dir>/scripts/terms.py <target-file>
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`: no compatible optimizer was
97
- registered or loaded, so no prose review or rewrite ran; include the
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 a prose-polishing skill such as natural-japanese).
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/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
- 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
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