🎖️

Claude Code / Cursor向けにAIセッションを構造化して投稿するスキルの設計と実装判断

に公開

本文

/lovai は、Claude CodeやCursorなどのAIセッションを5ブロックに構造化し、Lovaiに投稿するコマンドです。Codex、Gemini CLIにも対応しています。

毎日のAIセッションで蓄積される判断の過程やハマりポイントは、セッションを閉じた瞬間に消えていきます。この問題を解決するために設計・実装しました。

この記事では、このスキルの設計判断と実装の詳細を書きます。なぜこの構造にしたのか、どこでハマったのか、複数ツール対応でどんな判断をしたのか。Note版の記事(血と涙のAIセッションをいい感じにポストできるskillを公開しました)が「なぜ作ったか」の思想記事なので、こちらは「どう設計・実装したか」の技術記事として読んでいただけたらと思います。


全体アーキテクチャ

まず、スキルの全体像を整理します。

ユーザーが /lovai を実行

Step 1: セッション解析(会話コンテキストから5ブロックを抽出)

Step 2: セキュリティフィルタリング(シークレット検出・除去)

Step 3: ブロック構成(公開レベルの自動判定)

Step 4: メタデータ付与(ツール名・モデル名・カテゴリ)

Step 5: プレビュー表示 → ユーザー確認

Step 6: API経由でLovaiに投稿(ドラフト or 公開)

Claude Codeのスキルは、~/.claude/skills/ 配下にマークダウンファイルとして定義する仕組みです。コードではなく「振る舞いの指示書」に近いのですが、だからこそ設計判断が実装品質に直結します。

正直なところ、最初は「スキルで投稿まで完結させる」のは無理かなと思っていました。でもBashツールでcurlが使えるので、API連携までスキル内で完結できることに気づいて、一気に設計が固まりました。


セッション解析のプロンプト設計——5ブロック構造

スキルの中核は、セッションの会話コンテキストから何を抽出するか、という設計です。ここが一番時間をかけた部分かもしれません。

最終的に、以下の5ブロック構造に落ち着きました。

ブロック 名称 役割 公開レベル
1 Core Insight セッションで最も重要な成果・決定(1-2文) public
2 Why なぜこの方法を選んだか、他の選択肢との比較 public
3 Gotchas 詰まった箇所、予想外の問題、解決方法 public
4 Details 重要なコード変更、設定、コマンド premium
5 Learnings 次回に活かせる知見、TIPS public

スキル定義の中で、AIにどう命令しているか

実際のスキル定義(マークダウン)では、こう指示しています。

### Step 1: Analyze Session

Extract from the current conversation context:

1. **Core Insight**: Most important outcome/decision (1-2 sentences)
2. **Why**: Why this approach was chosen
3. **Gotchas**: Unexpected problems and how they were solved
4. **Code Details**: Key code changes, configs, commands
5. **Learnings**: Tips for next time

これが5ブロックのブロック定義に対応します。

Block 1: Core Insight  -> section: "insight"
Block 2: Approach      -> section: "why"
Block 3: Gotchas       -> section: "how"
Block 4: Code Details  -> section: "detail"
Block 5: Learnings     -> section: "tips"

なぜこの5つなのか

最初は3ブロック(概要・詳細・学び)で試しました。でもこれだと、「なぜその方法を選んだのか」と「どこでハマったのか」が「詳細」に混ざってしまって、読みづらくなりました。

一方で、7ブロックや8ブロックに増やすと、出力のばらつきが大きくなります。AIは指示が多すぎると、重要でないブロックにも無理やり内容を生成しようとする傾向がある気がします。

5ブロックにしたのは、「判断の過程を再現するのに必要十分な粒度」を探った結果です。特にWhy(なぜ)とGotchas(ハマりポイント)を独立させたのは、この2つが一次情報としての価値が最も高いと感じたからです。完成したコードは誰でも再現できるけれど、「なぜ他の方法を捨てたのか」と「どこで予想外に詰まったのか」は、その場にいた人にしか書けません。

ブロックごとのセクション属性

Lovaiの投稿は section 属性でブロックの意味を指定できます。

{
  "blockType": "text",
  "privacy": "public",
  "section": "why",
  "content": "..."
}

section に使える値は insight(核心)、why(なぜ)、how(どうやって)、tips(ハマりポイント・学び)、detail(コード詳細)の5つです。Lovaiの投稿画面で「きっかけ」「やったこと」「ハマりポイント」のブロック構造に対応する属性です。

ここで少し反省があるのですが、最初は section をスキル側で自由に決められる文字列だと思い込んでいて、"section": "core-insight" みたいに指定していました。当然Lovai側で認識されず、投稿画面でブロックのラベルが表示されなくなりました。API仕様をちゃんと読めば避けられたミスです。


セキュリティフィルタリング——シークレット検出と除去

セッションログには、APIキーやトークンが含まれている可能性がほぼ確実にあります。これを投稿してしまったら事故になるので、セキュリティフィルタリングは最優先で設計しました。

実際のシークレット検出パターン

