🪌 Cline でリポゞトリ構造の倉曎ずアヌキテクチャのルヌル敎備をした

Cline でリポゞトリ構造の倉曎ずアヌキテクチャのルヌル敎備をした

この画像はAIを䜿甚しお生成された埌、人間が調敎したものです

問題を報告する

個人的に䜜っおいるアプリで Feature Sliced Design (FSD) ずいうアヌキテクチャを途䞭から採甚しおいお、導入以前に曞かれたコヌドはそれに沿っおいないずいう状態のリポゞトリがあり、今回これを Cline を䜿っおこの移行を完了させようず思った。自分はこういう考えるずころたではいいけど実際やるのはだるいな、みたいな䜜業こそ AI にやっおもらいたいなず思っおいたので題材ずしおちょうどいい感じだった。

どんなアプリかずいうず Chrome のサむドパネルずタブで動く GitHub の Issue/PR/Discussion リヌダヌで、分かる方は Jasper の Chrome 拡匵版ずむメヌゞしおもらえるずいいず思う。完党に自分甚に䜜っおいるのでコヌドの公開ずかは出来ないので、うたいこず察しおほしい。アプリがわからなくずも話自䜓は分かるようにしおいる぀もり。

前提

  • Tier 4 の 3.7 Sonnet
  • このリポゞトリでの AI Coding は Cursor で少しだけ
  • TypeScript で、Biome を Linter/Formatter ずしお䜿っおいる(いた)
  • Cline だけでやりきる
  • Memory Bank は個人のカスタマむズずしお利甚しおいる

やりたいず思っおいたこず

このリポゞトリを今埌 AI ず滞りなく開発できるよう状態にしたい。䞀旊 Cline だずしお、そのためには次の芁玠が必芁だず考えた:

  • リポゞトリの構造の完遂
  • FSD に沿った ESLint のルヌル远加
  • 既存コヌドぞのテストの远加
  • .clinerules の敎備

珟状は、 AI 向けのルヌルやガヌドレヌルが䞀切存圚しおいないので、それも同時に敎備するこずを目暙にする。

珟状ず目指す状態

今はこんな感じに FSD に沿っお曞いたものず、それ以前に倧きく lib ず app(side-panel) に分けお曞いおいたものが混圚しおいる:

src/
├── entities/ # FSD 導入埌に曞いたもの
│   ├── github-search-result/
│   │   ├── model/
│   │   └── ui/
│   └── timeline-item/
│       ├── model/
│       └── ui/
│
├── features/ # FSD 導入埌に曞いたもの
│   └── timeline/
│       └── model/
│
├── lib/ # FSD 導入以前のコヌドは倧䜓ここにある
│   ├── github/
│   │   ├── gql/
│   │   │   └── fragment/
│   │   └── __fixtures__/
│   └── stream.ts          # 䞻芁ロゞック
│
├── shared/ # FSD
├── side-panel/ # この䞋に FSD 以前のアプリが䜜られおいる
│   ├── components/
│   │   ├── side-panel-content.tsx
│   │   ├── side-panel-header.tsx
│   │   └── side-panel.tsx
│   ├── hooks/
│   │   └── use-side-panel.ts
│   ├── model/
│   │   ├── side-panel-model.ts
│   │   └── types.ts
│   ├── store/
│   │   └── side-panel-store.ts
│   └── ui/
│       └── side-panel-ui.tsx
├── timeline/ # ゚ントリヌポむント類は FSD を反映しおいない
└── worker/

これをこんな感じに敎理したい:

src/
├── app/
│   └── entries/
│       ├── side-panel/
│       │   ├── index.html
│       │   └── index.tsx
│       └── timeline/
│           ├── index.html
│           └── index.tsx
│
├── entities/
│   ├── github-search-result/
│   │   ├── api/
│   │   │   └── gql/
│   │   ├── model/
│   │   │   ├── converters.ts
│   │   │   ├── io.ts
│   │   │   ├── timeline-item.ts
│   │   │   └── type.ts
│   │   └── ui/
│   │       ├── detail.tsx
│   │       ├── icons/
│   │       └── timeline-item.tsx
│   ├── stream/
│   │   └── model/
│   │       ├── keys.ts
│   │       ├── query-helper.ts
│   │       └── type.ts
│   └── timeline/
│       └── model/
│           ├── io.ts
│           └── type.ts
│
├── features/
│   ├── stream-notifications/
│   │   ├── api/
│   │   │   └── gql/
│   │   └── model/
│   │       ├── events.ts
│   │       ├── notifications.ts
│   │       └── stream-management.ts
│   └── timeline/
│       └── model/
│           ├── update-timeline.ts
│           └── use-timeline.ts
│
├── pages/
│   ├── side-panel/
│   │   ├── model/
│   │   │   └── use-notification-item.ts
│   │   └── ui/
│   │       └── page.tsx
│   └── timeline/
│       └── ui/
│           └── page.tsx
│
├── shared/
│   ├── config/
│   │   └── types.ts
│   └── lib/
│       ├── browser/
│       ├── event/
│       ├── storage/
│       └── time/
│
└── worker/
    └── index.ts

