Communityコーディング&開発github.com

oliverswitzer/lens

製品レンズ(プロダクトレンズ)をセットアップ・維持します。これは、製品の目的、コアループ、ペルソナ、インタビュー対象者、エビデンス、主張、フォーカス仮説、出荷済みサーフェス、アイデア、代替品・競合、市場インサイト、コンテンツピラー、意思決定などを1つのJSONグラフとしてGit管理し、コメント可能な自己完結型index.htmlと差分ビューに構築するスキルです。レンズの作成、インタビューやエビデンスの追加、サーフェスの更新、ブレインストーミングからのアイデア追加、競合追加、レンズの変更内容表示などを依頼されたときに使用します。

lens とは?

このスキル(lens)は、製品の構造化された知識ベースを「レンズ」として構築・管理するためのものです。レンズは1つの`model.json`(JSONグラフ)と、それをブラウザで表示・コメント可能な自己完結型`index.html`で構成されます。製品の目的、コアループ、ペルソナ、競合分析、市場インサイト、出荷済み機能、アイデア、意思決定記録などを、エビデンスに基づいた主張(観測/オーナー視点/仮説)として体系的に整理します。すべての主張にはソースとタイムスタンプが必須で、矛盾するエビデンスも積極的に記録することで、フォーカス仮説の信頼度を自動計算します。gitで追跡され、変更差分を視覚化できるため、時間経過による製品理解の変化や意思決定の根拠を明確にトレースできます。Node 20+とgitのみで動作し、依存関係は不要です。

対応✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/oliverswitzer/product-lens/tree/main/skills/lens

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Lens(レンズ)

レンズとは、1つのJSONグラフ(<lens>/model.json)を、誰でも開いてコメントできる自己完結型の<lens>/index.htmlにビルドしたものです。<lens>はスキャフォールディング先のフォルダ(デフォルトはdocs/lens)です。<lens>/README.mdには、ノードの種類、関係、主張の形状、整合性ルールが記載されています。ビルドはルールを破るモデルを拒否するため、ビルドが成功することが最初のチェックポイントとなります。

このスキル自身のフォルダには、template/(リポジトリにコピーされるもの)、scripts/(競合用ヘルパー)、competitors.mdがあります。リポジトリ内では.claude/skills/lens/、グローバルインストール時は~/.claude/skills/lens/にあります。以下、$SKILLはそのフォルダを指します。

要件: Node 20+ および git。package.jsonや依存関係は不要です。

新しいレンズをセットアップする

  1. フォルダを選ぶ。 ユーザーにレンズを置く場所を確認します。デフォルトはdocs/lensです。既存のmodel.jsonがある場所への上書きは拒否します。

  2. テンプレートをコピーする(ビルド済みのindex.htmlは除く):

    mkdir -p docs/lens && cp -R "$SKILL/template/." docs/lens/ && rm -f docs/lens/index.html
    echo '[]' > docs/lens/ids.lock.json      # スターターのIDはあなたのものではありません。ロックは空で開始します
    
  3. ユーザーにインタビューし、スターターモデルを置き換える。 model.jsonには、一般的な種類をすべて示す架空の「Acme Notes」の例が含まれています。その形状は維持し、内容を置き換えます。一度に1つのトピックずつ質問し、白紙の質問ではなく、修正可能な具体的なドラフトを提示します:

    • 製品名(title)とオーナー名(owner person node、デフォルトのIDはp-owner、ただしユーザーが別のIDを希望する場合はそれに従う。そのtitleはすべてのview claimで「’s view」と表示される);
    • 1文での目的(purpose、オーナーのソースを引用するview claim);
    • コアループ(2〜5個の順序付きloop-step);
    • 誰のためのものか(avatar、ペルソナマップ付き)、何が痛いか(problem);
    • すでに行ったインタビューやその他のエビデンス(source + personノード。引用やタイムスタンプを決して捏造しないこと。文字起こしがない場合は、オーナーの報告として記録する);
    • 出荷済みのもの(surface、shipped: true、実際のスクリーンショット付き)と、アイデアのみのもの(shipped: false);
    • 代わりに人々が使っているもの(alternative);
    • 今すぐ証明すべき最大3つの賭け(hypothesisノード、focus: 1|2|3付き)。 ユーザーがエビデンスなしで言ったことはすべて、view(彼らの見解)またはhypothesisです。ソースとatなしでは、何もobservedになりません。ユーザーが公開される可能性のあるファイルに実名を望まない限り、プレースホルダー名(「Interviewee A」)を使用します。
  4. 例をクリーンアップする。 ノードが参照しなくなったassets/example.pngを削除し、スクリーンショットはassets/surfaces/<surface-id>/<slug>.pngを指すようにします。サーフェスのcodeとadrsパスはリポジトリルートからの相対パスであり、存在しなければなりません(adrsはリポジトリが決定記録を保持している場所、例えばdocs/adr/を指します)。

  5. ビルドしてテストする:

    node docs/lens/build.mjs && node --test docs/lens/tests/*.test.mjs
    
  6. 確認する。 コメントはブラウザのオリジンごとに保存されるため、ファイルを直接開くのではなく、サーブして確認します:

    python3 -m http.server 8767 --bind 127.0.0.1    # リポジトリルートから実行
    open "http://127.0.0.1:8767/docs/lens/index.html"
    
  7. 競合の広告を追跡する場合は、docs/lens/assets/ads/**/*.mp4 の.gitignore行を提案します。model.json、ids.lock.json、index.html、assets/、およびフォルダの残りを、リポジトリ自身のコミットルールに従ってコミットします。

