Blog / Site Maintenance
SkillMediaMaintenance of site articles and settings. Interactively handles: creating new blog posts, toggling published/unpublished state, editing frontmatter (title, description, tags), deleting posts, listing posts, adding/replacing member images, and editing build article body text (build concept / damage
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Blog / Site Maintenance skill
What this skill tells your AI
The instructions your AI receives, as published by pkdxtools/pkdx in .claude/skills/blog/SKILL.md and read by ahel’s review.
box/blog/, box/teams/, box/site.config.json に対するメンテナンス操作を提供する。team-builder / breed がコンテンツを「生成」する側であるのに対し、このスキルはコンテンツを「管理」する側に特化する。
本スキルは以下を 1 セッションで扱う:
- コンテンツ編集(Phase A-F)
- 編集結果の確認と git push(Phase G、ユーザー承認必須)
git push は Phase G で必ずユーザーの YES 確認を得てから実行する。 無確認で push することはない。push を拒否された場合はローカルに変更を残したまま終了する。
Skip 判定 共通ルール
全 Phase の AskUserQuestion に適用される共通ルール:
skip してよい条件(いずれか該当):
- ユーザー発話に既に当該質問の回答が含まれている(例: 「カバルドン記事を削除したい」→ コレクション選択・記事選択 を skip)
- 直前の Phase / AskUserQuestion で既に確定した情報を再確認する質問
- ユーザーが操作対象パスを絶対 / 相対パスで明示している
skip 時の扱い:
- 想定される回答を skill 記載の手順通りに内部で決定
- skip した事実と選んだ想定応答をユーザーへ通常メッセージで開示(例: 「対象コレクションを
teamsと判定して続行します」)
絶対に skip してはいけない質問:
- 削除確認ダイアログ(Phase D-3)
- Phase G-2 の反映可否確認
- Phase G-3 の送信先最終確認(「反映する」経路)
- Phase G-4 の commit メッセージ確認
Phase 間遷移の原則
各 Phase の完了報告(例: A-5, B-5, C-5, D-5, E-5, H-5, I-8)直後の Phase G への遷移は自動で行う。間に確認の AskUserQuestion は挟まない。ユーザーは完了報告テキストを見て、Phase G の差分提示へと続けて移行する。
パス定義
SKILL_DIR=(このSKILL.mdが置かれたディレクトリ)
REPO_ROOT=$SKILL_DIR/../../..
BLOG_DIR=$REPO_ROOT/box/blog
TEAMS_DIR=$REPO_ROOT/box/teams
SITE_CONFIG=$REPO_ROOT/box/site.config.json
Phase 0: 初期化
0-1: box 配下の存在確認
ls "$REPO_ROOT/box" >/dev/null 2>&1 || { echo "box/ が見つかりません。setup.sh を先に実行してください。"; exit 1; }
0-2: site.config.json 読み込み
$SITE_CONFIG が存在しない場合は作成する:
if [ ! -f "$SITE_CONFIG" ]; then
cat > "$SITE_CONFIG" <<'JSON'
{
"site_name": "pkdx site",
"author": null,
"enabled": true
}
JSON
fi
存在する場合は Read で現状を取得する。
0-3: 操作選択
最初の AskUserQuestion で編集系 4 操作(Phase A/B/C/D)のみ提示し、それらすべてに該当しないときだけ 2 段目 AskUserQuestion を出す。ユーザー発話にすでに明確な意図がある場合(「サイト名を〇〇に変えたい」「一覧を見たい」等)は 1 段目を skip して該当 Phase に直接遷移してよい。
AskUserQuestion(1問、ユーザー意図が既に明確なら skip 可):
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | どの操作を行いますか? | 操作 | 新規ブログ記事作成(desc: box/blog/ に新しい記事ファイルを作る), 記事の編集/削除/公開切替(desc: 既存記事の frontmatter 編集・削除・published フラグ反転), サイト設定 or 記事一覧(desc: site_name / author / enabled / 記事一覧表示), その他(desc: 上記以外を Other で入力) | false |
選択内容に応じて以下へ:
- 「新規ブログ記事作成」 → Phase A
- 「記事の編集/削除/公開切替」 → 続く AskUserQuestion(下記)で B/C/D のどれかを確定
- 「サイト設定 or 記事一覧」 → 続く AskUserQuestion(下記)で E/F のどちらかを確定
- 「その他」→ Other キーワードから判定
「記事の編集/削除/公開切替」を選んだ場合、続く AskUserQuestion:
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | どの操作? | 操作 | 公開/非公開切替(desc: Phase B), フロントマター編集(desc: Phase C), 削除(desc: Phase D), メンバー画像メンテ(desc: Phase H — team 記事のメンバー画像追加・差し替え), 本文編集(desc: Phase I — 構築コンセプト / ダメージ計算 / ポケモンメモ / 選出 の編集 (team 記事のみ)) | false |
「サイト設定 or 記事一覧」を選んだ場合、続く AskUserQuestion:
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | どちら? | 操作 | サイト設定変更(desc: Phase E), 記事一覧表示(desc: Phase F) | false |
Other での直接キーワード判定は以下:
- 「新規」「作成」「書く」→ Phase A
- 「公開」「下書き」「非公開」→ Phase B
- 「編集」「タイトル変更」→ Phase C
- 「削除」「消す」→ Phase D
- 「画像」「メンバー画像」「ポケモン画像」→ Phase H
- 「コンセプト」「構築コンセプト」「ダメ計」「ダメージ計算」「ポケモンメモ」「役割メモ」「role」「選出」「表選出」「裏選出」「本文」→ Phase I
- 「設定」「サイト名」「author」→ Phase E(2段目 skip 可)
- 「一覧」「リスト」→ Phase F(2段目 skip 可)
選択結果に応じて該当 Phase へ遷移する。
0-4: ローカル dev プレビューの起動(任意)
操作 Phase へ遷移する直前に、ユーザーへローカル dev プレビューを起動するか確認する。dev サーバーを立ち上げておくと、Edit / Write のたびに Astro が hot reload するので、ユーザーは編集結果をブラウザでリアルタイムに確認しながら作業できる。
Phase F (一覧表示) を選んだ場合は本ステップを skip (差分が出ない / プレビューする意味がないため)。それ以外は以下を実行。
0-4-1: 起動希望の確認
先に port 4321 の使用状況を確認してから AskUserQuestion を出す:
ALREADY_RUNNING=$(lsof -nP -iTCP:4321 -sTCP:LISTEN 2>/dev/null | tail -n +2 | head -1)
$ALREADY_RUNNING が空でない場合は通常メッセージで以下を出してskip (既起動の dev server をユーザーは別ターミナルで使っている想定):
http://localhost:4321 で既に何かがリッスンしています。
別ターミナルで起動済みの dev server を使うか、ご自分で確認してください。
$ALREADY_RUNNING が空の場合のみ AskUserQuestion (1問)。質問には bun runtime が必要である旨の断りを必ず含める (setup.sh は bun を入れないため、ユーザーが未導入の可能性がある):
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | ローカルで dev プレビューしながら作業しますか? (Astro の hot reload で編集結果がリアルタイムに反映されます。プレビュー起動には bun runtime が必要です。未導入の場合は scripts/install_bun.sh で自動インストールするか確認します) | プレビュー | 起動しない(default, desc: 編集だけ進めて、ブラウザで確認しない), 起動する(desc: preview_start で site を起動。bun 未導入なら先にインストール確認。Phase G 終了時に停止確認) | false |
「起動しない」を選ばれた場合はそのまま該当 Phase へ。
0-4-2: dev server を preview_start で起動
「起動する」を選ばれた場合、mcp__Claude_Preview__preview_start ツールを name: "site" で呼び出す。生 Bash で bun run dev & を回さない (プロセス管理が手動になり Phase G-7 の停止が不安定になるため、preview_start に一任する)。
前提として .claude/launch.json に site エントリが必要 (この repo では既に配置済み)。preview_start は launch.json の runtimeExecutable / runtimeArgs / cwd / port を読んで bun run dev を site/ 配下で起動する。
bun の存在確認: runtimeExecutable: "bun" のため bun が PATH 上に無いと preview_start は失敗する。setup.sh は意図的に bun を入れないため、以下で検出し、未インストール時は scripts/install_bun.sh を案内する:
if ! command -v bun &>/dev/null; then
echo "MISSING_BUN"
fi
MISSING_BUN が出力された場合、通常メッセージで以下を提示してから AskUserQuestion (1問) を出す:
dev server 起動には bun runtime が必要ですが、PATH 上に見つかりません。
scripts/install_bun.sh で bun 1.3.12 (site/ の pin バージョン) をインストールできます。
(~/.bun/bin/bun に入り、~/.zshrc / ~/.bashrc に PATH 追記されます)
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | bun を今インストールしますか? | インストール | インストールする(default, desc: scripts/install_bun.sh を実行), スキップ(desc: dev プレビューを使わず編集だけ進める) | false |
「インストールする」を選ばれた場合:
bash "$REPO_ROOT/scripts/install_bun.sh"
# 新シェルではなく現行シェル向けに PATH を通す
export BUN_INSTALL="$HOME/.bun"
export PATH="$BUN_INSTALL/bin:$PATH"
command -v bun # 再確認
インストールに失敗 (exit code != 0) または command -v bun が依然空なら、「起動しない」相当で該当 Phase へ遷移する (DEV_PREVIEW_ID は設定しない)。成功したら次の preview_start 呼び出しへ進む。
「スキップ」を選ばれた場合は「起動しない」相当で該当 Phase へ。
preview_start(name="site")
→ { serverId, port: 4321, name: "site", reused: <bool> }
serverId をセッション内変数 DEV_PREVIEW_ID として記憶する (Phase G-7 で停止する際に必要)。
起動後、最大 15 秒ほど待ってから http://localhost:4321/pkdx/ がリッスンし始めたかを以下で確認:
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
if curl -fsS -o /dev/null http://localhost:4321/pkdx/; then
echo "ready"
break
fi
sleep 1
done
起動できたら通常メッセージで案内 (Astro は astro.config.mjs で base: "/pkdx/" を設定しているため、ルート / は 404 になる。プレビューを開くときは必ず /pkdx/ 付きで案内する):
✓ dev server を起動しました
URL: http://localhost:4321/pkdx/ ← base path /pkdx/ を必ず付けること (ルートは 404)
編集するたびに自動でブラウザに反映されます (要リロード)
【Claude Desktop でプレビューを開く】
- Preview ペインを開く: Cmd+Shift+P (macOS) / Ctrl+Shift+P (Windows)
- または セッションツールバーの「Preview」ドロップダウンから
- Preview ペインが開いていれば上記 URL が自動でロードされます
- Claude Desktop 以外で確認する場合はブラウザに URL を直接開いてください
Phase G 完了時に停止確認します。
15 秒以内に起動しなかった場合は、preview_start の戻り値や mcp__Claude_Preview__preview_logs でエラーを確認し、DEV_PREVIEW_ID を破棄してから該当 Phase へ進む (プレビューなしで作業継続)。
Phase A: 新規ブログ記事作成
A-1: 基本情報の収集
AskUserQuestion(3問、全て Other で自由入力):
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | 記事のタイトルは? | タイトル | Otherで入力(desc: 日本語可) | false |
| 2 | ファイル名(slug, 半角英数とハイフン)は? | slug | Otherで入力(desc: 例 first-post, 2026-release-notes), 日付付きで自動生成(desc: post-YYYY-MM-DD) | false |
| 3 | description(検索結果やカードに出る短い紹介)は? | description | Otherで入力(desc: 1-2文程度), 省略(desc: 空欄にする) | false |
slug バリデーション: ^[a-zA-Z0-9][a-zA-Z0-9\-]*$。NG の場合は再入力を促す。
A-2: タグ(任意)
AskUserQuestion(1問):
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | タグを追加しますか? | タグ | なし(desc: タグ空で作成), Otherで入力(desc: カンマ区切り 例: 対戦,考察) | false |
A-3: 公開状態
AskUserQuestion(1問):
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | 公開状態は? | 公開 | 下書き(published: false)(desc: まず非公開で作成。後から切り替え), 公開(published: true)(desc: いきなり公開) | false |
A-4: ファイル生成
frontmatter 表記ルール(site/src/content.config.ts の blogSchema 準拠):
title/description: ダブルクォート囲み(例:title: "記事タイトル")date: クォート無し ISO 日付(date: 2026-04-22)tags: inline flow 配列、各要素ダブルクォート囲み(例:tags: ["astro", "dev"])published: クォート無しの真偽値(published: false)description/tags/eyecatchは schema 側で optional。値が空の場合はキーごと省略してよい(tags: []空配列も OK だが省略が推奨)
重複チェック:
DEST="$BLOG_DIR/<slug>.md"
if [ -e "$DEST" ]; then
echo "既に存在します: $DEST"
# AskUserQuestion で上書き確認 or slug 再入力
fi
今日の日付を取得:
TODAY=$(date +%Y-%m-%d)
Write tool で $DEST を上述の表記ルールに完全準拠した形式で生成する。以下は具体例 (タイトル="Astro アップグレードメモ", slug=astro-upgrade-notes, tags=astro/dev, published=false, date は 必ず date +%Y-%m-%d の実行結果を使う。skill 内の例日付をそのまま写すと、実行日と違う場合にバグる):
---
title: "Astro アップグレードメモ"
date: 2026-04-22 # ← TODAY=$(date +%Y-%m-%d) の結果。例の値をそのまま使わない
description: "Astro 6 系への移行メモ"
tags: ["astro", "dev"]
published: false
---
# Astro アップグレードメモ
ここから本文を書いてください。
上記の各フィールドについて:
title: ユーザー入力を必ずダブルクォートで囲むdate:TODAY=$(date +%Y-%m-%d)の値をクォート無しでdescription: ダブルクォートで囲む。空なら行ごと省略tags: inline flow で各要素ダブルクォート。空なら行ごと省略(tags: []より推奨)published:true/falseをクォート無しで
A-5: 完了報告
新規記事を作成しました: box/blog/<slug>.md
公開状態: <下書き | 公開>
本文を編集するには box/blog/<slug>.md を直接エディタで開いてください。
続いて A-6 (dev server 再起動)を判定してから Phase G: 反映確認 に進む(action = "create", slug = <slug>)。
A-6: dev server 再起動(新規ファイル反映のため)
Astro の content collection (glob loader) は既存ファイルの Edit には hot reload で追従するが、新規追加された md は dev server を再起動するまで /pkdx/blog/<slug>/ ルートが生成されない([...slug].astro の getStaticPaths が dev 起動時のスナップショットで固定されるため)。従って Phase A で新規作成した直後は、プレビュー中の dev server を必ず再起動してから Phase G の反映確認へ進む。
skip 条件: DEV_PREVIEW_ID が未設定(Phase 0-4 で dev server を起動していない / 起動失敗した)場合は本ステップを skip。ユーザー確認も挟まず次へ進む。
DEV_PREVIEW_ID が設定されている場合のみ、以下を無確認で自動実行する(ユーザーは Phase 0-4 でプレビュー前提の作業に同意済みのため、再起動ごとに問い合わせると煩わしい):
mcp__Claude_Preview__preview_stop(serverId: "<DEV_PREVIEW_ID>")
mcp__Claude_Preview__preview_start(name: "site")
→ 戻り値の serverId で DEV_PREVIEW_ID を上書き
preview_start 直後は Phase 0-4-2 と同様に最大 15 秒まで curl -fsS http://localhost:4321/pkdx/ でリッスン確認する。起動できたら通常メッセージで以下を報告:
✓ dev server を再起動しました (新規記事ルート反映のため)
URL: http://localhost:4321/pkdx/blog/<slug>/
published: false の場合はビルド対象外となるため /pkdx/blog/ 一覧には出ませんが、
URL 直打ちでプレビューできます。
再起動に失敗した場合(preview_start がエラー / 15 秒タイムアウト)は DEV_PREVIEW_ID をクリアし、通常メッセージで以下を出してから Phase G へ進む(Phase G-7 の停止確認は skip される):
⚠ dev server の再起動に失敗しました。手動で再起動してください:
mcp__Claude_Preview__preview_list → preview_stop → preview_start
Phase B: 公開/非公開切替
B-1: 対象コレクション選択
skip 判定: ユーザー発話に対象パスが含まれる場合(例: box/teams/...md や「team 記事」「blog 記事」の明示)、本ステップを skip して B-2 に直接進んでよい。skip した場合、対象コレクションは発話から判定する。
AskUserQuestion(1問、skip 条件未適用時のみ):
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | どのコレクションの記事を切り替えますか? | 対象 | blog(desc: box/blog/ 配下), teams(desc: box/teams/ 配下) | false |
B-2: 記事選択
対象ディレクトリから *.md をリスト化(TEMPLATE.md.example は除外):
TARGET_DIR=$BLOG_DIR # or $TEAMS_DIR
find "$TARGET_DIR" -maxdepth 1 -name '*.md' ! -name 'TEMPLATE*' | sort
各記事の現状 published フラグを Read して把握する:
for f in *.md; do
grep -E '^published:' "$f" || echo "(未設定)"
done
表示例:
[1] SAMPLE-カバルドン-build-2026-04-21.md published: true
AskUserQuestion(1問、最大4件ずつバッチ):
- 記事数 ≤ 4: そのまま選択肢に並べる
- 記事数 > 4: 「ファイル名を直接指定」等で Other 入力を受ける
B-3: published フラグ反転
Edit tool で対象ファイルの frontmatter 内 published: を反転する:
published: true→published: falsepublished: false→published: true- 未設定の場合:
date:行の直後にpublished: falseを挿入(publishedのデフォルトは true だが、明示しておく)
B-4: publishedAt リセット考慮
published: false → true へ切り替えた場合、publishedAt の扱い:
with-published-at.tsloader が git log で自動補完するため、特に何もしないのが正解- ただし「今から公開した」扱いで並び順を最新に寄せたい場合は
publishedAt:行を明示的に追加する提案をする
AskUserQuestion(false → true の場合のみ、1問):
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | 公開日時をどう扱いますか? | 公開日時 | 自動(推奨)(desc: git log から自動補完される), 今の時刻を publishedAt に明記(desc: 一覧で最新として扱われる) | false |
「今の時刻」を選んだ場合:
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
Edit tool で frontmatter に publishedAt: <NOW> を追加(既存があれば上書き)。
B-5: 完了報告
<ファイル名> の公開状態を <new_state> に変更しました。
続いて Phase G: 反映確認 に進む(action = "toggle", slug = <slug>, new_state = <new_state>)。
Phase C: 記事のフロントマター編集
C-1: 対象選択
Phase B-1 / B-2 と同じフローで対象記事を選ぶ。B-1 の skip 判定ルールも同様に適用される。
C-2: 編集項目選択
Read で現状の frontmatter を取得してユーザーに提示した上で:
AskUserQuestion(1問, multiSelect):
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | どの項目を編集しますか? | 項目 | title, description, tags, eyecatch | true |
C-3: 新しい値の入力
選ばれた項目ごとに AskUserQuestion で Other 入力を受ける。各項目:
title: 文字列(空禁止)description: 文字列(空許可 → 行ごと削除)tags: カンマ区切り →[tag1, tag2]形式に整形eyecatch: パス文字列(空許可 → 行ごと削除)
C-4: Edit 適用
Edit tool で frontmatter 内の対象行を書き換える。値が空で削除対象の場合は replace_all: false で行そのものを消す。
C-5: 完了報告
<ファイル名> のフロントマターを更新しました:
- <field>: <before> → <after>
続いて Phase G: 反映確認 に進む(action = "edit", slug = <slug>, fields = <updated fields>)。
Phase D: 記事削除
D-1: 対象選択
Phase B-1 / B-2 と同じフローで対象記事を選ぶ。B-1 の skip 判定ルール(ユーザー発話に対象パスが含まれる場合、コレクション選択 AskUserQuestion を skip)も同様に適用される。ユーザーが「box/teams/SAMPLE-カバルドン-...md を削除したい」のようにパスを明示した場合は、コレクション選択を飛ばして D-2 に進む。
D-2: 関連 meta.json の検出
team-builder 由来の記事(box/teams/ 配下のみ)には <slug>.meta.json が併置されている。box/blog/ 配下には .meta.json 併置無しのため、対象が blog の場合は本ステップ自体を skip する:
SLUG=$(basename "$FILE" .md)
META="$(dirname "$FILE")/$SLUG.meta.json"
# box/teams/ 配下のみ検出対象
if [[ "$FILE" == *"/box/teams/"* ]] && [ -f "$META" ]; then
echo "meta file exists: $META"
fi
D-3: 最終確認
削除対象一覧の提示方法: AskUserQuestion の質問文本体には含めず、質問直前の通常メッセージとして別途提示する(質問文に長い一覧を詰めない):
# 通常メッセージとして先に出力
削除対象:
- box/teams/SAMPLE-カバルドン-build-2026-04-21.md
- box/teams/SAMPLE-カバルドン-build-2026-04-21.meta.json
そのあと AskUserQuestion を出す。
AskUserQuestion(1問):
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | 上記ファイルを削除してよいですか? | 削除確認 | キャンセル(desc: 削除しない), 削除する(desc: ファイルを削除。復元は git から可能) | false |
削除対象には .md と(存在すれば).meta.json の両方を含める。
user が「削除する」を選択した場合のみ削除を実行する。 キャンセル がデフォルト表示になるよう最初に配置する。本ダイアログは「Skip 判定 共通ルール」で skip 禁止と定めた critical 質問に該当するため、絶対に skip しない。
D-4: 削除実行
rm -- "$FILE"
[ -f "$META" ] && rm -- "$META"
D-5: 完了報告
削除しました: <files>
誤って削除してしまった場合、リポジトリルートで以下を実行すると復元できます:
git checkout HEAD -- <file path>
続いて Phase G: 反映確認 に進む(action = "delete", slug = <slug>)。
Phase E: サイト設定変更
E-1: 現状表示
$SITE_CONFIG を Read して現状を表示:
現在のサイト設定:
site_name: <value>
author: <value or null>
enabled: <true | false>
E-2: 変更項目選択
AskUserQuestion(1問, multiSelect):
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | どの項目を変更しますか? | 項目 | site_name(desc: サイト名 ヘッダーや に使用), author(desc: 著者名 フッターに表示), enabled(desc: false にするとデプロイが停止する) | true |
multiSelect 挙動: ユーザーが選んだ項目のみ E-3 で新値入力を求める。選ばれなかった項目は touch しない(Edit も発火しない)。選択が 0 件なら E-4 以降を skip し「変更項目がないため終了」でセッション終了。
E-3: 新しい値の入力
E-2 で選ばれた項目それぞれについて新値を問う。選ばれなかった項目は本ステップを発火させない:
site_name: 文字列(空禁止)author: 文字列 or null(「省略」で null に)enabled:true/falseの2択 AskUserQuestion
E-4: JSON 書き換え
Edit tool で $SITE_CONFIG のうち E-2 で選ばれた行のみ書き換える。Write tool で全体を書き直してもよいが、Edit の方が他キーを温存できるため優先する。
author: null の場合は "author": null と記述する(文字列 "null" ではなく JSON の null)。
E-5: 完了報告
サイト設定を更新しました:
- <field>: <before> → <after>
enabled: false にした場合は GitHub Actions のデプロイが skip されます。
続いて Phase G: 反映確認 に進む(action = "site-config", fields = <updated fields>)。
Phase F: 記事一覧表示
$BLOG_DIR と $TEAMS_DIR をスキャンし、各記事の title と published を Read で抽出してテーブル表示する。
=== box/blog ===
(記事なし)
=== box/teams ===
| ファイル | 軸 | 公開状態 |
| SAMPLE-カバルドン-build-2026-04-21.md | カバルドン | true |
表示後、AskUserQuestion で「この記事を編集/切替/削除しますか?」と促し、YES ならそれぞれ Phase B/C/D に遷移する。NO ならスキル終了(Phase G は呼ばない — 一覧表示のみでは差分が無いため)。
Phase H: メンバー画像メンテ
box/teams/<slug>.md の members[] に対応するポケモン画像 (site/public/pokemons/<member.name>.{png,jpg}) をユーザーがチャットに添付する形で追加・差し替えする。MemberCard コンポーネントは member.name から自動的に画像を解決して右上に表示する。
ユーザーが手動で site/public/pokemons/ にファイルを置く必要はない。チャット添付されたファイルを agent (Claude) が file で検証して cp で保存する。外部スクリプト・追加 runtime は不要。
H-1: 対象 team 記事を選択
ls "$TEAMS_DIR"/*.md 2>/dev/null
複数ある場合は AskUserQuestion で 1 件を選ばせる (Phase B-2 と同様のパス選択)。発話に box/teams/...md が含まれていれば skip して直接対象記述を採用する。
H-2: members 一覧の取得 + 画像有無の表示
選択された <slug>.md の frontmatter を Read で取得して members: 配列を抽出する。配列の各 <name> について以下を実行:
if [ -f "$REPO_ROOT/site/public/pokemons/${name}.png" ]; then
echo "${name} ✓ (png)"
elif [ -f "$REPO_ROOT/site/public/pokemons/${name}.jpg" ]; then
echo "${name} ✓ (jpg)"
else
echo "${name} ✗ (未配置)"
fi
通常メッセージとして一覧を表示する:
画像配置状況 (site/public/pokemons/):
カバルドン ✓ (png)
マンムー ✗ (未配置)
アーマーガア ✗ (未配置)
...
H-3: 添付対象を multiSelect で選択
AskUserQuestion (multiSelect):
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | どのメンバーの画像を追加 / 差し替えますか? | 対象 | members[] 各エントリ (各 desc に「未配置 / 既存あり (差し替え)」を表示) | true |
選択 0 件なら Phase H をここで終了し Phase G へ遷移する。
H-4: 各メンバーの画像を添付・検証・保存
選択リストを順番に処理する。各メンバーごとに通常メッセージで以下を提示:
[1/3] カバルドン の画像を添付してください
仕様: 340×340 以上の正方形、PNG または JPEG
(skip する場合は「skip」と入力)
ユーザーが画像を添付すると、agent にローカルファイルパス (例: /var/folders/.../tmp/Image-xxxxx.png) が渡される。以下をこの順序で実行する。
H-4-a: symlink を拒否
SRC="<添付ファイルの絶対パス>"
if [ -L "$SRC" ]; then
echo "REJECT_SYMLINK"
fi
REJECT_SYMLINK が出力されたら通常メッセージで「symlink は受け付けません。実体ファイルを添付してください」と提示し再添付要求 (skip なら次のメンバー)。
H-4-b: 形式と寸法を file で取得
file "$SRC"
出力例:
…: PNG image data, 480 x 480, 8-bit/color RGB, non-interlaced…: JPEG image data, JFIF standard 1.01, … precision 8, 1024x768…: GIF image data, version 89a, …
agent が出力をパースして以下を判定する (BSD/GNU 共通フォーマット):
| 項目 | 判定 |
|---|---|
| 形式 | PNG image data / JPEG image data 以外は reject (「対応形式は PNG / JPEG のみ」) |
| 寸法 | <W> x <H> または <W>x<H> を抽出。失敗したら 添付画像を agent が直接見ておおよその寸法を判断 |
| 正方形 | W !== H なら reject (「正方形にしてください: 480×320 が添付されました」) |
| 最小サイズ | W < 340 なら reject (「340×340 以上にしてください: 200×200 が添付されました」) |
reject の場合は理由を通常メッセージで提示し再添付要求。再添付なし (skip) なら次のメンバーへ。
H-4-c: 拡張子の決定 + 既存衝突確認
形式から拡張子を決める (PNG → .png、JPEG → .jpg)。保存先パスを組み立て:
DEST="$REPO_ROOT/site/public/pokemons/<member.name>.<ext>"
既存ファイルがある場合のみ AskUserQuestion (1問):
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | <member.name> の画像 (<DEST>) が既に存在します。どうしますか? | 上書き | 上書きする(desc: 既存を消して新しい画像で置き換え), skip(desc: 既存を残す), 中断(desc: Phase H を中断して Phase G に遷移) | false |
- 「skip」→ 次のメンバーへ
- 「中断」→ 残メンバー処理せず Phase G へ
- 「上書きする」→ H-4-d へ進む
H-4-d: cp で保存
mkdir -p "$REPO_ROOT/site/public/pokemons"
cp "$SRC" "$DEST"
成功なら通常メッセージで「✓ <member.name>. を保存しました (×)」と報告し、次のメンバーへ。
H-5: 完了報告
全メンバー処理後、まとめを通常メッセージで出力:
画像メンテ完了:
✓ カバルドン.png 保存
✓ マンムー.png 保存
- アーマーガア skip (再添付なし)
差分は site/public/pokemons/ 配下に作成されています。
続けて Phase G (反映確認) に遷移します。
直後に自動で Phase G へ遷移する (確認 AskUserQuestion は挟まない)。Phase G-1 の git status --short -- box/ site/public/pokemons/ で ?? site/public/pokemons/カバルドン.png などが検出される。
Phase I: 構築記事 本文編集
box/teams/<slug>.md と同名の .meta.json のペアに対する本文編集を提供する。4 種類のサブ操作 (構築コンセプト / ダメージ計算 追加・削除 / ポケモンメモ / 選出やりなおし) をサポート。
背景・設計原則
- team-builder が生成する構築記事は
.mdと.meta.jsonのペア構造。site の team ページ (site/src/pages/teams/[...slug].astro) は.md本文を描画せず、.meta.jsonの構造化データを Astro コンポーネント (MemberCard / DamageCalcTable / MatchupPlans 等) に流し込んで描画する。つまり site ページの見た目を司るのは.meta.json側 (title / description / tags など frontmatter は.mdから読む)。 - 一方
.md本文は GitHub などで raw 閲覧したときに人間が読む用に同じ内容が書き出されている。.meta.jsonと一致するように同期を保つのが望ましい。 - Phase I は meta.json (SSoT) を更新しつつ、同期のため md 本文の対応箇所も Edit で書き換える。両方を更新することで site 表示と raw 閲覧の整合を取る。
- frontmatter (title / description / tags / published / edited など) は一切触らない。これらは Phase C の守備範囲。
pkdx write teams --forceによる md 再生成は 使わない。再生成は frontmatter を既定値で上書きしてしまい、Phase C で付けた title / description / tags /edited: trueを失うため。
対象は box/teams/*.md のみ。box/blog/*.md には meta.json が併置されないため Phase I では扱わない (blog 記事の本文編集はユーザー自身がエディタで直接編集する)。
Phase I 共通 skip ルール
Phase I の AskUserQuestion は、上部「Skip 判定 共通ルール」(L22–30) に加えて次の Phase I 固有 skip 判定が適用される。全て「skip した事実と想定応答を通常メッセージで開示」必須。
-
I-1 の対象ファイル選択: 発話に以下のいずれかが含まれれば skip して採用ファイルを確定する:
- 完全パス
box/teams/<slug>.mdまたは絶対パス相当 - slug 文字列 (例:
SAMPLE-カバルドン-build-2026-04-21) - slug の部分文字列で
find "$TEAMS_DIR" -name "*<部分文字列>*.md"が 1 件に収束する場合 (例:SAMPLE-カバルドン)
2 件以上にヒットした場合は skip せず AskUserQuestion を出す。
- 完全パス
-
I-2 の編集項目選択 (multiSelect): 発話で明示された項目が 1 個以上ある場合は skip して発話項目のみを採用する。発話内容との対応付けキーワード:
- 「コンセプト」「概念」「狙い」→ 構築コンセプト
- 「ダメ計 追加」「damage_calcs 追加」「計算を足す」→ ダメージ計算 追加
- 「ダメ計 削除」「ダメ計 消す」「ダメ計 整理」「重複削除」→ ダメージ計算 削除
- 「メモ」「役割」「role」→ ポケモンメモ
- 「選出」「表選出」「裏選出」「選出やりなおし」→ 選出やりなおし
発話から項目が 1 つも推定できない場合のみ AskUserQuestion を発火。
-
I-5-2 の削除対象 index 選択: 発話に index 列 (「1, 3, 5」「index 0 と 4」など) が含まれていれば skip して該当 index を採用する。発話の index が 1-indexed か 0-indexed かはユーザーに確認 (AskUserQuestion Other)。ただし発話文脈から明らかな場合 (SKILL の出力一覧
[0]..[N]を参照していると読める) は 0-indexed で確定し通常メッセージで開示。 -
I-6-2 / I-7-3 のメンバー・note 選択: 発話にメンバー名列・note 本文が含まれていれば skip。
-
I-7-2 の選出操作 (multiSelect): 発話から操作 (表書換 / 裏書換 / 表 null / 裏 null) が推定できる場合 skip。「書き換える」と「未設定にする」が同一対象で同時に解釈可能な場合は「書き換える」を優先する (未設定にしてから書き換えるのと最終状態が同じで、かつ意図推定として書き換えが自然)。
meta.json の updated_at 更新規則
Phase I の jq 更新では、該当フィールド (concept / members[i].role / damage_calcs / primary_selection / alternate_selection) を書き換えると同時に .updated_at フィールドも 必ず $(date +%Y-%m-%d) で更新する。writer 側の meta.json と挙動を揃えるため。
具体例 (I-3-3 の jq に統合):
NOW=$(date +%Y-%m-%d)
tmp=$(mktemp)
jq --arg v "$NEW_CONCEPT" --arg ts "$NOW" '.concept = $v | .updated_at = $ts' "$META" > "$tmp" && mv "$tmp" "$META"
I-5-3 / I-6-4 / I-7-4 の全 jq テンプレートにも同様に .updated_at = $ts を追加する。
I-1: 対象 team 記事を選択
前述「Phase I 共通 skip ルール」1 の条件に合致すれば skip して発話のファイルを採用する。
合致しない場合は候補を列挙:
find "$TEAMS_DIR" -maxdepth 1 -name '*.md' ! -name 'TEMPLATE*' | sort
- 1 件のみ → 自動選択 (通常メッセージで「対象を と判定して続行します」と開示)
- 複数件 → AskUserQuestion (1問) で 1 件選ばせる (Phase B-2 と同様のバッチ方式)
選択後 meta 併置を確認:
SLUG=$(basename "$FILE" .md)
META="$(dirname "$FILE")/$SLUG.meta.json"
if [ ! -f "$META" ]; then
echo "NO_META"
fi
NO_META が出力された場合、通常メッセージで以下を提示して skill 終了 (Phase G に遷移しない):
<$FILE> は team-builder 由来ではありません (meta.json なし)。
Phase I は meta.json のある構築記事専用です。frontmatter だけ編集したい場合は Phase C をご利用ください。
I-2: 編集項目を選択 (multiSelect)
AskUserQuestion (1問, multiSelect):
| # | 質問 | header | オプション | multiSelect |
|---|---|---|---|---|
| 1 | どの項目を編集しますか? (複数選択可) | 項目 | 構築コンセプト(desc: concept 本文), ダメージ計算 追加(desc: /calc / pkdx damage --attach-team への案内), ダメージ計算 削除(desc: 既存の damage_calcs を除去), ポケモンメモ(desc: members[i].role の役割・型名メモ), 選出やりなおし(desc: 表選出 / 裏選出 の再入力) | true |
選択 0 件 → Phase G へそのまま遷移 (通常メッセージで「編集項目なしで終了」)。 選択 1 件以上 → 選んだ順 (I-3 → I-4 → I-5 → I-6 → I-7) で処理。
I-3: 構築コンセプト編集
I-3-1: 現状取得・提示
CUR_CONCEPT=$(jq -r '.concept // ""' "$META")
通常メッセージで提示:
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 54
- Forks
- 113
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
blog-pkdxtools- Source
- github.com/pkdxtools/pkdx