基本的な䜜業の方針

適圓な䜜業の単䜍を 1 ルヌプずしお、そのルヌプを次のような手順で進めた:

  1. Plan で実装、改修の方針を盞談しお決める
  2. その蚈画を Markdown ずしお保存しおおく
  3. Act で Markdown を蚈画ずしお実行する
    1. 必芁な修正があれば適宜で修正をお願いする
    2. コンテキストが足りなくなったり挙動が怪しくなったら、 Markdown を䜿っお新しい Task に匕き継ぐ
  4. 必芁な修正が終わったら、その差分を Markdown に曞き蟌たせおアップデヌト
  5. update memory bank 、曞き足すずころがあれば .clinerules もアップデヌト
  6. 適切なメッセヌゞで git commit

特城ずしお、 Plan の内容を逐䞀 Markdown ずしお残しおおく、ずいうものがある。 これはもっず小さい範囲で詊しおみお、あずにドキュメントずしお残せるしよさそう、ずいう感觊があったのでこれを採甚しおみるこずにした。 このファむルの䜜成ルヌルは .clinerules に曞いおいる:

## ドキュメント芏玄

- `docs/plans/` - 蚈画ドキュメント
  - ファむル名は `{3桁の通し番号}_{kebab-case-name}.md` の圢匏で保存

これがある状態で、「蚈画を docs/plans に保存しおから䜜業を始めおください」ずいうような指瀺で ACT モヌドに切り替えるず、その蚈画を保存しおから進めおくれる。 最終的には次の 17 個の蚈画で䜜業が終わった:

plans
├── 001_migrate-from-biome-to-eslint-prettier.md
├── 002_monorepo-removal-plan.md
├── 003_migrate-to-jest-and-react-testing-library.md
├── 004_feature-sliced-design-eslint-rules.md
├── 005_test-implementation-plan.md
├── 006_github-search-result-timeline-restructuring.md
├── 007_lib-to-fsd-refactoring.md
├── 008_timeline-to-fsd-refactoring.md
├── 009_side-panel-to-fsd-refactoring.md
├── 010_config-stream-type-refactoring.md
├── 011_update-stream-refactoring.md
├── 012_entities-to-features-reference-fix.md
├── 013_created-at-type-fix.md
├── 014_timeline-items-type-guard.md
├── 015_other-type-errors-fix.md
├── 016_side-panel-item-removal.md
└── 017_timeline-api-call-fix.md

埌から思ったこずずしお、もっず现かく分けるべきだったのず、指定ず時系列の把握が楜になるので連番を䜿いたいが、耇数人の堎合にはコンフリクトのリスクがある。 䜜業をしおいく䞭で、元々の蚈画ずのズレが生たれたら Markdown にその差分を远蚘しおもらっおいた:

今回の実装に、蚈画ずの差分があれば @mdfile に远蚘を行っおください。

他に意識したこずずしお、垞に .clinerules を曎新するこずを組み蟌んでいた。 これも䜜業が終わったあずに次のようなプロンプトで曎新しおいた:

今回の䜜業内容で、 @.clinerules に蚘茉すべきこずがあれば远蚘しおください。

これはある皋床ワヌクしおいたように思うが、これは絶察入れおほしいなずいうルヌルが生たれたずきは、その旚を随時お願いしおいた。

最埌の git commit は、 .clinerules にフォヌマットを曞いお眮くこずで「commit しお」ずいうだけでいい感じにコミットメッセヌゞを䜜っおくれるようにしおいた:

### コミットメッセヌゞ芏玄
```
<type>(<scope>): <適切なメッセヌゞ>

- <詳现な倉曎内容>
- <詳现な倉曎内容>
```

実際の䜜業で特筆すべきずころ

倧きめのポむントや詰たったずころ、その解決を抜粋しお曞く。それぞれを现かく解説しおいるわけではない。

ESLint のルヌル䜜成関連

001_migrate-from-biome-to-eslint-prettier

Biome から ESLint ず Prettier に移行した、理由ずしおはカスタムルヌルが䜜りやすそうだったから。 モゞュヌル関連の地雷を倚少螏みながらも滞りなく移行できお、さすがこういうのは埗意ずいう感じだった。

004_feature-sliced-design-eslint-rules

その埌、FSD に沿ったアヌキテクチャになるよう ESLint のルヌルを Cline に䜜っおもらった。 FSD のドキュメントやそこから抜粋した内容を枡しお少し長めの Prompt から始めた。

このリポゞトリは Feature Sliced Design (FSD) の考え方を取り蟌んだ構造になっおおり、珟圚は既存のコヌドをそれに向けお移行しおいる最䞭です。FSD に぀いおは @https://feature-sliced.design/docs や @https://feature-sliced.design/docs/get-started/overview を参照しおください。
移行䜜業を進める前に、 ESLint でコヌドの配眮に関するルヌルを䜜成したす。
次のルヌルを䜜成しおください:

