mcpField
J
joelclaw-web

joelclaw-web

github.com/joelhooks開発6535 回インストール1 日前に更新
リポジトリ

joelclaw-web について

joelclaw-web は、joelclaw.com の Next.js アプリを保守するためのガイドで、コンテンツ管理、公開、デザイン、インフラページの安全対策を扱います。Convex を実行時のコンテンツソースとし、公開ページでのインフラ情報の扱いに注意を促します。

主な機能

  1. 01Convex を優先するコンテンツ管理
  2. 02記事の下書きと公開手順
  3. 03コンテンツの再検証と確認
  4. 04サイトデザインとナビゲーション指針
  5. 05インフラの機密情報の非表示

活用シーン

  1. 01記事の下書きや公開
  2. 02ADR と発見コンテンツの更新
  3. 03非公開情報を出さずにネットワーク状況ページを保守

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/joelhooks/joelclaw --skill joelclaw-web

インストール後、Claude Code で/joelclaw-webを入力して読み込まれたか確認します。

同梱ファイルagents/openai.yamlassets/small-logo.svg

詳細

joelclaw.com Web アプリ

joelclaw モノレポ(~/Code/joelhooks/joelclaw/)内の apps/web/ にある Next.js アプリです。 main へのプッシュ時に Vercel にデプロイされます。ダークテーマで、ミニマルかつシステム風の美観です。

OPSEC ルール

公開ページで実際のインフラの詳細を決して公開しないでください。

  • ノード名/ホスト名:実際の tailnet ホスト名は使わず、Stephen King の作品世界に登場する別名を使ってください
  • ポート:サービスのポート番号を公開しないでください
  • ユーザー名:サービス識別子から com.joel. などのプレフィックスを取り除いてください
  • IP/サブネット:実際の IP アドレスや CIDR 範囲を決して表示しないでください
  • ネットワーク機器のブランド/モデル:一般化してください(例:「Synology DS1821+」ではなく「NAS」)
  • Tailscale:メッシュ VPN 製品として言及するのは問題ありませんが、tailnet 名やノードの IP は公開しないでください

公開する図では、コントロールプレーン、アーカイブ、開発クライアント、リレー、ルーターなど、汎用的な役割名を使ってください。実際のホスト名を架空の別名に見せかけて公開しないでください。プライベートなトポロジーの境界は引き続き守ってください。

コンテンツモデル(Convex 優先)

実行時の正規コンテンツは Convex の contentResources に保存されます。

  • article:<slug>(type = article)
  • adr:<slug>(type = adr)
  • discovery:<slug>(type = discovery)

apps/web/content/ 以下のファイルシステム上のコンテンツは、シード/バックフィル用の素材であり、実行時のソースではありません。

実行時の読み取りポリシー:

  • apps/web/lib/posts.ts → Convex 優先の記事(article:*)
  • apps/web/lib/adrs.ts → Convex 優先の ADR(adr:*)
  • apps/web/lib/discoveries.ts → Convex 優先のディスカバリー(discovery:*)
  • apps/web/lib/convex-api.ts → チェック済みの Web 向け Convex 関数コントラクトと、実行時の anyApi プロキシ
  • 兄弟ディレクトリにある非公開の joelclaw-api チェックアウトへの link: 依存関係は追加しないでください。Vercel がクローンするのはこのリポジトリのみです。
  • 任意のローカル用フォールバック(本番環境以外のみ):
    • JOELCLAW_ALLOW_FILESYSTEM_POSTS_FALLBACK=1(記事)
    • JOELCLAW_ALLOW_FILESYSTEM_CONTENT_FALLBACK=1(ADR/ディスカバリー)

記事のフィールドは引き続き MDX フロントマターの形式に対応します:

---
title: "Post Title"
type: "article" | "essay" | "note" | "tutorial"
date: "2026-02-19T11:00:00"        # ISO datetime, NOT just date
updated: "2026-02-19T14:30:00"     # optional, bumps sort position
description: "One-liner for cards and meta"
tags: ["tag1", "tag2"]
draft: true                         # optional, hides from prod
source: "https://..."               # optional, for video-notes
channel: "Channel Name"             # optional, for video-notes
duration: "00:42:02"                # optional, for video-notes
---

並べ替え:投稿は updated ?? date の降順で並びます。順序を決定的にするため、日付のみではなく完全な ISO 日時を使用してください。updated を設定すると、元の公開日を変えずに投稿を先頭へ移動できます。

スラッグ:Convex の fields.slug から導出されます(resourceId = article:<slug>)。

ハードトリガーワークフロー

write article about X
  1. タイトル/トピックからスラッグを生成します。
  2. resourceId = article:<slug>、type = "article"、完全な MDX 本文を fields.content に、fields.draft = true として contentResources を upsert します。
  3. fields.date に現在の ISO タイムスタンプを設定します。
  4. スラッグと下書きプレビューリンクを返します(開発環境で下書きを表示できる場合は /<slug>)。
publish article <slug>
  1. Convex から article:<slug> を読み込みます。
  2. fields.draft = false および fields.updated = now でパッチ/upsert します。
  3. POST /api/revalidate を使い、影響を受けるすべてのサーフェスを再検証します。以下を指定します:
    • タグ:post:<slug>、article:<slug>、articles
    • パス:/、/<slug>、/<slug>.md、/<slug>/md、/feed.xml、/sitemap.md
  4. /、/<slug>、/<slug>.md、/feed.xml に公開済みの投稿が含まれていることを確認します。