シークレット検出は二段構えです。まずスキル側(マークダウン指示)で「.envの内容は絶対に含めない」「APIキーやトークンは除去する」とAIに指示しています。その上で、サーバー側(API受信時)でさらに以下の正規表現で二重チェックしています。

const SECRET_PATTERNS: RegExp[] = [
  /sk-lovai-[\w-]+/g,
  /sk-proj-[\w-]+/g,     // OpenAI
  /sk-ant-[\w-]+/g,      // Anthropic
  /sk_live_[\w]+/g,       // Stripe
  /sk-[\w]{20,}/g,        // 汎用キー
  /ghp_[\w]+/g,           // GitHub PAT
  /AKIA[\w]+/g,           // AWS
  /eyJ[\w-]+\.eyJ[\w-]+\.[\w-]+/g,  // JWT
  /[\w_]*(SECRET|KEY|TOKEN|PASSWORD)\s*=\s*\S+/gi,  // .env形式
];

検出されたパターンは [REDACTED] に置換されます。

// 除去前
const apiKey = "sk-proj-abc123def456ghi789";
const dbUrl = "postgres://user:password@localhost:5432/mydb";

// 除去後
const apiKey = "[REDACTED]";
const dbUrl = "[REDACTED]";

さらに、ファイルパスは絶対パスから相対パス(src/lib/...)に変換します。絶対パスはユーザー名やディレクトリ構造を漏洩させるので、地味だけれど重要な対策です。

ここは反省ですが

正直に書くと、最初のバージョンではパターンマッチングだけに頼っていました。でもそれだと、たとえば NEXT_PUBLIC_ で始まる環境変数(本来は公開してよいもの)まで除去してしまったり、逆にカスタムプレフィックスのシークレット(MYAPP_SECRET_KEY のような)を見逃したりする。

現在のアプローチは「パターンマッチで候補を検出 + AIのコンテキスト理解で最終判断」の二段構えにしています。スキルの指示として「.env ファイルの内容は絶対に含めない」と明記しつつ、AIが会話コンテキストからシークレットかどうかを判断する。パターンマッチだけでは精度に限界があるので、AIの理解力に頼る部分も残しています。

完璧ではないですが、少なくとも「うっかり本番のAPIキーを投稿してしまった」という最悪のケースは防げていると思います。


複数ツール対応の設計判断

Claude Code、Cursor、Codex、Gemini CLI。これらのAIコーディングツールはそれぞれセッションの形式が異なります。

ツールごとの違い

ツール セッション形式 スキルの対応方法
Claude Code 会話コンテキストがそのままスキルに渡る 直接解析
Cursor エディタ内の会話ログ コンテキストとして読み取り
Codex CLIベースのセッション 会話コンテキストから解析
Gemini CLI 独自の対話形式 会話コンテキストから解析

ここで設計判断として迷ったのは、「各ツール専用のパーサーを書くか、汎用的な解析に統一するか」です。

結論としては、汎用解析に統一しました。理由は2つあります。

1つ目は、スキルがマークダウンベースの指示書である以上、ツールごとに分岐ロジックを精密に書くのは現実的ではないこと。コードではなく自然言語の指示なので、条件分岐が複雑になるほどAIの解釈がブレます。

2つ目は、セッションの本質的な構造はツールが違っても同じだということ。「何を達成しようとしたか」「何を試したか」「何がうまくいかなかったか」「何を学んだか」。この4要素はClaude CodeでもCursorでも変わりません。

ただし、config.jsonclient フィールドで使用ツールを指定する仕組みは入れています。これはメタデータとして投稿に付与するためのもので、解析ロジックを分岐させるためではありません。

{
  "apiKey": "sk-lovai-xxxxx",
  "endpoint": "https://lovai.app/api/posts/external-create",
  "defaultLanguage": "ja",
  "client": "claude-code"
}

Lovai API連携——なぜcurlで、なぜSDKを作らなかったのか

スキルからLovaiへの投稿は、curlコマンドで実行しています。SDK(npm パッケージなど)を作ることも考えましたが、やめました。理由は、スキルの実行環境がBashだからです。Claude Codeのスキルはマークダウンベースの指示書なので、Node.jsのインポートはできません。curlならBashツールから直接叩ける。結果的に依存ゼロで動くので、メンテナンスも楽になりました。

セットアップ

Lovaiでアカウントを作る。設定画面からAPIキーを発行する。表示されるセットアップコマンドをコピペしてClaude CodeやCursorに貼る。以上です。

APIキーは ~/.claude/skills/lovai/config.json に保存されます。~/.claude/ 配下はユーザーローカルなので、リポジトリにコミットされることはありません。

ちなみに、構造化された出力はLovaiへの投稿だけが使い道ではありません。ZennやQiitaの記事の下書きとして転用してもいいし、自分用の作業ログとして残しておくだけでも十分価値があります。セッションの中身を構造化して手元に残すこと自体に意味があって、そこから先の使い方は自由です。

投稿リクエスト