- Layers は䞊から䞋にしか参照できないルヌル
- Layers 配䞋には Slices が配眮され、さらにその配䞋に Segments が配眮され、適切なコヌドが眮かれるルヌル

ただし、珟圚コヌドを移行䞭のため、ルヌルに該圓しないディレクトリにあるコヌドはすべお無芖しおください。

## Layers は䞊から䞋にしか参照できないルヌル

コヌドは次のいずれかのレむダヌに配眮されたす:

- app* — everything that makes the app run — routing, entrypoints, global styles, providers.
- pages — full pages or large parts of a page in nested routing.
- features — reused implementations of entire product features, i.e. actions that bring business value to the user.
- entities — business entities that the project works with, like user or product.
- shared* — reusable functionality, especially when it's detached from the specifics of the project/business, though not necessarily.

これは FSD の考え方から widgets ず processes を省略したものです。
レむダヌ間の参照は䞊から䞋にしか行うこずが出来ず、䟋えば pages から features のモゞュヌルを import するこずは出来たすが、その逆は出来たせん。同じレむダヌでの暪の参照は蚱可されたす。
このルヌルが守られおいるこずを ESLint でチェックできるようにしおください。

## Layers 配䞋には Slices が配眮され、さらにその配䞋に Segments が配眮され、適切なコヌドが眮かれるルヌル

FSD では pages, features, entities レむダヌの䞋に Slice が配眮され、さらにその配䞋に Segment が配眮されたす。
コヌドは任意の堎所に配眮出来ないため、このルヌルを守るこずを ESLint でチェックできるようにしおください。

公匏から匕甚したルヌルは次のようになりたす:
Slices
Next up are slices, which partition the code by business domain. You're free to choose any names for them, and create as many as you wish. Slices make your codebase easier to navigate by keeping logically related modules close together.

Slices cannot use other slices on the same layer, and that helps with high cohesion and low coupling.

Segments
Slices, as well as layers App and Shared, consist of segments, and segments group your code by its purpose. Segment names are not constrained by the standard, but there are several conventional names for the most common purposes:

ui — everything related to UI display: UI components, date formatters, styles, etc.
api — backend interactions: request functions, data types, mappers, etc.
model — the data model: schemas, interfaces, stores, and business logic.
lib — library code that other modules on this slice need.
config — configuration files and feature flags.
Usually these segments are enough for most layers, you would only create your own segments in Shared or App, but this is not a rule.

Cline ず盞談しお、最終的に 5 ぀のルヌルを䜜るこずになった:

# Feature Sliced Design (FSD) に基づいたESLintルヌルの実装

Feature Sliced Design (FSD) アヌキテクチャに基づいたESLintルヌルを実装したした。これにより、コヌドベヌスがFSDの原則に埓っおいるかを自動的にチェックできるようになりたす。

## 実装したルヌル

###レむダヌ間の参照ルヌル

- 䞊䜍レむダヌから䞋䜍レむダヌぞの参照のみ蚱可
- app → pages → features → entities → shared の順で参照可胜

### 同䞀レむダヌ内のSlice間参照犁止

- 䟋entities/userからentities/productぞの参照は犁止

### レむダヌ構造ルヌル

- appずsharedレむダヌは盎接Segmentsを配眮
- pages, features, entitiesレむダヌはSlices配䞋にSegmentsを配眮

### Public API匷制ルヌル

- Sliceの内郚実装を盎接参照せず、公開APIのみを通じお参照
- 䟋import { Something } from '@src/entities/user'は蚱可
- 䟋import { Something } from '@src/entities/user/model/something'は犁止

### バレルファむル内容怜蚌ルヌル

- index.tsファむルはimportずexportのみを含むべき
- ロゞックや実装詳现は含めない

泚意点ずしお、ESLint を䜜ろうずするずきに広く䜿われおいる TIPS を取り蟌んだり、 ESLint のプラグむンだず認識しおいるからかやたら汎甚的なコヌドを曞きたがる傟向があるように思う。 前者ずしおは、 @src のようないわゆる paths をこのリポゞトリでは採甚しおいなかったが、それが採甚されおいるかのようなコヌドを曞いおいた。

埌者は、䟋えば蚭定を eslint.config.js で出来るようなコヌドを曞こうずしお、結果耇雑になる䞊に動䜜しないような状態になっおいた。 これは「蚭定ファむルで出来る必芁はないし、シンプルにベタ曞きで実装すればいいよ」ずいうような指瀺をしおあげるず、考えるこずが枛るのかちゃんず動くコヌドが出おくる

そんなこんなで ESLint を実行するずルヌルが適甚され、 Cline が自身の曞いたコヌドの誀りを怜出できるようになった:

/workspaces/chrome-github-plugin/src/entities/github-search-result/ui/detail.tsx
  1:1  error  Sliceの内郚実装を盎接参照せず、公開API(features/timeline)を通じお参照しおください。各Sliceはindex.tsでのみ倖郚に公開されるべきです。               fsd/public-api
  1:1  error  レむダヌ 'entities' から䞊䜍レむダヌ 'features' ぞの参照は犁止されおいたす。レむダヌは䞊から䞋にのみ参照可胜です。                                fsd/layer-imports
  2:1  error  同䞀レむダヌ 'entities' 内の異なるスラむス間の参照は犁止されおいたす。スラむス 'github-search-result' から 'timeline-item' ぞの参照はできたせん。  fsd/slice-imports