作業の基礎を固める

オーナーに何かを尋ねる前に、関連するノード、その主張、およびそのソースを読みます。修正できる具体的な合成結果を提示します。サーフェスの場合、その動作が重要であれば、そのコード(code、adrs)を確認します。決定記録には何が決定されたかが書かれているのであって、今日のコードが何をするかではありません。

エビデンスを追加する

すべてのステートメントは、kind、evidence、およびオプションのnextを持つ主張です:

  • observed(聞いた/見た): 誰かが言ったこと、またはしたこと。at(文字起こしのタイムスタンプ、または文書のセクション)でソースを引用します。近い言い回しで引用または言い換えます。曖昧な表現(「役に立つかもしれない」)を決して格上げしません。
  • view(オーナーの見解): オーナーの解釈。そのpeopleにモデルのownerが含まれるソースを引用する必要があります。
  • hypothesis(未テスト): 未テストのものすべて。エージェントの提案や実装アイデアを含みます。
  • 仮説はノードの種類でもあります。各エビデンスをstance(supports または contradicts)とatとともに記録します。信頼度を設定しないでください。フォーカス仮説のメーターは、次のビルド時にそのエビデンスから計算されます(それを支持する異なるインタビュー対象者の数。誰かが反証した場合は1段階ダウン。オーナーの見解がそれを上げることはありません)。反証するエビデンスも支持するエビデンスと同様に積極的に記録します。
  • 弱い主張にはすべてnextを付けます。具体的で、電話で尋ねられる質問を指定します。これはNext conversationに表示されます。
  • 誰が言ったかを正確に把握します。オーナーが提起し、誰かが同意したアイデアは、その人のニーズではありません。
  • 新しいインタビューは、date、people、locator、短いtakeawaysを持つsourceであり、それが支持する主張によって引用されます。製品に無関係な個人情報は省きます。文字起こしは、リポジトリがプライベートで、インタビュー対象者が同意した場合にのみ、<lens>/sources/の下にアーカイブします。

サーフェスを更新する(変更後)

変更によってサーフェスの外観や動作が変わる場合、同じブランチで以下の操作を行います:

  1. 実行中の製品から変更後の状態をキャプチャし、<lens>/assets/surfaces/<surface-id>/<slug>.pngに保存します(古いものは置き換え、1〜4枚を維持)。
  2. サーフェスのscreenshotsエントリを更新します: caption、capturedAt(今日の日付)、origin: "app"、version。ページは、サーフェスのcodeへの最後のコミットよりも古いスクリーンショットにフラグを立てます。
  3. 動作が変更された場合はunique / situationsを更新し、ファイルが移動した場合はcode / adrsを更新します。

