From 5a20c8cca2d61ac002ee18af3f619be47ffc37f8 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sun, 18 Oct 2026 03:29:22 +0000 Subject: [PATCH 01/10] =?UTF-8?q?Update:=20=E3=82=B9=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=83=89=E3=81=AE=E6=9E=A0=E7=B5=84=E3=81=BF=E3=81=AE=E7=AB=A0?= =?UTF-8?q?=E6=89=89=E3=82=92=E3=83=8E=E3=83=BC=E3=83=88=E3=82=92=E6=8C=81?= =?UTF-8?q?=E3=81=9F=E3=81=9A=E5=8F=B0=E6=9C=AC=E3=81=AE=E7=95=AA=E5=8F=B7?= =?UTF-8?q?=E3=82=92=E9=80=B2=E3=82=81=E3=81=AA=E3=81=84=E3=83=9A=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=81=AB=E3=81=97=E3=80=81=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E6=95=B0=E3=81=AE=E7=85=A7=E5=90=88=E3=81=8B=E3=82=89=E5=A4=96?= =?UTF-8?q?=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 汎用テンプレートの section は台本のページを消費しない。見本の台本から章扉の区切りを外し、 テストに章扉のノート・ページ番号・照合の振る舞いを足した。 Co-Authored-By: Claude Opus 5.5 --- .../release/assets/slides/build-deck.js | 5 ++- .../release/assets/slides/lib/deck-frame.js | 29 ++++++++---- .../skills/release/assets/slides/script.md | 7 +-- .../release/tests/test_slide_template.py | 45 ++++++++++++++++--- 4 files changed, 64 insertions(+), 22 deletions(-) diff --git a/plugins/ndf/skills/release/assets/slides/build-deck.js b/plugins/ndf/skills/release/assets/slides/build-deck.js index d11fa1eb3..c08879a61 100644 --- a/plugins/ndf/skills/release/assets/slides/build-deck.js +++ b/plugins/ndf/skills/release/assets/slides/build-deck.js @@ -2,6 +2,7 @@ // // node build-deck.js <出力>.pptx [] // +// 章扉は台本に区切りを置かない(台本は表紙と本編の 2 ページ)。 // 写した後はページの中身だけを書き換える。書体と色は theme.json で差し替え、ここに値を書かない。 // 色は theme.colors の項目名(accent・ink・panel1 など)で指す。 const path = require("path"); @@ -14,10 +15,10 @@ run(__dirname, { script: SCRIPT, title: "<資料の題>", author: "<作成者>" // 1. 表紙 d.cover("<資料の題>", "<副題>", "<日付> <作成者>"); - // 2. 章扉 + // 章扉(台本の番号を進めず、ノートを持たない) d.section("現状", "数値で見る前の期間との違い"); - // 3. 本編(表と注) + // 2. 本編(表と注) const s = d.page("現状の数値", "件数は増え、時間は短くなった。費用だけが増えた"); d.tbl(s, [ [d.H("指標"), d.H("前の期間"), d.H("今の期間"), d.H("差")], diff --git a/plugins/ndf/skills/release/assets/slides/lib/deck-frame.js b/plugins/ndf/skills/release/assets/slides/lib/deck-frame.js index 1301748a6..629959582 100644 --- a/plugins/ndf/skills/release/assets/slides/lib/deck-frame.js +++ b/plugins/ndf/skills/release/assets/slides/lib/deck-frame.js @@ -5,6 +5,8 @@ // - 書体と色の値は DEFAULT_THEME と theme.json だけが持つ。関数に渡す色は theme.colors の項目名で受ける // - 台本(script.md)を渡すと、ページを作るたびに同じ番号の読み上げをスピーカーノートへ付け、 // 保存の前に台本のページ数と作ったページ数を照合する +// - 章扉(section)は構成を見せるページで、スピーカーノートを持たず、台本の番号を進めない。 +// ページ数の照合からも外す(ページ番号は章扉も数える) // // 資料のディレクトリへ写して使う。写した後に書き換えない(プロジェクトの値は theme.json へ書く)。 @@ -242,7 +244,9 @@ function createDeck(opts) { if (o.title) p.subject = o.title; const F = theme.font; const logo = theme.logo ? "image/png;base64," + fs.readFileSync(theme.logo).toString("base64") : null; - let pageNo = 0; + let pageNo = 0; // 作ったページ(章扉を含む)。ページ番号に使う + let scriptNo = 0; // 台本のページを消費したページ(章扉を除く) + let sections = 0; function color(name) { if (!(name in theme.colors)) throw new DeckError(`色は theme.colors の項目名で指す: ${name}`, EXIT_INPUT); @@ -271,12 +275,18 @@ function createDeck(opts) { return t.map((x) => (x && typeof x === "object" ? { ...x, options: resolve(x.options) } : x)); } - function newSlide() { + // withScript が false のページ(章扉)は台本の番号を進めず、ノートを持たない + function newSlide(withScript) { const s = p.addSlide(); pageNo++; s.background = { color: color("background") }; + if (withScript === false) { + sections++; + return s; + } + scriptNo++; if (script) { - const sp = script[pageNo - 1]; + const sp = script[scriptNo - 1]; if (sp && sp.reading) s.addNotes(sp.reading); } return s; @@ -322,7 +332,7 @@ function createDeck(opts) { } function section(title, sub) { - const s = newSlide(); + const s = newSlide(false); const c = FRAME.section; s.addShape(p.ShapeType.rect, { ...c.band, fill: { color: color("accent") }, line: { type: "none" } }); putLogo(s, FRAME.logo); @@ -366,11 +376,11 @@ function createDeck(opts) { async function save(out) { if (!out || !/\.pptx$/i.test(out)) throw new DeckError("出力のパスは .pptx で終える", EXIT_INPUT); - if (script && script.length !== pageNo) { - throw new DeckError(`ページ数が台本と合わない: 台本 ${script.length} ページ / 作ったページ ${pageNo}`, EXIT_INPUT); + if (script && script.length !== scriptNo) { + throw new DeckError(`ページ数が台本と合わない: 台本 ${script.length} ページ / 作ったページ ${scriptNo}(章扉 ${sections} を除く)`, EXIT_INPUT); } await p.writeFile({ fileName: out }); - return { out, pages: pageNo }; + return { out, pages: pageNo, sections }; } return { @@ -381,6 +391,9 @@ function createDeck(opts) { get pageNo() { return pageNo; }, + get scriptNo() { + return scriptNo; + }, color, text, cover, @@ -410,7 +423,7 @@ function run(dir, opts, build) { const deck = createDeck({ ...opts, theme, baseDir: dir }); await build(deck); const r = await deck.save(out); - console.log(`${r.out}: ${r.pages} ページ`); + console.log(`${r.out}: ${r.pages} ページ(章扉 ${r.sections})`); })().catch((e) => { console.error(e.message); process.exit(e instanceof DeckError ? e.code : 1); diff --git a/plugins/ndf/skills/release/assets/slides/script.md b/plugins/ndf/skills/release/assets/slides/script.md index fdce47a13..824dbc686 100644 --- a/plugins/ndf/skills/release/assets/slides/script.md +++ b/plugins/ndf/skills/release/assets/slides/script.md @@ -5,6 +5,7 @@ ページの区切りは `## <番号>. <見出し>`、読み上げは `>` で始まる行。見出しの末尾に `(<秒>秒)` を付けると配分秒数になる。 ほかの行はページに載せる要素の覚え書きで、スピーカーノートにも字数にも入らない。 +章扉(生成スクリプトの `section`)は台本に区切りを置かない。章扉はノートを持たず、台本の番号を進めない。 ## 1. 表紙(20秒) @@ -12,11 +13,7 @@ > 今日は〇〇について話します。最初に結論、次に根拠、最後にお願いしたいことの順に進めます。 -## 2. 章扉: 現状(10秒) - -> まず現状です。 - -## 3. 現状の数値(60秒) +## 2. 現状の数値(60秒) 表: 指標・前の期間・今の期間・差 diff --git a/plugins/ndf/skills/release/tests/test_slide_template.py b/plugins/ndf/skills/release/tests/test_slide_template.py index c9ee0d1da..279c38083 100644 --- a/plugins/ndf/skills/release/tests/test_slide_template.py +++ b/plugins/ndf/skills/release/tests/test_slide_template.py @@ -196,25 +196,56 @@ def test_raw_color_value_in_build_script_stops(deck: Path, tmp_path: Path) -> No assert "F3F3F3" in cp.stderr -def test_notes_come_from_script(deck: Path, tmp_path: Path) -> None: +def test_notes_come_from_script_and_section_takes_none(deck: Path, tmp_path: Path) -> None: out = tmp_path / "n.pptx" assert _build(deck, out).returncode == 0 readings = script_readings(deck / "script.md") - notes = [s.notes for s in read_pptx(out)["slides"]] - assert notes == [readings[i] for i in range(1, 4)] - assert "\n" in notes[2] # `>` だけの行が段落を分ける + assert len(readings) == 2 # 表紙と本編。章扉は台本に区切りを持たない + cover, section, body = read_pptx(out)["slides"] + assert cover.notes == readings[1] + assert not section.notes # 章扉はノートを持たない + assert body.notes == readings[2] # 章扉の後のページは、台本の次の番号を受ける + assert "\n" in body.notes # `>` だけの行が段落を分ける + + +def test_page_numbers_count_sections(deck: Path, tmp_path: Path) -> None: + out = tmp_path / "p.pptx" + assert _build(deck, out).returncode == 0 + _, section, body = read_pptx(out)["slides"] + assert any(s.text == "2" for s in section.shapes) + assert any(s.text == "3" for s in body.shapes) + + +def _add_section_after_cover(deck: Path) -> None: + src = (deck / "build-deck.js").read_text(encoding="utf-8") + anchor = ' d.section("現状"' + assert anchor in src + (deck / "build-deck.js").write_text(src.replace(anchor, ' d.section("前置き", "");\n' + anchor, 1), encoding="utf-8") + + +def test_sections_are_left_out_of_page_count(deck: Path, tmp_path: Path) -> None: + _add_section_after_cover(deck) + out = tmp_path / "s.pptx" + cp = _build(deck, out) + assert cp.returncode == 0, cp.stderr + slides = read_pptx(out)["slides"] + assert len(slides) == 4 + readings = script_readings(deck / "script.md") + assert [bool(s.notes) for s in slides] == [True, False, False, True] + assert slides[3].notes == readings[2] def test_page_count_must_match_script(deck: Path, tmp_path: Path) -> None: with open(deck / "script.md", "a", encoding="utf-8") as fh: - fh.write("\n## 4. 増やしたページ\n\n> 4 ページ目の読み上げ。\n") + fh.write("\n## 3. 増やしたページ\n\n> 3 ページ目の読み上げ。\n") cp = _build(deck, tmp_path / "x.pptx") assert cp.returncode == 1 - assert "4" in cp.stderr and "3" in cp.stderr + # 作ったのは 3 ページだが、章扉を除くと 2 ページで、台本の 3 ページと合わない + assert "台本 3 ページ" in cp.stderr and "作ったページ 2" in cp.stderr and "章扉 1" in cp.stderr assert not (tmp_path / "x.pptx").exists() -@pytest.mark.parametrize(("swap", "to", "message"), [("## 2.", "## 4.", "欠ける: 2"), ("## 3.", "## 2.", "重なる: 2")]) +@pytest.mark.parametrize(("swap", "to", "message"), [("## 2.", "## 4.", "欠ける: 2"), ("## 1.", "## 2.", "重なる: 2")]) def test_broken_script_numbers_stop_with_2(deck: Path, tmp_path: Path, swap: str, to: str, message: str) -> None: script = deck / "script.md" script.write_text(script.read_text(encoding="utf-8").replace(swap, to), encoding="utf-8") From 59e65081d6015d25b0cf5861215328bde1cafac6 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sun, 18 Oct 2026 03:37:08 +0000 Subject: [PATCH 02/10] =?UTF-8?q?Update:=20=E7=99=BA=E8=A1=A8=E8=B3=87?= =?UTF-8?q?=E6=96=99=E3=81=AE=E5=85=A8=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AE?= =?UTF-8?q?=E6=96=87=E8=A8=80=E3=82=92=E3=82=B9=E3=83=A9=E3=82=A4=E3=83=89?= =?UTF-8?q?=E3=81=AE=E8=A8=80=E8=91=89=E9=81=A3=E3=81=84=E3=81=B8=E7=9B=B4?= =?UTF-8?q?=E3=81=97=E3=80=81=E7=AB=A0=E6=89=89=206=20=E3=81=A4=E3=82=92?= =?UTF-8?q?=E5=85=A5=E3=82=8C=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ページには 1 つのメッセージと短い句だけを置き、内部の語は言い換えるか一言で説明した。 17 ページは導入したプロジェクトの Issue の一覧を整理し続ける趣旨へ直した。枠組み lib/ を 写し直し、README のハッシュと構成メモの表・章立てを合わせた。台本は変えていない。 Co-Authored-By: Claude Opus 5.5 --- .../README.md | 6 +- .../notes.md | 67 ++-- .../slides/build-deck.js | 299 ++++++++++-------- .../slides/lib/deck-frame.js | 29 +- tests/test_presentation_decks.py | 28 +- 5 files changed, 252 insertions(+), 177 deletions(-) diff --git a/docs/presentations/2026-10-17-skill-plugin-development/README.md b/docs/presentations/2026-10-17-skill-plugin-development/README.md index 92de0ad33..9fd1f7a49 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/README.md +++ b/docs/presentations/2026-10-17-skill-plugin-development/README.md @@ -1,12 +1,12 @@ # 独自スキルプラグイン開発の薦め -社外の勉強会・登壇で話す 23 ページのスライド。NDF の機能を例に、自分用のスキルプラグインを作って育てることを薦める。 +社外の勉強会・登壇で話すスライド(台本の 23 ページと章扉 6 つの 29 ページ)。NDF の機能を例に、自分用のスキルプラグインを作って育てることを薦める。 `release` の `form-slide.md` の生成スクリプトの経路で作った。 | ファイル | 中身 | | --- | --- | | [script.md](script.md) | 台本(第 7 稿の写し)。ページごとの見出しと読み上げ。スピーカーノートの正本 | -| [notes.md](notes.md) | 構成メモ。ページごとの見出し・役割・根拠の表と、尺 | +| [notes.md](notes.md) | 構成メモ。ページごとの見出し・役割・根拠の表と、章立て・尺 | | [slides/build-deck.js](slides/build-deck.js) | 生成スクリプト。スライドの正本 | | [slides/theme.json](slides/theme.json) | トーン&マナーのファイル | | `slides/lib/` | NDF の汎用テンプレートの枠組み(`release` の `assets/slides/lib/`)の写し。書き換えない | @@ -28,7 +28,7 @@ Google Slides を成果物にする。pptx は生成スクリプトから作り | 色 | 既定値 | | 著作権表示 | `NDF — github.com/devbasex/ai-plugins` | | ロゴ | なし | -| 写した時点の `lib/` の内容のハッシュ | `86c3acf6f8b74c80bb92ffa3a1dfe17cde6ee17d199e6f76d029744f7d6a9133`(`cat slides/lib/*.js \| sha256sum`) | +| 写した時点の `lib/` の内容のハッシュ | `e1fbac1642aef0fe49a57b00d7a2575eaf363bbfd5c55b324d53aa778f632800`(`cat slides/lib/*.js \| sha256sum`) | ## 生成と確認の手順 diff --git a/docs/presentations/2026-10-17-skill-plugin-development/notes.md b/docs/presentations/2026-10-17-skill-plugin-development/notes.md index 0f80066c7..dff8c97ed 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/notes.md +++ b/docs/presentations/2026-10-17-skill-plugin-development/notes.md @@ -1,6 +1,6 @@ # 構成メモ: 独自スキルプラグイン開発の薦め -台本([script.md](script.md)。第 7 稿の写し)を、社外の勉強会・登壇で話す 23 ページのスライドにした構成。 +台本([script.md](script.md)。第 7 稿の写し)を、社外の勉強会・登壇で話すスライド(台本の 23 ページと章扉 6 つ)にした構成。 各ページの読み上げはスピーカーノートに入り、生成スクリプト([slides/build-deck.js](slides/build-deck.js))が台本から読み込む。 ## 構成 @@ -8,34 +8,53 @@ キーメッセージ「AI に自分のやり方を教えるのは 1 回でよい。スキルプラグインにして、フォークして育てる」から逆算し、 課題(1〜4)→ NDF の機能(5〜18)→ 事例(19〜20)→ 転換と実践(21〜22)→ まとめ(23)の順に並べた。 台本はページに載せる要素を持たないため、各ページの図・表・箇条書きは読み上げから起こした。読み上げの文をそのまま -ページに写さず、ページには構造(表・流れ・数値)だけを置く。事例のプロジェクトは種類で呼び、名前は出さない。 +ページに写さず、縮めても載せない。ページには 1 つのメッセージと、それを支える短い句(体言止め)・表・流れ・数値だけを置く。 +聞き手は NDF を知らないため、内部の語(ゲート・フェーズ・conductor など)は聞き手に通じる語へ言い換えるか、ページの中で +一言で説明する。Skill 名(`/ndf:xxx`)はリードか箱の見出しに置き、何をするものかを必ず添える。事例のプロジェクトは種類で +呼び、名前は出さない。 -| # | 見出し | 役割 | 根拠 | +表の番号は台本の番号で、章扉(「章立て」の節)は数えない。 + +| # | ページの見出し | 役割 | 根拠 | | ---: | --- | --- | --- | | 1 | 表紙 | — | — | -| 2 | AI に同じことを何度も言っていませんか | 繰り返す 3 つの指摘を吹き出しで並べ、「セッションが切れると忘れる」を注意の地で示す | 台本 2 | -| 3 | CLAUDE.md への追記だけでは足りない理由 | 4 つの壁(コンテキスト・強制力・手順・ポータビリティ)を 2 × 2 の区画で示す | 台本 3 | +| 2 | 同じ指摘を、何度もしていませんか | 繰り返す 3 つの指摘を吹き出しで並べ、「セッションが切れると忘れる」を注意の地で示す | 台本 2 | +| 3 | CLAUDE.md への追記では足りない | 4 つの壁(コンテキスト・強制力・手順・ポータビリティ)を 2 × 2 の区画で示す | 台本 3 | | 4 | スキルプラグインとは | 構成要素 4 つと役割の表と、4 つのランタイムの並び | 台本 4 | -| 5 | NDF: 1 人の開発スタイルをプラグインにした | リリース 102 回・Skill 48 個・ランタイム 4 つ・導入 10 リポジトリの数値と、リポジトリの URL | 台本 5(2026-10-17 の実測) | -| 6 | NDF の全体像 | マイルストーン → グループ → スプリント(グループごとに並列)→ リリースの図と、2 つの承認ゲート。直前のリリースの例は注 | 台本 6 | -| 7 | /ndf:development-workflow | 5 つのモードと対象の表。モードごとに通る Skill が違うことを下の区画で示す | 台本 7、`development-workflow` の `references/workflow-modes.md` | -| 8 | standard モードの全体の流れ | 設計・実装・リリースの 3 行に工程を並べ、ゲート 1・2 を注意の地で示す | 台本 8 | -| 9 | 実績のある開発技法を、AI が守れる形で組み込む | DDD・テストファースト・アジャイルの 3 列。用語集 580 語と WSJF を載せる | 台本 9 | -| 10 | /ndf:requirements-design と /ndf:design | 「何を満たせば完了か」と「どう作るか」の 2 列。設計 PR とゲート 1 を下の区画で示す | 台本 10 | -| 11 | /ndf:document-restructuring | 書いた順と読み手の順の対比、内容を足さない 5 つの手順 | 台本 11 | -| 12 | /ndf:cross-review | 繰り返しの流れと、指摘の再現率(1 つの AI 0.21〜0.49、2 つで 0.91)の棒 | 台本 12(手元の実測) | -| 13 | /ndf:cross-refactoring | 提案 → 選ぶ → 時間内に実施 → 取り消しの流れと、逆算と内訳の 2 区画 | 台本 13 | -| 14 | 3 層のエージェント | conductor・supervisor・worker の階段の図と、再開とセッションの切り替えの 2 区画 | 台本 14 | -| 15 | 判断基準: MVV と 2 つの承認ゲート | Mission・Vision・Value の 3 区画(Value の例 2 つ)と、必ず人の承認が要る操作 | 台本 15、`.ndf/` のプロジェクト MVV | -| 16 | /ndf:quality-gates と /ndf:investigation-rules | AI の言葉 2 つと、それぞれを受け止める Skill の対応 | 台本 16 | -| 17 | 自分を直すループ | 3 つの Skill の流れと、棚卸しの数値 2 つ(56%・49 件) | 台本 17(棚卸しの記録と 2026-10-17 の調査) | -| 18 | その他の特徴的な機能 | estimate・release・hook の表 | 台本 18 | -| 19 | 事例: 他のプロジェクトでも動く | 4 つのプロジェクトの種類と結果の表。インフラの注記(社内ツールの移行と重なる)を表の中に残す | 台本 19、事例の材料(インフラ・業務システム・機械学習と価格推定) | -| 20 | 事例から分かったこと | 詰まりが次の版で直る例(17 件の Issue と 3 つの前提の違い)と、プロジェクトごとに足したルールの表 | 台本 20、事例の材料 | -| 21 | だから「フォークして自分用に」 | NDF のルールの例と、そのまま使う仕組みの対比、メリット 3 つ | 台本 21 | +| 5 | NDF: 自分のやり方をプラグインに | リリース 102 回・Skill 48 個・ランタイム 4 つ・導入 10 リポジトリの数値と、リポジトリの URL | 台本 5(2026-10-17 の実測) | +| 6 | Issue の束を渡せば、並列に進む | マイルストーン → グループ → スプリント(グループごとに並列)→ リリースの図と、2 回の承認(設計・本番リリース)。直前のリリースの例は注 | 台本 6 | +| 7 | 変更の大きさで、踏む工程を変える | `/ndf:development-workflow` が変更を分ける 5 つのモードの表と、light と standard の工程の違い | 台本 7、`development-workflow` の `references/workflow-modes.md` | +| 8 | 機能追加は、この流れで 1 周 | standard モードの工程を設計・実装・リリースの 3 行に並べ、承認 1・2 を注意の地で示す | 台本 8 | +| 9 | 実績のある開発技法を組み込む | DDD・テストファースト・アジャイルの 3 列。用語集 580 語と WSJF を載せる | 台本 9 | +| 10 | コードの前に「何を」「どう」 | `/ndf:requirements-design`(完了の条件)と `/ndf:design`(作り方)の 2 列。設計 PR と承認 1 を下の区画で示す | 台本 10 | +| 11 | 結論から読める順に並べ替える | `/ndf:document-restructuring` の、書いた順と読み手の順の対比と、内容を足さない 5 つの手順 | 台本 11 | +| 12 | 2 つの AI で、指摘が尽きるまで | `/ndf:cross-review` の繰り返しの流れと、指摘の再現率(1 つの AI 0.21〜0.49、2 つで 0.91)の棒 | 台本 12(手元の実測) | +| 13 | 全員で提案、1 人が時間内に直す | `/ndf:cross-refactoring` の提案 → 選ぶ → 時間内に実施 → 取り消しの流れと、逆算と内訳の 2 区画 | 台本 13 | +| 14 | 3 層のエージェントで止めない | conductor(指揮役)・supervisor(監督役)・worker(作業役)の階段の図と、再開とセッションの切り替えの 2 区画 | 台本 14 | +| 15 | 迷ったら MVV で判断する | Mission・Vision・Value の 3 区画と Value の例 2 つ、必ず人が承認する操作 | 台本 15、`.ndf/` のプロジェクト MVV | +| 16 | 「完了」も「無い」も証拠で | AI の言葉 2 つと、それぞれを受け止める `/ndf:quality-gates`・`/ndf:investigation-rules` の対応 | 台本 16 | +| 17 | やるべきことを整理し続ける | 導入したプロジェクトの Issue の一覧を現状に保つ 3 つの Skill の流れと、何の数かを添えた棚卸しの数値 2 つ(56%・49 件) | 台本 17(棚卸しの記録と 2026-10-17 の調査) | +| 18 | そのほかの特徴的な機能 | estimate・release・hook の、何をするかと要点の表 | 台本 18 | +| 19 | 事例: ほかのプロジェクトでも動く | 4 つのプロジェクトの種類と結果の表。インフラの注記(社内ツールの移行と重なる)を表の中に残す | 台本 19、事例の材料(インフラ・業務システム・機械学習と価格推定) | +| 20 | 事例から分かった 2 つのこと | 詰まりが次の版で直る例(17 件の Issue と 3 つの前提の違い)と、プロジェクトごとに足したルールの表 | 台本 20、事例の材料 | +| 21 | だから、フォークして自分用に | NDF のルールの例と、そのまま使う仕組みの対比、メリット 3 つ | 台本 21 | | 22 | 始め方と育て方 | フォークする・削る・育てるの 3 列と、気をつける点 | 台本 22 | | 23 | まとめ: 明日からの 3 ステップ | 3 つの手順とリポジトリの URL | 台本 23 | +## 章立て + +章扉(生成スクリプトの `d.section`)で構成を見せる。章扉はスピーカーノートを持たず、台本の番号を進めない。 +資料は台本の 23 ページと章扉 6 つの 29 ページになる(ページ番号は章扉も数える)。 + +| 章 | 章扉の一言 | 台本のページ | +| --- | --- | --- | +| 1. なぜスキルプラグインか | CLAUDE.md に書くだけでは届かない | 2〜4 | +| 2. NDF の全体像 | Issue の束を渡すと、リリースまで進む | 5〜8 | +| 3. 組み込んだ開発技法 | 実績のある技法を、AI が守れる手順に | 9〜13 | +| 4. AI に任せ切る仕組み | 止まらない・迷わない・ごまかさない | 14〜18 | +| 5. 事例 | ほかのプロジェクトでも動くか | 19〜20 | +| 6. 始め方 | フォークして、自分用に育てる | 21〜23 | + ## 尺 `node slides/lib/script-time.js script.md --limit 35` の出力(350 字/分)。合計 5,317 字・15 分 12 秒で、 @@ -71,5 +90,5 @@ ## ページに収まらない読み上げの扱い 読み上げはスピーカーノートに全文が入る。ページには読み上げの要点だけを置き、例(6 ページの直前のリリース、 -17 ページの「放っておくと…」)は注へ回した。見出しが 1 行に収まらないページ(6・7・9〜14・16・17・20)は、 -ページの見出しを短くし、台本の見出しの後半をリードの文へ回した。 +17 ページの「放っておくと…」)は注へ回した。ページの見出しは台本の見出しと別に、そのページの結論を短く書いた +(目安 20 字以内)。Skill を紹介するページ(7・10〜13・16・17)は、Skill 名と何をするものかをリードか箱の見出しに置いた。 diff --git a/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js b/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js index 7f2200292..095da747f 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js +++ b/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js @@ -1,10 +1,11 @@ -// 発表資料「独自スキルプラグイン開発の薦め」の生成スクリプト(23 ページ)。 +// 発表資料「独自スキルプラグイン開発の薦め」の生成スクリプト(台本の 23 ページと章扉 6 つの 29 ページ)。 // // node build-deck.js <出力>.pptx [] // // 枠組みは lib/(NDF の release の assets/slides/lib/ の写し。書き換えない)、 // トーン&マナーは theme.json、スピーカーノートは ../script.md(台本)から入る。 -// ページの役割と根拠は ../notes.md にある。 +// ページの役割と根拠、章立ては ../notes.md にある。章扉(d.section)は台本の番号を進めない。 +// ページには要点だけを置く(読み上げはスピーカーノートが持つ)。1 ページ 1 メッセージ・短い句か体言止め。 const path = require("path"); const { run } = require("./lib/deck-frame"); @@ -71,44 +72,50 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の // 1. タイトル d.cover("独自スキルプラグイン開発の薦め", "NDF を例に、自分の開発スタイルを AI に持たせる", "github.com/devbasex/ai-plugins"); + // ==== 章 1 ==== + d.section("1. なぜスキルプラグインか", "CLAUDE.md に書くだけでは届かない"); + // 2. AI に同じことを何度も言っていませんか - s = d.page("AI に同じことを何度も言っていませんか", "指摘の回数は、自分のやり方がまだ AI に伝わっていない量である"); + s = d.page("同じ指摘を、何度もしていませんか", "指摘の回数 = AI にまだ伝わっていない量"); cols(3).forEach((c, i) => { const q = ["「PR は develop 宛てで」", "「テストを先に書いて」", "「無いと言う前に\n検索結果を見せて」"][i]; d.chip(s, c.x, 1.5, c.w, 1.0, q, "panel1", { fontSize: 15 }); }); d.arrow(s, 4.99, 2.62, 0, 0.42); - d.callout(s, 3.16, 0.76, "caution", "セッションが切れるたびに AI は忘れ、指摘は消えていく", "毎日使うほど、同じ指摘を何度もしていることに気づく。"); + d.callout(s, 3.16, 0.76, "caution", "セッションが切れると、AI は忘れる", "毎日使うほど、同じ指摘が積み上がる"); // 3. CLAUDE.md への追記だけでは足りない理由 - s = d.page("CLAUDE.md への追記だけでは足りない理由", "まず CLAUDE.md や AGENTS.md に追記するが、4 つの壁に当たる"); + s = d.page("CLAUDE.md への追記では足りない", "追記はすぐに 4 つの壁に当たる"); [ - ["コンテキスト", "毎回すべてを読み込む。書くほどコストが上がり、重要なルールが埋もれる"], - ["強制力", "書いても守られないことがある"], - ["手順", "「どの順に何を実行するか」は文章では伝わりにくい"], - ["ポータビリティ", "リポジトリやランタイムを変えると失われる"], + ["コンテキスト", "毎回すべて読む。書くほど重く、ルールが埋もれる"], + ["強制力", "書いても守られない"], + ["手順", "実行の順序は文章で伝わりにくい"], + ["ポータビリティ", "リポジトリやランタイムを変えると消える"], ].forEach((r, i) => { const c = cols(2)[i % 2]; - card(d, s, c.x, 1.42 + Math.floor(i / 2) * 1.62, c.w, 1.46, r[0], r[1], "panel1", { band: "accent", headSize: 15, bodySize: 12, headH: 0.42 }); + card(d, s, c.x, 1.42 + Math.floor(i / 2) * 1.62, c.w, 1.46, r[0], r[1], "panel1", { band: "accent", headSize: 15, bodySize: 13, headH: 0.42 }); }); // 4. スキルプラグインとは - s = d.page("スキルプラグインとは", "判断基準とワークフローをパッケージにして、どのリポジトリでも再利用する"); + s = d.page("スキルプラグインとは", "やり方をパッケージにして、どこでも再利用"); d.tbl(s, [ [d.H("構成要素"), d.H("役割")], - [d.B("Skill"), "「いつ・何を・どの順で」を書いたワークフロー。必要なときだけ読み込まれる"], - [d.B("スクリプト"), "決まった処理を確実に実行する"], - [d.B("hook"), "ルールを強制する"], - [d.B("サブエージェント"), "役割を分ける"], - ], { x: X0, y: 1.4, w: W, colW: [2.2, 7.28], rowH: [0.32, 0.42, 0.42, 0.42, 0.42], fontSize: 11 }); - d.text(s, "まとめて配布し、同じものを 4 つのランタイムで使える", { x: X0, y: 3.62, w: W, h: 0.3, fontSize: 11, bold: true, color: "text" }); - cols(4).forEach((c, i) => d.chip(s, c.x, 4.0, c.w, 0.62, ["Claude Code", "Codex", "Kiro", "agy"][i], "accentSoft", { fontSize: 13 })); + [d.B("Skill"), "手順書。「いつ・何を・どの順で」。必要なときだけ読む"], + [d.B("スクリプト"), "決まった処理を確実に実行"], + [d.B("hook"), "ルールを強制(AI の操作の直前に割り込む)"], + [d.B("サブエージェント"), "役割ごとに分けた AI"], + ], { x: X0, y: 1.4, w: W, colW: [2.2, 7.28], rowH: [0.34, 0.46, 0.46, 0.46, 0.46], fontSize: 13 }); + d.text(s, "1 つの配布物を、4 つのランタイムで", { x: X0, y: 3.74, w: W, h: 0.3, fontSize: 12, bold: true, color: "text" }); + cols(4).forEach((c, i) => d.chip(s, c.x, 4.1, c.w, 0.62, ["Claude Code", "Codex", "Kiro", "agy"][i], "accentSoft", { fontSize: 13 })); + + // ==== 章 2 ==== + d.section("2. NDF の全体像", "Issue の束を渡すと、リリースまで進む"); // 5. NDF: 1 人の開発スタイルをプラグインにした - s = d.page("NDF: 1 人の開発スタイルをプラグインにした", "GitHub で公開している。ほぼ 1 人と AI で開発した"); + s = d.page("NDF: 自分のやり方をプラグインに", "GitHub で公開。ほぼ 1 人と AI で開発"); [ - ["102", "回", "リリース(2026 年 4 月に開発を始めて半年)"], - ["48", "個", "Skill"], + ["102", "回", "リリース(開発を始めて半年)"], + ["48", "個", "Skill(手順書)"], ["4", "つ", "対応するランタイム"], ["10", "リポジトリ", "自分以外への導入"], ].forEach((r, i) => { @@ -116,13 +123,13 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の stat(d, s, c.x, 1.55, c.w, 1.7, r[0], r[1], r[2]); }); d.text(s, [{ text: REPO, options: { hyperlink: { url: REPO }, color: "accent" } }], { x: X0, y: 3.6, w: W, h: 0.36, fontSize: 14, bold: true, align: "center", valign: "middle" }); - d.note(s, "数値は 2026-10-17 時点の実測"); + d.note(s, "数値は 2026-10-17 時点の実測。開発の開始は 2026 年 4 月"); // 6. NDF の全体像 - s = d.page("NDF の全体像", "マイルストーンを渡せば、スプリントとして並列に進む"); + s = d.page("Issue の束を渡せば、並列に進む", "人が判断するのは 2 回の承認だけ"); d.chip(s, X0, 1.62, 1.55, 0.9, "マイルストーン\n(Issue の束)", "panel2", { fontSize: 10 }); d.arrow(s, 1.85, 2.07, 0.3, 0); - d.chip(s, 2.19, 1.62, 1.55, 0.9, "グループに分ける\n触る場所と依存関係", "panel2", { fontSize: 10 }); + d.chip(s, 2.19, 1.62, 1.55, 0.9, "触る場所と依存で\nグループに分ける", "panel2", { fontSize: 10 }); d.arrow(s, 3.78, 2.07, 0.3, 0); d.box(s, 4.12, 1.36, 4.0, 1.98, "panel1", false); d.text(s, "スプリント(グループごとに並列)", { x: 4.24, y: 1.4, w: 3.8, h: 0.28, fontSize: 10, bold: true, color: "text", valign: "middle" }); @@ -135,189 +142,206 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の d.chip(s, 8.5, 1.9, 1.24, 0.9, "リリース", "panel2", { fontSize: 11 }); cols(2).forEach((c, i) => { const g = [ - ["ゲート 1: 設計の承認", "スプリントの設計 PR をまとめて 1 回"], - ["ゲート 2: 本番リリースの承認", "開発版で確かめた後に 1 回"], + ["承認 1: 設計", "スプリントの設計をまとめて 1 回"], + ["承認 2: 本番リリース", "開発版で確かめた後に 1 回"], ][i]; - card(d, s, c.x, 3.52, c.w, 0.82, g[0], g[1], "caution", { headSize: 11, bodySize: 10, headH: 0.28 }); + card(d, s, c.x, 3.52, c.w, 0.86, g[0], g[1], "caution", { headSize: 12, bodySize: 11, headH: 0.3 }); }); - d.text(s, "人が判断するのは 2 か所だけ。承認には決めたこと・未検証の点・判断の根拠がそろって届く", { x: X0, y: 4.44, w: W, h: 0.3, fontSize: 10, bold: true, color: "text", valign: "middle" }); - d.note(s, "例: 直前のリリースでは、致命的な Issue 18 件を 2 つのスプリントに分け、1 つのスプリントの中で 3 つのグループの設計と実装を並列に進めた"); + d.text(s, "決めたこと・未検証の点・根拠がそろって届く → 1 回で決められる", { x: X0, y: 4.46, w: W, h: 0.3, fontSize: 11, bold: true, color: "text", valign: "middle" }); + d.note(s, "例: 直前のリリース — 致命的な Issue 18 件を 2 スプリントに分け、スプリントの中は 3 グループを並列に"); // 7. /ndf:development-workflow - s = d.page("/ndf:development-workflow", "変更を 5 つのモードに分類し、モードごとに通る Skill を切り替える"); + s = d.page("変更の大きさで、踏む工程を変える", "/ndf:development-workflow — 変更を 5 つのモードに分ける"); d.tbl(s, [ [d.H("モード"), d.H("対象")], - [d.B("light"), "文言や設定などの局所的な変更"], - [d.B("operation"), "コードを変えずに外部の状態を変える作業"], - [d.B("standard"), "機能追加やバグ修正"], - [d.B("legacy-refactor"), "テストの薄いコードのリファクタリング"], - [d.B("documentation"), "提案書や報告書などのビジネス文書"], - ], { x: X0, y: 1.4, w: W, colW: [2.4, 7.08], rowH: 0.36, fontSize: 11 }); - d.callout(s, 3.78, 0.72, "panel1", "通る Skill はモードごとの表で決まっている", "light なら実装してレビューするだけ。standard なら要件定義から振り返りまで通る。すべての変更にフルのワークフローを課すと遅くなる。"); + [d.B("light"), "文言・設定の小さな変更"], + [d.B("operation"), "コードを変えない外部の作業"], + [d.B("standard"), "機能追加・バグ修正"], + [d.B("legacy-refactor"), "テストの薄いコードの整理"], + [d.B("documentation"), "提案書・報告書などの文書"], + ], { x: X0, y: 1.4, w: W, colW: [2.6, 6.88], rowH: 0.38, fontSize: 13 }); + cols(2).forEach((c, i) => { + const r = [ + ["light", "実装 → レビューだけ"], + ["standard", "要件定義 → … → 振り返り"], + ][i]; + card(d, s, c.x, 3.9, c.w, 0.82, r[0], r[1], i ? "accentSoft" : "panel1", { headSize: 12, bodySize: 12, headH: 0.3 }); + }); // 8. standard モードの全体の流れ - s = d.page("standard モードの全体の流れ", "スプリント単位で 1 周する。中の設計と実装は Issue のグループごとに並列に進む"); + s = d.page("機能追加は、この流れで 1 周", "standard モード: スプリントごとに 1 周、中はグループごとに並列"); [ - ["設計", ["要件定義", "worktree", "設計", "ドキュメントの\n再構成", "クロスレビュー", { text: "ゲート 1\n設計の承認" }]], - ["実装", ["実装計画", "テストファースト\nの実装", "クロス\nリファクタリング", "クロスレビュー", "完了判定(証跡)", "マージ"]], - ["リリース", ["仕様書へ変換", "開発版リリース", { text: "ゲート 2\n本番の承認" }, "本番リリース", "リリース後テスト", "振り返り", "Issue の棚卸し"]], + ["設計", ["要件定義", "作業用の\nworktree", "設計", "構成の\n見直し", "クロス\nレビュー", { text: "承認 1\n設計" }]], + ["実装", ["実装計画", "テスト\nファースト", "クロス\nリファクタ", "クロス\nレビュー", "完了判定\n(証跡)", "マージ"]], + ["リリース", ["仕様書へ", "開発版\nリリース", { text: "承認 2\n本番" }, "本番\nリリース", "リリース後\nテスト", "振り返り", "Issue の\n棚卸し"]], ].forEach((row, i) => { const y = 1.48 + i * 1.08; d.text(s, row[0], { x: X0, y, w: 0.8, h: 0.7, fontSize: 11, bold: true, color: "text", valign: "middle" }); const n = row[1].length; const g = 0.2; - flow(d, s, 1.06, y, (8.68 * n + g * (n - 7)) / 7, 0.7, row[1], { gap: g, size: 8.5 }); + flow(d, s, 1.06, y, (8.68 * n + g * (n - 7)) / 7, 0.7, row[1], { gap: g, size: 9.5 }); // 段の続きは左の見出しの列で示す(工程の箱の間に置くと、箱どうしの流れに読める) if (i < 2) d.arrow(s, X0 + 0.3, y + 0.76, 0, 0.26); }); - d.note(s, "クロスレビューの指摘は修正して繰り返す。開発版は自分で使って確かめてからゲート 2 へ出す"); + d.note(s, "レビューの指摘は直して繰り返す。開発版は自分で使って確かめてから承認 2 へ"); + + // ==== 章 3 ==== + d.section("3. 組み込んだ開発技法", "実績のある技法を、AI が守れる手順に"); // 9. 実績のある開発技法を組み込む - s = d.page("実績のある開発技法を、AI が守れる形で組み込む", "新しい方法論の発明ではない。人間のチームで実績のある技法を、AI が毎回守れる手順にした"); + s = d.page("実績のある開発技法を組み込む", "新しい発明ではない。AI が毎回守れる手順にした"); [ - ["DDD: ユビキタス言語", ["要件定義の前に用語集を作り、語・意味・コンテキストを決める", "同じ「注文」でも受注と配送では意味が違う。コンテキストごとに 1 つの意味に決め、AI はその語で設計とコードを書く", "このリポジトリの用語集は 580 語。CI が食い違いを検査する"]], - ["テストファースト", ["失敗するテストを書く → 通す最小の実装 → 整理、のサイクル", "テストが薄いコードは、先に今の振る舞いを固定するテストを書いてからリファクタリングする"]], - ["アジャイル", ["マイルストーンをバックログとして扱い、スプリントで進める", "スプリントごとに振り返りとバックログリファインメントを行う", "優先順位は WSJF(遅延コスト ÷ 大きさ)で付ける"]], + ["DDD: ユビキタス言語", ["要件定義の前に用語集", "1 つの語に 1 つの意味", "例: 「注文」は受注と配送で別", "用語集 580 語、CI が検査"]], + ["テストファースト", ["失敗するテスト", "- → 通す最小の実装", "- → 整理", "薄いコードは", "- 先に今の振る舞いを固定"]], + ["アジャイル", ["マイルストーン = バックログ", "スプリントごとに振り返り", "バックログの整理も毎回", "優先順位は WSJF", "- 遅延コスト ÷ 大きさ"]], ].forEach((r, i) => { const c = cols(3)[i]; - card(d, s, c.x, 1.42, c.w, 2.75, r[0], bullets(r[1], 11.5), "panel1", { band: "accent", headSize: 13 }); + card(d, s, c.x, 1.42, c.w, 2.5, r[0], bullets(r[1], 13), "panel1", { band: "accent", headSize: 14 }); }); // 10. /ndf:requirements-design と /ndf:design - s = d.page("/ndf:requirements-design と /ndf:design", "コードより先に「何を」「どう」を決める"); + s = d.page("コードの前に「何を」「どう」", "実装の前に、要件と設計の 2 つの文書を書く"); [ - ["requirements-design: 何を満たせば完了か", ["観測できる受け入れ条件にする", "「いい感じに」「速く」のような評価語を、確かめられる条件に変える"]], - ["design: どう作るか", ["データ構造・入出力の契約・処理の流れ・決定の理由を書く", "担当する AI が変わっても同じ構成の成果物になるよう、書く内容を規約で決めている"]], + ["/ndf:requirements-design — 完了の条件", ["確かめられる受け入れ条件に", "「いい感じに」「速く」を条件へ"]], + ["/ndf:design — 作り方", ["データ構造・入出力・流れ・理由", "担当の AI が変わっても同じ構成に"]], ].forEach((r, i) => { const c = cols(2)[i]; - card(d, s, c.x, 1.42, c.w, 1.9, r[0], bullets(r[1], 12.5), "panel1", { band: "accent", headSize: 13 }); + card(d, s, c.x, 1.42, c.w, 1.5, r[0], bullets(r[1], 13), "panel1", { band: "accent", headSize: 13 }); }); - d.callout(s, 3.56, 0.72, "caution", "設計は PR として出す", "主題ごとに並列で開き、ゲート 1 でまとめて承認する。"); + d.callout(s, 3.2, 0.72, "caution", "設計は PR で出す", "主題ごとに並列に開き、承認 1 でまとめて承認"); // 11. /ndf:document-restructuring - s = d.page("/ndf:document-restructuring", "書いた順ではなく、読み手が知りたい順へ並べ替える"); - d.text(s, "AI が書いた順(調べた順)", { x: X0, y: 1.4, w: 4.3, h: 0.3, fontSize: 11, bold: true, color: "text" }); - flow(d, s, X0, 1.76, 4.3, 0.56, ["問題に気づく", "測る", "結論"], { fill: "panel2", size: 10 }); + s = d.page("結論から読める順に並べ替える", "/ndf:document-restructuring — 書き上げた文書の再構成"); + d.text(s, "AI が書いた順(調べた順)", { x: X0, y: 1.4, w: 4.3, h: 0.3, fontSize: 12, bold: true, color: "text" }); + flow(d, s, X0, 1.76, 4.3, 0.56, ["問題に気づく", "測る", "結論"], { fill: "panel2", size: 11 }); d.arrow(s, 4.66, 2.04, 0.5, 0, "accent"); - d.text(s, "読み手が知りたい順", { x: 5.32, y: 1.4, w: 4.42, h: 0.3, fontSize: 11, bold: true, color: "text" }); - flow(d, s, 5.32, 1.76, 4.42, 0.56, ["結論", "根拠(測った結果)", "経緯"], { size: 10 }); - d.text(s, "手順(内容は足さない)", { x: X0, y: 2.62, w: W, h: 0.3, fontSize: 11, bold: true, color: "text" }); - flow(d, s, X0, 2.98, W, 0.62, ["測る", "章立てを読み手の\n問いの順に並べる", "長い文を分ける", "対比を表にする", "もう一度測る"], { size: 10 }); - d.callout(s, 3.86, 0.7, "panel1", "レビューの前に通す", "レビューの指摘が、構成の指摘と内容の指摘で混ざらなくなる。"); + d.text(s, "読み手が知りたい順", { x: 5.32, y: 1.4, w: 4.42, h: 0.3, fontSize: 12, bold: true, color: "text" }); + flow(d, s, 5.32, 1.76, 4.42, 0.56, ["結論", "根拠", "経緯"], { size: 11 }); + d.text(s, "手順(内容は足さない)", { x: X0, y: 2.62, w: W, h: 0.3, fontSize: 12, bold: true, color: "text" }); + flow(d, s, X0, 2.98, W, 0.62, ["測る", "章を読み手の\n問いの順に", "長い文を分ける", "対比は表に", "もう一度測る"], { size: 11 }); + d.callout(s, 3.86, 0.7, "panel1", "レビューの前に通す", "構成の指摘と内容の指摘が混ざらない"); // 12. /ndf:cross-review - s = d.page("/ndf:cross-review", "2 つの AI で、新しい指摘が出なくなるまでレビューする"); - flow(d, s, X0, 1.5, 5.0, 0.7, ["AI を 2 つ選ぶ\nclaude・codex・kiro", "レビュー", "指摘を修正"], { size: 9 }); - d.arrow(s, 2.9, 2.32, 0, 0.36); - d.chip(s, 1.5, 2.72, 2.8, 0.62, "新しい指摘が出たか", "panel2", { fontSize: 10 }); - d.text(s, "出た → 組み合わせを入れ替えて繰り返す\n出ない → 終える", { x: X0, y: 3.46, w: 5.0, h: 0.56, fontSize: 10 }); - d.text(s, "レビューの観点は PR の中身で切り替わり、リポジトリごとにレビューの基準を設定できる", { x: X0, y: 4.1, w: 5.0, h: 0.6, fontSize: 10 }); - d.text(s, "指摘の再現率(手元の実測)", { x: 5.6, y: 1.42, w: 4.14, h: 0.3, fontSize: 11, bold: true, color: "text" }); + s = d.page("2 つの AI で、指摘が尽きるまで", "/ndf:cross-review — 別々の AI が交互にレビュー"); + d.text(s, "同じ AI は、同じ見落としをする", { x: X0, y: 1.38, w: 5.0, h: 0.3, fontSize: 12, bold: true, color: "text" }); + flow(d, s, X0, 1.76, 5.0, 0.7, ["AI を 2 つ選ぶ\nclaude・codex・kiro", "レビュー", "指摘を修正"], { size: 10 }); + d.arrow(s, 2.9, 2.54, 0, 0.3); + d.chip(s, 1.5, 2.9, 2.8, 0.56, "新しい指摘は?", "panel2", { fontSize: 11 }); + d.text(s, "出た → 組み合わせを替えて繰り返す\n出ない → 終了", { x: X0, y: 3.56, w: 5.0, h: 0.56, fontSize: 11 }); + d.text(s, "観点は PR の中身で切り替え。基準はリポジトリごと", { x: X0, y: 4.22, w: 5.0, h: 0.4, fontSize: 10.5 }); + d.text(s, "指摘の再現率(手元の実測)", { x: 5.6, y: 1.42, w: 4.14, h: 0.3, fontSize: 12, bold: true, color: "text" }); [ ["1 つの AI(低い方)", 0.21, "panel2"], ["1 つの AI(高い方)", 0.49, "panel2"], ["2 つの AI を合わせる", 0.91, "accent"], ].forEach((r, i) => { const y = 1.86 + i * 0.86; - d.text(s, r[0], { x: 5.6, y, w: 4.14, h: 0.28, fontSize: 10 }); + d.text(s, r[0], { x: 5.6, y, w: 4.14, h: 0.28, fontSize: 11 }); d.box(s, 5.6, y + 0.3, 3.4 * r[1], 0.36, r[2], false); d.text(s, r[1].toFixed(2), { x: 5.66 + 3.4 * r[1], y: y + 0.3, w: 0.6, h: 0.36, fontSize: 13, bold: true, color: "text", valign: "middle" }); }); // 13. /ndf:cross-refactoring - s = d.page("/ndf:cross-refactoring", "全員で提案し、1 人が時間内に実施する"); - flow(d, s, X0, 1.5, W, 0.86, ["参加する AI 全員が\n1 回ずつ多面的に提案", "1 つの AI を選ぶ", "指定した時間(既定 30 分)に\n収まる分だけ実施", "テストが壊れた変更は\n取り消す"], { size: 10 }); - d.text(s, "実施の中身: 計画 → テスト追加 → 実装 → 検証", { x: 5.0, y: 2.48, w: 4.74, h: 0.3, fontSize: 10, align: "center" }); - card(d, s, X0, 3.0, 4.66, 1.2, "終わりが見える", "時間に関わる数値は、すべて指定した時間から逆算する", "panel1", { band: "accent", headSize: 13, bodySize: 12 }); - card(d, s, 5.08, 3.0, 4.66, 1.2, "結果が追える", "採用と取り消しの内訳を報告に残す", "panel1", { band: "accent", headSize: 13, bodySize: 12 }); + s = d.page("全員で提案、1 人が時間内に直す", "/ndf:cross-refactoring — 複数の AI でリファクタリング"); + flow(d, s, X0, 1.5, W, 0.86, ["全員の AI が\n1 回ずつ提案", "1 つの AI を選ぶ", "時間内(既定 30 分)\nに収まる分を実施", "テストが壊れたら\n取り消す"], { size: 11 }); + d.text(s, "実施: 計画 → テスト追加 → 実装 → 検証", { x: 5.0, y: 2.48, w: 4.74, h: 0.3, fontSize: 11, align: "center" }); + card(d, s, X0, 3.0, 4.66, 1.2, "終わりが見える", "時間の数値は、すべて持ち時間から逆算", "panel1", { band: "accent", headSize: 13, bodySize: 12.5 }); + card(d, s, 5.08, 3.0, 4.66, 1.2, "結果が追える", "採用と取り消しの内訳を報告に残す", "panel1", { band: "accent", headSize: 13, bodySize: 12.5 }); + + // ==== 章 4 ==== + d.section("4. AI に任せ切る仕組み", "止まらない・迷わない・ごまかさない"); // 14. 3 層のエージェント - s = d.page("3 層のエージェント", "長いタスクを止めずに進め、止まっても再開できる"); + s = d.page("3 層のエージェントで止めない", "長い作業も、止まったら続きから"); [ - ["conductor", "人と話して全体を指揮する"], - ["supervisor", "1 つのフェーズ(設計・実装・テストなど)を通す"], - ["worker", "1 つの作業(調査・修正など)を行う"], + ["conductor", "指揮役。人と話し全体を進める"], + ["supervisor", "監督役。1 つの工程を通す"], + ["worker", "作業役。調査や修正を行う"], ].forEach((r, i) => { const y = 1.46 + i * 1.02; d.chip(s, X0 + i * 0.3, y, 1.7, 0.74, r[0], i === 0 ? "accent" : "accentSoft", { fontSize: 13, color: i === 0 ? "onHeader" : "text" }); - d.text(s, r[1], { x: 2.12 + i * 0.3, y, w: 3.0, h: 0.74, fontSize: 10.5, valign: "middle" }); + d.text(s, r[1], { x: 2.12 + i * 0.3, y, w: 2.95, h: 0.74, fontSize: 11.5, valign: "middle" }); if (i < 2) d.arrow(s, 1.1 + i * 0.3, y + 0.76, 0.2, 0.24); }); - card(d, s, 5.7, 1.46, 4.04, 1.3, "止まっても続きから再開", "フェーズは JSON のプランとしてスクリプトが実行する", "panel1", { band: "accent", headSize: 13, bodySize: 12 }); - card(d, s, 5.7, 2.96, 4.04, 1.5, "セッションを自動で切り替える", "コンテキストが大きくなったら、次に実行するコマンドを残して新しいセッションへ移る", "panel1", { band: "accent", headSize: 13, bodySize: 12 }); + card(d, s, 5.7, 1.46, 4.04, 1.3, "止まっても続きから", "工程を JSON の計画にし、スクリプトが実行", "panel1", { band: "accent", headSize: 13, bodySize: 12.5 }); + card(d, s, 5.7, 2.96, 4.04, 1.5, "セッションを自動で切り替え", "コンテキストが膨らんだら、次のコマンドを残して新しいセッションへ", "panel1", { band: "accent", headSize: 13, bodySize: 12.5 }); // 15. 判断基準: MVV と 2 つの承認ゲート - s = d.page("判断基準: MVV と 2 つの承認ゲート", "AI はプロジェクトの MVV を根拠に判断し、根拠の番号をログに残す"); - d.text(s, "AI が迷う場面: 指摘を直すか見送るか、人の承認を省いてよいか", { x: X0, y: 1.38, w: W, h: 0.3, fontSize: 10.5 }); + s = d.page("迷ったら MVV で判断する", "判断の根拠の番号をログに残す"); + d.text(s, "AI が迷う場面: 指摘を直すか見送るか・承認を省いてよいか", { x: X0, y: 1.38, w: W, h: 0.3, fontSize: 11.5 }); [ ["Mission", "何のためにやるか"], ["Vision", "目指す姿"], - ["Value", "判断の基準\n例: 「まず届ける」\n「処理の改善に寄与しない指摘は直さない」"], + ["Value", "判断の基準"], ].forEach((r, i) => { const c = cols(3, 0.16, X0, 5.9)[i]; - card(d, s, c.x, 1.8, c.w, 1.9, r[0], r[1], "accentSoft", { headSize: 15, bodySize: 11 }); + card(d, s, c.x, 1.8, c.w, 1.2, r[0], r[1], "accentSoft", { headSize: 15, bodySize: 12.5 }); }); - card(d, s, 6.4, 1.8, 3.34, 1.9, "必ず人の承認が要る操作", "本番デプロイのような操作は、MVV に照らしても AI が省かない。ここに定義する", "caution", { headSize: 12, bodySize: 11.5 }); - d.callout(s, 3.9, 0.4, "panel1", "どこまで AI に任せるかは、進め方の設定で事前に選べる", null); + d.text(s, "Value の例: 「まず届ける」「処理の改善に寄与しない指摘は直さない」", { x: X0, y: 3.16, w: W, h: 0.34, fontSize: 12, valign: "middle" }); + card(d, s, 6.4, 1.8, 3.34, 1.2, "必ず人が承認する操作", "本番デプロイなど\nMVV に照らしても AI は省かない", "caution", { headSize: 13, bodySize: 12 }); + d.callout(s, 3.7, 0.4, "panel1", "どこまで AI に任せるかは、事前に設定で選べる", null); // 16. /ndf:quality-gates と /ndf:investigation-rules - s = d.page("/ndf:quality-gates と /ndf:investigation-rules", "完了も「無い」も、証拠がそろって初めて認める"); + s = d.page("「完了」も「無い」も証拠で", "AI の言葉を、そのままは信じない"); [ - ["「完了しました」", "quality-gates", "実行したコマンド・終了コード・実行時刻・対象範囲がそろわない限り、完了と認めない"], - ["「該当のコードはありません」", "investigation-rules", "「無い」という結論には、必ず検索のコマンドと結果を添えさせる"], + ["「完了しました」", "/ndf:quality-gates — 完了の判定", "次の 4 つがそろうまで完了としない\nコマンド・終了コード・時刻・範囲"], + ["「該当のコードはありません」", "/ndf:investigation-rules — 調査の規則", "「無い」の結論には必ず\n検索のコマンドと結果を添える"], ].forEach((r, i) => { const c = cols(2)[i]; d.chip(s, c.x, 1.48, c.w, 0.72, r[0], "panel2", { fontSize: 15 }); d.arrow(s, c.x + c.w / 2, 2.26, 0, 0.3); - card(d, s, c.x, 2.62, c.w, 1.4, r[1], r[2], "panel1", { band: "accent", headSize: 13, bodySize: 12.5 }); + card(d, s, c.x, 2.62, c.w, 1.4, r[1], r[2], "panel1", { band: "accent", headSize: 12.5, bodySize: 13 }); }); - d.text(s, "AI はもっともらしいが間違った推論で「無い」と断定しがちである", { x: X0, y: 4.3, w: W, h: 0.34, fontSize: 11, bold: true, color: "text", align: "center", valign: "middle" }); + d.text(s, "AI はもっともらしい推論で「無い」と断定しがち", { x: X0, y: 4.3, w: W, h: 0.34, fontSize: 12, bold: true, color: "text", align: "center", valign: "middle" }); - // 17. 自分を直すループ - s = d.page("自分を直すループ", "out-of-scope・retrospective・backlog-refinement で、NDF 自身を直し続ける"); + // 17. 自分を直すループ(導入先のプロジェクトの Issue の一覧を整理し続ける) + s = d.page("やるべきことを整理し続ける", "導入したプロジェクトの Issue の一覧を、いつも現状に"); [ - ["out-of-scope", "作業中に見つけた範囲外の問題を、その場で Issue にする"], - ["retrospective", "リリースのたびに進め方を見直す"], - ["backlog-refinement", "スプリントの終わりに Issue の一覧を現状に合わせ、優先順位を付け直す"], + ["/ndf:out-of-scope — 見つけたら起票", "作業中に見つけた範囲外の問題を、その場で Issue に"], + ["/ndf:retrospective — 振り返り", "リリースごとに進め方を見直す"], + ["/ndf:backlog-refinement — 一覧の手入れ", "スプリントの終わりに、Issue を現状へ合わせて並べ直す"], ].forEach((r, i) => { const y = 1.42 + i * 1.08; - card(d, s, X0, y, 4.9, 0.92, r[0], r[1], "accentSoft", { headSize: 12, bodySize: 10, headH: 0.28 }); + card(d, s, X0, y, 4.9, 0.92, r[0], r[1], "accentSoft", { headSize: 12, bodySize: 11, headH: 0.28 }); if (i < 2) d.arrow(s, 2.7, y + 0.93, 0, 0.14); }); - stat(d, s, 5.4, 1.42, 4.34, 1.44, "56", "%", "ある時点の棚卸しで、open の Issue 43 件のうち 24 件の本文が現状と食い違っていた"); - stat(d, s, 5.4, 3.0, 4.34, 1.5, "49", "件", "今日 71 件を 6 つの worker で並列に調べたところ、49 件は本文が古く、2 件はすでに修正済みだった"); - d.note(s, "放っておくと、すでに直った Issue を直すことになる"); + stat(d, s, 5.4, 1.42, 4.34, 1.44, "56", "%", "本文が現状とずれていた open の Issue\n(43 件中 24 件・ある時点の棚卸し)"); + stat(d, s, 5.4, 3.0, 4.34, 1.5, "49", "件", "本文が古くなっていた Issue\n(今日 71 件を並列に調査・ほかに 2 件は修正済み)"); + d.note(s, "放っておくと、もう直った Issue を直すことになる"); // 18. その他の特徴的な機能 - s = d.page("その他の特徴的な機能", "見積もり・届け方の違う成果物のリリース・AI がやりがちなことの強制的な停止"); + s = d.page("そのほかの特徴的な機能", "見積もり・リリース・AI の操作の強制停止"); d.tbl(s, [ - [d.H("機能"), d.H("何をするか")], - [d.B("estimate"), "AI エージェントが実施する前提で工数を見積もる。人日では見積もらず、AI の実行時間・人の作業時間・完了までの時間・費用の 4 つを、似た過去の実績から別々に出す"], - [d.B("release"), "パッケージ・Web サービス・スライドや文書まで、届け方の違う成果物を同じ骨格でリリースする"], - [d.B("hook"), "メインディレクトリの直接編集の警告、sleep による待ちの禁止、同じファイルの繰り返し読み込みの防止など、AI がやりがちなことを強制的に止める"], - ], { x: X0, y: 1.42, w: W, colW: [1.8, 7.68], rowH: [0.36, 1.0, 0.76, 1.0], fontSize: 12 }); + [d.H("機能"), d.H("何をするか"), d.H("要点")], + [d.B("/ndf:estimate"), "AI が作業する前提の見積もり", "人日を使わない。AI の時間・人の時間・完了まで・費用を過去の実績から"], + [d.B("/ndf:release"), "届け方の違う成果物を同じ骨格でリリース", "パッケージ・Web サービス・スライド・文書"], + [d.B("hook"), "AI がやりがちな操作を強制的に止める", "直接編集の警告・sleep の待ちの禁止・同じファイルの読み直しの防止"], + ], { x: X0, y: 1.42, w: W, colW: [1.9, 3.3, 4.28], rowH: [0.36, 0.86, 0.7, 0.86], fontSize: 12 }); + + // ==== 章 5 ==== + d.section("5. 事例", "ほかのプロジェクトでも動くか"); // 19. 事例: 他のプロジェクトでも動く - s = d.page("事例: 他のプロジェクトでも動く", "言語もチームも違う 4 つのプロジェクトで使った結果"); + s = d.page("事例: ほかのプロジェクトでも動く", "言語もチームも違う 4 つのプロジェクト"); d.tbl(s, [ [d.H("プロジェクトの種類"), d.H("結果")], - [d.B("AWS のインフラ"), "導入前後の 37 日で、1 日あたりのマージ PR 数が 0.95 から 3.38 に増えた(社内ツールの移行と重なっており、すべてが NDF の効果ではない)"], - [d.B("PHP の業務システム\n(20 人で開発)"), "設計 PR 1 本の承認から 51 時間で、31 本の実装 PR が入った"], - [d.B("機械学習のモデル"), "精度改善の調査で 4 か月分のデータ欠損を見つけ、修正からバックフィルまでを 1 つのスプリントで本番に出した。相関係数は 0.73 から 0.954 に改善した"], - [d.B("価格推定"), "PR 数は変わらず、レビューを必ず通す開発プロセスに変わった"], - ], { x: X0, y: 1.42, w: W, colW: [2.3, 7.18], rowH: [0.32, 0.72, 0.56, 0.72, 0.5], fontSize: 11 }); + [d.B("AWS のインフラ"), "1 日のマージ PR 0.95 → 3.38(導入前後 37 日)\n※ 社内ツールの移行と重なる。すべてが NDF の効果ではない"], + [d.B("PHP の業務システム\n(20 人で開発)"), "設計 PR 1 本の承認から 51 時間で、実装 PR 31 本"], + [d.B("機械学習のモデル"), "4 か月分のデータ欠損を発見。修正〜本番を 1 スプリントで\n相関係数 0.73 → 0.954"], + [d.B("価格推定"), "PR 数は同じ。レビュー必須の開発プロセスへ"], + ], { x: X0, y: 1.42, w: W, colW: [2.3, 7.18], rowH: [0.34, 0.74, 0.6, 0.74, 0.5], fontSize: 12 }); d.note(s, "プロジェクトは種類で呼び、名前は出さない。数値は 2026-10-17 時点の実測"); // 20. 事例から分かったこと - s = d.page("事例から分かったこと", "詰まったところが次のバージョンになり、ルールはプロジェクトごとに違う"); - card(d, s, X0, 1.42, 4.66, 2.6, "1. どこでも最初は詰まり、次の版で直る", bullets([ - "詰まったところが NDF の Issue として上がり、次のバージョンで直った", - "PHP のプロジェクトでは前提の違いから 17 件の Issue が上がった", - "- worktree に vendor/ が無い", - "- 全テストに 64 分かかる", - "- マージがそのまま本番リリースになる", - ], 12), "panel1", { band: "accent", headSize: 12.5 }); - d.text(s, "2. どのプロジェクトも独自のルールを足した", { x: 5.08, y: 1.48, w: 4.66, h: 0.32, fontSize: 12, bold: true, color: "text", valign: "middle" }); + s = d.page("事例から分かった 2 つのこと", "詰まりは次の版で直り、ルールはプロジェクトごと"); + card(d, s, X0, 1.42, 4.66, 2.48, "1. 最初は詰まり、次の版で直る", bullets([ + "詰まりが NDF の Issue になる", + "PHP では前提の違いで 17 件", + "- 作業用の worktree に vendor/ が無い", + "- 全テストに 64 分", + "- マージ = 本番リリース", + ], 13), "panel1", { band: "accent", headSize: 13 }); + d.text(s, "2. どこも独自のルールを足した", { x: 5.08, y: 1.48, w: 4.66, h: 0.32, fontSize: 13, bold: true, color: "text", valign: "middle" }); d.tbl(s, [ [d.H("プロジェクト"), d.H("足したルール")], [d.B("インフラ"), "本番デプロイの承認"], @@ -325,33 +349,36 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の [d.B("価格推定"), "全 PR へのレビュー"], ], { x: 5.08, y: 1.9, w: 4.66, colW: [1.5, 3.16], rowH: 0.5, fontSize: 12 }); + // ==== 章 6 ==== + d.section("6. 始め方", "フォークして、自分用に育てる"); + // 21. だから「フォークして自分用に」 - s = d.page("だから「フォークして自分用に」", "仕組みはそのまま使い、ルールを自分のものに差し替える"); - card(d, s, X0, 1.42, 4.66, 1.86, "NDF も私のルールのかたまり", bullets(["承認は 2 回だけ", "ドキュメントでは「道具」でなく「Tool」と書く", "文言を照合するテストは書かない"], 12), "warn", { headSize: 13 }); - card(d, s, 5.08, 1.42, 4.66, 1.86, "そのまま使う仕組み", bullets(["ワークフロー", "3 層のエージェント", "クロスレビュー", "判断基準"], 12), "accentSoft", { headSize: 13 }); - d.text(s, "他人のルールは、自分にとってはノイズになる。そのまま導入するより、フォークする", { x: X0, y: 3.36, w: W, h: 0.3, fontSize: 10.5, bold: true, color: "text", align: "center", valign: "middle" }); + s = d.page("だから、フォークして自分用に", "仕組みはそのまま、ルールだけ差し替える"); + card(d, s, X0, 1.42, 4.66, 1.86, "NDF も私のルールのかたまり", bullets(["承認は 2 回だけ", "文書では「道具」でなく「Tool」", "文言を照合するテストは書かない"], 12.5), "warn", { headSize: 13 }); + card(d, s, 5.08, 1.42, 4.66, 1.86, "そのまま使う仕組み", bullets(["ワークフロー", "3 層のエージェント", "クロスレビュー", "判断基準(MVV)"], 12.5), "accentSoft", { headSize: 13 }); + d.text(s, "他人のルールは自分にはノイズ → 導入よりフォーク", { x: X0, y: 3.36, w: W, h: 0.34, fontSize: 12, bold: true, color: "text", align: "center", valign: "middle" }); cols(3).forEach((c, i) => d.chip(s, c.x, 3.84, c.w, 0.72, ["指摘が資産になる", "ランタイムに依存しない", "計測して改善できる"][i], "panel1", { fontSize: 13 })); // 22. 始め方と育て方 s = d.page("始め方と育て方", "フォークする → 削る → 育てる"); [ - ["1. フォークする", ["プラグイン名とマーケットプレイス名を自分のものに変える", "自分のリポジトリからインストール"]], - ["2. 削る", ["配布する Skill はマニフェストで決まる。使わない Skill は外すだけ", "残すものはセッションのログの呼び出し回数で決める"]], - ["3. 育てる", ["同じ指摘を 2 回したら形にする", "手順なら Skill", "決まった処理ならスクリプト", "必ず守らせたいなら hook"]], + ["1. フォークする", ["名前を自分のものに", "- プラグイン名", "- マーケットプレイス名", "自分のリポジトリから導入"]], + ["2. 削る", ["配布する Skill は一覧で決まる", "使わない Skill は外すだけ", "残すかは呼び出し回数で"]], + ["3. 育てる", ["同じ指摘を 2 回したら形に", "手順 → Skill", "決まった処理 → スクリプト", "必ず守らせる → hook"]], ].forEach((r, i) => { const c = cols(3)[i]; - card(d, s, c.x, 1.42, c.w, 2.12, r[0], bullets(r[1], 11), "accentSoft", { headSize: 13 }); + card(d, s, c.x, 1.42, c.w, 2.12, r[0], bullets(r[1], 12), "accentSoft", { headSize: 13 }); }); d.callout(s, 3.7, 1.04, "caution", "気をつける点", bullets([ - "description を増やしすぎない(すべてのセッションで読み込まれる)", - "思いつきは experimental に置き、効いたものだけを本体に移す", - "開発版を自分で使ってから正式版にする", - ], 9.5)); + "description(Skill の説明文)を増やしすぎない。全セッションで読まれる", + "思いつきは experimental で試し、効いたものだけ本体へ", + "開発版を自分で使ってから正式版に", + ], 10.5)); // 23. まとめ: 明日からの 3 ステップ - s = d.page("まとめ: 明日からの 3 ステップ", "AI に自分のやり方を教えるのは 1 回でよい。スキルプラグインにして、フォークして育てる"); + s = d.page("まとめ: 明日からの 3 ステップ", "教えるのは 1 回。プラグインにして、フォークして育てる"); [ - ["1", "ai-plugins をフォークして\n自分の名前でインストールする"], + ["1", "ai-plugins をフォークして\n自分の名前でインストール"], ["2", "使わない Skill を外す"], ["3", "同じ指摘を 2 回したら\nSkill にする"], ].forEach((r, i) => { diff --git a/docs/presentations/2026-10-17-skill-plugin-development/slides/lib/deck-frame.js b/docs/presentations/2026-10-17-skill-plugin-development/slides/lib/deck-frame.js index 1301748a6..629959582 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/slides/lib/deck-frame.js +++ b/docs/presentations/2026-10-17-skill-plugin-development/slides/lib/deck-frame.js @@ -5,6 +5,8 @@ // - 書体と色の値は DEFAULT_THEME と theme.json だけが持つ。関数に渡す色は theme.colors の項目名で受ける // - 台本(script.md)を渡すと、ページを作るたびに同じ番号の読み上げをスピーカーノートへ付け、 // 保存の前に台本のページ数と作ったページ数を照合する +// - 章扉(section)は構成を見せるページで、スピーカーノートを持たず、台本の番号を進めない。 +// ページ数の照合からも外す(ページ番号は章扉も数える) // // 資料のディレクトリへ写して使う。写した後に書き換えない(プロジェクトの値は theme.json へ書く)。 @@ -242,7 +244,9 @@ function createDeck(opts) { if (o.title) p.subject = o.title; const F = theme.font; const logo = theme.logo ? "image/png;base64," + fs.readFileSync(theme.logo).toString("base64") : null; - let pageNo = 0; + let pageNo = 0; // 作ったページ(章扉を含む)。ページ番号に使う + let scriptNo = 0; // 台本のページを消費したページ(章扉を除く) + let sections = 0; function color(name) { if (!(name in theme.colors)) throw new DeckError(`色は theme.colors の項目名で指す: ${name}`, EXIT_INPUT); @@ -271,12 +275,18 @@ function createDeck(opts) { return t.map((x) => (x && typeof x === "object" ? { ...x, options: resolve(x.options) } : x)); } - function newSlide() { + // withScript が false のページ(章扉)は台本の番号を進めず、ノートを持たない + function newSlide(withScript) { const s = p.addSlide(); pageNo++; s.background = { color: color("background") }; + if (withScript === false) { + sections++; + return s; + } + scriptNo++; if (script) { - const sp = script[pageNo - 1]; + const sp = script[scriptNo - 1]; if (sp && sp.reading) s.addNotes(sp.reading); } return s; @@ -322,7 +332,7 @@ function createDeck(opts) { } function section(title, sub) { - const s = newSlide(); + const s = newSlide(false); const c = FRAME.section; s.addShape(p.ShapeType.rect, { ...c.band, fill: { color: color("accent") }, line: { type: "none" } }); putLogo(s, FRAME.logo); @@ -366,11 +376,11 @@ function createDeck(opts) { async function save(out) { if (!out || !/\.pptx$/i.test(out)) throw new DeckError("出力のパスは .pptx で終える", EXIT_INPUT); - if (script && script.length !== pageNo) { - throw new DeckError(`ページ数が台本と合わない: 台本 ${script.length} ページ / 作ったページ ${pageNo}`, EXIT_INPUT); + if (script && script.length !== scriptNo) { + throw new DeckError(`ページ数が台本と合わない: 台本 ${script.length} ページ / 作ったページ ${scriptNo}(章扉 ${sections} を除く)`, EXIT_INPUT); } await p.writeFile({ fileName: out }); - return { out, pages: pageNo }; + return { out, pages: pageNo, sections }; } return { @@ -381,6 +391,9 @@ function createDeck(opts) { get pageNo() { return pageNo; }, + get scriptNo() { + return scriptNo; + }, color, text, cover, @@ -410,7 +423,7 @@ function run(dir, opts, build) { const deck = createDeck({ ...opts, theme, baseDir: dir }); await build(deck); const r = await deck.save(out); - console.log(`${r.out}: ${r.pages} ページ`); + console.log(`${r.out}: ${r.pages} ページ(章扉 ${r.sections})`); })().catch((e) => { console.error(e.message); process.exit(e instanceof DeckError ? e.code : 1); diff --git a/tests/test_presentation_decks.py b/tests/test_presentation_decks.py index b863e472b..49de7fcf1 100644 --- a/tests/test_presentation_decks.py +++ b/tests/test_presentation_decks.py @@ -1,8 +1,8 @@ """`docs/presentations/` の生成スクリプトの資料が作れること(#2065)。 資料のディレクトリ(`<日付>-/`)ごとに、生成スクリプトを実行して pptx を検査する。 -- 生成が終了コード 0 で、ページ数が台本と構成メモの表の行数に等しい -- 全ページのスピーカーノートが、台本の同じ番号の読み上げと一致する +- 生成が終了コード 0 で、章扉を除いたページ数が台本と構成メモの表の行数に等しい +- 章扉を除く全ページのスピーカーノートが、台本の同じ番号の読み上げと一致する。章扉はノートを持たない - 生成スクリプトが写した枠組み(`slides/lib/`)を使い、その内容が README に書いた写した時点のハッシュと一致する """ @@ -20,6 +20,9 @@ from slide_deck_support import ensure_node_modules, read_pptx, run_node, script_readings # noqa: E402 +# 章扉の帯(枠組みの FRAME.section.band。インチ)。章扉だけがこの図形を持つ +SECTION_BAND = (0.0, 0.0, 0.36, 5.62) + DECKS = sorted(p.parents[1] for p in (ROOT / "docs" / "presentations").glob("*/slides/build-deck.js")) @@ -30,6 +33,10 @@ def _notes_rows(notes: Path) -> list[int]: return [int(m.group(1)) for m in re.finditer(r"^\|\s*(\d+)\s*\|", section, re.M)] +def _is_section(slide) -> bool: + return any(s.kind == "sp" and (s.x, s.y, s.w, s.h) == SECTION_BAND for s in slide.shapes) + + def _lib_hash(lib: Path) -> str: return hashlib.sha256(b"".join(p.read_bytes() for p in sorted(lib.glob("*.js")))).hexdigest() @@ -48,12 +55,15 @@ def test_deck_builds_with_notes_from_script(deck: Path, tmp_path: Path) -> None: readings = script_readings(deck / "script.md") info = read_pptx(out) - pages = info["slides"] + sections = [p for p in info["slides"] if _is_section(p)] + pages = [p for p in info["slides"] if not _is_section(p)] assert len(pages) == len(readings) assert _notes_rows(deck / "notes.md") == list(range(1, len(pages) + 1)) + for page in sections: + assert not page.notes, "章扉にスピーカーノートがある" for no, page in enumerate(pages, start=1): - assert page.notes, f"{no} ページ目にスピーカーノートが無い" - assert page.notes == readings[no], f"{no} ページ目のノートが台本と違う" + assert page.notes, f"台本の {no} ページ目にスピーカーノートが無い" + assert page.notes == readings[no], f"台本の {no} ページ目のノートが台本と違う" @pytest.mark.parametrize("deck", DECKS, ids=[d.name for d in DECKS]) @@ -65,7 +75,13 @@ def test_deck_uses_copied_frame(deck: Path) -> None: assert _lib_hash(deck / "slides" / "lib") == recorded.group(1) -def test_skill_plugin_development_has_23_pages() -> None: +def test_skill_plugin_development_has_23_pages_and_6_sections(tmp_path: Path) -> None: deck = ROOT / "docs" / "presentations" / "2026-10-17-skill-plugin-development" assert len(script_readings(deck / "script.md")) == 23 assert _notes_rows(deck / "notes.md") == list(range(1, 24)) + ensure_node_modules(deck / "slides") + out = tmp_path / "deck.pptx" + assert run_node(deck / "slides" / "build-deck.js", out, cwd=deck).returncode == 0 + slides = read_pptx(out)["slides"] + assert len(slides) == 29 + assert sum(_is_section(p) for p in slides) == 6 From ff880ce0982af7c6cb69f0e207c0187d2302627a Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sun, 18 Oct 2026 03:37:41 +0000 Subject: [PATCH 03/10] =?UTF-8?q?Docs:=20#2065=20=E3=81=AE=E8=A8=AD?= =?UTF-8?q?=E8=A8=88=E3=81=AB=E7=AB=A0=E6=89=89=E3=81=AE=E6=89=B1=E3=81=84?= =?UTF-8?q?=E3=82=92=E6=B1=BA=E5=AE=9A=2014=20=E3=81=A8=E3=81=97=E3=81=A6?= =?UTF-8?q?=E8=B6=B3=E3=81=97=E3=80=81I5=20=E3=81=A8=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E8=A8=AD=E8=A8=88=E3=82=92=E5=90=88=E3=82=8F=E3=81=9B?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- issues/issue-2065-design-decisions.md | 18 ++++++++++++++++++ issues/issue-2065-design.md | 7 ++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/issues/issue-2065-design-decisions.md b/issues/issue-2065-design-decisions.md index 9426949c8..b64096d71 100644 --- a/issues/issue-2065-design-decisions.md +++ b/issues/issue-2065-design-decisions.md @@ -143,3 +143,21 @@ Marp を残して経路の 1 つにする案は、編集できない成果物を 2 つを外し、生成スクリプトの経路だけが残る。設計文書の「構成要素」の値の集合の記述もこの決定に従って読む。 根拠: Value 1・Value 6・Value 8(MVV 版 2) + +### 決定 14: 構成を聞き手に見せるため、章扉を台本のページを消費しないページにし、ページの文言を台本の文から起こさない + +利用者の差し戻し(2026-10-18)。発表資料を Google Slides で見て「読みづらい。スライドらしい言葉遣いに改め、章扉を入れる」 +「(内部用語の見出しが)意味が分からない」と指摘した。原因は 2 つある。各ページの文言が台本の文を縮めただけで長く、 +聞き手の知らない内部の語が説明なしに残っていた。章扉が無く、構成が見えなかった。 + +- **章扉はスピーカーノートを持たず、台本の番号を進めない。** 枠組みの `section` は台本のページを消費せず、ページ数の + 照合(I5)からも外れる。ページ番号は章扉も数える。台本は話す内容の正本で、章扉に読み上げは無いため、台本に章扉の + 区切りを置くと、読み上げの無いページを台本に持つことになる +- **ページの文言は台本の文を縮めて載せない。** 1 ページ 1 メッセージにし、見出しはそのページの結論を短く、箱と表は + 短い句か体言止めにする。聞き手が知らない内部の語は言い換えるか、ページの中で一言で説明する。規約は体裁設計 + (`layout-slide.md`)と生成の手順(`form-slide.md`)に置き、体裁レビュー(`layout-review`)が聞き手の目で見る + +章扉を台本の区切りとして持ち、ノートを空にする案は、台本に読み上げの無いページが混ざり、尺の計算と台本の番号が +章扉の数だけずれるため採らない。 + +根拠: Value 6・Value 8(MVV 版 2) diff --git a/issues/issue-2065-design.md b/issues/issue-2065-design.md index f4bd99d61..ca6ee7683 100644 --- a/issues/issue-2065-design.md +++ b/issues/issue-2065-design.md @@ -56,7 +56,7 @@ | I2 | 資料 | トーン&マナーの差し替えは `slides/theme.json` だけで済み、枠組みのファイル(`slides/lib/`)を書き換えない。枠組みを写し直してもプロジェクトの値が残る | 枠組みを書き換えた資料は、汎用テンプレートの更新を取り込めない。書き換えかどうかは、資料の `slides/lib/` の内容のハッシュを、写した時点の `lib/` の内容のハッシュ(資料の `README.md` に書いた値)と比べて決め、一致しなければ書き換えとみなしてプロジェクトの値を `theme.json` へ移す。NDF の版は基準にしない(版はスプリント単位でしか上がらず、同じ版数のまま develop で `lib/` が変わるため、版では写した元の `lib/` を一意に指せない)。新しい汎用テンプレートの `lib/` を写し直すのは更新の取り込みであり、書き換えとはみなさない。取り込んだら `README.md` のハッシュを写し直した `lib/` の値へ書き換える | | I3 | 資料 | 生成スクリプトと枠組みの関数の本体に、書体名と色の値を直接書かない。色は `theme.json` の項目名で指す | 直書きが残ると、差し替えた後も既定の値が pptx に残る。テストが落とす | | I4 | 資料 | スピーカーノートは台本のファイルの読み上げから入れる。生成スクリプトに読み上げを写さない | 写すと台本と食い違う。枠組みが台本を読み込み、ページ番号で対応させる | -| I5 | 資料 | 生成した pptx のページ数は、台本のページ数と等しい | 生成スクリプトが終了コード 1 で止まり、両方の数を出す | +| I5 | 資料 | 生成した pptx のページ数から章扉を除いた数は、台本のページ数と等しい。章扉はスピーカーノートを持たず、台本の番号を進めない(決定 14) | 生成スクリプトが終了コード 1 で止まり、台本のページ数と、章扉を除いて作ったページ数と、章扉の数を出す | | I6 | 資料 | 尺は台本の読み上げの字数を 350 字/分で割った値で、合計はページごとの値の和に等しい | 尺の上限(引数)を超えたら終了コード 1 を返し、構成メモで削るページを決める | | I7 | 汎用テンプレート・資料 | 汎用テンプレートと資料に、手本の社名・案件名・コーポレートカラーの値・ロゴを入れない | 完了判定で差分の追加行を検索し、1 件でもあれば直す(受け入れ条件 13) | @@ -340,6 +340,7 @@ classDiagram | 読み上げ | 区切りの中の `>` で始まる行。`>` だけの行は段落の区切り。スピーカーノートは読み上げの段落を改行でつないだ文 | | ほかの行 | ページに載せる要素の覚え書き。ノートにも字数にも入れない | | 最初の区切りより前 | 題・想定・構成の表。読まない | +| 章扉 | 台本に区切りを置かない。生成スクリプトの `section` が作り、ノートを持たず、台本の番号を進めない(決定 14) | | 失敗 | 区切りが 1 つも無い・番号が欠ける・重なる → 終了コード 2 と、どの番号かを出す | ### 構成メモ(`notes.md`) @@ -407,7 +408,7 @@ sequenceDiagram ## 決定の記録 -決定の記録は [issue-2065-design-decisions.md](issue-2065-design-decisions.md) にある(決定 1〜13。決定 3 は決定 12・13 が置き換えた)。 +決定の記録は [issue-2065-design-decisions.md](issue-2065-design-decisions.md) にある(決定 1〜14。決定 3 は決定 12・13 が置き換えた)。 ## テスト設計 @@ -420,7 +421,7 @@ sequenceDiagram | 受け入れ条件 5・I2・I3 | 書体・色・著作権表示・ロゴを差し替えた `theme.json` で生成すると、全ページの XML の書体名と色が差し替えた値だけになり、既定値が残らない。`lib/` を写し直しても同じ結果になる | 枠組みか見本に書体名・色を直書きすると落ちる。`theme.json` の項目を重ねずに丸ごと置き換えると、書いていない既定値が欠けて落ちる | | `theme.json` の知らない項目 | 知らない項目名を書くと終了コード 1 で、項目名を出す | 知らない項目を読み捨てると落ちる | | I4・受け入れ条件 11 | 発表資料を生成すると 23 ページすべてにノートがあり、各ノートが `script.md` の同じ番号の読み上げと一致する | ノートを生成スクリプトに写して食い違わせる・番号をずらすと落ちる | -| I5 | 台本のページ数と作ったページ数が違うと、生成が終了コード 1 で両方の数を出す | 照合を外すと落ちる | +| I5 | 台本のページ数と、章扉を除いて作ったページ数が違うと、生成が終了コード 1 で両方の数と章扉の数を出す。章扉はノートを持たず、章扉の後のページは台本の次の番号の読み上げを受ける。発表資料は章扉を除く 23 ページが台本と一致する | 照合を外す・章扉で台本の番号を進める・章扉にノートを付けると落ちる | | 台本の形の失敗 | 区切りの番号が欠ける・重なる台本で、生成と尺の計算が終了コード 2 と番号を出す | 欠けた番号を読み飛ばすと落ちる | | I6・受け入れ条件 7 | 読み上げの字数が分かっている台本で、ページごとの字数と秒(350 字/分)と合計が期待どおりで、合計がページの和に等しい。`--limit` を下回る値にすると終了コード 1 | 空白や強調の記号を字数に数える・`>` 以外の行を数えると落ちる | | 受け入れ条件 6 | 発表資料の `notes.md` の構成の表が 23 行で、番号が 1〜23(`tests/test_presentation_decks.py` が表を数える) | 行を欠かすと落ちる | From d4944bf10e899bb6cb97c5220c6e341725398281 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sun, 18 Oct 2026 03:38:36 +0000 Subject: [PATCH 04/10] =?UTF-8?q?Docs:=20=E3=82=B9=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=83=89=E3=81=AE=E8=A6=8F=E7=B4=84=E3=81=AB=E3=83=9A=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=81=AE=E6=96=87=E8=A8=80=E3=81=AE=E6=9B=B8=E3=81=8D?= =?UTF-8?q?=E6=96=B9=E3=81=A8=E7=AB=A0=E6=89=89=E3=81=AE=E6=89=B1=E3=81=84?= =?UTF-8?q?=E3=82=92=E8=B6=B3=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 体裁設計にページの文言と章立ての項目を、生成の手順にページの文言を書く節と章扉が台本の ページを消費しないことを、体裁レビューに聞き手の目で文言が読めるかの観点を足した。 Co-Authored-By: Claude Opus 5.5 --- .../ndf/skills/design/references/layout-slide.md | 4 +++- plugins/ndf/skills/layout-review/SKILL.md | 5 +++-- .../ndf/skills/release/references/form-slide.md | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/plugins/ndf/skills/design/references/layout-slide.md b/plugins/ndf/skills/design/references/layout-slide.md index 692f57a69..ce0b75cd2 100644 --- a/plugins/ndf/skills/design/references/layout-slide.md +++ b/plugins/ndf/skills/design/references/layout-slide.md @@ -14,7 +14,9 @@ | 書体 | 見出しと本文。**配布先の環境にある書体か**を確かめる | | 文字の大きさ | 見出し / 本文 / 注釈の 3 種類 | | 色 | 地・文字・強調の 3 つ。**強調は 1 色に絞る** | -| 1 枚あたりの情報量 | **1 枚 1 主張。** 行数と図の数の上限 | +| 1 ページあたりの情報量 | **1 ページ 1 メッセージ。** 行数と図の数の上限 | +| ページの文言 | **台本の文を縮めて載せない。** 見出しはそのページの結論を短く、箱・表・箇条書きは短い句か体言止めにする。聞き手が知らない内部の語は、聞き手に通じる語へ言い換えるか、ページの中で一言で説明を添える | +| 章立て | **章扉で構成を見せる。** 章の名前と一言を決める。章扉はスピーカーノートを持たず、台本のページを消費しない | | 図表の種類 | 使う図の種類と、使わない種類 | | トーン&マナー | 上の書体・色・文字の大きさに、著作権表示の文言とロゴの有無を加えた組。**既定は会社色を持たない汎用テンプレートの値**で、変える項目だけを決める | | ページの枠組み | 見出し・罫線・ロゴ・著作権表示・ページ番号・注の位置。**汎用テンプレートの枠組みの座標を使い、資料ごとに決め直さない** | diff --git a/plugins/ndf/skills/layout-review/SKILL.md b/plugins/ndf/skills/layout-review/SKILL.md index 61cb5e830..ba4e3b7e5 100644 --- a/plugins/ndf/skills/layout-review/SKILL.md +++ b/plugins/ndf/skills/layout-review/SKILL.md @@ -24,7 +24,7 @@ allowed-tools: | --- | --- | | 事実・数値・出典・表現・構成 | コードレビュー(`cross-review`)と完了判定の事実確認 | | 生成物と正本の内容の一致 | リリースの工程の内容照合(`document-systems` の `import.md`) | -| **版面**(文字あふれ・図の重なり・改ページ・書体) | **この Skill** | +| **版面**(文字あふれ・図の重なり・改ページ・書体・聞き手の目で文言が読めるか) | **この Skill** | **重なる観点を持たない。** 版面の指摘と内容の指摘が混ざると、どちらの工程へ戻すのかが 決まらない。 @@ -41,7 +41,7 @@ allowed-tools: | 見方 | 項目 | | --- | --- | | 機械で測る | 外部への参照が残っていないか / 図の横幅 / ページ数 / 1 枚あたりの文字数 | -| **人が画像を見る** | **枠からの文字あふれ / 図の重なり / 改ページの位置 / 書体の代替** | +| **人が画像を見る** | **枠からの文字あふれ / 図の重なり / 改ページの位置 / 書体の代替 / 聞き手の目で文言が読めるか(1 ページの文字量・文の長さ・説明の無い内部の語)** | **機械で測るものを先に通す。** 落ちたらそこで生成へ戻す。画像を見るのはその後である。 @@ -93,6 +93,7 @@ allowed-tools: | 原因 | 戻す先 | | --- | --- | | 版面の決め方(余白・文字の大きさ・1 枚あたりの情報量) | 体裁設計(`design` の `layout-<出力の形>.md`) | +| ページの文言(台本の文を縮めて載せた・説明の無い内部の語が残る) | 生成スクリプトのページの文言(`release` の `form-<出力の形>.md`) | | 生成の経路(変換器の設定、指示の与え方、生成スクリプト) | リリースの生成の手順(`release` の `form-<出力の形>.md`) | | 内容が多すぎて収まらない | **執筆へ戻す**(主張の数を減らす) | diff --git a/plugins/ndf/skills/release/references/form-slide.md b/plugins/ndf/skills/release/references/form-slide.md index aa8edc9da..894673340 100644 --- a/plugins/ndf/skills/release/references/form-slide.md +++ b/plugins/ndf/skills/release/references/form-slide.md @@ -28,6 +28,9 @@ AI エージェントがその場で組む経路は使わない。変換器の **台本の `## <番号>. <見出し>` の 1 つが 1 ページに対応する。** 生成スクリプトはページを作るたびに同じ番号の 読み上げをスピーカーノートへ入れ、保存の前に台本のページ数と作ったページ数を照合する。 +**章扉(`section`)は台本のページを消費しない。** 章扉はスピーカーノートを持たず、台本の番号を進めず、ページ数の +照合から外れる。台本に章扉の区切りを置かない。ページ番号は章扉も数える。 + ## 生成スクリプトの経路 汎用テンプレートはこの Skill の [assets/slides/](../assets/slides/) にある。版面は 16:9(10 × 5.625 インチ)、 @@ -109,6 +112,7 @@ cat <資料>/slides/lib/*.js | sha256sum | 読み上げ | 区切りの中の `>` で始まる行。`>` だけの行は段落の区切り | | ほかの行 | ページに載せる要素の覚え書き。ノートにも字数にも入らない | | 最初の区切りより前 | 題・想定・構成の表。読まない | +| 章扉 | 区切りを置かない(章扉は生成スクリプトの `section` が作る) | ```bash node <資料>/slides/lib/script-time.js <資料>/script.md --limit <持ち時間の分> @@ -117,6 +121,17 @@ node <資料>/slides/lib/script-time.js <資料>/script.md --limit <持ち時間 ページごとと合計の字数・尺(350 字/分)・配分秒数の表を出す。出力を構成メモか `README.md` に載せる。合計が `--limit` を超えたら終了コード 1 で、構成メモで削るページを決めて台本を直す。台本の番号が欠ける・重なると終了コード 2。 +### ページの文言を書く + +**読み上げはスピーカーノートが持つ。ページには要点だけを置く。** + +- 台本の文を縮めて載せない。1 ページ 1 メッセージにする +- 見出しはそのページの結論を短く書く。リードは 1 行にする +- 箱・表・箇条書きは短い句か体言止めにする(「〜する」「〜である」の文を並べない) +- 聞き手が知らない内部の語は、聞き手に通じる語へ言い換えるか、ページの中で一言で説明を添える。Skill やコマンドの + 名前を出すときは、何をするものかを添える +- 章扉(`section`)で構成を見せる。章の名前と一言は構成メモに書く + ### 生成する ```bash From b478e00bab76cecaff43a4ed50670acf7a5f7575 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sun, 18 Oct 2026 03:43:52 +0000 Subject: [PATCH 05/10] =?UTF-8?q?Update:=20=E7=99=BA=E8=A1=A8=E8=B3=87?= =?UTF-8?q?=E6=96=99=E3=81=AE=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AE=E6=96=87?= =?UTF-8?q?=E8=A8=80=E3=82=92=E3=81=95=E3=82=89=E3=81=AB=E5=89=8A=E3=82=8A?= =?UTF-8?q?=E3=80=81=E4=BD=99=E7=99=BD=E3=81=AE=E5=81=8F=E3=82=8A=E3=81=A8?= =?UTF-8?q?=E6=8A=98=E3=82=8A=E8=BF=94=E3=81=97=E3=82=92=E7=9B=B4=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit サムネイルで全ページを見て、補足の文を注へ回すか落とし、文字の大きさと区画の高さを合わせた。 数値と台本は変えていない。 Co-Authored-By: Claude Opus 5.5 --- .../slides/build-deck.js | 134 +++++++++--------- 1 file changed, 66 insertions(+), 68 deletions(-) diff --git a/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js b/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js index 095da747f..954e21c79 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js +++ b/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js @@ -82,30 +82,30 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の d.chip(s, c.x, 1.5, c.w, 1.0, q, "panel1", { fontSize: 15 }); }); d.arrow(s, 4.99, 2.62, 0, 0.42); - d.callout(s, 3.16, 0.76, "caution", "セッションが切れると、AI は忘れる", "毎日使うほど、同じ指摘が積み上がる"); + d.callout(s, 3.16, 0.5, "caution", "セッションが切れると、AI は忘れる", null); // 3. CLAUDE.md への追記だけでは足りない理由 s = d.page("CLAUDE.md への追記では足りない", "追記はすぐに 4 つの壁に当たる"); [ - ["コンテキスト", "毎回すべて読む。書くほど重く、ルールが埋もれる"], + ["コンテキスト", "毎回全部読む。書くほど埋もれる"], ["強制力", "書いても守られない"], - ["手順", "実行の順序は文章で伝わりにくい"], - ["ポータビリティ", "リポジトリやランタイムを変えると消える"], + ["手順", "順序は文章で伝わらない"], + ["ポータビリティ", "環境を変えると消える"], ].forEach((r, i) => { const c = cols(2)[i % 2]; - card(d, s, c.x, 1.42 + Math.floor(i / 2) * 1.62, c.w, 1.46, r[0], r[1], "panel1", { band: "accent", headSize: 15, bodySize: 13, headH: 0.42 }); + card(d, s, c.x, 1.5 + Math.floor(i / 2) * 1.5, c.w, 1.3, r[0], r[1], "panel1", { band: "accent", headSize: 18, bodySize: 16, headH: 0.46 }); }); // 4. スキルプラグインとは s = d.page("スキルプラグインとは", "やり方をパッケージにして、どこでも再利用"); d.tbl(s, [ [d.H("構成要素"), d.H("役割")], - [d.B("Skill"), "手順書。「いつ・何を・どの順で」。必要なときだけ読む"], - [d.B("スクリプト"), "決まった処理を確実に実行"], - [d.B("hook"), "ルールを強制(AI の操作の直前に割り込む)"], - [d.B("サブエージェント"), "役割ごとに分けた AI"], + [d.B("Skill"), "手順書。必要なときだけ読む"], + [d.B("スクリプト"), "決まった処理を確実に"], + [d.B("hook"), "ルールを強制"], + [d.B("サブエージェント"), "役割ごとの AI"], ], { x: X0, y: 1.4, w: W, colW: [2.2, 7.28], rowH: [0.34, 0.46, 0.46, 0.46, 0.46], fontSize: 13 }); - d.text(s, "1 つの配布物を、4 つのランタイムで", { x: X0, y: 3.74, w: W, h: 0.3, fontSize: 12, bold: true, color: "text" }); + d.text(s, "同じものを 4 つのランタイムで", { x: X0, y: 3.74, w: W, h: 0.3, fontSize: 12, bold: true, color: "text" }); cols(4).forEach((c, i) => d.chip(s, c.x, 4.1, c.w, 0.62, ["Claude Code", "Codex", "Kiro", "agy"][i], "accentSoft", { fontSize: 13 })); // ==== 章 2 ==== @@ -114,10 +114,10 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の // 5. NDF: 1 人の開発スタイルをプラグインにした s = d.page("NDF: 自分のやり方をプラグインに", "GitHub で公開。ほぼ 1 人と AI で開発"); [ - ["102", "回", "リリース(開発を始めて半年)"], - ["48", "個", "Skill(手順書)"], - ["4", "つ", "対応するランタイム"], - ["10", "リポジトリ", "自分以外への導入"], + ["102", "回", "リリース(半年)"], + ["48", "個", "Skill"], + ["4", "つ", "ランタイム"], + ["10", "リポジトリ", "自分以外へ導入"], ].forEach((r, i) => { const c = cols(4)[i]; stat(d, s, c.x, 1.55, c.w, 1.7, r[0], r[1], r[2]); @@ -142,12 +142,12 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の d.chip(s, 8.5, 1.9, 1.24, 0.9, "リリース", "panel2", { fontSize: 11 }); cols(2).forEach((c, i) => { const g = [ - ["承認 1: 設計", "スプリントの設計をまとめて 1 回"], - ["承認 2: 本番リリース", "開発版で確かめた後に 1 回"], + ["承認 1: 設計", "設計をまとめて 1 回"], + ["承認 2: 本番リリース", "開発版で確かめて 1 回"], ][i]; card(d, s, c.x, 3.52, c.w, 0.86, g[0], g[1], "caution", { headSize: 12, bodySize: 11, headH: 0.3 }); }); - d.text(s, "決めたこと・未検証の点・根拠がそろって届く → 1 回で決められる", { x: X0, y: 4.46, w: W, h: 0.3, fontSize: 11, bold: true, color: "text", valign: "middle" }); + d.text(s, "判断の材料はそろって届く", { x: X0, y: 4.46, w: W, h: 0.3, fontSize: 11, bold: true, color: "text", valign: "middle" }); d.note(s, "例: 直前のリリース — 致命的な Issue 18 件を 2 スプリントに分け、スプリントの中は 3 グループを並列に"); // 7. /ndf:development-workflow @@ -191,35 +191,35 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の // 9. 実績のある開発技法を組み込む s = d.page("実績のある開発技法を組み込む", "新しい発明ではない。AI が毎回守れる手順にした"); [ - ["DDD: ユビキタス言語", ["要件定義の前に用語集", "1 つの語に 1 つの意味", "例: 「注文」は受注と配送で別", "用語集 580 語、CI が検査"]], - ["テストファースト", ["失敗するテスト", "- → 通す最小の実装", "- → 整理", "薄いコードは", "- 先に今の振る舞いを固定"]], - ["アジャイル", ["マイルストーン = バックログ", "スプリントごとに振り返り", "バックログの整理も毎回", "優先順位は WSJF", "- 遅延コスト ÷ 大きさ"]], + ["DDD: ユビキタス言語", ["要件定義の前に用語集", "1 語 1 意味", "580 語を CI が検査"]], + ["テストファースト", ["失敗するテストから", "- → 実装 → 整理", "薄いコードは先にテスト"]], + ["アジャイル", ["マイルストーン = バックログ", "スプリントごとに振り返り", "優先順位は WSJF"]], ].forEach((r, i) => { const c = cols(3)[i]; - card(d, s, c.x, 1.42, c.w, 2.5, r[0], bullets(r[1], 13), "panel1", { band: "accent", headSize: 14 }); + card(d, s, c.x, 1.5, c.w, 2.1, r[0], bullets(r[1], 14), "panel1", { band: "accent", headSize: 14 }); }); // 10. /ndf:requirements-design と /ndf:design s = d.page("コードの前に「何を」「どう」", "実装の前に、要件と設計の 2 つの文書を書く"); [ - ["/ndf:requirements-design — 完了の条件", ["確かめられる受け入れ条件に", "「いい感じに」「速く」を条件へ"]], - ["/ndf:design — 作り方", ["データ構造・入出力・流れ・理由", "担当の AI が変わっても同じ構成に"]], + ["/ndf:requirements-design — 完了の条件", ["確かめられる受け入れ条件", "「速く」を測れる条件へ"]], + ["/ndf:design — 作り方", ["構造・入出力・流れ・理由", "AI が変わっても同じ構成"]], ].forEach((r, i) => { const c = cols(2)[i]; - card(d, s, c.x, 1.42, c.w, 1.5, r[0], bullets(r[1], 13), "panel1", { band: "accent", headSize: 13 }); + card(d, s, c.x, 1.5, c.w, 1.5, r[0], bullets(r[1], 15), "panel1", { band: "accent", headSize: 13 }); }); - d.callout(s, 3.2, 0.72, "caution", "設計は PR で出す", "主題ごとに並列に開き、承認 1 でまとめて承認"); + d.callout(s, 3.2, 0.72, "caution", "設計は PR で出す", "主題ごとに並列、承認 1 でまとめて"); // 11. /ndf:document-restructuring s = d.page("結論から読める順に並べ替える", "/ndf:document-restructuring — 書き上げた文書の再構成"); - d.text(s, "AI が書いた順(調べた順)", { x: X0, y: 1.4, w: 4.3, h: 0.3, fontSize: 12, bold: true, color: "text" }); + d.text(s, "AI が書いた順", { x: X0, y: 1.4, w: 4.3, h: 0.3, fontSize: 12, bold: true, color: "text" }); flow(d, s, X0, 1.76, 4.3, 0.56, ["問題に気づく", "測る", "結論"], { fill: "panel2", size: 11 }); d.arrow(s, 4.66, 2.04, 0.5, 0, "accent"); d.text(s, "読み手が知りたい順", { x: 5.32, y: 1.4, w: 4.42, h: 0.3, fontSize: 12, bold: true, color: "text" }); flow(d, s, 5.32, 1.76, 4.42, 0.56, ["結論", "根拠", "経緯"], { size: 11 }); d.text(s, "手順(内容は足さない)", { x: X0, y: 2.62, w: W, h: 0.3, fontSize: 12, bold: true, color: "text" }); - flow(d, s, X0, 2.98, W, 0.62, ["測る", "章を読み手の\n問いの順に", "長い文を分ける", "対比は表に", "もう一度測る"], { size: 11 }); - d.callout(s, 3.86, 0.7, "panel1", "レビューの前に通す", "構成の指摘と内容の指摘が混ざらない"); + flow(d, s, X0, 2.98, W, 0.62, ["測る", "問いの順に並べる", "文を分ける", "対比は表に", "測り直す"], { size: 11 }); + d.callout(s, 3.86, 0.5, "panel1", "レビューの前に通す → 構成と内容の指摘を分ける", null); // 12. /ndf:cross-review s = d.page("2 つの AI で、指摘が尽きるまで", "/ndf:cross-review — 別々の AI が交互にレビュー"); @@ -227,8 +227,7 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の flow(d, s, X0, 1.76, 5.0, 0.7, ["AI を 2 つ選ぶ\nclaude・codex・kiro", "レビュー", "指摘を修正"], { size: 10 }); d.arrow(s, 2.9, 2.54, 0, 0.3); d.chip(s, 1.5, 2.9, 2.8, 0.56, "新しい指摘は?", "panel2", { fontSize: 11 }); - d.text(s, "出た → 組み合わせを替えて繰り返す\n出ない → 終了", { x: X0, y: 3.56, w: 5.0, h: 0.56, fontSize: 11 }); - d.text(s, "観点は PR の中身で切り替え。基準はリポジトリごと", { x: X0, y: 4.22, w: 5.0, h: 0.4, fontSize: 10.5 }); + d.text(s, "出た → 組み替えて繰り返す\n出ない → 終了", { x: X0, y: 3.56, w: 5.0, h: 0.56, fontSize: 11 }); d.text(s, "指摘の再現率(手元の実測)", { x: 5.6, y: 1.42, w: 4.14, h: 0.3, fontSize: 12, bold: true, color: "text" }); [ ["1 つの AI(低い方)", 0.21, "panel2"], @@ -243,10 +242,10 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の // 13. /ndf:cross-refactoring s = d.page("全員で提案、1 人が時間内に直す", "/ndf:cross-refactoring — 複数の AI でリファクタリング"); - flow(d, s, X0, 1.5, W, 0.86, ["全員の AI が\n1 回ずつ提案", "1 つの AI を選ぶ", "時間内(既定 30 分)\nに収まる分を実施", "テストが壊れたら\n取り消す"], { size: 11 }); + flow(d, s, X0, 1.5, W, 0.86, ["全員の AI が提案", "1 つを選ぶ", "既定 30 分で\n収まる分を実施", "テストが壊れたら\n取り消す"], { size: 11 }); d.text(s, "実施: 計画 → テスト追加 → 実装 → 検証", { x: 5.0, y: 2.48, w: 4.74, h: 0.3, fontSize: 11, align: "center" }); - card(d, s, X0, 3.0, 4.66, 1.2, "終わりが見える", "時間の数値は、すべて持ち時間から逆算", "panel1", { band: "accent", headSize: 13, bodySize: 12.5 }); - card(d, s, 5.08, 3.0, 4.66, 1.2, "結果が追える", "採用と取り消しの内訳を報告に残す", "panel1", { band: "accent", headSize: 13, bodySize: 12.5 }); + card(d, s, X0, 3.0, 4.66, 1.1, "終わりが見える", "すべて持ち時間から逆算", "panel1", { band: "accent", headSize: 15, bodySize: 15, headH: 0.38 }); + card(d, s, 5.08, 3.0, 4.66, 1.1, "結果が追える", "採用と取り消しを報告", "panel1", { band: "accent", headSize: 15, bodySize: 15, headH: 0.38 }); // ==== 章 4 ==== d.section("4. AI に任せ切る仕組み", "止まらない・迷わない・ごまかさない"); @@ -263,60 +262,59 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の d.text(s, r[1], { x: 2.12 + i * 0.3, y, w: 2.95, h: 0.74, fontSize: 11.5, valign: "middle" }); if (i < 2) d.arrow(s, 1.1 + i * 0.3, y + 0.76, 0.2, 0.24); }); - card(d, s, 5.7, 1.46, 4.04, 1.3, "止まっても続きから", "工程を JSON の計画にし、スクリプトが実行", "panel1", { band: "accent", headSize: 13, bodySize: 12.5 }); - card(d, s, 5.7, 2.96, 4.04, 1.5, "セッションを自動で切り替え", "コンテキストが膨らんだら、次のコマンドを残して新しいセッションへ", "panel1", { band: "accent", headSize: 13, bodySize: 12.5 }); + card(d, s, 5.7, 1.46, 4.04, 1.3, "止まっても続きから", "JSON の計画をスクリプトが実行", "panel1", { band: "accent", headSize: 13, bodySize: 12.5 }); + card(d, s, 5.7, 2.96, 4.04, 1.5, "セッションを自動で切り替え", "次のコマンドを残して新しいセッションへ", "panel1", { band: "accent", headSize: 13, bodySize: 12.5 }); // 15. 判断基準: MVV と 2 つの承認ゲート s = d.page("迷ったら MVV で判断する", "判断の根拠の番号をログに残す"); - d.text(s, "AI が迷う場面: 指摘を直すか見送るか・承認を省いてよいか", { x: X0, y: 1.38, w: W, h: 0.3, fontSize: 11.5 }); [ ["Mission", "何のためにやるか"], ["Vision", "目指す姿"], ["Value", "判断の基準"], ].forEach((r, i) => { const c = cols(3, 0.16, X0, 5.9)[i]; - card(d, s, c.x, 1.8, c.w, 1.2, r[0], r[1], "accentSoft", { headSize: 15, bodySize: 12.5 }); + card(d, s, c.x, 1.5, c.w, 1.3, r[0], r[1], "accentSoft", { headSize: 15, bodySize: 13 }); }); - d.text(s, "Value の例: 「まず届ける」「処理の改善に寄与しない指摘は直さない」", { x: X0, y: 3.16, w: W, h: 0.34, fontSize: 12, valign: "middle" }); - card(d, s, 6.4, 1.8, 3.34, 1.2, "必ず人が承認する操作", "本番デプロイなど\nMVV に照らしても AI は省かない", "caution", { headSize: 13, bodySize: 12 }); - d.callout(s, 3.7, 0.4, "panel1", "どこまで AI に任せるかは、事前に設定で選べる", null); + d.text(s, "Value の例: 「まず届ける」「処理の改善に寄与しない指摘は直さない」", { x: X0, y: 2.96, w: W, h: 0.34, fontSize: 12.5, valign: "middle" }); + card(d, s, 6.4, 1.5, 3.34, 1.3, "必ず人が承認する操作", "本番デプロイなど\nAI は省かない", "caution", { headSize: 13, bodySize: 12 }); + d.callout(s, 3.5, 0.4, "panel1", "AI に任せる範囲は、事前に設定で選ぶ", null); // 16. /ndf:quality-gates と /ndf:investigation-rules s = d.page("「完了」も「無い」も証拠で", "AI の言葉を、そのままは信じない"); [ - ["「完了しました」", "/ndf:quality-gates — 完了の判定", "次の 4 つがそろうまで完了としない\nコマンド・終了コード・時刻・範囲"], - ["「該当のコードはありません」", "/ndf:investigation-rules — 調査の規則", "「無い」の結論には必ず\n検索のコマンドと結果を添える"], + ["「完了しました」", "/ndf:quality-gates — 完了の判定", "4 点がそろって完了\nコマンド・終了コード・時刻・範囲"], + ["「該当のコードはありません」", "/ndf:investigation-rules — 調査の規則", "検索のコマンドと結果を添える"], ].forEach((r, i) => { const c = cols(2)[i]; d.chip(s, c.x, 1.48, c.w, 0.72, r[0], "panel2", { fontSize: 15 }); d.arrow(s, c.x + c.w / 2, 2.26, 0, 0.3); card(d, s, c.x, 2.62, c.w, 1.4, r[1], r[2], "panel1", { band: "accent", headSize: 12.5, bodySize: 13 }); }); - d.text(s, "AI はもっともらしい推論で「無い」と断定しがち", { x: X0, y: 4.3, w: W, h: 0.34, fontSize: 12, bold: true, color: "text", align: "center", valign: "middle" }); + d.text(s, "AI は「無い」と断定しがち", { x: X0, y: 4.3, w: W, h: 0.34, fontSize: 12, bold: true, color: "text", align: "center", valign: "middle" }); // 17. 自分を直すループ(導入先のプロジェクトの Issue の一覧を整理し続ける) s = d.page("やるべきことを整理し続ける", "導入したプロジェクトの Issue の一覧を、いつも現状に"); [ - ["/ndf:out-of-scope — 見つけたら起票", "作業中に見つけた範囲外の問題を、その場で Issue に"], - ["/ndf:retrospective — 振り返り", "リリースごとに進め方を見直す"], - ["/ndf:backlog-refinement — 一覧の手入れ", "スプリントの終わりに、Issue を現状へ合わせて並べ直す"], + ["/ndf:out-of-scope — 見つけたら起票", "範囲外の問題を即 Issue に"], + ["/ndf:retrospective — 振り返り", "リリースごとに見直す"], + ["/ndf:backlog-refinement — 一覧の手入れ", "スプリント末に一覧を現状へ"], ].forEach((r, i) => { const y = 1.42 + i * 1.08; card(d, s, X0, y, 4.9, 0.92, r[0], r[1], "accentSoft", { headSize: 12, bodySize: 11, headH: 0.28 }); if (i < 2) d.arrow(s, 2.7, y + 0.93, 0, 0.14); }); - stat(d, s, 5.4, 1.42, 4.34, 1.44, "56", "%", "本文が現状とずれていた open の Issue\n(43 件中 24 件・ある時点の棚卸し)"); - stat(d, s, 5.4, 3.0, 4.34, 1.5, "49", "件", "本文が古くなっていた Issue\n(今日 71 件を並列に調査・ほかに 2 件は修正済み)"); - d.note(s, "放っておくと、もう直った Issue を直すことになる"); + stat(d, s, 5.4, 1.42, 4.34, 1.44, "56", "%", "本文が現状とずれた open の Issue\n(43 件中 24 件)"); + stat(d, s, 5.4, 3.0, 4.34, 1.5, "49", "件", "本文が古かった Issue\n(71 件を調査。2 件は修正済み)"); + d.note(s, "56% はある時点の棚卸し、49 件は 2026-10-17 の調査。放っておくと、直った Issue を直すことになる"); // 18. その他の特徴的な機能 s = d.page("そのほかの特徴的な機能", "見積もり・リリース・AI の操作の強制停止"); d.tbl(s, [ [d.H("機能"), d.H("何をするか"), d.H("要点")], - [d.B("/ndf:estimate"), "AI が作業する前提の見積もり", "人日を使わない。AI の時間・人の時間・完了まで・費用を過去の実績から"], - [d.B("/ndf:release"), "届け方の違う成果物を同じ骨格でリリース", "パッケージ・Web サービス・スライド・文書"], - [d.B("hook"), "AI がやりがちな操作を強制的に止める", "直接編集の警告・sleep の待ちの禁止・同じファイルの読み直しの防止"], - ], { x: X0, y: 1.42, w: W, colW: [1.9, 3.3, 4.28], rowH: [0.36, 0.86, 0.7, 0.86], fontSize: 12 }); + [d.B("/ndf:estimate"), "AI 前提の見積もり", "人日でなく、時間と費用の 4 指標"], + [d.B("/ndf:release"), "成果物を同じ骨格でリリース", "パッケージ〜スライド・文書"], + [d.B("hook"), "AI のやりがちな操作を止める", "直接編集・sleep の待ち・読み直し"], + ], { x: X0, y: 1.42, w: W, colW: [2.1, 3.5, 3.88], rowH: [0.42, 0.72, 0.72, 0.72], fontSize: 14 }); // ==== 章 5 ==== d.section("5. 事例", "ほかのプロジェクトでも動くか"); @@ -325,18 +323,18 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の s = d.page("事例: ほかのプロジェクトでも動く", "言語もチームも違う 4 つのプロジェクト"); d.tbl(s, [ [d.H("プロジェクトの種類"), d.H("結果")], - [d.B("AWS のインフラ"), "1 日のマージ PR 0.95 → 3.38(導入前後 37 日)\n※ 社内ツールの移行と重なる。すべてが NDF の効果ではない"], - [d.B("PHP の業務システム\n(20 人で開発)"), "設計 PR 1 本の承認から 51 時間で、実装 PR 31 本"], - [d.B("機械学習のモデル"), "4 か月分のデータ欠損を発見。修正〜本番を 1 スプリントで\n相関係数 0.73 → 0.954"], - [d.B("価格推定"), "PR 数は同じ。レビュー必須の開発プロセスへ"], + [d.B("AWS のインフラ"), "マージ PR 0.95 → 3.38 件/日\n※ 社内ツールの移行と重なる"], + [d.B("PHP の業務システム\n(20 人で開発)"), "設計の承認から 51 時間で実装 PR 31 本"], + [d.B("機械学習のモデル"), "データ欠損を発見し 1 スプリントで本番へ\n相関係数 0.73 → 0.954"], + [d.B("価格推定"), "PR 数は同じ。レビュー必須へ"], ], { x: X0, y: 1.42, w: W, colW: [2.3, 7.18], rowH: [0.34, 0.74, 0.6, 0.74, 0.5], fontSize: 12 }); - d.note(s, "プロジェクトは種類で呼び、名前は出さない。数値は 2026-10-17 時点の実測"); + d.note(s, "インフラは導入前後 37 日の比較。プロジェクトは種類で呼ぶ。数値は 2026-10-17 時点の実測"); // 20. 事例から分かったこと s = d.page("事例から分かった 2 つのこと", "詰まりは次の版で直り、ルールはプロジェクトごと"); card(d, s, X0, 1.42, 4.66, 2.48, "1. 最初は詰まり、次の版で直る", bullets([ - "詰まりが NDF の Issue になる", - "PHP では前提の違いで 17 件", + "詰まりが Issue になり、次の版で直る", + "PHP では 17 件", "- 作業用の worktree に vendor/ が無い", "- 全テストに 64 分", "- マージ = 本番リリース", @@ -356,23 +354,23 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の s = d.page("だから、フォークして自分用に", "仕組みはそのまま、ルールだけ差し替える"); card(d, s, X0, 1.42, 4.66, 1.86, "NDF も私のルールのかたまり", bullets(["承認は 2 回だけ", "文書では「道具」でなく「Tool」", "文言を照合するテストは書かない"], 12.5), "warn", { headSize: 13 }); card(d, s, 5.08, 1.42, 4.66, 1.86, "そのまま使う仕組み", bullets(["ワークフロー", "3 層のエージェント", "クロスレビュー", "判断基準(MVV)"], 12.5), "accentSoft", { headSize: 13 }); - d.text(s, "他人のルールは自分にはノイズ → 導入よりフォーク", { x: X0, y: 3.36, w: W, h: 0.34, fontSize: 12, bold: true, color: "text", align: "center", valign: "middle" }); + d.text(s, "他人のルールはノイズ → フォークする", { x: X0, y: 3.36, w: W, h: 0.34, fontSize: 12, bold: true, color: "text", align: "center", valign: "middle" }); cols(3).forEach((c, i) => d.chip(s, c.x, 3.84, c.w, 0.72, ["指摘が資産になる", "ランタイムに依存しない", "計測して改善できる"][i], "panel1", { fontSize: 13 })); // 22. 始め方と育て方 s = d.page("始め方と育て方", "フォークする → 削る → 育てる"); [ - ["1. フォークする", ["名前を自分のものに", "- プラグイン名", "- マーケットプレイス名", "自分のリポジトリから導入"]], - ["2. 削る", ["配布する Skill は一覧で決まる", "使わない Skill は外すだけ", "残すかは呼び出し回数で"]], + ["1. フォークする", ["名前を自分のものに", "自分のリポジトリから導入"]], + ["2. 削る", ["使わない Skill は外す", "残すかは呼び出し回数で"]], ["3. 育てる", ["同じ指摘を 2 回したら形に", "手順 → Skill", "決まった処理 → スクリプト", "必ず守らせる → hook"]], ].forEach((r, i) => { const c = cols(3)[i]; - card(d, s, c.x, 1.42, c.w, 2.12, r[0], bullets(r[1], 12), "accentSoft", { headSize: 13 }); + card(d, s, c.x, 1.42, c.w, 1.9, r[0], bullets(r[1], 13.5), "accentSoft", { headSize: 14 }); }); - d.callout(s, 3.7, 1.04, "caution", "気をつける点", bullets([ - "description(Skill の説明文)を増やしすぎない。全セッションで読まれる", - "思いつきは experimental で試し、効いたものだけ本体へ", - "開発版を自分で使ってから正式版に", + d.callout(s, 3.5, 1.04, "caution", "気をつける点", bullets([ + "description(Skill の説明文)は最小限", + "思いつきは experimental で試す", + "開発版を使ってから正式版に", ], 10.5)); // 23. まとめ: 明日からの 3 ステップ From be237ca91bcf68b2bcb789eb33b1d8b5fcf335a4 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sun, 18 Oct 2026 05:30:30 +0000 Subject: [PATCH 06/10] =?UTF-8?q?Update:=20=E7=99=BA=E8=A1=A8=E8=B3=87?= =?UTF-8?q?=E6=96=99=E3=81=AE=20cross-review=20=E3=81=AE=E3=83=9A=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=81=8B=E3=82=89=E5=86=8D=E7=8F=BE=E7=8E=87=E3=81=AE?= =?UTF-8?q?=E6=95=B0=E5=80=A4=E3=82=92=E5=A4=96=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 指摘の再現率(0.21 / 0.49 / 0.91)は測り直すと再現せず、指標が修正担当の判断を正解にした 自己採点だった(#2071 で指標を決め直す)。指標が決まるまでページと台本に数値を載せない。 - 生成スクリプト: 棒グラフと見出しを外し、AI の入れ替えと観点の切り替えの 2 区画に置き換える - 台本: 12 ページの読み上げから数値の 1 文を外す(正本の Google ドキュメントからも同じ 1 文を消した) - 構成メモ: 根拠の列と尺(5,261 字・15 分 2 秒)を合わせる Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JmtvCAsKEdaC7W9gdwjiaN --- .../2026-10-17-skill-plugin-development/notes.md | 8 ++++---- .../2026-10-17-skill-plugin-development/script.md | 2 +- .../slides/build-deck.js | 13 ++----------- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/docs/presentations/2026-10-17-skill-plugin-development/notes.md b/docs/presentations/2026-10-17-skill-plugin-development/notes.md index dff8c97ed..f867f432b 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/notes.md +++ b/docs/presentations/2026-10-17-skill-plugin-development/notes.md @@ -28,7 +28,7 @@ | 9 | 実績のある開発技法を組み込む | DDD・テストファースト・アジャイルの 3 列。用語集 580 語と WSJF を載せる | 台本 9 | | 10 | コードの前に「何を」「どう」 | `/ndf:requirements-design`(完了の条件)と `/ndf:design`(作り方)の 2 列。設計 PR と承認 1 を下の区画で示す | 台本 10 | | 11 | 結論から読める順に並べ替える | `/ndf:document-restructuring` の、書いた順と読み手の順の対比と、内容を足さない 5 つの手順 | 台本 11 | -| 12 | 2 つの AI で、指摘が尽きるまで | `/ndf:cross-review` の繰り返しの流れと、指摘の再現率(1 つの AI 0.21〜0.49、2 つで 0.91)の棒 | 台本 12(手元の実測) | +| 12 | 2 つの AI で、指摘が尽きるまで | `/ndf:cross-review` の繰り返しの流れと、AI の組み合わせの入れ替え・観点の切り替えの 2 区画(数値は載せない) | 台本 12 | | 13 | 全員で提案、1 人が時間内に直す | `/ndf:cross-refactoring` の提案 → 選ぶ → 時間内に実施 → 取り消しの流れと、逆算と内訳の 2 区画 | 台本 13 | | 14 | 3 層のエージェントで止めない | conductor(指揮役)・supervisor(監督役)・worker(作業役)の階段の図と、再開とセッションの切り替えの 2 区画 | 台本 14 | | 15 | 迷ったら MVV で判断する | Mission・Vision・Value の 3 区画と Value の例 2 つ、必ず人が承認する操作 | 台本 15、`.ndf/` のプロジェクト MVV | @@ -57,7 +57,7 @@ ## 尺 -`node slides/lib/script-time.js script.md --limit 35` の出力(350 字/分)。合計 5,317 字・15 分 12 秒で、 +`node slides/lib/script-time.js script.md --limit 35` の出力(350 字/分)。合計 5,261 字・15 分 2 秒で、 持ち時間(30〜35 分)に収まる。削るページは無い。 | # | 見出し | 字数 | 尺(秒) | 配分(秒) | @@ -73,7 +73,7 @@ | 9 | 実績のある開発技法を、AI が守れる形で組み込む — DDD・テストファースト・アジャイル | 381 | 65.3 | — | | 10 | /ndf:requirements-design と /ndf:design — コードより先に「何を」「どう」を決める | 230 | 39.4 | — | | 11 | /ndf:document-restructuring — 書いた順ではなく、読み手が知りたい順へ | 190 | 32.6 | — | -| 12 | /ndf:cross-review — 2 つの AI で、新しい指摘が出なくなるまで | 223 | 38.2 | — | +| 12 | /ndf:cross-review — 2 つの AI で、新しい指摘が出なくなるまで | 167 | 28.6 | — | | 13 | /ndf:cross-refactoring — 全員で提案し、1 人が時間内に実施する | 206 | 35.3 | — | | 14 | 3 層のエージェント — 長いタスクを止めずに、止まっても再開できる | 249 | 42.7 | — | | 15 | 判断基準: MVV と 2 つの承認ゲート | 193 | 33.1 | — | @@ -85,7 +85,7 @@ | 21 | だから「フォークして自分用に」 | 225 | 38.6 | — | | 22 | 始め方と育て方 | 312 | 53.5 | — | | 23 | まとめ: 明日からの 3 ステップ | 180 | 30.9 | — | -| 合計 | 23 ページ(350 字/分) | 5317 | 911.5(15分12秒) | — | +| 合計 | 23 ページ(350 字/分) | 5261 | 901.9(15分02秒) | — | ## ページに収まらない読み上げの扱い diff --git a/docs/presentations/2026-10-17-skill-plugin-development/script.md b/docs/presentations/2026-10-17-skill-plugin-development/script.md index c82c5aff7..7ab91f425 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/script.md +++ b/docs/presentations/2026-10-17-skill-plugin-development/script.md @@ -65,7 +65,7 @@ ## 12. /ndf:cross-review — 2 つの AI で、新しい指摘が出なくなるまで -> 同じ AI に書かせてレビューさせると、同じ見落としをします。cross-review は、claude・codex・kiro などから 2 つを選んでレビューさせ、指摘を修正し、新しい指摘が出なくなるまで繰り返します。組み合わせはラウンドごとに入れ替えます。手元の実測では、1 つの AI だけの指摘の再現率は 0.21〜0.49 でしたが、2 つを合わせると 0.91 になりました。レビューの観点は PR の中身で切り替わり、リポジトリごとにレビューの基準を設定できます。 +> 同じ AI に書かせてレビューさせると、同じ見落としをします。cross-review は、claude・codex・kiro などから 2 つを選んでレビューさせ、指摘を修正し、新しい指摘が出なくなるまで繰り返します。組み合わせはラウンドごとに入れ替えます。レビューの観点は PR の中身で切り替わり、リポジトリごとにレビューの基準を設定できます。 ## 13. /ndf:cross-refactoring — 全員で提案し、1 人が時間内に実施する diff --git a/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js b/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js index 954e21c79..9f63b899d 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js +++ b/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js @@ -228,17 +228,8 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の d.arrow(s, 2.9, 2.54, 0, 0.3); d.chip(s, 1.5, 2.9, 2.8, 0.56, "新しい指摘は?", "panel2", { fontSize: 11 }); d.text(s, "出た → 組み替えて繰り返す\n出ない → 終了", { x: X0, y: 3.56, w: 5.0, h: 0.56, fontSize: 11 }); - d.text(s, "指摘の再現率(手元の実測)", { x: 5.6, y: 1.42, w: 4.14, h: 0.3, fontSize: 12, bold: true, color: "text" }); - [ - ["1 つの AI(低い方)", 0.21, "panel2"], - ["1 つの AI(高い方)", 0.49, "panel2"], - ["2 つの AI を合わせる", 0.91, "accent"], - ].forEach((r, i) => { - const y = 1.86 + i * 0.86; - d.text(s, r[0], { x: 5.6, y, w: 4.14, h: 0.28, fontSize: 11 }); - d.box(s, 5.6, y + 0.3, 3.4 * r[1], 0.36, r[2], false); - d.text(s, r[1].toFixed(2), { x: 5.66 + 3.4 * r[1], y: y + 0.3, w: 0.6, h: 0.36, fontSize: 13, bold: true, color: "text", valign: "middle" }); - }); + card(d, s, 5.6, 1.38, 4.14, 1.26, "別の AI が見落としを拾う", "2 つの AI を入れ替えながら\n新しい指摘が出なくなるまで", "panel1", { band: "accent", headSize: 14, bodySize: 14, headH: 0.38 }); + card(d, s, 5.6, 2.86, 4.14, 1.26, "観点は PR の中身で切り替え", "レビューの基準は\nリポジトリごとに設定", "panel1", { band: "accent", headSize: 14, bodySize: 14, headH: 0.38 }); // 13. /ndf:cross-refactoring s = d.page("全員で提案、1 人が時間内に直す", "/ndf:cross-refactoring — 複数の AI でリファクタリング"); From 23246cb9796c337bc9a885c1d8066b98fa1a9830 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sun, 18 Oct 2026 22:59:12 +0000 Subject: [PATCH 07/10] =?UTF-8?q?Update:=20=E7=99=BA=E8=A1=A8=E8=B3=87?= =?UTF-8?q?=E6=96=99=E3=81=AE=E3=82=B9=E3=82=AD=E3=83=AB=E3=83=97=E3=83=A9?= =?UTF-8?q?=E3=82=B0=E3=82=A4=E3=83=B3=E3=81=AE=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=81=AB=20Anthropic=20=E3=81=AE=E5=85=AC=E5=BC=8F=E3=83=89?= =?UTF-8?q?=E3=82=AD=E3=83=A5=E3=83=A1=E3=83=B3=E3=83=88=E3=81=B8=E3=81=AE?= =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=AF=E3=82=92=E8=B6=B3=E3=81=99=20(#2065?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JmtvCAsKEdaC7W9gdwjiaN --- .../slides/build-deck.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js b/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js index 9f63b899d..e5026db17 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js +++ b/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js @@ -104,7 +104,22 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の [d.B("スクリプト"), "決まった処理を確実に"], [d.B("hook"), "ルールを強制"], [d.B("サブエージェント"), "役割ごとの AI"], - ], { x: X0, y: 1.4, w: W, colW: [2.2, 7.28], rowH: [0.34, 0.46, 0.46, 0.46, 0.46], fontSize: 13 }); + ], { x: X0, y: 1.4, w: 5.5, colW: [1.9, 3.6], rowH: [0.34, 0.46, 0.46, 0.46, 0.46], fontSize: 13 }); + // 公式ドキュメント(Anthropic)へのリンク + d.box(s, 5.92, 1.4, 3.82, 2.18, "panel1", false); + d.box(s, 5.92, 1.4, 3.82, 0.06, "accent", false); + d.text(s, "公式ドキュメント(Anthropic)", { x: 6.06, y: 1.5, w: 3.54, h: 0.32, fontSize: 12, bold: true, color: "text", valign: "middle" }); + const OFFICIAL = [ + ["Claude Code: プラグイン", "https://code.claude.com/docs/ja/plugins"], + ["Claude Code: Skills", "https://code.claude.com/docs/ja/skills"], + ["プラグインマーケットプレイス", "https://code.claude.com/docs/ja/plugin-marketplaces"], + ["Agent Skills の紹介(Engineering Blog)", "https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills"], + ["公式 Skill の例(anthropics/skills)", "https://github.com/anthropics/skills"], + ]; + d.text(s, OFFICIAL.map(([label, url], i) => ({ + text: label, + options: { hyperlink: { url }, color: "accent", bullet: { indent: 12 }, breakLine: i < OFFICIAL.length - 1 }, + })), { x: 6.06, y: 1.86, w: 3.54, h: 1.64, fontSize: 11, valign: "top", paraSpaceAfter: 4 }); d.text(s, "同じものを 4 つのランタイムで", { x: X0, y: 3.74, w: W, h: 0.3, fontSize: 12, bold: true, color: "text" }); cols(4).forEach((c, i) => d.chip(s, c.x, 4.1, c.w, 0.62, ["Claude Code", "Codex", "Kiro", "agy"][i], "accentSoft", { fontSize: 13 })); From 83453e45a1726908f6598058259f4fa6e7d9f097 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sun, 18 Oct 2026 23:07:54 +0000 Subject: [PATCH 08/10] =?UTF-8?q?Update:=20=E7=99=BA=E8=A1=A8=E8=B3=87?= =?UTF-8?q?=E6=96=99=E3=81=AE=E3=80=8C=E3=82=B9=E3=82=AD=E3=83=AB=E3=83=97?= =?UTF-8?q?=E3=83=A9=E3=82=B0=E3=82=A4=E3=83=B3=E3=80=8D=E3=82=92=E5=85=AC?= =?UTF-8?q?=E5=BC=8F=E3=81=AE=E3=80=8C=E3=82=B9=E3=82=AD=E3=83=AB=E3=80=8D?= =?UTF-8?q?=E3=81=A8=E3=80=8C=E3=83=97=E3=83=A9=E3=82=B0=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=80=8D=E3=81=B8=E6=8F=83=E3=81=88=E3=82=8B=20(#2065)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Anthropic と OpenAI の公式はスキル(作る単位)とプラグイン(配る単位)を呼び分け、 「スキルプラグイン」という語を使わない。題を「独自プラグイン開発の薦め」にし、 台本 4 のページを「スキルとプラグイン」に作り直して Codex と Agent Skills 規格の公式リンクを足した。 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JmtvCAsKEdaC7W9gdwjiaN --- .../README.md | 4 +- .../notes.md | 18 +++---- .../script.md | 14 +++--- .../slides/build-deck.js | 49 +++++++++++-------- docs/presentations/README.md | 2 +- 5 files changed, 48 insertions(+), 39 deletions(-) diff --git a/docs/presentations/2026-10-17-skill-plugin-development/README.md b/docs/presentations/2026-10-17-skill-plugin-development/README.md index 9fd1f7a49..05319e3bf 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/README.md +++ b/docs/presentations/2026-10-17-skill-plugin-development/README.md @@ -1,6 +1,6 @@ -# 独自スキルプラグイン開発の薦め +# 独自プラグイン開発の薦め -社外の勉強会・登壇で話すスライド(台本の 23 ページと章扉 6 つの 29 ページ)。NDF の機能を例に、自分用のスキルプラグインを作って育てることを薦める。 +社外の勉強会・登壇で話すスライド(台本の 23 ページと章扉 6 つの 29 ページ)。NDF の機能を例に、自分用のスキルを作り、プラグインにして配り、育てることを薦める。 `release` の `form-slide.md` の生成スクリプトの経路で作った。 | ファイル | 中身 | diff --git a/docs/presentations/2026-10-17-skill-plugin-development/notes.md b/docs/presentations/2026-10-17-skill-plugin-development/notes.md index f867f432b..cff154a65 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/notes.md +++ b/docs/presentations/2026-10-17-skill-plugin-development/notes.md @@ -1,11 +1,11 @@ -# 構成メモ: 独自スキルプラグイン開発の薦め +# 構成メモ: 独自プラグイン開発の薦め 台本([script.md](script.md)。第 7 稿の写し)を、社外の勉強会・登壇で話すスライド(台本の 23 ページと章扉 6 つ)にした構成。 各ページの読み上げはスピーカーノートに入り、生成スクリプト([slides/build-deck.js](slides/build-deck.js))が台本から読み込む。 ## 構成 -キーメッセージ「AI に自分のやり方を教えるのは 1 回でよい。スキルプラグインにして、フォークして育てる」から逆算し、 +キーメッセージ「AI に自分のやり方を教えるのは 1 回でよい。プラグインにして、フォークして育てる」から逆算し、 課題(1〜4)→ NDF の機能(5〜18)→ 事例(19〜20)→ 転換と実践(21〜22)→ まとめ(23)の順に並べた。 台本はページに載せる要素を持たないため、各ページの図・表・箇条書きは読み上げから起こした。読み上げの文をそのまま ページに写さず、縮めても載せない。ページには 1 つのメッセージと、それを支える短い句(体言止め)・表・流れ・数値だけを置く。 @@ -20,7 +20,7 @@ | 1 | 表紙 | — | — | | 2 | 同じ指摘を、何度もしていませんか | 繰り返す 3 つの指摘を吹き出しで並べ、「セッションが切れると忘れる」を注意の地で示す | 台本 2 | | 3 | CLAUDE.md への追記では足りない | 4 つの壁(コンテキスト・強制力・手順・ポータビリティ)を 2 × 2 の区画で示す | 台本 3 | -| 4 | スキルプラグインとは | 構成要素 4 つと役割の表と、4 つのランタイムの並び | 台本 4 | +| 4 | スキルとプラグイン | スキル(作る単位・手順書)とプラグイン(配る単位・スキル・スクリプト・hook・サブエージェントをまとめる)の 2 区画、公式ドキュメント(Anthropic・OpenAI・Agent Skills の規格)へのリンク、4 つのランタイムの並び | 台本 4、Claude Code と Codex の公式ドキュメント | | 5 | NDF: 自分のやり方をプラグインに | リリース 102 回・Skill 48 個・ランタイム 4 つ・導入 10 リポジトリの数値と、リポジトリの URL | 台本 5(2026-10-17 の実測) | | 6 | Issue の束を渡せば、並列に進む | マイルストーン → グループ → スプリント(グループごとに並列)→ リリースの図と、2 回の承認(設計・本番リリース)。直前のリリースの例は注 | 台本 6 | | 7 | 変更の大きさで、踏む工程を変える | `/ndf:development-workflow` が変更を分ける 5 つのモードの表と、light と standard の工程の違い | 台本 7、`development-workflow` の `references/workflow-modes.md` | @@ -48,7 +48,7 @@ | 章 | 章扉の一言 | 台本のページ | | --- | --- | --- | -| 1. なぜスキルプラグインか | CLAUDE.md に書くだけでは届かない | 2〜4 | +| 1. なぜプラグインか | CLAUDE.md に書くだけでは届かない | 2〜4 | | 2. NDF の全体像 | Issue の束を渡すと、リリースまで進む | 5〜8 | | 3. 組み込んだ開発技法 | 実績のある技法を、AI が守れる手順に | 9〜13 | | 4. AI に任せ切る仕組み | 止まらない・迷わない・ごまかさない | 14〜18 | @@ -57,15 +57,15 @@ ## 尺 -`node slides/lib/script-time.js script.md --limit 35` の出力(350 字/分)。合計 5,261 字・15 分 2 秒で、 +`node slides/lib/script-time.js script.md --limit 35` の出力(350 字/分)。合計 5,306 字・15 分 10 秒で、 持ち時間(30〜35 分)に収まる。削るページは無い。 | # | 見出し | 字数 | 尺(秒) | 配分(秒) | | ---: | --- | ---: | ---: | ---: | -| 1 | タイトル | 108 | 18.5 | — | +| 1 | タイトル | 111 | 19.0 | — | | 2 | AI に同じことを何度も言っていませんか | 142 | 24.3 | — | | 3 | CLAUDE.md への追記だけでは足りない理由 | 160 | 27.4 | — | -| 4 | スキルプラグインとは | 216 | 37.0 | — | +| 4 | スキルとプラグイン | 261 | 44.7 | — | | 5 | NDF: 1 人の開発スタイルをプラグインにした | 130 | 22.3 | — | | 6 | NDF の全体像: マイルストーンを渡せば、スプリントとして並列に進む | 316 | 54.2 | — | | 7 | /ndf:development-workflow — 変更の種類でワークフローを切り替える | 283 | 48.5 | — | @@ -84,8 +84,8 @@ | 20 | 事例から分かったこと: 詰まったところが次のバージョンに、ルールはプロジェクトごとに違う | 244 | 41.8 | — | | 21 | だから「フォークして自分用に」 | 225 | 38.6 | — | | 22 | 始め方と育て方 | 312 | 53.5 | — | -| 23 | まとめ: 明日からの 3 ステップ | 180 | 30.9 | — | -| 合計 | 23 ページ(350 字/分) | 5261 | 901.9(15分02秒) | — | +| 23 | まとめ: 明日からの 3 ステップ | 177 | 30.3 | — | +| 合計 | 23 ページ(350 字/分) | 5306 | 909.5(15分10秒) | — | ## ページに収まらない読み上げの扱い diff --git a/docs/presentations/2026-10-17-skill-plugin-development/script.md b/docs/presentations/2026-10-17-skill-plugin-development/script.md index 7ab91f425..2b3a8a967 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/script.md +++ b/docs/presentations/2026-10-17-skill-plugin-development/script.md @@ -1,8 +1,8 @@ -# 台本(第 7 稿): 独自スキルプラグイン開発の薦め — NDF を例に +# 台本(第 7 稿): 独自プラグイン開発の薦め — NDF を例に - 想定: 社外の勉強会・登壇。30〜35 分・23 ページ - 対象: Claude Code / Codex などの AI コーディングエージェントを使っている、または使い始めたエンジニア -- キーメッセージ: **AI に自分のやり方を教えるのは 1 回でよい。スキルプラグインにして、フォークして育てる** +- キーメッセージ: **AI に自分のやり方を教えるのは 1 回でよい。プラグインにして、フォークして育てる** - 数値は 2026-10-17 時点の実測。事例のプロジェクトは種類で呼び、名前は出さない ## 構成 @@ -19,9 +19,9 @@ ## 1. タイトル -独自スキルプラグイン開発の薦め — NDF を例に、自分の開発スタイルを AI に持たせる +独自プラグイン開発の薦め — スキルを作り、プラグインで配る。NDF を例に、自分の開発スタイルを AI に持たせる -> 今日は「自分用のスキルプラグインを作って育てよう」という話をします。例として私が開発している NDF プラグインの機能を中心に紹介します。ゴールは NDF を使ってもらうことではなく、皆さんが自分のプラグインを持つことです。 +> 今日は「自分用のスキルを作り、プラグインにして育てよう」という話をします。例として私が開発している NDF プラグインの機能を中心に紹介します。ゴールは NDF を使ってもらうことではなく、皆さんが自分のプラグインを持つことです。 ## 2. AI に同じことを何度も言っていませんか @@ -31,9 +31,9 @@ > まず CLAUDE.md や AGENTS.md に追記しますが、4 つの壁に当たります。コンテキスト: 毎回すべてを読み込むので、書くほどコストが上がり、重要なルールが埋もれる。強制力: 書いても守られないことがある。手順: 「どの順に何を実行するか」は文章では伝わりにくい。ポータビリティ: リポジトリやランタイムを変えると失われる。 -## 4. スキルプラグインとは +## 4. スキルとプラグイン -> そこでスキルプラグインです。構成要素は 4 つ。Skill は「いつ・何を・どの順で」を書いたワークフローで、必要なときだけ読み込まれます。スクリプトは決まった処理を確実に実行します。hook はルールを強制します。サブエージェントは役割を分けます。これをまとめて配布でき、Claude Code・Codex・Kiro・agy で同じものを使えます。判断基準とワークフローをパッケージにして、どのリポジトリでも再利用できる。これがポイントです。 +> そこでスキルとプラグインです。スキルは作る単位で、「いつ・何を・どの順で」を書いた手順書です。必要なときだけ読み込まれます。プラグインは配る単位で、スキルに、決まった処理を確実に実行するスクリプト、ルールを強制する hook、役割を分けるサブエージェントを加えてまとめて配布します。Claude Code・Codex・Kiro・agy で同じものを使えます。呼び方は Anthropic と OpenAI の公式ドキュメントに合わせています。判断基準とワークフローをパッケージにして、どのリポジトリでも再利用できる。これがポイントです。 ## 5. NDF: 1 人の開発スタイルをプラグインにした @@ -109,4 +109,4 @@ ## 23. まとめ: 明日からの 3 ステップ -> AI に自分のやり方を教えるのは 1 回でよい。スキルプラグインにして、フォークして育ててください。明日からの 3 ステップは、1. ai-plugins をフォークして自分の名前でインストールする、2. 使わない Skill を外す、3. 同じ指摘を 2 回したら Skill にする。リポジトリは github.com/devbasex/ai-plugins です。ありがとうございました。 +> AI に自分のやり方を教えるのは 1 回でよい。プラグインにして、フォークして育ててください。明日からの 3 ステップは、1. ai-plugins をフォークして自分の名前でインストールする、2. 使わない Skill を外す、3. 同じ指摘を 2 回したら Skill にする。リポジトリは github.com/devbasex/ai-plugins です。ありがとうございました。 diff --git a/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js b/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js index e5026db17..3ee7b4472 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js +++ b/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js @@ -1,4 +1,4 @@ -// 発表資料「独自スキルプラグイン開発の薦め」の生成スクリプト(台本の 23 ページと章扉 6 つの 29 ページ)。 +// 発表資料「独自プラグイン開発の薦め」の生成スクリプト(台本の 23 ページと章扉 6 つの 29 ページ)。 // // node build-deck.js <出力>.pptx [] // @@ -66,14 +66,14 @@ function flow(d, s, x, y, w, h, labels, opt) { }); } -run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の薦め", author: "NDF" }, (d) => { +run(__dirname, { script: SCRIPT, title: "独自プラグイン開発の薦め", author: "NDF" }, (d) => { let s; // 1. タイトル - d.cover("独自スキルプラグイン開発の薦め", "NDF を例に、自分の開発スタイルを AI に持たせる", "github.com/devbasex/ai-plugins"); + d.cover("独自プラグイン開発の薦め", "スキルを作り、プラグインで配る\nNDF を例に、自分の開発スタイルを AI に持たせる", "github.com/devbasex/ai-plugins"); // ==== 章 1 ==== - d.section("1. なぜスキルプラグインか", "CLAUDE.md に書くだけでは届かない"); + d.section("1. なぜプラグインか", "CLAUDE.md に書くだけでは届かない"); // 2. AI に同じことを何度も言っていませんか s = d.page("同じ指摘を、何度もしていませんか", "指摘の回数 = AI にまだ伝わっていない量"); @@ -96,32 +96,41 @@ run(__dirname, { script: SCRIPT, title: "独自スキルプラグイン開発の card(d, s, c.x, 1.5 + Math.floor(i / 2) * 1.5, c.w, 1.3, r[0], r[1], "panel1", { band: "accent", headSize: 18, bodySize: 16, headH: 0.46 }); }); - // 4. スキルプラグインとは - s = d.page("スキルプラグインとは", "やり方をパッケージにして、どこでも再利用"); - d.tbl(s, [ - [d.H("構成要素"), d.H("役割")], - [d.B("Skill"), "手順書。必要なときだけ読む"], - [d.B("スクリプト"), "決まった処理を確実に"], - [d.B("hook"), "ルールを強制"], - [d.B("サブエージェント"), "役割ごとの AI"], - ], { x: X0, y: 1.4, w: 5.5, colW: [1.9, 3.6], rowH: [0.34, 0.46, 0.46, 0.46, 0.46], fontSize: 13 }); - // 公式ドキュメント(Anthropic)へのリンク - d.box(s, 5.92, 1.4, 3.82, 2.18, "panel1", false); + // 4. スキルとプラグイン + s = d.page("スキルとプラグイン", "スキルで作り、プラグインで配る"); + card(d, s, X0, 1.4, 5.5, 0.92, "スキル(Skill)= 作る単位", "手順書(いつ・何を・どの順で)\n必要なときだけ読む", "panel1", { band: "accent", headSize: 14, bodySize: 12 }); + card(d, s, X0, 2.44, 5.5, 1.62, "プラグイン = 配る単位", bullets([ + "スキル", + "スクリプト(決まった処理を確実に)", + "hook(ルールを強制)", + "サブエージェント(役割ごとの AI)", + ], 12), "panel1", { band: "accent", headSize: 14 }); + d.text(s, "同じものを 4 つのランタイムで", { x: X0, y: 4.14, w: 5.5, h: 0.28, fontSize: 11, bold: true, color: "text", valign: "middle" }); + cols(4, 0.1, X0, 5.5).forEach((c, i) => d.chip(s, c.x, 4.46, c.w, 0.5, ["Claude Code", "Codex", "Kiro", "agy"][i], "accentSoft", { fontSize: 11 })); + // 公式ドキュメントへのリンク(呼び方は Anthropic と OpenAI の公式に合わせる) + d.box(s, 5.92, 1.4, 3.82, 3.56, "panel1", false); d.box(s, 5.92, 1.4, 3.82, 0.06, "accent", false); - d.text(s, "公式ドキュメント(Anthropic)", { x: 6.06, y: 1.5, w: 3.54, h: 0.32, fontSize: 12, bold: true, color: "text", valign: "middle" }); + d.text(s, "公式ドキュメント", { x: 6.06, y: 1.5, w: 3.54, h: 0.32, fontSize: 12, bold: true, color: "text", valign: "middle" }); const OFFICIAL = [ + ["Anthropic(Claude Code)"], ["Claude Code: プラグイン", "https://code.claude.com/docs/ja/plugins"], ["Claude Code: Skills", "https://code.claude.com/docs/ja/skills"], ["プラグインマーケットプレイス", "https://code.claude.com/docs/ja/plugin-marketplaces"], ["Agent Skills の紹介(Engineering Blog)", "https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills"], ["公式 Skill の例(anthropics/skills)", "https://github.com/anthropics/skills"], + ["OpenAI(Codex)"], + ["Codex: Skills", "https://developers.openai.com/codex/skills"], + ["Codex: Plugins", "https://developers.openai.com/codex/plugins"], + ["規格"], + ["Agent Skills(agentskills.io)", "https://agentskills.io"], ]; d.text(s, OFFICIAL.map(([label, url], i) => ({ text: label, - options: { hyperlink: { url }, color: "accent", bullet: { indent: 12 }, breakLine: i < OFFICIAL.length - 1 }, - })), { x: 6.06, y: 1.86, w: 3.54, h: 1.64, fontSize: 11, valign: "top", paraSpaceAfter: 4 }); - d.text(s, "同じものを 4 つのランタイムで", { x: X0, y: 3.74, w: W, h: 0.3, fontSize: 12, bold: true, color: "text" }); - cols(4).forEach((c, i) => d.chip(s, c.x, 4.1, c.w, 0.62, ["Claude Code", "Codex", "Kiro", "agy"][i], "accentSoft", { fontSize: 13 })); + options: Object.assign( + url ? { hyperlink: { url }, color: "accent", bullet: { indent: 12 } } : { bold: true, color: "text", paraSpaceBefore: i ? 6 : 0 }, + { breakLine: i < OFFICIAL.length - 1 }, + ), + })), { x: 6.06, y: 1.86, w: 3.54, h: 3.0, fontSize: 11.5, valign: "top", paraSpaceAfter: 3 }); // ==== 章 2 ==== d.section("2. NDF の全体像", "Issue の束を渡すと、リリースまで進む"); diff --git a/docs/presentations/README.md b/docs/presentations/README.md index 18383b0a9..2a67eba2f 100644 --- a/docs/presentations/README.md +++ b/docs/presentations/README.md @@ -28,7 +28,7 @@ | 資料 | 内容 | 想定時間 | |---|---|---| -| [2026-10-17-skill-plugin-development/](2026-10-17-skill-plugin-development/README.md) | 独自スキルプラグイン開発の薦め。NDF の機能を例に、自分用のスキルプラグインを作って育てることを薦める(社外の勉強会・登壇向け) | 30〜35分 / 23ページ | +| [2026-10-17-skill-plugin-development/](2026-10-17-skill-plugin-development/README.md) | 独自プラグイン開発の薦め。NDF の機能を例に、自分用のスキルを作り、プラグインにして配り、育てることを薦める(社外の勉強会・登壇向け) | 30〜35分 / 23ページ | | [2026-08-06-ai-plugins-intro.md](2026-08-06-ai-plugins-intro.md) | ai-plugins / NDF v5.0.0 の個別機能紹介。各スキルに何が書いてあり、何を重視しているかを扱う | 15分 / 15枚 | `2026-10-17-skill-plugin-development` の成果物は Google Slides で、pptx は生成スクリプトから作り直せるためコミットしません。 From 508e8a7c0a15b51c4491745882749e7f63a14cfb Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sun, 18 Oct 2026 23:17:49 +0000 Subject: [PATCH 09/10] =?UTF-8?q?Update:=20=E7=99=BA=E8=A1=A8=E8=B3=87?= =?UTF-8?q?=E6=96=99=E3=81=AE=E4=BC=BC=E3=81=9F=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=82=92=202=20=E7=B5=84=E3=81=BE=E3=81=A8=E3=82=81=E3=80=8127?= =?UTF-8?q?=20=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AB=E3=81=99=E3=82=8B=20(#2?= =?UTF-8?q?065)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 台本の 10・11(requirements-design・design と document-restructuring)を 1 ページ「コードの前に、読める文書を」へ - 台本の 12・13(cross-review と cross-refactoring)を 1 ページ「複数の AI で、見て直す」へ - 以後の番号を詰め、台本の写し・構成メモ・README・索引・テストを 21 ページ(章扉を含め 27 ページ)に合わせた - 尺は 5,306 字・15 分 10 秒 → 4,955 字・14 分 09 秒 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SyiLCs8wNrE2PKfeeqZDyT --- .../README.md | 2 +- .../notes.md | 76 ++++++++--------- .../script.md | 46 +++++------ .../slides/build-deck.js | 82 +++++++++---------- docs/presentations/README.md | 2 +- tests/test_presentation_decks.py | 8 +- 6 files changed, 98 insertions(+), 118 deletions(-) diff --git a/docs/presentations/2026-10-17-skill-plugin-development/README.md b/docs/presentations/2026-10-17-skill-plugin-development/README.md index 05319e3bf..9ee60cd3f 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/README.md +++ b/docs/presentations/2026-10-17-skill-plugin-development/README.md @@ -1,6 +1,6 @@ # 独自プラグイン開発の薦め -社外の勉強会・登壇で話すスライド(台本の 23 ページと章扉 6 つの 29 ページ)。NDF の機能を例に、自分用のスキルを作り、プラグインにして配り、育てることを薦める。 +社外の勉強会・登壇で話すスライド(台本の 21 ページと章扉 6 つの 27 ページ)。NDF の機能を例に、自分用のスキルを作り、プラグインにして配り、育てることを薦める。 `release` の `form-slide.md` の生成スクリプトの経路で作った。 | ファイル | 中身 | diff --git a/docs/presentations/2026-10-17-skill-plugin-development/notes.md b/docs/presentations/2026-10-17-skill-plugin-development/notes.md index cff154a65..b11f6beb3 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/notes.md +++ b/docs/presentations/2026-10-17-skill-plugin-development/notes.md @@ -1,12 +1,12 @@ # 構成メモ: 独自プラグイン開発の薦め -台本([script.md](script.md)。第 7 稿の写し)を、社外の勉強会・登壇で話すスライド(台本の 23 ページと章扉 6 つ)にした構成。 +台本([script.md](script.md)。第 7 稿の写し)を、社外の勉強会・登壇で話すスライド(台本の 21 ページと章扉 6 つ)にした構成。 各ページの読み上げはスピーカーノートに入り、生成スクリプト([slides/build-deck.js](slides/build-deck.js))が台本から読み込む。 ## 構成 キーメッセージ「AI に自分のやり方を教えるのは 1 回でよい。プラグインにして、フォークして育てる」から逆算し、 -課題(1〜4)→ NDF の機能(5〜18)→ 事例(19〜20)→ 転換と実践(21〜22)→ まとめ(23)の順に並べた。 +課題(1〜4)→ NDF の機能(5〜16)→ 事例(17〜18)→ 転換と実践(19〜20)→ まとめ(21)の順に並べた。 台本はページに載せる要素を持たないため、各ページの図・表・箇条書きは読み上げから起こした。読み上げの文をそのまま ページに写さず、縮めても載せない。ページには 1 つのメッセージと、それを支える短い句(体言止め)・表・流れ・数値だけを置く。 聞き手は NDF を知らないため、内部の語(ゲート・フェーズ・conductor など)は聞き手に通じる語へ言い換えるか、ページの中で @@ -26,39 +26,37 @@ | 7 | 変更の大きさで、踏む工程を変える | `/ndf:development-workflow` が変更を分ける 5 つのモードの表と、light と standard の工程の違い | 台本 7、`development-workflow` の `references/workflow-modes.md` | | 8 | 機能追加は、この流れで 1 周 | standard モードの工程を設計・実装・リリースの 3 行に並べ、承認 1・2 を注意の地で示す | 台本 8 | | 9 | 実績のある開発技法を組み込む | DDD・テストファースト・アジャイルの 3 列。用語集 580 語と WSJF を載せる | 台本 9 | -| 10 | コードの前に「何を」「どう」 | `/ndf:requirements-design`(完了の条件)と `/ndf:design`(作り方)の 2 列。設計 PR と承認 1 を下の区画で示す | 台本 10 | -| 11 | 結論から読める順に並べ替える | `/ndf:document-restructuring` の、書いた順と読み手の順の対比と、内容を足さない 5 つの手順 | 台本 11 | -| 12 | 2 つの AI で、指摘が尽きるまで | `/ndf:cross-review` の繰り返しの流れと、AI の組み合わせの入れ替え・観点の切り替えの 2 区画(数値は載せない) | 台本 12 | -| 13 | 全員で提案、1 人が時間内に直す | `/ndf:cross-refactoring` の提案 → 選ぶ → 時間内に実施 → 取り消しの流れと、逆算と内訳の 2 区画 | 台本 13 | -| 14 | 3 層のエージェントで止めない | conductor(指揮役)・supervisor(監督役)・worker(作業役)の階段の図と、再開とセッションの切り替えの 2 区画 | 台本 14 | -| 15 | 迷ったら MVV で判断する | Mission・Vision・Value の 3 区画と Value の例 2 つ、必ず人が承認する操作 | 台本 15、`.ndf/` のプロジェクト MVV | -| 16 | 「完了」も「無い」も証拠で | AI の言葉 2 つと、それぞれを受け止める `/ndf:quality-gates`・`/ndf:investigation-rules` の対応 | 台本 16 | -| 17 | やるべきことを整理し続ける | 導入したプロジェクトの Issue の一覧を現状に保つ 3 つの Skill の流れと、何の数かを添えた棚卸しの数値 2 つ(56%・49 件) | 台本 17(棚卸しの記録と 2026-10-17 の調査) | -| 18 | そのほかの特徴的な機能 | estimate・release・hook の、何をするかと要点の表 | 台本 18 | -| 19 | 事例: ほかのプロジェクトでも動く | 4 つのプロジェクトの種類と結果の表。インフラの注記(社内ツールの移行と重なる)を表の中に残す | 台本 19、事例の材料(インフラ・業務システム・機械学習と価格推定) | -| 20 | 事例から分かった 2 つのこと | 詰まりが次の版で直る例(17 件の Issue と 3 つの前提の違い)と、プロジェクトごとに足したルールの表 | 台本 20、事例の材料 | -| 21 | だから、フォークして自分用に | NDF のルールの例と、そのまま使う仕組みの対比、メリット 3 つ | 台本 21 | -| 22 | 始め方と育て方 | フォークする・削る・育てるの 3 列と、気をつける点 | 台本 22 | -| 23 | まとめ: 明日からの 3 ステップ | 3 つの手順とリポジトリの URL | 台本 23 | +| 10 | コードの前に、読める文書を | `/ndf:requirements-design`(完了の条件)と `/ndf:design`(作り方)の 2 区画、`/ndf:document-restructuring` の書いた順と読み手の順の対比(内容は足さない)、設計 PR と承認 1 の区画 | 台本 10 | +| 11 | 複数の AI で、見て直す | `/ndf:cross-review`(2 つの AI を入れ替えて指摘が尽きるまで)と `/ndf:cross-refactoring`(全員が提案し 1 つが既定 30 分で実施、壊れたら取り消す)の 2 列の流れと要点(数値は載せない) | 台本 11 | +| 12 | 3 層のエージェントで止めない | conductor(指揮役)・supervisor(監督役)・worker(作業役)の階段の図と、再開とセッションの切り替えの 2 区画 | 台本 12 | +| 13 | 迷ったら MVV で判断する | Mission・Vision・Value の 3 区画と Value の例 2 つ、必ず人が承認する操作 | 台本 13、`.ndf/` のプロジェクト MVV | +| 14 | 「完了」も「無い」も証拠で | AI の言葉 2 つと、それぞれを受け止める `/ndf:quality-gates`・`/ndf:investigation-rules` の対応 | 台本 14 | +| 15 | やるべきことを整理し続ける | 導入したプロジェクトの Issue の一覧を現状に保つ 3 つの Skill の流れと、何の数かを添えた棚卸しの数値 2 つ(56%・49 件) | 台本 15(棚卸しの記録と 2026-10-17 の調査) | +| 16 | そのほかの特徴的な機能 | estimate・release・hook の、何をするかと要点の表 | 台本 16 | +| 17 | 事例: ほかのプロジェクトでも動く | 4 つのプロジェクトの種類と結果の表。インフラの注記(社内ツールの移行と重なる)を表の中に残す | 台本 17、事例の材料(インフラ・業務システム・機械学習と価格推定) | +| 18 | 事例から分かった 2 つのこと | 詰まりが次の版で直る例(17 件の Issue と 3 つの前提の違い)と、プロジェクトごとに足したルールの表 | 台本 18、事例の材料 | +| 19 | だから、フォークして自分用に | NDF のルールの例と、そのまま使う仕組みの対比、メリット 3 つ | 台本 19 | +| 20 | 始め方と育て方 | フォークする・削る・育てるの 3 列と、気をつける点 | 台本 20 | +| 21 | まとめ: 明日からの 3 ステップ | 3 つの手順とリポジトリの URL | 台本 21 | ## 章立て 章扉(生成スクリプトの `d.section`)で構成を見せる。章扉はスピーカーノートを持たず、台本の番号を進めない。 -資料は台本の 23 ページと章扉 6 つの 29 ページになる(ページ番号は章扉も数える)。 +資料は台本の 21 ページと章扉 6 つの 27 ページになる(ページ番号は章扉も数える)。 | 章 | 章扉の一言 | 台本のページ | | --- | --- | --- | | 1. なぜプラグインか | CLAUDE.md に書くだけでは届かない | 2〜4 | | 2. NDF の全体像 | Issue の束を渡すと、リリースまで進む | 5〜8 | -| 3. 組み込んだ開発技法 | 実績のある技法を、AI が守れる手順に | 9〜13 | -| 4. AI に任せ切る仕組み | 止まらない・迷わない・ごまかさない | 14〜18 | -| 5. 事例 | ほかのプロジェクトでも動くか | 19〜20 | -| 6. 始め方 | フォークして、自分用に育てる | 21〜23 | +| 3. 組み込んだ開発技法 | 実績のある技法を、AI が守れる手順に | 9〜11 | +| 4. AI に任せ切る仕組み | 止まらない・迷わない・ごまかさない | 12〜16 | +| 5. 事例 | ほかのプロジェクトでも動くか | 17〜18 | +| 6. 始め方 | フォークして、自分用に育てる | 19〜21 | ## 尺 -`node slides/lib/script-time.js script.md --limit 35` の出力(350 字/分)。合計 5,306 字・15 分 10 秒で、 -持ち時間(30〜35 分)に収まる。削るページは無い。 +`node slides/lib/script-time.js script.md --limit 35` の出力(350 字/分)。合計 4,955 字・14 分 09 秒で、 +持ち時間(30〜35 分)に収まる。似たページを 2 組まとめた(まとめる前の台本の 10・11 ページと 12・13 ページ)。 | # | 見出し | 字数 | 尺(秒) | 配分(秒) | | ---: | --- | ---: | ---: | ---: | @@ -71,24 +69,22 @@ | 7 | /ndf:development-workflow — 変更の種類でワークフローを切り替える | 283 | 48.5 | — | | 8 | standard モードの全体の流れ | 277 | 47.5 | — | | 9 | 実績のある開発技法を、AI が守れる形で組み込む — DDD・テストファースト・アジャイル | 381 | 65.3 | — | -| 10 | /ndf:requirements-design と /ndf:design — コードより先に「何を」「どう」を決める | 230 | 39.4 | — | -| 11 | /ndf:document-restructuring — 書いた順ではなく、読み手が知りたい順へ | 190 | 32.6 | — | -| 12 | /ndf:cross-review — 2 つの AI で、新しい指摘が出なくなるまで | 167 | 28.6 | — | -| 13 | /ndf:cross-refactoring — 全員で提案し、1 人が時間内に実施する | 206 | 35.3 | — | -| 14 | 3 層のエージェント — 長いタスクを止めずに、止まっても再開できる | 249 | 42.7 | — | -| 15 | 判断基準: MVV と 2 つの承認ゲート | 193 | 33.1 | — | -| 16 | /ndf:quality-gates と /ndf:investigation-rules — 完了も「無い」も証拠で | 182 | 31.2 | — | -| 17 | 自分を直すループ — /ndf:out-of-scope・/ndf:retrospective・/ndf:backlog-refinement | 299 | 51.3 | — | -| 18 | その他の特徴的な機能 | 243 | 41.7 | — | -| 19 | 事例: 他のプロジェクトでも動く | 328 | 56.2 | — | -| 20 | 事例から分かったこと: 詰まったところが次のバージョンに、ルールはプロジェクトごとに違う | 244 | 41.8 | — | -| 21 | だから「フォークして自分用に」 | 225 | 38.6 | — | -| 22 | 始め方と育て方 | 312 | 53.5 | — | -| 23 | まとめ: 明日からの 3 ステップ | 177 | 30.3 | — | -| 合計 | 23 ページ(350 字/分) | 5306 | 909.5(15分10秒) | — | +| 10 | /ndf:requirements-design・/ndf:design・/ndf:document-restructuring — コードより先に文書を書き、読み手の順に並べる | 222 | 38.1 | — | +| 11 | /ndf:cross-review と /ndf:cross-refactoring — 複数の AI で、見落としを拾い、時間内に直す | 220 | 37.7 | — | +| 12 | 3 層のエージェント — 長いタスクを止めずに、止まっても再開できる | 249 | 42.7 | — | +| 13 | 判断基準: MVV と 2 つの承認ゲート | 193 | 33.1 | — | +| 14 | /ndf:quality-gates と /ndf:investigation-rules — 完了も「無い」も証拠で | 182 | 31.2 | — | +| 15 | 自分を直すループ — /ndf:out-of-scope・/ndf:retrospective・/ndf:backlog-refinement | 299 | 51.3 | — | +| 16 | その他の特徴的な機能 | 243 | 41.7 | — | +| 17 | 事例: 他のプロジェクトでも動く | 328 | 56.2 | — | +| 18 | 事例から分かったこと: 詰まったところが次のバージョンに、ルールはプロジェクトごとに違う | 244 | 41.8 | — | +| 19 | だから「フォークして自分用に」 | 225 | 38.6 | — | +| 20 | 始め方と育て方 | 312 | 53.5 | — | +| 21 | まとめ: 明日からの 3 ステップ | 177 | 30.3 | — | +| 合計 | 21 ページ(350 字/分) | 4955 | 849.4(14分09秒) | — | ## ページに収まらない読み上げの扱い 読み上げはスピーカーノートに全文が入る。ページには読み上げの要点だけを置き、例(6 ページの直前のリリース、 -17 ページの「放っておくと…」)は注へ回した。ページの見出しは台本の見出しと別に、そのページの結論を短く書いた -(目安 20 字以内)。Skill を紹介するページ(7・10〜13・16・17)は、Skill 名と何をするものかをリードか箱の見出しに置いた。 +15 ページの「放っておくと…」)は注へ回した。ページの見出しは台本の見出しと別に、そのページの結論を短く書いた +(目安 20 字以内)。Skill を紹介するページ(7・10・11・14・15)は、Skill 名と何をするものかをリードか箱の見出しに置いた。 diff --git a/docs/presentations/2026-10-17-skill-plugin-development/script.md b/docs/presentations/2026-10-17-skill-plugin-development/script.md index 2b3a8a967..539b4b801 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/script.md +++ b/docs/presentations/2026-10-17-skill-plugin-development/script.md @@ -1,6 +1,6 @@ # 台本(第 7 稿): 独自プラグイン開発の薦め — NDF を例に -- 想定: 社外の勉強会・登壇。30〜35 分・23 ページ +- 想定: 社外の勉強会・登壇。30〜35 分・21 ページ - 対象: Claude Code / Codex などの AI コーディングエージェントを使っている、または使い始めたエンジニア - キーメッセージ: **AI に自分のやり方を教えるのは 1 回でよい。プラグインにして、フォークして育てる** - 数値は 2026-10-17 時点の実測。事例のプロジェクトは種類で呼び、名前は出さない @@ -10,10 +10,10 @@ | パート | ページ | 問い | | --- | --- | --- | | 1. 課題 | 1〜4 | AI に同じことを何度も言っていないか。なぜ CLAUDE.md への追記では足りないのか | -| 2. NDF の機能 | 5〜18 | 自分のやり方をプラグインにすると何ができるのか(ワークフロー全体・組み込んだ開発技法・主な Skill) | -| 3. 事例 | 19〜20 | 他のプロジェクトでも動くのか | -| 4. 転換と実践 | 21〜22 | なぜフォークして自分用にするのか。どう始めて、どう育てるか | -| 5. まとめ | 23 | 明日から何をするか | +| 2. NDF の機能 | 5〜16 | 自分のやり方をプラグインにすると何ができるのか(ワークフロー全体・組み込んだ開発技法・主な Skill) | +| 3. 事例 | 17〜18 | 他のプロジェクトでも動くのか | +| 4. 転換と実践 | 19〜20 | なぜフォークして自分用にするのか。どう始めて、どう育てるか | +| 5. まとめ | 21 | 明日から何をするか | --- @@ -55,58 +55,50 @@ > NDF は新しい方法論を発明したわけではありません。人間のチームで実績のある技法を、AI が毎回守れる手順にしています。DDD からはユビキタス言語。要件定義の前に用語集を作り、語・意味・コンテキストを決めます。同じ「注文」でも受注と配送では意味が違うので、コンテキストごとに 1 つの意味に決め、AI はその語で設計とコードを書きます。このリポジトリの用語集は 580 語あり、CI が用語集との食い違いを検査します。テストファーストからは、失敗するテストを書く → 通す最小の実装 → 整理、のサイクル。テストが薄いコードは、先に今の振る舞いを固定するテストを書いてからリファクタリングします。アジャイルからは、マイルストーンをバックログとして扱い、スプリントで進め、スプリントごとに振り返りとバックログリファインメントを行います。優先順位は WSJF(遅延コスト ÷ 大きさ)で付けます。 -## 10. /ndf:requirements-design と /ndf:design — コードより先に「何を」「どう」を決める +## 10. /ndf:requirements-design・/ndf:design・/ndf:document-restructuring — コードより先に文書を書き、読み手の順に並べる -> 実装の前に 2 つの文書を書きます。requirements-design は「何を満たせば完了か」を観測できる受け入れ条件にします。「いい感じに」「速く」のような評価語を、確かめられる条件に変えるのが仕事です。design は「どう作るか」、つまりデータ構造・入出力の契約・処理の流れ・決定の理由を書きます。担当する AI が変わっても同じ構成の成果物になるよう、書く内容を規約で決めています。設計は PR として出し、主題ごとに並列で開いて、ゲート 1 でまとめて承認します。 +> 実装の前に 2 つの文書を書きます。requirements-design は「何を満たせば完了か」を、確かめられる受け入れ条件にします。design は「どう作るか」、つまりデータ構造・入出力の契約・処理の流れ・決定の理由を書きます。書いた文書は document-restructuring で、調べた順から、読み手が知りたい結論から読める順に並べ替えます。内容は足しません。設計は PR として出し、主題ごとに並列で開いて、ゲート 1 でまとめて承認します。 -## 11. /ndf:document-restructuring — 書いた順ではなく、読み手が知りたい順へ +## 11. /ndf:cross-review と /ndf:cross-refactoring — 複数の AI で、見落としを拾い、時間内に直す -> AI が書いた文書は、調べた順に並びがちです。「問題に気づく → 測る → 結論」の順に書かれますが、読み手が最初に知りたいのは結論です。document-restructuring は、書き上げた文書を測り、章立てを読み手の問いの順に並べ替え、長い文を分け、対比を表にして、もう一度測ります。内容は足しません。レビューの前に通すことで、レビューが構成の指摘と内容の指摘で混ざらなくなります。 +> 同じ AI に書かせてレビューさせると、同じ見落としをします。cross-review は、claude・codex・kiro などから 2 つを選んでレビューさせ、組み合わせを入れ替えながら、新しい指摘が出なくなるまで繰り返します。cross-refactoring は、参加する AI 全員が直す場所を提案し、選ばれた 1 つの AI が、指定した時間(既定 30 分)に収まる分だけを実施します。テストが壊れた変更は取り消し、採用と取り消しの内訳を報告に残します。 -## 12. /ndf:cross-review — 2 つの AI で、新しい指摘が出なくなるまで - -> 同じ AI に書かせてレビューさせると、同じ見落としをします。cross-review は、claude・codex・kiro などから 2 つを選んでレビューさせ、指摘を修正し、新しい指摘が出なくなるまで繰り返します。組み合わせはラウンドごとに入れ替えます。レビューの観点は PR の中身で切り替わり、リポジトリごとにレビューの基準を設定できます。 - -## 13. /ndf:cross-refactoring — 全員で提案し、1 人が時間内に実施する - -> リファクタリングは、どこを直すかの発見と、直した結果の検証が難しい作業です。cross-refactoring では、参加する AI 全員が 1 回ずつ多面的にリファクタリングを提案し、選ばれた 1 つの AI が、指定した時間(既定 30 分)に収まる分だけを計画・テスト追加・実装・検証します。時間に関わる数値はすべてその時間から逆算するので、終わりが見えます。テストが壊れた変更は取り消し、採用と取り消しの内訳を報告に残します。 - -## 14. 3 層のエージェント — 長いタスクを止めずに、止まっても再開できる +## 12. 3 層のエージェント — 長いタスクを止めずに、止まっても再開できる > 長いタスクは 1 つのセッションでは終わりません。NDF は conductor・supervisor・worker の 3 層で進めます。conductor は人と話して全体を指揮し、supervisor は設計・実装・テストといった 1 つのフェーズを通し、worker は調査や修正といった 1 つの作業を行います。フェーズは JSON のプランとしてスクリプトが実行するので、途中で止まっても続きから再開できます。セッションのコンテキストが大きくなったら、次に実行するコマンドを残して自動で新しいセッションに切り替えます。 -## 15. 判断基準: MVV と 2 つの承認ゲート +## 13. 判断基準: MVV と 2 つの承認ゲート > AI は判断のたびに迷います。指摘を直すか見送るか、人の承認を省いてよいか。NDF はプロジェクトの Mission・Vision・Value を明文化し、AI がそれを根拠に判断して、根拠の番号をログに残します。例えば「まず届ける」「処理の改善に寄与しない指摘は直さない」。本番デプロイのように必ず人の承認が必要な操作もここに定義します。どこまで AI に任せるかも、進め方の設定で事前に選べます。 -## 16. /ndf:quality-gates と /ndf:investigation-rules — 完了も「無い」も証拠で +## 14. /ndf:quality-gates と /ndf:investigation-rules — 完了も「無い」も証拠で > AI の「完了しました」「該当のコードはありません」は、そのまま信じると危険です。quality-gates は、実行したコマンド・終了コード・実行時刻・対象範囲がそろわない限り完了と認めません。investigation-rules は、「無い」という結論には必ず検索のコマンドと結果を添えさせます。AI はもっともらしいが間違った推論で「無い」と断定しがちだからです。 -## 17. 自分を直すループ — /ndf:out-of-scope・/ndf:retrospective・/ndf:backlog-refinement +## 15. 自分を直すループ — /ndf:out-of-scope・/ndf:retrospective・/ndf:backlog-refinement > NDF でいちばん効いているのは、自分自身を直すループです。out-of-scope は、作業中に見つけた範囲外の問題をその場で Issue 化します。retrospective は、リリースのたびに進め方を見直します。backlog-refinement は、スプリントの終わりに Issue の一覧を現状に合わせ、優先順位を付け直します。ある時点の棚卸しでは、open の Issue 43 件のうち 24 件(56%)の本文が現状と食い違っていました。放っておくと、すでに直った Issue を直すことになります。今日も 71 件を 6 つの worker で並列に調べたところ、2 件はすでに修正済み、49 件は本文が古くなっていました。 -## 18. その他の特徴的な機能 +## 16. その他の特徴的な機能 > 他にも特徴的な機能があります。estimate は、AI エージェントが実施する前提で工数を見積もります。人日では見積もらず、AI の実行時間・人の作業時間・完了までの時間・費用の 4 つを、似た過去の実績から別々に出します。release は、パッケージ・Web サービス・スライドや文書まで、届け方の違う成果物を同じ骨格でリリースします。hook は、メインディレクトリの直接編集の警告、sleep による待ちの禁止、同じファイルの繰り返し読み込みの防止など、AI がやりがちなことを強制的に止めます。 -## 19. 事例: 他のプロジェクトでも動く +## 17. 事例: 他のプロジェクトでも動く > ここまでは NDF が自分自身を開発している話です。大事なのは、言語もチームも違うプロジェクトで動くかです。4 つ紹介します。AWS のインフラでは、導入前後の 37 日で 1 日あたりのマージ PR 数が 0.95 から 3.38 に増えました(社内ツールの移行と重なっており、すべてが NDF の効果ではありません)。20 人で開発する PHP の業務システムでは、設計 PR 1 本の承認から 51 時間で 31 本の実装 PR が入りました。機械学習のモデルでは、精度改善の調査で 4 か月分のデータ欠損を見つけ、修正からバックフィルまでを 1 つのスプリントで本番に出し、相関係数は 0.73 から 0.954 に改善しました。価格推定のプロジェクトでは PR 数は変わらず、レビューを必ず通す開発プロセスに変わりました。 -## 20. 事例から分かったこと: 詰まったところが次のバージョンに、ルールはプロジェクトごとに違う +## 18. 事例から分かったこと: 詰まったところが次のバージョンに、ルールはプロジェクトごとに違う > 事例から分かったことは 2 つです。1 つ目、どのプロジェクトでも最初は詰まり、それが NDF の Issue として上がって次のバージョンで直りました。PHP のプロジェクトでは worktree に vendor/ が無い、全テストに 64 分かかる、マージがそのまま本番リリースになる、といった前提の違いから 17 件の Issue が上がりました。2 つ目、どのプロジェクトも独自のルールを追加していました。インフラは本番デプロイの承認、業務システムは「マージ = 本番リリース」の承認、価格推定は全 PR へのレビューです。 -## 21. だから「フォークして自分用に」 +## 19. だから「フォークして自分用に」 > NDF 自体も、承認は 2 回だけ、ドキュメントでは「道具」でなく「Tool」と書く、文言を照合するテストは書かない、といった私のルールのかたまりです。他人のルールは、自分にとってはノイズになります。だから NDF をそのまま導入するより、フォークしてください。ワークフロー・3 層のエージェント・クロスレビュー・判断基準といった仕組みはそのまま使い、ルールを自分のものに差し替える。メリットは 3 つ。指摘が資産になる、ランタイムに依存しない、計測して改善できる。 -## 22. 始め方と育て方 +## 20. 始め方と育て方 > 始め方は 3 段階です。まずフォークして、プラグイン名とマーケットプレイス名を自分のものに変え、自分のリポジトリからインストールします。次に削ります。配布する Skill はマニフェストで決まるので、使わない Skill は外すだけ。どれを残すかはセッションのログから呼び出し回数を計測して決めます。最後に育てます。同じ指摘を 2 回したら、手順なら Skill、決まった処理ならスクリプト、必ず守らせたいなら hook にします。気をつける点は、description を増やしすぎないこと(すべてのセッションで読み込まれる)、思いつきは experimental に置いて効いたものだけ本体に移すこと、開発版を自分で使ってから正式版にすることです。 -## 23. まとめ: 明日からの 3 ステップ +## 21. まとめ: 明日からの 3 ステップ > AI に自分のやり方を教えるのは 1 回でよい。プラグインにして、フォークして育ててください。明日からの 3 ステップは、1. ai-plugins をフォークして自分の名前でインストールする、2. 使わない Skill を外す、3. 同じ指摘を 2 回したら Skill にする。リポジトリは github.com/devbasex/ai-plugins です。ありがとうございました。 diff --git a/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js b/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js index 3ee7b4472..164b87304 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js +++ b/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js @@ -1,4 +1,4 @@ -// 発表資料「独自プラグイン開発の薦め」の生成スクリプト(台本の 23 ページと章扉 6 つの 29 ページ)。 +// 発表資料「独自プラグイン開発の薦め」の生成スクリプト(台本の 21 ページと章扉 6 つの 27 ページ)。 // // node build-deck.js <出力>.pptx [] // @@ -223,49 +223,41 @@ run(__dirname, { script: SCRIPT, title: "独自プラグイン開発の薦め", card(d, s, c.x, 1.5, c.w, 2.1, r[0], bullets(r[1], 14), "panel1", { band: "accent", headSize: 14 }); }); - // 10. /ndf:requirements-design と /ndf:design - s = d.page("コードの前に「何を」「どう」", "実装の前に、要件と設計の 2 つの文書を書く"); + // 10. /ndf:requirements-design・/ndf:design・/ndf:document-restructuring + s = d.page("コードの前に、読める文書を", "要件と設計を書き、結論から読める順に並べ替える"); [ - ["/ndf:requirements-design — 完了の条件", ["確かめられる受け入れ条件", "「速く」を測れる条件へ"]], - ["/ndf:design — 作り方", ["構造・入出力・流れ・理由", "AI が変わっても同じ構成"]], + ["/ndf:requirements-design — 完了の条件", "確かめられる受け入れ条件"], + ["/ndf:design — 作り方", "構造・入出力・流れ・理由"], ].forEach((r, i) => { const c = cols(2)[i]; - card(d, s, c.x, 1.5, c.w, 1.5, r[0], bullets(r[1], 15), "panel1", { band: "accent", headSize: 13 }); + card(d, s, c.x, 1.42, c.w, 0.96, r[0], r[1], "panel1", { band: "accent", headSize: 13, bodySize: 14 }); + }); + d.text(s, "/ndf:document-restructuring — 内容は足さず、順だけ直す", { x: X0, y: 2.56, w: W, h: 0.3, fontSize: 13, bold: true, color: "text", valign: "middle" }); + flow(d, s, X0, 2.96, 4.3, 0.58, ["問題に気づく", "測る", "結論"], { fill: "panel2", size: 11.5 }); + d.arrow(s, 4.66, 3.25, 0.5, 0, "accent"); + flow(d, s, 5.32, 2.96, 4.42, 0.58, ["結論", "根拠", "経緯"], { size: 11.5 }); + d.text(s, "AI が書いた順", { x: X0, y: 3.58, w: 4.3, h: 0.26, fontSize: 10, align: "center" }); + d.text(s, "読み手が知りたい順", { x: 5.32, y: 3.58, w: 4.42, h: 0.26, fontSize: 10, align: "center" }); + d.callout(s, 3.98, 0.72, "caution", "設計は PR で出す", "主題ごとに並列、承認 1 でまとめて"); + + // 11. /ndf:cross-review と /ndf:cross-refactoring + s = d.page("複数の AI で、見て直す", "見落としは別の AI が拾い、直すのは時間内に"); + [ + ["/ndf:cross-review — 指摘が尽きるまで", ["2 つの AI が\nレビュー", "指摘を修正", "組み替えて\n繰り返す"], "新しい指摘が出なくなったら終了", "観点は PR の中身で切り替え"], + ["/ndf:cross-refactoring — 時間内に直す", ["全員の AI が\n提案", "1 つが既定\n30 分で実施", "壊れたら\n取り消す"], "数値は持ち時間から逆算", "採用と取り消しを報告"], + ].forEach((r, i) => { + const c = cols(2, 0.3)[i]; + d.box(s, c.x, 1.42, c.w, 2.96, "panel1", false); + d.box(s, c.x, 1.42, c.w, 0.06, "accent", false); + d.text(s, r[0], { x: c.x + 0.14, y: 1.54, w: c.w - 0.28, h: 0.34, fontSize: 13, bold: true, color: "text", valign: "middle" }); + flow(d, s, c.x + 0.14, 2.02, c.w - 0.28, 0.96, r[1], { gap: 0.2, size: 12.5 }); + d.text(s, bullets([r[2], r[3]], 14), { x: c.x + 0.14, y: 3.2, w: c.w - 0.28, h: 1.0, valign: "middle", paraSpaceAfter: 8 }); }); - d.callout(s, 3.2, 0.72, "caution", "設計は PR で出す", "主題ごとに並列、承認 1 でまとめて"); - - // 11. /ndf:document-restructuring - s = d.page("結論から読める順に並べ替える", "/ndf:document-restructuring — 書き上げた文書の再構成"); - d.text(s, "AI が書いた順", { x: X0, y: 1.4, w: 4.3, h: 0.3, fontSize: 12, bold: true, color: "text" }); - flow(d, s, X0, 1.76, 4.3, 0.56, ["問題に気づく", "測る", "結論"], { fill: "panel2", size: 11 }); - d.arrow(s, 4.66, 2.04, 0.5, 0, "accent"); - d.text(s, "読み手が知りたい順", { x: 5.32, y: 1.4, w: 4.42, h: 0.3, fontSize: 12, bold: true, color: "text" }); - flow(d, s, 5.32, 1.76, 4.42, 0.56, ["結論", "根拠", "経緯"], { size: 11 }); - d.text(s, "手順(内容は足さない)", { x: X0, y: 2.62, w: W, h: 0.3, fontSize: 12, bold: true, color: "text" }); - flow(d, s, X0, 2.98, W, 0.62, ["測る", "問いの順に並べる", "文を分ける", "対比は表に", "測り直す"], { size: 11 }); - d.callout(s, 3.86, 0.5, "panel1", "レビューの前に通す → 構成と内容の指摘を分ける", null); - - // 12. /ndf:cross-review - s = d.page("2 つの AI で、指摘が尽きるまで", "/ndf:cross-review — 別々の AI が交互にレビュー"); - d.text(s, "同じ AI は、同じ見落としをする", { x: X0, y: 1.38, w: 5.0, h: 0.3, fontSize: 12, bold: true, color: "text" }); - flow(d, s, X0, 1.76, 5.0, 0.7, ["AI を 2 つ選ぶ\nclaude・codex・kiro", "レビュー", "指摘を修正"], { size: 10 }); - d.arrow(s, 2.9, 2.54, 0, 0.3); - d.chip(s, 1.5, 2.9, 2.8, 0.56, "新しい指摘は?", "panel2", { fontSize: 11 }); - d.text(s, "出た → 組み替えて繰り返す\n出ない → 終了", { x: X0, y: 3.56, w: 5.0, h: 0.56, fontSize: 11 }); - card(d, s, 5.6, 1.38, 4.14, 1.26, "別の AI が見落としを拾う", "2 つの AI を入れ替えながら\n新しい指摘が出なくなるまで", "panel1", { band: "accent", headSize: 14, bodySize: 14, headH: 0.38 }); - card(d, s, 5.6, 2.86, 4.14, 1.26, "観点は PR の中身で切り替え", "レビューの基準は\nリポジトリごとに設定", "panel1", { band: "accent", headSize: 14, bodySize: 14, headH: 0.38 }); - - // 13. /ndf:cross-refactoring - s = d.page("全員で提案、1 人が時間内に直す", "/ndf:cross-refactoring — 複数の AI でリファクタリング"); - flow(d, s, X0, 1.5, W, 0.86, ["全員の AI が提案", "1 つを選ぶ", "既定 30 分で\n収まる分を実施", "テストが壊れたら\n取り消す"], { size: 11 }); - d.text(s, "実施: 計画 → テスト追加 → 実装 → 検証", { x: 5.0, y: 2.48, w: 4.74, h: 0.3, fontSize: 11, align: "center" }); - card(d, s, X0, 3.0, 4.66, 1.1, "終わりが見える", "すべて持ち時間から逆算", "panel1", { band: "accent", headSize: 15, bodySize: 15, headH: 0.38 }); - card(d, s, 5.08, 3.0, 4.66, 1.1, "結果が追える", "採用と取り消しを報告", "panel1", { band: "accent", headSize: 15, bodySize: 15, headH: 0.38 }); // ==== 章 4 ==== d.section("4. AI に任せ切る仕組み", "止まらない・迷わない・ごまかさない"); - // 14. 3 層のエージェント + // 12. 3 層のエージェント s = d.page("3 層のエージェントで止めない", "長い作業も、止まったら続きから"); [ ["conductor", "指揮役。人と話し全体を進める"], @@ -280,7 +272,7 @@ run(__dirname, { script: SCRIPT, title: "独自プラグイン開発の薦め", card(d, s, 5.7, 1.46, 4.04, 1.3, "止まっても続きから", "JSON の計画をスクリプトが実行", "panel1", { band: "accent", headSize: 13, bodySize: 12.5 }); card(d, s, 5.7, 2.96, 4.04, 1.5, "セッションを自動で切り替え", "次のコマンドを残して新しいセッションへ", "panel1", { band: "accent", headSize: 13, bodySize: 12.5 }); - // 15. 判断基準: MVV と 2 つの承認ゲート + // 13. 判断基準: MVV と 2 つの承認ゲート s = d.page("迷ったら MVV で判断する", "判断の根拠の番号をログに残す"); [ ["Mission", "何のためにやるか"], @@ -294,7 +286,7 @@ run(__dirname, { script: SCRIPT, title: "独自プラグイン開発の薦め", card(d, s, 6.4, 1.5, 3.34, 1.3, "必ず人が承認する操作", "本番デプロイなど\nAI は省かない", "caution", { headSize: 13, bodySize: 12 }); d.callout(s, 3.5, 0.4, "panel1", "AI に任せる範囲は、事前に設定で選ぶ", null); - // 16. /ndf:quality-gates と /ndf:investigation-rules + // 14. /ndf:quality-gates と /ndf:investigation-rules s = d.page("「完了」も「無い」も証拠で", "AI の言葉を、そのままは信じない"); [ ["「完了しました」", "/ndf:quality-gates — 完了の判定", "4 点がそろって完了\nコマンド・終了コード・時刻・範囲"], @@ -307,7 +299,7 @@ run(__dirname, { script: SCRIPT, title: "独自プラグイン開発の薦め", }); d.text(s, "AI は「無い」と断定しがち", { x: X0, y: 4.3, w: W, h: 0.34, fontSize: 12, bold: true, color: "text", align: "center", valign: "middle" }); - // 17. 自分を直すループ(導入先のプロジェクトの Issue の一覧を整理し続ける) + // 15. 自分を直すループ(導入先のプロジェクトの Issue の一覧を整理し続ける) s = d.page("やるべきことを整理し続ける", "導入したプロジェクトの Issue の一覧を、いつも現状に"); [ ["/ndf:out-of-scope — 見つけたら起票", "範囲外の問題を即 Issue に"], @@ -322,7 +314,7 @@ run(__dirname, { script: SCRIPT, title: "独自プラグイン開発の薦め", stat(d, s, 5.4, 3.0, 4.34, 1.5, "49", "件", "本文が古かった Issue\n(71 件を調査。2 件は修正済み)"); d.note(s, "56% はある時点の棚卸し、49 件は 2026-10-17 の調査。放っておくと、直った Issue を直すことになる"); - // 18. その他の特徴的な機能 + // 16. その他の特徴的な機能 s = d.page("そのほかの特徴的な機能", "見積もり・リリース・AI の操作の強制停止"); d.tbl(s, [ [d.H("機能"), d.H("何をするか"), d.H("要点")], @@ -334,7 +326,7 @@ run(__dirname, { script: SCRIPT, title: "独自プラグイン開発の薦め", // ==== 章 5 ==== d.section("5. 事例", "ほかのプロジェクトでも動くか"); - // 19. 事例: 他のプロジェクトでも動く + // 17. 事例: 他のプロジェクトでも動く s = d.page("事例: ほかのプロジェクトでも動く", "言語もチームも違う 4 つのプロジェクト"); d.tbl(s, [ [d.H("プロジェクトの種類"), d.H("結果")], @@ -345,7 +337,7 @@ run(__dirname, { script: SCRIPT, title: "独自プラグイン開発の薦め", ], { x: X0, y: 1.42, w: W, colW: [2.3, 7.18], rowH: [0.34, 0.74, 0.6, 0.74, 0.5], fontSize: 12 }); d.note(s, "インフラは導入前後 37 日の比較。プロジェクトは種類で呼ぶ。数値は 2026-10-17 時点の実測"); - // 20. 事例から分かったこと + // 18. 事例から分かったこと s = d.page("事例から分かった 2 つのこと", "詰まりは次の版で直り、ルールはプロジェクトごと"); card(d, s, X0, 1.42, 4.66, 2.48, "1. 最初は詰まり、次の版で直る", bullets([ "詰まりが Issue になり、次の版で直る", @@ -365,14 +357,14 @@ run(__dirname, { script: SCRIPT, title: "独自プラグイン開発の薦め", // ==== 章 6 ==== d.section("6. 始め方", "フォークして、自分用に育てる"); - // 21. だから「フォークして自分用に」 + // 19. だから「フォークして自分用に」 s = d.page("だから、フォークして自分用に", "仕組みはそのまま、ルールだけ差し替える"); card(d, s, X0, 1.42, 4.66, 1.86, "NDF も私のルールのかたまり", bullets(["承認は 2 回だけ", "文書では「道具」でなく「Tool」", "文言を照合するテストは書かない"], 12.5), "warn", { headSize: 13 }); card(d, s, 5.08, 1.42, 4.66, 1.86, "そのまま使う仕組み", bullets(["ワークフロー", "3 層のエージェント", "クロスレビュー", "判断基準(MVV)"], 12.5), "accentSoft", { headSize: 13 }); d.text(s, "他人のルールはノイズ → フォークする", { x: X0, y: 3.36, w: W, h: 0.34, fontSize: 12, bold: true, color: "text", align: "center", valign: "middle" }); cols(3).forEach((c, i) => d.chip(s, c.x, 3.84, c.w, 0.72, ["指摘が資産になる", "ランタイムに依存しない", "計測して改善できる"][i], "panel1", { fontSize: 13 })); - // 22. 始め方と育て方 + // 20. 始め方と育て方 s = d.page("始め方と育て方", "フォークする → 削る → 育てる"); [ ["1. フォークする", ["名前を自分のものに", "自分のリポジトリから導入"]], @@ -388,7 +380,7 @@ run(__dirname, { script: SCRIPT, title: "独自プラグイン開発の薦め", "開発版を使ってから正式版に", ], 10.5)); - // 23. まとめ: 明日からの 3 ステップ + // 21. まとめ: 明日からの 3 ステップ s = d.page("まとめ: 明日からの 3 ステップ", "教えるのは 1 回。プラグインにして、フォークして育てる"); [ ["1", "ai-plugins をフォークして\n自分の名前でインストール"], diff --git a/docs/presentations/README.md b/docs/presentations/README.md index 2a67eba2f..33e1815d6 100644 --- a/docs/presentations/README.md +++ b/docs/presentations/README.md @@ -28,7 +28,7 @@ | 資料 | 内容 | 想定時間 | |---|---|---| -| [2026-10-17-skill-plugin-development/](2026-10-17-skill-plugin-development/README.md) | 独自プラグイン開発の薦め。NDF の機能を例に、自分用のスキルを作り、プラグインにして配り、育てることを薦める(社外の勉強会・登壇向け) | 30〜35分 / 23ページ | +| [2026-10-17-skill-plugin-development/](2026-10-17-skill-plugin-development/README.md) | 独自プラグイン開発の薦め。NDF の機能を例に、自分用のスキルを作り、プラグインにして配り、育てることを薦める(社外の勉強会・登壇向け) | 30〜35分 / 21ページ | | [2026-08-06-ai-plugins-intro.md](2026-08-06-ai-plugins-intro.md) | ai-plugins / NDF v5.0.0 の個別機能紹介。各スキルに何が書いてあり、何を重視しているかを扱う | 15分 / 15枚 | `2026-10-17-skill-plugin-development` の成果物は Google Slides で、pptx は生成スクリプトから作り直せるためコミットしません。 diff --git a/tests/test_presentation_decks.py b/tests/test_presentation_decks.py index 49de7fcf1..9d0d47d0d 100644 --- a/tests/test_presentation_decks.py +++ b/tests/test_presentation_decks.py @@ -75,13 +75,13 @@ def test_deck_uses_copied_frame(deck: Path) -> None: assert _lib_hash(deck / "slides" / "lib") == recorded.group(1) -def test_skill_plugin_development_has_23_pages_and_6_sections(tmp_path: Path) -> None: +def test_skill_plugin_development_has_21_pages_and_6_sections(tmp_path: Path) -> None: deck = ROOT / "docs" / "presentations" / "2026-10-17-skill-plugin-development" - assert len(script_readings(deck / "script.md")) == 23 - assert _notes_rows(deck / "notes.md") == list(range(1, 24)) + assert len(script_readings(deck / "script.md")) == 21 + assert _notes_rows(deck / "notes.md") == list(range(1, 22)) ensure_node_modules(deck / "slides") out = tmp_path / "deck.pptx" assert run_node(deck / "slides" / "build-deck.js", out, cwd=deck).returncode == 0 slides = read_pptx(out)["slides"] - assert len(slides) == 29 + assert len(slides) == 27 assert sum(_is_section(p) for p in slides) == 6 From b11ba3cfce7c69cb154a222bea11ad7a3cffbfed Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sun, 18 Oct 2026 23:19:59 +0000 Subject: [PATCH 10/10] =?UTF-8?q?Update:=20=E7=99=BA=E8=A1=A8=E8=B3=87?= =?UTF-8?q?=E6=96=99=E3=81=AE=E3=80=8C=E6=AE=B5=E3=80=8D=E3=80=8C=E6=AE=B5?= =?UTF-8?q?=E9=9A=8E=E3=80=8D=E3=82=92=E6=A5=AD=E7=95=8C=E3=81=AE=E8=AA=9E?= =?UTF-8?q?=E3=81=B8=E7=9B=B4=E3=81=99=20(#2065)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 台本 20 の読み上げ「始め方は 3 段階です」を「3 ステップです」へ(写しと正本の両方。スピーカーノートにも入る) - 構成メモの「階段の図」を「斜めに下げて並べた図」へ、尺の表を合わせた(4,957 字・14 分 10 秒) - 生成スクリプトのコメントの「段」を言い換えた Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01SyiLCs8wNrE2PKfeeqZDyT --- .../2026-10-17-skill-plugin-development/notes.md | 8 ++++---- .../2026-10-17-skill-plugin-development/script.md | 2 +- .../slides/build-deck.js | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/presentations/2026-10-17-skill-plugin-development/notes.md b/docs/presentations/2026-10-17-skill-plugin-development/notes.md index b11f6beb3..00daa3767 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/notes.md +++ b/docs/presentations/2026-10-17-skill-plugin-development/notes.md @@ -28,7 +28,7 @@ | 9 | 実績のある開発技法を組み込む | DDD・テストファースト・アジャイルの 3 列。用語集 580 語と WSJF を載せる | 台本 9 | | 10 | コードの前に、読める文書を | `/ndf:requirements-design`(完了の条件)と `/ndf:design`(作り方)の 2 区画、`/ndf:document-restructuring` の書いた順と読み手の順の対比(内容は足さない)、設計 PR と承認 1 の区画 | 台本 10 | | 11 | 複数の AI で、見て直す | `/ndf:cross-review`(2 つの AI を入れ替えて指摘が尽きるまで)と `/ndf:cross-refactoring`(全員が提案し 1 つが既定 30 分で実施、壊れたら取り消す)の 2 列の流れと要点(数値は載せない) | 台本 11 | -| 12 | 3 層のエージェントで止めない | conductor(指揮役)・supervisor(監督役)・worker(作業役)の階段の図と、再開とセッションの切り替えの 2 区画 | 台本 12 | +| 12 | 3 層のエージェントで止めない | conductor(指揮役)・supervisor(監督役)・worker(作業役)を斜めに下げて並べた図と、再開とセッションの切り替えの 2 区画 | 台本 12 | | 13 | 迷ったら MVV で判断する | Mission・Vision・Value の 3 区画と Value の例 2 つ、必ず人が承認する操作 | 台本 13、`.ndf/` のプロジェクト MVV | | 14 | 「完了」も「無い」も証拠で | AI の言葉 2 つと、それぞれを受け止める `/ndf:quality-gates`・`/ndf:investigation-rules` の対応 | 台本 14 | | 15 | やるべきことを整理し続ける | 導入したプロジェクトの Issue の一覧を現状に保つ 3 つの Skill の流れと、何の数かを添えた棚卸しの数値 2 つ(56%・49 件) | 台本 15(棚卸しの記録と 2026-10-17 の調査) | @@ -55,7 +55,7 @@ ## 尺 -`node slides/lib/script-time.js script.md --limit 35` の出力(350 字/分)。合計 4,955 字・14 分 09 秒で、 +`node slides/lib/script-time.js script.md --limit 35` の出力(350 字/分)。合計 4,957 字・14 分 10 秒で、 持ち時間(30〜35 分)に収まる。似たページを 2 組まとめた(まとめる前の台本の 10・11 ページと 12・13 ページ)。 | # | 見出し | 字数 | 尺(秒) | 配分(秒) | @@ -79,9 +79,9 @@ | 17 | 事例: 他のプロジェクトでも動く | 328 | 56.2 | — | | 18 | 事例から分かったこと: 詰まったところが次のバージョンに、ルールはプロジェクトごとに違う | 244 | 41.8 | — | | 19 | だから「フォークして自分用に」 | 225 | 38.6 | — | -| 20 | 始め方と育て方 | 312 | 53.5 | — | +| 20 | 始め方と育て方 | 314 | 53.8 | — | | 21 | まとめ: 明日からの 3 ステップ | 177 | 30.3 | — | -| 合計 | 21 ページ(350 字/分) | 4955 | 849.4(14分09秒) | — | +| 合計 | 21 ページ(350 字/分) | 4957 | 849.7(14分10秒) | — | ## ページに収まらない読み上げの扱い diff --git a/docs/presentations/2026-10-17-skill-plugin-development/script.md b/docs/presentations/2026-10-17-skill-plugin-development/script.md index 539b4b801..ec945e3d5 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/script.md +++ b/docs/presentations/2026-10-17-skill-plugin-development/script.md @@ -97,7 +97,7 @@ ## 20. 始め方と育て方 -> 始め方は 3 段階です。まずフォークして、プラグイン名とマーケットプレイス名を自分のものに変え、自分のリポジトリからインストールします。次に削ります。配布する Skill はマニフェストで決まるので、使わない Skill は外すだけ。どれを残すかはセッションのログから呼び出し回数を計測して決めます。最後に育てます。同じ指摘を 2 回したら、手順なら Skill、決まった処理ならスクリプト、必ず守らせたいなら hook にします。気をつける点は、description を増やしすぎないこと(すべてのセッションで読み込まれる)、思いつきは experimental に置いて効いたものだけ本体に移すこと、開発版を自分で使ってから正式版にすることです。 +> 始め方は 3 ステップです。まずフォークして、プラグイン名とマーケットプレイス名を自分のものに変え、自分のリポジトリからインストールします。次に削ります。配布する Skill はマニフェストで決まるので、使わない Skill は外すだけ。どれを残すかはセッションのログから呼び出し回数を計測して決めます。最後に育てます。同じ指摘を 2 回したら、手順なら Skill、決まった処理ならスクリプト、必ず守らせたいなら hook にします。気をつける点は、description を増やしすぎないこと(すべてのセッションで読み込まれる)、思いつきは experimental に置いて効いたものだけ本体に移すこと、開発版を自分で使ってから正式版にすることです。 ## 21. まとめ: 明日からの 3 ステップ diff --git a/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js b/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js index 164b87304..cc6b9efd3 100644 --- a/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js +++ b/docs/presentations/2026-10-17-skill-plugin-development/slides/build-deck.js @@ -45,7 +45,7 @@ function stat(d, s, x, y, w, h, value, unit, label) { d.text(s, label, { x: x + 0.2, y: y + 0.8, w: w - 0.3, h: h - 0.9, fontSize: 10 }); } -// 箇条書き("- " で始まる項目は 1 段下げる) +// 箇条書き("- " で始まる項目は インデントを 1 つ下げる) function bullets(items, size) { return items.map((t, i) => { const sub = t.startsWith("- "); @@ -204,7 +204,7 @@ run(__dirname, { script: SCRIPT, title: "独自プラグイン開発の薦め", const n = row[1].length; const g = 0.2; flow(d, s, 1.06, y, (8.68 * n + g * (n - 7)) / 7, 0.7, row[1], { gap: g, size: 9.5 }); - // 段の続きは左の見出しの列で示す(工程の箱の間に置くと、箱どうしの流れに読める) + // 行の続きは左の見出しの列で示す(工程の箱の間に置くと、箱どうしの流れに読める) if (i < 2) d.arrow(s, X0 + 0.3, y + 0.76, 0, 0.26); }); d.note(s, "レビューの指摘は直して繰り返す。開発版は自分で使って確かめてから承認 2 へ");