テストを䜜っおもらう

003_migrate-to-jest-and-react-testing-library

テストが党く無かったわけではなく、動䜜確認皋床に Node の組み蟌みランナヌでテストは曞いおいたが、 AI が曞いおくれるなら React もやるかずいうこずで Jest ず React Testing Library ぞ移行しおもらった。䟋によっおモゞュヌル関連でゎチャるこずがあるものの、抂ね埗意。

005_test-implementation-plan

党郚のコヌドのテストを曞いおもらう蚈画を立おた。ずはいえ、元々テストを曞くこずを考えお曞いおいないコヌドなので、コヌドを倉曎しないず曞けないのであれば無芖しおよい、ずいうルヌルにした:

src 以䞋のテストが曞かれおいないコヌドに察しお単䜓テストを远加したす。
たた、node:test から Jest ぞの移行が出来おいないテストがあれば、それを Jest ぞ移行しおください。
元のコヌドを倉曎するこずはせず、テストのためにコヌドを倉曎するこずは避けおください。
テストを曞くこずが難しければ、無芖しお構いたせん。

が、実際には「现かく分けたらテストできそうなので分けたした」ず分けおくるこずがあり぀らい。

テストの内容を曞いおもしょうもないのでそれは割愛する。テストの生成自䜓は党䜓的にはわりずうたくいくが、蚈画の実行方法に問題があり、すべおのテストを曞くのでそれなりの蚈画になっおいるのだが、「蚈画を実行しおください」だず途䞭で勝手に満足しお終わっおしたうずいうこずがあった。「いく぀かのファむルに察しおテストを远加・移行するこずができたした。」ではなく党郚やっおほしい:

# テスト远加・移行完了レポヌト
テスト远加・移行蚈画に基づいお䜜業を進め、いく぀かのファむルに察しおテストを远加・移行するこずができたした。

ここでは解決策ずしお「すべおのステップが終わるたで続けお」ずどの皋床やっおほしいのかを明確に䌝えるこずでワヌクしたのだが、あずで思ったこずずしおもっずタスクを分割するべきだった、ずいうのがある。これは蟻耄合わせフェヌズではこの圢を取るこずにした。

コヌドの移動や分割

これ党般に蚀えるこずずしお、テスト、Lint、型チェックを仮でもよいので党お通しおからこの䜜業に入るべきだった、ずいう反省がある。

過去の経隓から、倚少テストや Lint が萜ちおいる状態でも、その Task に関係がないものはうたく無芖出来おいる事が倚く、今回もそれがうたく働いお最埌に蟻耄を合わせればよいだろう、ず考えおいた。今回は コアドメむンの移動 → それを䜿うアプリの移動、ずいう順番で移行を進めおいたので、コアドメむンが移動するこずでアプリからの参照が切れお、ビルドが通らなかったり型チェッ クが萜ちるのは想定内。だがそれを「今回ずは関係ない゚ラヌ」ず認識しおいるこずがかなり倚く、これはなるべく修正しながら進んでほしかった。

結果ずしおは最埌の蟻耄合わせで倧した苊劎はなく、移行ずしおは成功しおいるのだが、途䞭でほずんどビルドが出来なかったりずいったずころでそれなりに䞍䟿があった。ずはいえ最埌にちゃんず合うなら党䜓ずしおみればよかったずも蚀えるので、少しむずかしいずころだなず思う。たぁこのあたりは人間がやる堎合でも倉わらないのではず蚀われればそうなのだが。

002_monorepo-removal-plan

このリポゞトリは元々 Monorepo になっおいたのだが、このタむミングでルヌトぞ統合した。倧量にファむルを移動するを AI にやらせるは無駄だずいう肌感があったので、移動はこちらでやるずいうこずを明瀺しお蚈画を立おおもらった。

007_lib-to-fsd-refactoring

今回の肝で、適圓に曞いおいたコヌドを FSD に沿っお分割しながら再配眮した。ぱっずむメヌゞがあったわけではないので、分析しお蚈画を倧たかに立おおもらうずころから始めた。

@/src/lib/ 以䞋のコヌドを FSD のルヌルにそっお適切な構成に䜜り倉えたす。
たずは倧たかにプランを考えおください。

いろいろ盞談しおいったのだが芁点ずしおは以䞋:

  • 䌌たような抂念が FSD ず lib それぞれに存圚しおいお、これらを別々のものずしお扱おうずしおいたので、統合しおもらうようにした
  • アプリのドメむン知識が足りずに Notification を䞀般的な通知機胜だず思い蟌み独立させようずしおいた、これはこのアプリでは少し違う抂念なので軌道修正した
    • これはアプリ内では「曎新があったIssue」くらいの意味で䜿われおいた
  • GraphQL の query の扱い方を盞談しお決めた

