Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ADOPT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 5 additions & 1 deletion README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -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(薄写)——人の言葉で書く

Expand Down Expand Up @@ -198,6 +199,9 @@ cp -r skills/bocode skills/boscope skills/book-writeback skills/bowrite <your-sk
**どの AI ツールで動きますか?**
`AGENTS.md` を指示ファイルとして読み、`SKILL.md` 形式をサポートするものなら何でも(ZCode、Claude Code、互換エージェント)。スキルがなくてもワークフローは劣化なしで機能します:guidelines が同じルールを文章で運びます。

**book は Obsidian などの PKM ツールで読めますか?**
はい、設定なしで。book は純粋な Markdown です——相対リンク、YAML frontmatter(description、tags)、独自形式は一切なし。`book/` を Obsidian の vault として開けば、インデックス、タグ、バックリンク、グラフがすべて使えます。ビルドはリンクも検証し、切れたリンクはビルドを失敗させるため、開くものはいつも辿れる網です。Obsidian 固定でもありません:Logseq、Foam、VS Code、Markdown を読めるものなら何でも。

**インデックスを再生成しないとどうなりますか?**
実行時には何も壊れません——しかしインデックスが古くなれば、システム全体が依存する発見可能性が崩れていきます。だからこそチェックは、description の欠落を派手に失敗させます。

Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,9 @@ Documentation you can't find doesn't exist. BoCode makes discoverability a build
- Every book document starts with a frontmatter `description` — one plain-language line about the *content*, not the genre.
- `code/tools/gen-book-index.mjs` (zero dependencies, plain Node) regenerates `book/README.md` — the master index of every document, grouped by directory.
- A document missing its description makes the build **fail** (exit 1). Zero warnings is the only passing state.
- Internal links must resolve — a broken link fails the build. The book doubles as an Obsidian vault; a broken link is a build bug.