curl -s -X POST "$ENDPOINT" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "マーケ戦略の壁打ちで見えた導線設計の方針転換",
    "primaryLanguage": "ja",
    "blocks": [
      {
        "blockType": "text",
        "privacy": "public",
        "section": "insight",
        "content": "LPの導線を3パターン試した結果、..."
      },
      ...
    ],
    "tools": ["claude-code"],
    "models": ["claude-sonnet-4"],
    "category": "マーケティング",
    "purpose": "session-capture",
    "publish": false
  }'

publish: false がデフォルトです。投稿は必ずドラフトとして作成され、Lovai上で確認・編集してから公開する設計にしています。

ここは「デフォルトを公開にするか、ドラフトにするか」で迷いました。CLIから1コマンドで公開まで完了するのは便利ですが、セッションログは生データに近いので、そのまま公開するのはリスクがある。セキュリティフィルタリングを通してはいますが、100%の精度は保証できません。ドラフトをデフォルトにしたのは、安全側に倒す判断です。

ドラフトと公開の選択

ユーザー確認のステップで、(1) ドラフト保存(2) 公開 を選べるようにしています。

━━━━━━━━━━━━━━━━━━━━━━━━━━
📝 Lovai 投稿プレビュー
━━━━━━━━━━━━━━━━━━━━━━━━━━
タイトル: マーケ戦略の壁打ちで見えた導線設計の方針転換
ブロック数: 5

🟢 [public] 核心
LPの導線を3パターン試した結果、「機能一覧→料金」より
「課題提示→解決例→料金」の方がCVRが高いと判断した

🟢 [public] アプローチ
最初は競合LPの構成を参考にしたが、自社の強みと合わなかった...

🟢 [public] はまりポイント
「機能が多い=強い」と思い込んでいたが、AIに壁打ちして
ユーザー視点で見直したら情報過多だった...

🔒 [premium] 具体データ
(冒頭のみ表示)

🟢 [public] 学び
LP設計はAIとの壁打ちで「自分が売りたい順」ではなく
「ユーザーが知りたい順」に組み替えるのが効く...

━━━━━━━━━━━━━━━━━━━━━━━━━━
この内容でLovaiに投稿しますか? (1) ドラフト保存 (2) 公開

個人開発だとマーケも自分でやることが多いので、こういう壁打ちログが残るのは地味に助かります。


公開レベルの自動判定——ブロック単位課金との連携

Lovaiの投稿はブロックごとに public(誰でも閲覧可能)、premium(購入者のみ)、private(自分だけ)を設定できます。

スキルでは、以下の基準で自動判定しています。

  • public にするもの: 概念的な説明、考え方、ハイレベルな方針、ハマりポイントの概要
  • premium にするもの: そのまま再利用できるコード片、設定ファイル、具体データやテンプレート
  • private にするもの: スキル側では設定しない(ユーザーがLovai上で変更可能)

この判定基準は、自分がコンテンツの買い手だった経験から来ています。「なぜその方法を選んだのか」は無料で知りたい。でも「具体的にどう実装したのか」のコードは、対価を払う価値があると思っているからです。

premiumブロックを含める場合は priceYen: 500 以上を設定する必要があります。また、投稿者がStripe Connectを未設定の場合、APIが STRIPE_REQUIRED エラーを返すので、その場合は設定ページへの案内を表示します。


構造化結果の実例(開発セッション)

参考として、Remotionで動画生成を実装していた3時間のセッションを /lovai で構造化した結果を載せておきます。

Core Insight — SpringアニメーションよりCSSトランジションの方が軽量で制御しやすかった
Why — framer-motionがRemotionのレンダリングパイプラインと競合したため
Gotchasfps: 30durationInFrames の関係を誤解して2秒が4秒に
Details — [premium]
Learnings — Remotionでは外部アニメーションライブラリを混ぜない方がいい

5ブロック、各1〜2行。3時間の試行錯誤がこの粒度に収まります。


振り返り——設計で大事だったこと

このスキルを設計してみて、いくつか気づいたことがあります。

スキルは「コード」ではなく「振る舞いの指示書」。 だからこそ、曖昧な指示はAIの解釈次第でブレる。5ブロック構造を明確に定義したのは、出力の再現性を担保するためです。

セキュリティは「デフォルトで安全」にしないと機能しない。 ドラフトデフォルト、シークレット自動除去、相対パス変換。ユーザーの善意に依存する設計は、いつか事故を起こします。

複数ツール対応は「違い」ではなく「共通点」に着目する。 ツールごとにパーサーを書く誘惑はありましたが、セッションの本質構造が同じであることに気づいてからは、汎用アプローチの方がメンテナンスも楽になりました。

まだ改善したい部分は多くて、特にセキュリティフィルタリングの精度と、ブロック構造のカスタマイズ性は今後の課題だと思っています。

/lovai のスキルはLovaiのアカウントがあれば誰でも使えます。AIセッションの知見を一次情報として残す仕組みとして、試してみていただけたら嬉しいです。


関連記事

テーマ関連

技術的に深掘りしたい方へ

Discussion