diff --git a/ADOPT.md b/ADOPT.md index cb173fa..d250f7f 100644 --- a/ADOPT.md +++ b/ADOPT.md @@ -49,7 +49,7 @@ Every migrated file gets a frontmatter `description` — one plain-language line 1. Copy `code/tools/gen-book-index.mjs` and wire the script (see the `scripts` block in BoCode's `code/package.json`). If the project has no JavaScript tooling at all, a shell wrapper calling `node` is fine — the script is zero-dependency. 2. Optional, recommended: copy `scripts/check-commit-message.mjs` and `.githooks/`, then run `git config core.hooksPath .githooks`. 3. Optional: copy `.github/workflows/git-policy.yml`. **Check the branch names** — if the project's integration branch isn't `dev`, either ask the user to adopt the branch model or adapt the workflow's branch filters. Don't silently rewrite their branch model. -4. Run the index generator: every book file must carry a `description`; zero warnings is the only passing state. +4. Run the index generator: every book file must carry a `description` and every internal link must resolve; zero warnings is the only passing state. ## Step 5 — Install the skills diff --git a/README.ja.md b/README.ja.md index a656720..78ec622 100644 --- a/README.ja.md +++ b/README.ja.md @@ -135,8 +135,9 @@ diff を読まないと分からない summary は summary ではありません - すべての book ドキュメントは frontmatter の `description` で始まる——体裁ではなく内容を語る、人の言葉で一行 - `code/tools/gen-book-index.mjs`(依存ゼロ、プレーン Node)が `book/README.md`——全ドキュメントのマスターインデックス——を再生成する - description の欠けたドキュメントはビルドを**失敗させる**(exit 1)。ゼロ警告だけが合格です +- 内部リンクは解決必須——切れたリンクもビルドを失敗させます。book は vault を兼ねるため、切れたリンクはビルドバグです -`book/README.md` は入口も兼ねます:エージェントはまずここで地図を手に入れる。フォルダはそのまま [Obsidian](https://obsidian.md) の vault として開けます。 +`book/README.md` は入口も兼ねます:エージェントはまずここで地図を手に入れる。フォルダはそのまま [Obsidian](https://obsidian.md) の vault として開けます——インデックス、タグ、バックリンク、グラフがすべて使えます(FAQ 参照)。 ### bowrite(薄写)——人の言葉で書く @@ -198,6 +199,9 @@ cp -r skills/bocode skills/boscope skills/book-writeback skills/bowrite ]+)>\)|\]\(([^)\s]+)\))/g; + let m; + while ((m = re.exec(text))) { + const target = (m[1] || m[2]).split("#")[0]; + if (target && !/^[a-z]+:/.test(target)) out.push(target); // skip http:, mailto:, obsidian:… + } + return out; +} + function link(rel) { return /[\s()]/.test(rel) ? `<${rel}>` : rel; } @@ -87,13 +99,19 @@ const files = walkMd(BOOK_ROOT) .sort(); const warnings = []; +const brokenLinks = []; const byDir = new Map(); for (const abs of files) { const rel = relative(BOOK_ROOT, abs); const dir = dirname(rel) === "." ? "" : dirname(rel); - const desc = parseDescription(abs); + const text = readFileSync(abs, "utf8"); + const desc = parseDescription(text); if (!desc) warnings.push(rel); + for (const target of collectInternalLinks(text)) { + const resolved = resolve(dirname(abs), target); + if (!existsSync(resolved)) brokenLinks.push(`${rel} → ${target}`); + } if (!byDir.has(dir)) byDir.set(dir, []); byDir.get(dir).push({ name: rel.split("/").pop(), rel, desc: desc ?? "(missing description)" }); } @@ -132,10 +150,16 @@ out += `\n`; writeFileSync(OUTPUT, out); -if (warnings.length > 0) { - console.error(`Files missing frontmatter description (${warnings.length}):`); - for (const w of warnings) console.error(` - ${w}`); - console.error(`Index written to ${OUTPUT} (${files.length} documents), but the files above lack a summary. Fix them and rerun.`); +if (warnings.length > 0 || brokenLinks.length > 0) { + if (warnings.length > 0) { + console.error(`Files missing frontmatter description (${warnings.length}):`); + for (const w of warnings) console.error(` - ${w}`); + } + if (brokenLinks.length > 0) { + console.error(`Broken internal links (${brokenLinks.length}):`); + for (const l of brokenLinks) console.error(` - ${l}`); + } + console.error(`Index written to ${OUTPUT} (${files.length} documents), but the issues above fail the build. Fix them and rerun.`); process.exit(1); } -console.log(`Index written to ${OUTPUT} — ${files.length} documents, zero warnings.`); +console.log(`Index written to ${OUTPUT} — ${files.length} documents, zero warnings, all links resolve.`);