蚈画が出来たら、现かいステップに分割しおずお願いした埌に、適圓な単䜍で git commit するようにお願いしおいた(これは .clinerules に含める以前の話):

蚈画の詳现さは維持したたた小さいステップに分割しお
各ステップで git commit を行うようにしおください、フォヌマットは以䞋

git add .
git commit -am "refactor: <適切なメッセヌゞ>

- 詳现
- 詳现
- 詳现
"

で、これがうたく行ったかずいうず前述した通りあたりうたくいかなくお、途䞭たで進めお満足したのか止たっおしたう。 ステップごずの git commit も途䞭たではうたく行っおいたのだけど、䞀床止たったあずに再開させるずその指瀺が Markdown に含たれおいるのにもかかわらず忘れおしたったりで、ぐぬぬずいう感じだった。 今回は Cline 自䜓の怜蚌も兌ねおいるので、満足行くような動きをするたでこのフェヌズを䜕床かやり盎しおいお、実装フェヌズの実行だけで合蚈 $30 くらい吹き飛んでいった。぀らい。

結果ずしおはここは「チェックリストを別ファむルずしお䜜っおアップデヌトしながらやっお」ずいうのがある皋床ワヌクしおいおやり切るこずは出来たのだが、曎新のタむミングがたちたちだったりしおあたりおすすめできる感じではなかった。ちゃんず曎新のルヌルを぀めおあげるずか、Roo Code であればそういう Mode を䜜ればうたくいきそうずいう感じはしたが、あたりおすすめできない感じ。こんなプロンプトでやっおいた:

@/docs/plans/007_lib-to-fsd-refactoring.md に沿っお、 src/lib 以䞋を FSD に準じたコヌドにする䜜業を進めたす。
最初に docs/checklist.md ずしお詳现な䜜業のチェックリストを䜜っおください。ステップ毎の䜜業は以䞋の手順で進めるようにしおください。

```
1.  pnpm test を実行し、前の䜜業が問題を残しおいないこずを確認する
2. 各ステップの実装を行う
3. 単䜓テスト: `pnpm test -- <䜜業したディレクトリ>`
4. 型チェック: `pnpm tsc --noEmit`
5. リンティング: `pnpm lint <䜜業したディレクトリ>`
6. 関連テスト: 䜜業内容に圱響を受ける可胜性のある他のテストを実行
7. 以䞋の圢匏で倉曎をすべお git commit

refactor: <適切なメッセヌゞ>

- 詳现な倉曎点
- 詳现な倉曎点
- 詳现な倉曎点
```

以埌䜕か䜜業を進めるごずに必ずこのチェックリストを曎新しながら進めたす。
準備が出来たら、すべおのステップの䜜業が完了するたで䜜業を続けおください。

䞀方でモデルの統合は蚈画で時間を曞けたからかうたくやっおくれおいた、えらい。 あずはテストを移行するのを忘れるので、ちゃんず指瀺に含めたほうがよさそうだった。

蟻耄合わせフェヌズ

010 以降がこれにあたり、前述通り蚈画が倧きすぎた反省を掻かしお、 Plan ずしおは「pnpm eslint を実行しお党郚盎しお」くらいのものから始めるが、蚈画を保存するずきに具䜓的な内容で分割しお保存するようにした:

deleteLogic ず ConfigStream ぞのリネヌムに関する䜜業蚈画ず、 updateStreamの呌び出しに関する䜜業蚈画をそれぞれ docs/plans に曞き出しおください

こうするず 010 ず 011 に蚈画を分けおくれたので、小さくなった個別の Task をそれぞれ実行させお完了させおいった。番号の぀け方はずもかく、これは結構うたく行ったように思う。

.clinerules

終盀になっお .clinerules にある皋床情報が溜たった来たずころで .clinerules をブラッシュアップさせた:

@/.clinerules をよりよいものにアップデヌトしたいず考えおいたす。
より良いずは、次のような基準です。

- 明確か぀簡朔であるこず
- 党䜓のトヌンが揃っおいるこず
- 重芁なアヌキテクチャの栞心に぀いおはより詳しく曞かれおいるこず
- 特殊な衚蚘を䜿わず、芋出し、段萜、箇条曞き、リストを䞭心ずした、人にも読みやすいシンプルな構成
- 蚘茉されおいる順序がよく緎られおおり、䞊から順に読めば開発に着手できる

たた、次のような芁玠を付け加えたいず考えおいたす。

- 簡単なコヌドを曞くこず
- 早期リタヌンを意識しフラットな構造にする
- 関数で曞くこずを優先する
- ...

よりよい .clinerules が䜜れるよう、コヌドベヌスも参考にしお考えおください。

最終的には次のような内容になった:

# プロゞェクトルヌル

## 重芁

1. コヌド倉曎前にテストを確認し、実行する
2. テストで期埅する動䜜を事前に定矩する
3. コヌド䜜成埌、テストず静的解析を実行
4. 3回連続で問題解決できない堎合、ナヌザヌに盞談する

## 開発ワヌクフロヌ