`book/README.md` doubles as the entry point: agents read it first to get the map; the folder also opens directly as an [Obsidian](https://obsidian.md) vault.
`book/README.md` doubles as the entry point: agents read it first to get the map; the folder also opens directly as an [Obsidian](https://obsidian.md) vault — index, tags, backlinks, and graph all work (see FAQ).

### bowrite(薄写)— speak human

Expand Down Expand Up @@ -200,6 +201,9 @@ No. The book is Markdown; the two scripts are plain Node with zero dependencies.
**Which AI tools does it work with?**
Anything that reads `AGENTS.md` as its instruction file and supports the `SKILL.md` format (ZCode, Claude Code, and compatible agents). The workflow degrades gracefully without skills: the guidelines carry the same rules in prose.

**Does the book work in Obsidian or other PKM tools?**
Yes, with zero setup. The book is plain Markdown — relative links, YAML frontmatter (`description`, `tags`), no proprietary formats. Open `book/` as an Obsidian vault and the index, tag pane, backlinks, and graph all work. The build also validates links — a broken link fails the build — so what you open is always a navigable web. Not Obsidian-locked either: Logseq, Foam, VS Code, anything that reads Markdown works.

**What if I don't regenerate the index?**
Nothing breaks at runtime — but a stale index erodes the discoverability the whole system rests on, which is why the check makes missing descriptions fail loudly.

Expand Down
6 changes: 5 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,9 @@ book 的文档按读者分流,而不是按文档类型(`book/notes/`):
- 每篇 book 文档开头有 frontmatter `description`——一行说人话的内容摘要,写内容,不写体裁
- `code/tools/gen-book-index.mjs`(零依赖、纯 Node)重新生成 `book/README.md`——全库文档总索引
- 缺 description 的文档让构建直接失败(退出码 1)。零警告是唯一通过状态
- 内部链接必须走得通:断链同样让构建失败——book 是个 vault,断链就是构建 bug

`book/README.md` 也是入口:agent 先读它拿地图;这个目录还能直接用 [Obsidian](https://obsidian.md) 打开当 vault。
`book/README.md` 也是入口:agent 先读它拿地图;这个目录还能直接用 [Obsidian](https://obsidian.md) 等知识管理工具打开当 vault——索引、标签、反链、关系图全部可用(详见 FAQ)

### 薄写(bowrite):说人话

Expand Down Expand Up @@ -198,6 +199,9 @@ cp -r skills/bocode skills/boscope skills/book-writeback skills/bowrite <your-sk
**支持哪些 AI 工具?**
只要读 `AGENTS.md`、认 `SKILL.md` 格式就行(ZCode、Claude Code 和兼容工具)。没装 skill 也能跑:guidelines 用文字载着同样的规则。

**book 能用 Obsidian 这类双链笔记工具读吗?**
能,零设置。book 全是普通 Markdown:相对链接、YAML frontmatter(description、tags),没有任何专有格式。把 `book/` 当 vault 打开,索引、标签面板、反向链接、关系图全部可用。构建还会校验链接——断链直接构建失败,你打开的永远是一张走得通的网。也不锁 Obsidian:Logseq、Foam、VS Code,任何认 Markdown 的工具都行。

**不刷索引会怎么样?**
运行不会出问题——但索引一过期,文档就慢慢找不到了,而这套体系靠的正是"找得到"。所以检查让缺 description 的文档直接报错,而不是悄悄放过去。

Expand Down
2 changes: 1 addition & 1 deletion book/guidelines/workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Phases 5 and 6 share one discipline: **record, never fix.** A problem found duri
4. **Plans drive code** — `book/plans/` is the input to implementation. The agent reads the plan, builds in `code/`, writes back to `book/`.
5. **Issues feed code** — `notes/issue/` records problems that become the direction of later iterations.
6. **The book compounds** — every feature should leave the book more complete than it found it. The book is both the agent's context and the human's reference manual.
7. **Refresh the index** — after any book change, run `cd code && npm run book:index` before wrapping up. New files must carry a frontmatter `description`; missing ones fail the build.
7. **Refresh the index** — after any book change, run `cd code && npm run book:index` before wrapping up. New files must carry a frontmatter `description`, and internal links must resolve — either failing fails the build.

## Document routing

Expand Down
52 changes: 38 additions & 14 deletions code/tools/gen-book-index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
* Usage: cd code && node tools/gen-book-index.mjs
* (or: npm run book:index)
*
* Convention: every document starts with frontmatter whose `description`
* is a one-line, plain-language summary of the content. Files missing it
* are listed on stderr and the script exits 1 — zero warnings is the
* only passing state. Zero dependencies; runs on plain Node (and bun).
* Conventions:
* 1. Every document starts with frontmatter whose `description` is a
* one-line, plain-language summary. Missing → warning, exit 1.
* 2. Internal links must resolve. Broken relative links → warning, exit 1 —
* the book doubles as an Obsidian vault, so a broken link is a build bug.
* Zero dependencies; runs on plain Node (and bun).
*/
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
import { join, dirname, relative } from "node:path";
import { readdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
import { join, dirname, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const TOOLS_DIR = dirname(fileURLToPath(import.meta.url));
Expand Down Expand Up @@ -66,8 +68,7 @@ function walkMd(dir) {
return out;
}

function parseDescription(abs) {
const text = readFileSync(abs, "utf8");
function parseDescription(text) {
if (!text.startsWith("---")) return null;
const lines = text.split("\n");
for (let i = 1; i < lines.length; i++) {
Expand All @@ -78,6 +79,17 @@ function parseDescription(abs) {
return null;
}

function collectInternalLinks(text) {
const out = [];
const re = /(?:\]\(<([^>]+)>\)|\]\(([^)\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;
}
Expand All @@ -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)" });
}
Expand Down Expand Up @@ -132,10 +150,16 @@ out += `<!-- ${files.length} documents -->\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.`);
Loading