まったく新しい出荷済みサーフェスは、shipped: true、少なくとも1つのスクリーンショット、対応する問題へのservesエッジ、ループステップへのimplementsエッジを持つ新しいsurfaceノードです。

アイデアを追加する(ブレインストーミングから)

議論されたがまだ構築されていないサーフェスは、shipped: falseのsurfaceです。Ideasの下に、視覚的に区別して表示されます。unique(hypothesis claimとして)、situations、heard(人々が言ったこと、エビデンス付き)、およびorigin: "design"の screenshotsとしてモックアップを設定します。出荷されたらshippedを反転し、モックアップを実際のキャプチャに置き換えます。

コンテンツピラーまたはアイデアを追加する

マーケティングテーマはpillarです。具体的な投稿、動画、イベントは、そのunderにあるcontent-ideaであり、formatを持ちます。ピラーまたはアイデアをspeaks-toでアバターにリンクし、アイデアはfeaturesで表示するサーフェスを、likeで同様の角度をすでに実行している競合をリンクします。アイデアとピラーは、コンテンツが人々に届くまではUntestedです。起こったことを、アイデアに関する仮説のエビデンスとして記録します。

市場インサイトを追加する

外部調査からのトレンドは、Market insightsタブのinsightです。記事をMarket researchソースとして記録し(オプションでsources/の下にスナップショット)、その事実をobservedなfindingsとして記述し、アバターにとって何を意味するかをビューまたは仮説として保持します。ソースの限界に関するfindingも含めます。影響を与えるすべてのものにbears-onでリンクします。

競合を追加または更新する

$SKILL/competitors.mdに従います: 調査(実際のブラウザでの広告チェック。壁を回避しない)、scripts/add-product.py、ステージングされた広告キャプチャ、scripts/apply-staged-ads.py(すべてをチェックしてから1ステップで適用)、scripts/smoke-ads.mjs。その後、.mp4を含めずにコミットします。レンズの目的とフォーカス仮説から製品と比較し、マーケティングページとは比較しません。その比較はUntestedとラベル付けします。

ユーザビリティに関する発見はレンズのコンテンツではない

インタラクションの問題(誰も見つけないボタン、フォーカス状態の欠如など)は、チームの課題トラッカーに属します。そこに報告し、製品としての意味がある場合にのみ、その意味をレンズに記録します。

名前の変更または削除

ids.lock.jsonは、最後のビルドからのコメント可能なすべてのIDを記録します。消えたIDは、model.jsonのrenamedにマッピングする必要があります("old-id": "new-id"、または意図的に削除した場合はnull)。そうしないとビルドは失敗します。これにより、既存のコメントが保持されます。

レンズ自体の変更

components/(ビューが表示するもの、ルーティング、検証、差分)の下での変更はすべて、<lens>/tests/での失敗するテストから始まります(node:test、tests/fixture.mjsの共有フィクスチャ、架空の名前のみ)。ロジックは純粋なモジュールに保ち、DOMに触れるのはcomponents/app.jsのみとします。次にテストを実行し、同じシナリオをブラウザで確認します。

差分を可視化する

オーナーが変更内容の確認、レビュー、または可視化を求めた場合:

node docs/lens/diff.mjs [rev]          # デフォルトはHEAD。変更リストを表示
python3 -m http.server 8767 --bind 127.0.0.1
open "http://127.0.0.1:8767/docs/lens/index.html"

緑の輪郭は追加されたアイテム、黄色は変更されたアイテムです。削除されたアイテムはバナーに赤でリストされます。追加または変更された各行は、そのアイテムにリンクしています。変更はIDで照合されるため、1つの主張だけがそのカード全体を照らすことなくハイライトされます。コミットする前に、必ずクリアして確認します:

node docs/lens/build.mjs
grep -c LENS-V2-DIFF docs/lens/index.html   # 0を出力する必要があります

リビルドして確認

node docs/lens/build.mjs && node --test docs/lens/tests/*.test.mjs

次に、影響を受けるビューをブラウザで確認します。コメントはブラウザのオリジンにローカルです。切り替える前にエクスポートします。最後に、レンズへのリンク、変更内容、エビデンス、および制限事項を示します。

関連スキル