### コマンド䞀芧
- **開発サヌバヌ起動**: `pnpm dev`
- **ビルド**: `pnpm build`
- **テスト実行**: `pnpm test`
  - 特定のテストファむル実行: `pnpm test -- <filename>`
- **テスト監芖モヌド**: `pnpm test:watch`
- **テストカバレッゞ**: `pnpm test:coverage`
- **リンティング**: `pnpm lint`
  - 特定ファむルのリント: `pnpm lint -- <filename>`
- **コヌド自動修正**: `pnpm lint:fix`
- **フォヌマット**: `pnpm format`

### 開発フロヌ
1. **機胜実装前**: テストを先に曞くTDD
2. **コヌド倉曎時**: 既存テストが通るこずを確認
3. **コヌド䜜成埌**: テストを実行しお期埅通りの動䜜を確認
   ```bash
   pnpm test -- <filename>  # 特定のファむルのテストを実行
   # たたは
   pnpm test                # すべおのテストを実行
   ```
4. **テスト倱敗時**: テストが通るようにコヌドを修正
5. **リファクタリング時**: テストが匕き続き通るこずを確認
6. **コミット前**: ESLintを実行しおコヌドスタむルを確認
   ```bash
   pnpm lint
   pnpm lint:fix  # 自動修正可胜な問題を修正
   ```

### 拡匵機胜のロヌド方法
1. Chromeで`chrome://extensions`を開く
2. デベロッパヌモヌドを有効化
3. `packages/extention/dist`ディレクトリを読み蟌む

### コミットメッセヌゞ芏玄
```
<type>(<scope>): <適切なメッセヌゞ>

- <詳现な倉曎内容>
- <詳现な倉曎内容>
```

## 開発の基本原則

### コヌド品質
- **シンプルなコヌド**: 耇雑な実装より読みやすいコヌドを優先
- **早期リタヌン**: 条件分岐はネストを避け、早期リタヌンでフラットな構造に
  ```typescript
  // 良い䟋
  function processItem(item) {
    if (!item) return null;
    if (item.isInvalid) return null;

    return transformItem(item);
  }

  // 避けるべき䟋
  function processItem(item) {
    if (item) {
      if (!item.isInvalid) {
        return transformItem(item);
      }
    }
    return null;
  }
  ```

### 関数型アプロヌチ
- **関数優先**: クラスよりも関数で実装
- **玔粋関数**: 副䜜甚を持たない関数を優先
- **ルヌプ凊理**: `forEach`を避け、`for...of`たたは配列メ゜ッドを䜿甚
  ```typescript
  // 良い䟋非同期凊理の堎合
  for (const item of items) {
    await processItem(item);
  }

  // 良い䟋同期凊理の堎合
  const results = items.map(item => processItem(item));

  // 避けるべき䟋
  items.forEach(async (item) => {
    await processItem(item); // 非同期凊理の制埡が難しい
  });
  ```

### 䟝存管理
- **䟝存の最小化**: 可胜な限りリポゞトリ内のコヌドで解決
- **䟝存远加時**: 必ずナヌザヌに確認
- **倖郚䟝存のモック**: テスト時に倖郚䟝存を適切にモック化

### 抜象化
- **過床な抜象化を避ける**: 具䜓的で理解しやすいコヌドを優先
- **Sliceの具䜓性**: 抜象的なコヌドよりも具䜓的な実装を優先

## アヌキテクチャFeature Sliced Design

### レむダヌ構造
1. **app** — アプリケヌション実行に必芁な芁玠
   - ゚ントリヌポむント、グロヌバルスタむル、ルヌティング
2. **pages** — 完党なペヌゞたたはネストされたペヌゞ郚分
3. **features** — ナヌザヌにビゞネス䟡倀をもたらす機胜
4. **entities** — ビゞネス゚ンティティGitHub Search Result、Timeline Itemなど
5. **shared** — 再利甚可胜な機胜プロゞェクト/ビゞネスから独立

### 参照ルヌル
- 䞊䜍レむダヌから䞋䜍レむダヌぞの参照のみ蚱可
  - `app` → `pages` → `features` → `entities` → `shared`
- 䞋䜍レむダヌから䞊䜍レむダヌぞの参照は犁止
- 同䞀レむダヌ内のSlice間の参照も犁止

### Slice構造
- **app**ず**shared**レむダヌは盎接Segmentsを配眮
- **pages**, **features**, **entities**レむダヌはSlices配䞋にSegmentsを配眮

### Segment構造
- **ui** — UI衚瀺コンポヌネント、スタむルなど
- **model** — デヌタモデルスキヌマ、ロゞックなど
- **api** — バック゚ンド連携リク゚スト関数など
- **lib** — 他のモゞュヌルが必芁ずするラむブラリコヌド
- **config** — 蚭定ファむルずフラグ

### Public API
- 各Sliceの公開APIはindex.tsで明瀺的に゚クスポヌト
- Sliceの内郚実装詳现を盎接参照するこずは犁止
- 各sliceの盎䞋には必ずindex.tsファむルを配眮

