@unlaxer/dde-toolkit 0.1.5 → 0.1.7

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/flows/quick.yaml CHANGED
@@ -16,6 +16,10 @@ workflow:
16
16
  - id: scan_glossary
17
17
  display_name: "既存 glossary をスキャン"
18
18
  note: "docs/glossary/ の既存記事一覧を収集。相互リンク候補 + 抽出除外リストに使う"
19
+ - id: detect_template
20
+ display_name: "記事テンプレートを確定"
21
+ mode: confirm
22
+ note: ".dde-template.md があれば再利用。なければ既存記事の H2 を多数決で抽出 → ユーザー確認 → 保存"
19
23
  - id: select_docs
20
24
  display_name: "対象ドキュメント群を選択"
21
25
  mode: confirm
package/lib/markdown.js CHANGED
@@ -85,6 +85,7 @@ export function processMarkdown(content, dictionary, lang = 'en', sourceFile = n
85
85
  * テキスト内で用語の置換箇所を検出する
86
86
  * - 段落ごとに 1 用語 1 回まで
87
87
  * - 最長一致(辞書はすでに降順ソート済み)
88
+ * - ASCII 用語は単語境界チェックを適用(複合語内マッチを防ぐ)
88
89
  */
89
90
  function findReplacements(text, dictionary, alreadyMatched, lang) {
90
91
  // 使用済み範囲を追跡(重複マッチ防止)
@@ -95,7 +96,20 @@ function findReplacements(text, dictionary, alreadyMatched, lang) {
95
96
  if (entry.lang !== lang && entry.lang !== 'en') continue;
96
97
  if (alreadyMatched.has(entry.term)) continue;
97
98
 
98
- const idx = text.indexOf(entry.term);
99
+ // 単語境界チェックが必要な ASCII 用語は、条件を満たす最初の出現を探す
100
+ let idx = -1;
101
+ const needsBoundary = isAsciiTerm(entry.term);
102
+ let searchFrom = 0;
103
+ while (true) {
104
+ const found = text.indexOf(entry.term, searchFrom);
105
+ if (found === -1) break;
106
+ if (needsBoundary && !hasWordBoundary(text, found, found + entry.term.length)) {
107
+ searchFrom = found + 1;
108
+ continue;
109
+ }
110
+ idx = found;
111
+ break;
112
+ }
99
113
  if (idx === -1) continue;
100
114
 
101
115
  // 重複範囲チェック
@@ -113,6 +127,23 @@ function findReplacements(text, dictionary, alreadyMatched, lang) {
113
127
  return replacements;
114
128
  }
115
129
 
130
+ /**
131
+ * 用語が ASCII 文字のみで構成されているか(日本語等は除外)
132
+ */
133
+ function isAsciiTerm(term) {
134
+ return /^[\x00-\x7F]+$/.test(term);
135
+ }
136
+
137
+ /**
138
+ * text[start..end] の前後が単語文字(\w)でないか確認する
139
+ */
140
+ function hasWordBoundary(text, start, end) {
141
+ const wordChar = /\w/;
142
+ if (start > 0 && wordChar.test(text[start - 1])) return false;
143
+ if (end < text.length && wordChar.test(text[end])) return false;
144
+ return true;
145
+ }
146
+
116
147
  /**
117
148
  * テキストと置換リストから AST ノード配列を生成
118
149
  * @param {string} sourceFile - リンクを埋め込むファイルのパス(相対パス計算用)
@@ -185,7 +216,21 @@ export function findUnlinked(content, dictionary, lang = 'en') {
185
216
 
186
217
  for (const entry of dictionary) {
187
218
  if (entry.lang !== lang && entry.lang !== 'en') continue;
188
- if (node.value.includes(entry.term)) {
219
+ const text = node.value;
220
+ const needsBoundary = isAsciiTerm(entry.term);
221
+ let found = false;
222
+ let searchFrom = 0;
223
+ while (true) {
224
+ const idx = text.indexOf(entry.term, searchFrom);
225
+ if (idx === -1) break;
226
+ if (needsBoundary && !hasWordBoundary(text, idx, idx + entry.term.length)) {
227
+ searchFrom = idx + 1;
228
+ continue;
229
+ }
230
+ found = true;
231
+ break;
232
+ }
233
+ if (found) {
189
234
  const key = entry.term;
190
235
  if (!unlinked.has(key)) {
191
236
  unlinked.set(key, { term: entry.term, file: entry.file, count: 0 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unlaxer/dde-toolkit",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Document Deficit Extraction — find what's not understood in your docs",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -67,10 +67,11 @@ DDE toolkit v0.1.3 — Document Deficit Extraction
67
67
  新規セッションの場合は skip。
68
68
 
69
69
  **0-C: 既存 glossary スキャン(MUST)**
70
- `docs/glossary/` の `.md` ファイル一覧を収集する。これを 2 つの目的に使う:
70
+ `docs/glossary/` の `.md` ファイル一覧を収集する。これを 3 つの目的に使う:
71
71
 
72
72
  1. **抽出除外リスト** — 既存記事がある用語は抽出対象から除外(Step 6)
73
73
  2. **相互リンク候補リスト** — 「さらに学ぶために」で使えるリンク一覧(Step 7)
74
+ 3. **テンプレート抽出** — 既存記事の H2 構造を分析してフォーマットを確定(後述)
74
75
 
75
76
  ```
76
77
  既存 glossary: 12 記事
@@ -81,6 +82,72 @@ DDE toolkit v0.1.3 — Document Deficit Extraction
81
82
 
82
83
  ファイルが存在しない場合は空リストで OK(新規プロジェクト)。
83
84
 
85
+ **0-D: 記事テンプレートの確定**
86
+
87
+ まず `docs/glossary/.dde-template.md` が存在するか確認する。
88
+
89
+ - **存在する場合**: そのテンプレートを使用(再分析不要。ユーザーに「既存テンプレートを使用します」と通知)
90
+ - **存在しない場合**: 以下の手順でテンプレートを抽出する
91
+
92
+ **テンプレート自動抽出手順:**
93
+ 1. 既存 `.md` ファイルから **EN 用と JA 用それぞれ** 3〜5 件をサンプリング(辞書順)
94
+ 2. 各ファイルの H2 見出し(`## ...`)を列挙
95
+ 3. **多数決**で共通パターンを確定(過半数のファイルに存在する見出しを採用)
96
+ 4. ユーザーに確認・修正を求める:
97
+
98
+ ```
99
+ 既存 glossary のセクション構造を検出しました:
100
+
101
+ EN 記事テンプレート:
102
+ ## What is it?
103
+ ## Why does it matter?
104
+ ## How does it work?
105
+ ## How does [project] use it?
106
+ ## Common mistakes / attacks
107
+ ## Further reading
108
+
109
+ JA 記事テンプレート:
110
+ ## これは何?
111
+ ## なぜ重要なのか?
112
+ ## どう動くのか?
113
+ ## [project] ではどう使われている?
114
+ ## よくある間違いと攻撃
115
+ ## さらに学ぶ
116
+
117
+ このテンプレートで記事を生成します。修正しますか?
118
+ 1. このまま使う(推奨)
119
+ 2. 修正する
120
+ 3. デフォルト(DDE educational format)を使う
121
+ ```
122
+
123
+ 5. 確定後、`docs/glossary/.dde-template.md` に保存する(次回セッションで再利用)
124
+
125
+ **`.dde-template.md` のフォーマット:**
126
+ ```markdown
127
+ <!-- DDE auto-detected template — edit freely -->
128
+ <!-- generated: 2024-01-15 from 5 sample articles -->
129
+
130
+ ## en
131
+
132
+ ## What is it?
133
+ ## Why does it matter?
134
+ ## How does it work?
135
+ ## How does [project] use it?
136
+ ## Common mistakes / attacks
137
+ ## Further reading
138
+
139
+ ---
140
+
141
+ ## ja
142
+
143
+ ## これは何?
144
+ ## なぜ重要なのか?
145
+ ## どう動くのか?
146
+ ## [project] ではどう使われている?
147
+ ## よくある間違いと攻撃
148
+ ## さらに学ぶ
149
+ ```
150
+
84
151
  **CRITICAL RULE — 相互リンク**:
85
152
  「さらに学ぶために」に書くリンクは、**記事生成時点で `docs/glossary/` に実在するファイルのみ**。
86
153
  存在しないファイルへのリンクは絶対に書かない。同一バッチで新規生成する記事同士は相互リンク可。
@@ -142,15 +209,14 @@ DDE toolkit v0.1.3 — Document Deficit Extraction
142
209
 
143
210
  **文字数ではなく「読んだ人にどうなってほしいか」で指定する。**
144
211
 
145
- Step 0-C で既存 glossary 記事が見つかった場合は、まずそのスタイルを分析して選択肢に追加する。
212
+ Step 0-D で確定したテンプレートを使用する。既存 glossary 記事が見つかった場合は「既存に合わせる」が推奨。
146
213
 
147
214
  **既存記事がある場合の表示例:**
148
215
  ```
149
- 既存 glossary のスタイルを分析しました:
150
- → educational narrative(例え話・ASCIIダイアグラム・相互リンクあり)
216
+ テンプレート: docs/glossary/.dde-template.md(5 件から抽出済み)
151
217
 
152
218
  記事のインテントを選んでください:
153
- 1. 既存に合わせる(推奨)— 既存記事のスタイル・分量・構成を踏襲
219
+ 1. 既存に合わせる(推奨)— Step 0-D で確定したテンプレート構成を使用
154
220
  2. educational — 背景・動機・仕組みごと理解できる記事
155
221
  例え話・図・なぜ?を含む。分量は目的に従って自然に決まる
156
222
  3. reference — 定義と使い方を簡潔に。調べたい人向け
package/version.txt CHANGED
@@ -1 +1 @@
1
- 0.1.5
1
+ 0.1.7