メディアの埋め込み

コンテキストを補足する場合は、/cool のディスカバリーと記事に YouTube 動画を必ず埋め込んでください。<YouTube id="VIDEO_ID" /> MDX コンポーネントを使います(.mdx の記事と .md のディスカバリーの両方で利用できます)。URL(youtube.com/watch?v=VIDEO_ID)から動画 ID を抽出してください。関連するセクションの上部、本文の説明より前に埋め込みを配置します。

執筆スタイル

本文の執筆には、正規の joel-writing-style スキルを使用してください。主な特徴:率直な表現、主張が実際に Joel のものである場合は一人称、効果的な悪態、短い段落、太字による強調、会話調でありながら技術的な文章です。企業的な言い回しは避け、Joel の信念や哲学を捏造しないでください。

デザインシステム

  • テーマ:ダーク(bg-[#0a0a0a])、ニュートラルグレー、--color-claw: #ff1493(ホットピンクのアクセント)
  • フォント:Geist Sans(本文)、Geist Mono(コード/データ)、Dank Mono(合字対応のコードブロック)
  • コンテンツ幅:max-w-2xl(672px)— 読みやすさを重視して意図的に狭くしています
  • ヘッダー:1行構成 — 左側にかぎ爪アイコンと「JoelClaw」、右側にナビゲーションリンクと検索。ヘッダーにタグラインはありません。
  • ナビゲーション項目:Writing(/)、Cool(/cool)、ADRs(/adrs)、Network(/network)
  • アクティブなナビゲーション:アクティブな項目は白、非アクティブな項目は neutral-500 で表示し、usePathname() で判定します
  • 検索:pagefind を使用した ⌘K ダイアログ。タイプ別のアイコン/バッジを表示します
  • モバイル:MobileNav コンポーネントを使った全画面オーバーレイナビゲーション
  • コードブロック:Catppuccin Macchiato テーマ、rehype-pretty-code
  • サイドノート:Tufte スタイルの CSS サイドノート(純粋な CSS で、JavaScript は使用しません)

主要ファイル

ファイル用途
app/layout.tsxルートレイアウト、フォント、メタデータ、フッター
app/page.tsxホームページ(投稿一覧)
app/[slug]/page.tsx投稿詳細ページ
app/adrs/page.tsxADR 一覧
app/adrs/[slug]/page.tsxADR 詳細(タイトルの重複を避けるため H1 を除去)
app/cool/page.tsxCool/発見事項の一覧
app/network/page.tsxインフラストラクチャのステータスページ
components/site-header.tsxアクティブなナビゲーションを含むヘッダー(クライアントコンポーネント)
components/mobile-nav.tsxモバイル用オーバーレイナビゲーション
components/search-dialog.tsx⌘K 検索
lib/posts.tsConvex から記事を読み込みます(article:*)
lib/adrs.tsConvex から ADR を読み込みます(adr:*)
lib/discoveries.tsConvex から発見事項を読み込みます(discovery:*)
lib/convex-api.tsデプロイ可能なランタイムプロキシと、型付きの Convex 公開コントラクト
lib/constants.tsサイト名、URL、タグライン
lib/claw.tsかぎ爪アイコンの SVG パス

ADR の表示ルール

  • ADR のランタイムソースは Convex です(resourceId = adr:<slug> の contentResources)
  • Vault 同期では、引き続きリポジトリのスナップショットを apps/web/content/adrs/ に更新します
  • プロジェクトのスナップショットを Convex に登録します:bun scripts/seed-adrs-discoveries.ts
  • 詳細ページ(app/adrs/[slug]/page.tsx)では、ページ側ですでに ADR 番号の接頭辞付きタイトルを表示するため、Markdown コンテンツから H1 を除去します
  • 正規表現:content.replace(/^#\s+(?:ADR-\d+:\s*)?.*$/m, "").trim()

新しい投稿の追加

  1. Convex で下書きを作成します(contentResources.upsert、resourceId = article:<slug>、draft: true)。
  2. date フィールドには ISO 形式の日時を使用します。
  3. 必要に応じて画像を apps/web/public/images/<slug>/ に追加し、MDX で /images/<slug>/... を参照します。
  4. draft: false に設定して公開し、タグとパスを再検証します(post:<slug>、article:<slug>、articles、/、/<slug>、/<slug>.md、/<slug>/md、/feed.xml、/sitemap.md)。
  5. ルート、Markdown 版、ホームページ、フィードの整合性を確認します。

バックフィルスクリプト:

  • 記事リソースには scripts/seed-articles.ts
  • ADR および発見事項のリソースには scripts/seed-adrs-discoveries.ts

ネットワークページ

ネットワークページ(app/network/page.tsx)には、エイリアス名を使って実際のインフラストラクチャを表示します。更新時は次の手順に従います。

  1. 実際のシステム状態を確認します(kubectl get pods、tailscale status、launchctl print など)。
  2. OPSEC ルールを適用し、すべてのホスト名をエイリアスに置き換え、ポート/IP アドレス/ユーザー名を削除します。
  3. 更新しやすいよう、データ配列をファイルの先頭に置きます。
  4. ステータスドット:緑(オンライン、ピングアニメーション付き)、黄(アイドル)、灰色(オフライン)

コメント