```typescript
// entities/github-search-result/index.ts
export * from './api';
export * from './model/io';
export * from './model/timeline-item';
export * from './model/type';
export * from './ui/detail';
// ...
```

## コヌディング芏玄

### TypeScript
- 型定矩を厳栌に行う
- 型ガヌド関数を掻甚しお型安党性を確保
  ```typescript
  function isCommentTimelineItem(item: any): item is CommentTimelineItem {
    return item.type === 'IssueComment' && 'bodyText' in item;
  }
  ```

### Reactコンポヌネント
- 関数コンポヌネントずしお実装
- Reactフックを掻甚
- プロパティベヌスの蚭蚈䞋䜍レむダヌのコンポヌネントは䞊䜍レむダヌの機胜を盎接参照しない

### スタむリング
- CSSモゞュヌルを䜿甚しおスタむルをコンポヌネントスコヌプに保぀
  ```typescript
  import styles from './component.module.css';

  function Component() {
    return <div className={styles.container}>...</div>;
  }
  ```

### ファむル呜名
- ファむル名はキャメルケヌスを䜿甚
- テストファむルは`.spec.ts(x)`ずいう呜名芏則

### むンポヌト
- 盞察パスのみ䜿甚゚むリアスは䜿甚しない
- パス蚘法は`./{path}`、`../{path}`のみ
  ```typescript
  // 良い䟋
  import { Button } from '../../shared/ui/button';

  // 避けるべき䟋
  import { Button } from '@shared/ui/button';
  ```

## 実装パタヌン

### ストリヌム曎新パタヌン
- `updateStream`関数でGitHubからデヌタを取埗
- 各ストリヌムは`onUpdateThisLoop`関数で曎新タむミングを制埡
- 曎新埌は`notifyUpdated`でむベントを発行

```typescript
export const updateStream = async (name: keyof typeof streams, storage: Storage) => {
  // デヌタ取埗ロゞック
  // ...

  // 通知条件の刀定
  if (item.type === 'Issue' && shouldNotifyIssue(item, 'username', lastUpdated)) {
    createNotificationIfNeeds(storage, item.id, lastUpdated);
  }

  // ストレヌゞ曎新
  await Promise.all([
    setToStorage(storage, keyForItem(item.id), item),
    setToStorage(storage, keyForStreamItems(stream), newIds),
  ]);

  // むベント発行
  notifyUpdated(name, updatedIds);
};
```

### ストレヌゞ管理パタヌン
- キヌは`keyFor*`関数で生成䞀貫性のため
- `getFromStorageOrDefault`ず`setToStorage`を䜿甚
- アむテムIDをリストずしお保存し、実際のデヌタは個別に保存

```typescript
const ids = await getFromStorageOrDefault(storage, keyForStreamItems(stream), () => []);
const items = [];

for (const id of ids) {
  const item = await getFromStorageOrDefault(storage, keyForItem(id), () => null);
  if (item) {
    items.push(item);
  }
}
```

### 通知システム
- `shouldNotifyIssue`ず`shouldNotifyDiscussion`で通知条件を刀定
- `createNotificationIfNeeds`で通知を䜜成
- `notifyNotificationUpdated`でむベントを発行

### むベント凊理
- カスタムむベント`stream:updated`、`notification:updated`を䜿甚
- `subscribeStreamUpdated`ず`subscribeNotificationUpdated`で賌読

```typescript
// むベント発行
export const notifyUpdated = (name: string, ids: string[], worker = false) => {
  dispatchUpdate('stream:updated', { name, ids }, worker);
};

// むベント賌読
useEffect(() => {
  const unsubscribe = subscribeStreamUpdated(({ name }) => {
    if (name === streamName) {
      fetchData();
    }
  });

  return () => unsubscribe();
}, [streamName, fetchData]);
```

### プロパティベヌスのコンポヌネント蚭蚈
- FSDレむダヌ間参照ルヌルに埓うため、䞋䜍レむダヌentitiesのコンポヌネントは䞊䜍レむダヌfeaturesの機胜を盎接参照しない
- 通知状態などの機胜は䞊䜍レむダヌからプロパティずしお泚入する
- 䟋: `IssueLine`コンポヌネントは`hasNotification`や`onDeleteNotification`をプロパティずしお受け取る

### 非同期凊理パタヌン
- async/awaitを䜿甚した読みやすい非同期コヌド
- Promise.allを䜿甚した䞊列凊理
- for...ofルヌプを䜿甚した非同期凊理forEachではなく

```typescript
// 䞊列凊理
const results = await Promise.all(
  queries.map(query => fetchData(query))
);

// 順次凊理
for (const item of items) {
  await processItem(item);
}
```

## テスト戊略

### テストファむルの配眮
- 実装ファむルず同じディレクトリに配眮
- `.spec.ts(x)`ずいう呜名芏則を䜿甚

### テストファヌスト
- テストを先に曞くこずで、仕様を明確にする
- 実装前にテストを曞くこずで、APIや動䜜の蚭蚈が掗緎される
- テストが通るように実装するこずで、芁件を満たす保蚌になる

### テスティングトロフィヌ
- テスティングトロフィヌを意識し、過床なモック化は避ける
- 単䜓テスト、統合テスト、E2Eテストのバランスを考慮
- 実際のナヌザヌ䜓隓に近いテストを優先
- モックは必芁最小限にずどめ、可胜な限り実際の実装を䜿甚

### テスト関数
- `test`関数を䜿甚`describe`や`it`は䜿甚しない
- 日本語でテスト内容を蚘述

```typescript
test('初期状態ではpendingがtrueで、デヌタ取埗埌にfalseになる', async () => {
  // テスト実装
});
```

### モックの掻甚
- Jestのモック機胜を䜿甚
- 倖郚䟝存関係を適切にモック化
- 過床なモック化は避け、実際の動䜜を反映したテストを心がける

```typescript
jest.mock('../../../entities/timeline/model/io');
jest.mock('../../../shared/lib/event');

beforeEach(() => {
  (timelineIO.getTimeline as jest.Mock).mockResolvedValue(mockTimeline);
});
```

### UIコンポヌネントテスト
- React Testing Libraryを䜿甚
- レンダリングの怜蚌
- ナヌザヌむベントのシミュレヌション
- アクセシビリティを考慮したセレクタの䜿甚

```typescript
test('「Show all events」ボタンをクリックするずすべおのむベントが衚瀺される', () => {
  render(<TimelineItems items={items} />);

  // ボタンをクリック
  fireEvent.click(screen.getByText(/Show all events/));

  // 衚瀺を怜蚌
  expect(screen.getByText(/testUser commented/)).toBeInTheDocument();
});
```

## コメント芏玄

- `TODO`: 将来的に実装すべき機胜
- `MEMO`: 実装の背景や理由の説明

## ドキュメント芏玄

- `docs/plans/` - 蚈画ドキュメント
  - ファむル名は `{3桁の通し番号}_{kebab-case-name}.md` の圢匏で保存

## 制玄ず泚意点

### GitHub API制限
- レヌト制限に泚意短時間に倚数のリク゚ストを送らない
- ゚ラヌハンドリングを適切に実装

### サヌビスワヌカヌ制玄
- サヌビスワヌカヌのラむフサむクルに泚意長時間実行できない
- 定期的なりェむクアップず凊理の分散

### 既知の問題
- ナヌザヌ名がハヌドコヌドされおいる箇所がある将来的に蚭定から取埗するよう倉曎予定
- 初回デヌタ取埗時の挙動に制限あり

ちなみにこれを曞いおいる途䞭に .clinerules がディレクトリにできるようになった。

所感

蚈画の管理方法ず区切り方

管理方法に関しおは答えがあたり考えられおいないのだけど、蚈画の Markdown を䞭心に回すのは自分ずしおは奜みのフロヌだった。 フロヌ以倖にも、想定しおいないずか動䜜がおかしなコヌドが芋぀かったずきに、それがどこで生たれたかを Markdown を遡っお探しに行っおくれおいたりしお、そういう意味もあったほうがいいのだずは思う。 ずはいえ、ちょっずした bugfix たですべお管理したいのかず蚀われるずそうでもなく、 PR の単䜍で䞀぀あればいいような枩床感なので、 Plan ずいうよりは Design Doc のような感芚で残せるずいいのかなず思う。

番号は指定が楜になるので割ずほしくお、耇数人で管理する堎合はやはり GitHub Issue などに連携できるずよさそう。そうするずそれをフロヌ化した Roo Code のカスタムモヌドが䜿いたくなるな、ずいう感じだった。

実際の管理方法をどのようにしおいくかにもよるが、蚈画自䜓は思っおいる 3 倍くらいは现かくしたほうがいいなず思った。 Design Doc のような圢にする堎合は、あたり分かれすぎおいおも効果が薄そうなので、 Git 管理䞋にない堎所で個人的に䜿うのも手かもしれない。

Lint は AI に敎備させる

結構ちゃんずしたものがすぐに出おくるので䟿利。 汎甚的なものを䜜ろうずする傟向にあるように思うので、局所最適しお良いこずを䌝えるず粟床が䞊がるように思う。

倚分䜜り盎させたほうがいい

既存のコヌドを倧きく曞き換えるよりも、既存のコヌドを解析させお仕様抜出、その仕様をもずに新しいコヌドを生成させる方が埗意だろうず思うので、今回のように Cline で移動させるみたいなのはあんたり向いおいない気もする。 途䞭でやり盎しおいるフェヌズがいく぀かあるずはいえ、やり切るのに倧䜓 $100 かかっおいお、「これ䜜り盎したほうが安くね」ず思いながらやっおいた。

名前を暪着せずにちゃんず぀ける

別に人間がコヌドを曞く堎合でも倉わらない話ではあるが、抜象床が高い名前を䜿っおいるず勝手に圹割を掚枬されおいるような感芚があった。 新しく䜜る堎合よりは、既存のコヌドを解析させたりリファクタリングさせる堎合にこれは倧きく圱響しおきそう、ずいう肌感。

yaakai.to