
書き捨てではなく継続開発可能なコードを Cursor Agent で書くために意識していること
こんにちは。リテールアプリ共創部のきんじょーです。
昨年の夏頃にメインで使うエディターを VS Code から Cursor に変えて、1 年弱が経ちました。
Cursor は GitHub Copilot よりも Cursor Tab 補完の精度の良さを感じたり、Chat で@アノテーションを用いて与えられるコンテキストの種類が豊富な点、Cursor Composer(現 Agent)による AI コーディングエージェントが比較的早い段階で実装され、プロンプトだけで複数ファイルを跨いだ機能実装をサクサク進められる点に魅力を感じていました。
一方で、Agent によって出力されたコードを見ると、TypeScript を利用していながら型エラーを握りつぶすようなコードが書かれていたり、すでに存在する機能を改めて別のファイルで再実装していたりと、動きはするがプロダクションコードとして納得できる品質のコードを書いてくれないことが多くあり、Agent には Vibe Coding で書き捨てのコードを書かせたり、機能実装の大枠だけ担当させ、細かい点は自分で手直しするようにしていました。
先日 Claude 4 Sonnet が出たこともあり、Agent をより使いこなしたコーディングスキルを磨こうと 改めて Cursor のドキュメントを読み直し、ドキュメントから有用だと感じた内容をピックアップし、実務の中で実践したテクニックの中で効果のあったものをこのブログでご紹介します。
Cursor を使い始めてみたが納得できる品質のコードを AI が書いてくれなくて悩んでいる方、AI に書かせるよりも自分で書いた方が早いと感じて気づいたら Cursor Tab 補完ばかり利用している方に是非読んでいただきたいです。
本記事で取り扱う言語は TypeScript を想定しています。
Cursor の主要機能
まずは Cursor が持つ主要機能をドキュメントベースでおさらいしていきましょう。
Cursor Tab
AI によるコードの先読み補完をいち早く提供したのは GitHub Copilot です。
Cursor Tab も同様にコードの先読み補完を提供してくれるのですが、GitHub Copilot との大きな違いは、挿入するコードの提案だけではなく、カーソル周囲の既存のコードを編集・削除を提案してくれる点でした。
これにより修正したい点にカーソルを置くだけで、Cursor が編集提案をしてくれ、他の離れたコードについても順次編集提案をしてくれるため、Tab で提案を受け入れるだけで開発者の意図を汲み取ったコードの編集を進められます。
※画像引用元: Cursor Tab
最近はあまり GitHub Copilot を利用していませんが、Cursor に乗り換えた直後には Cursor が自動収集するコンテキストに優位性を感じ、 AI の提案を Tab で受け入れる率が格段に上がったことを記憶しています。
直近の変更内容、恐らく開いているファイルやコピーしたクリップボードの中身までも考慮した上で、開発者が次に行う変更を推測してくれており、提案の精度が高いのでCursor Tabだけでもサクサク開発が進められます。
GitHub Copilot でも Next Edit Suggestionという同様な機能が少し前に提供されたようなので、現在の GitHub Copilot と比較してみたいところです。
import 文の補完
現時点では TypeScript と Python(ベータ)限定の機能ですが、エディタが提供する import 補完よりもプロジェクト構成全体を考慮した提案を Cursor Tab がしてくれます。
一見地味な機能ですが、これ無しでは開発効率が大きく下がるくらい頼りきりになっていたことに、上記以外の言語(Dart)を書いていて気づきました。
参考: Auto Import
Cmd + K
)
Inline Edit(エディタ上でCmd/Ctrl + K
を押すと、編集中のファイル内に限定したインラインのチャットを開くことができます。
インラインのチャット機能はドキュメント上でもCmd + K
と呼ばれているため、本記事でもCmd + K
と表記します。
Cmd + K
には 3 つのモードがあります。
- Edit Selection
- 選択した範囲のコードに対して直接編集指示を出せます
- Edit Full File
- 選択したファイル全体に対して編集指示を出せます
- Quick Question
- 選択したコードやファイルについて質問できる
Chat よりも機能は制限されるものの、より高速にコード生成が完了するので、修正箇所が限定的で修正方法が明らかに明確な場合は、Cmd + K
で修正指示を出しています。
例えばfor文
をforeach()
に書き換えさせたりするような用途です。
Cmd + K
はターミナル上でも開くことができます。
ターミナルでのコマンド生成を Cursor に任せることで、CLI のオプションをググる回数が大きく減りました。
参考: Inline Edit
Cursor Chat
Cursor Chat は自然言語で AI エージェントと対話をしながら開発を進めたり、コードベースについて理解を深めることができるモードです。
Chat には以下 4 つのモードがあります。
- Agent Mode
- Chat のデフォルトモードで、最も AI が自律的にコーディングタスクを処理できるモード
- Cursor が自律的にコードベースを探索し、タスクの実行計画を立て、コーディングを進める
- Ask Mode
- 読み取り専用でコードベースには変更を加えないモード。
- Manual Mode
- 明確にファイルを指定してコードの編集を指示するモード。AI によるコードベースの探索は行われない。
Cmd+K
と Agent の中間のモードに見えますが、使い所がわからずあまり使用したことはありません。
- Custom Mode
- 使用するモデルやツール、初期プロンプトをカスタマイズできるモード。
各モードの使い分けなどの Tips は後述します。
主要機能の使い分け
Cursor Tab と Cmd + K
、Chat の使い分けについてドキュメント上に以下の記載がありました。
ツール | 使用場面 | 強み | 制限 |
---|---|---|---|
Cursor Tab | 素早い手動での変更 | 完全なコントロール、高速 | 単一ファイル |
Cmd+K | 1 つのファイル内での限定的な変更 | 集中的な編集 | 単一ファイル |
Chat | より大きな、複数ファイルにまたがる変更 | 自動的なコンテキスト収集、深い編集 | 低速、コンテキスト重視 |
また、Web 開発のチュートリアルには以下の記載があります。
- すべてを自動化する必要はありません。システムが複雑になりすぎた場合は、Tab キーと Cmd+K キーを使って、より精密な編集を行ってください。
- カーソルは、自動操縦ではなく副操縦士として機能した時に最も威力を発揮します。カーソルは、自分の意思決定に取って代わるものではなく、改善するために活用しましょう。
Cursor Agent にコーディングを一任するのであれば(いわゆる Vibe Coding)、Rules を整備したり、プロンプトエンジニアリングを頑張る必要があると考えていましたが、Cursor 公式の見解として、遂行したいタスクに応じて適切にツールを使い分けるべきという落ち着いた見解があったのは少し安心しました。
一方で Cursor Tab に頼りすぎでせっかくの Agent モードを使いこなせていないのは勿体無いので、ここからは Agent モードを使いこなすために工夫した Tips をご紹介していきます。
参考:
Cursor Agent を使いこなすためにしている工夫
Ask モードで計画し、Agent モードで実装する
チームメンバーにタスクを依頼する際、前提条件やゴール、想定している実装方針が違うと大きな手戻りにつながります。
Cursor Agent も同様で、一度に多くのコードを生成することができますが、大量のコード編集には時間を要します。編集されたコードがこちらの意図とは異なるものだと、無駄に工数と Fast Request 回数を消費してしまいます。
Agent
モードのカーソルは認識合わせをしようとしてもすぐにコードを書き始めるので、実装開始前にAsk
モードを使って計画を立てて、計画について確認が取れてからAgent
モードに切り替えて実装を開始しましょう。
また、Ask
モードで実装すべき機能の仕様、既存のコードベースなど必要なコンテキストを与えた際に、最後に必ず「実装を進めるにあたり不明点はありますか?
」を聞いています。
これにより実装を行う上での技術的な判断や、不明確な制約事項を Cursor が勝手に決めて実装してしまうことを防ぐことができます。
実装計画の TODO リストを Markdown ファイルで管理する
普段自分でコーディングする際、細かい TODO リストの md ファイルを用いてタスク管理していたのですが、AI エージェントと機能開発をする際も、feature ブランチ毎に TODO リストを作成させ、リストベースで Cursor と認識合わせ、タスク・進捗状況の管理を行なっています。
Ask モードで建てた計画をより細かい単位でファイルに落とし込ませ、ファイルに落とし込むことで Cursor と実装計画を共同編集できるようになります。
これにより実装に入る前に軌道を修正できるのと同時に、実装中に新たに見つけた TODO をリストに積んでおくことで、別のセッションで対応できるようになります。
セッションを終了する際に、タスクの進捗状況、そのセッションがうまく進んだ原因、失敗した原因、ユーザーから与えられた制約事項などを Cursor に考察させ TODO リストを更新してもらいます。
Cursor には Cline の様に標準で MemoryBank 機能が存在しませんが、このように Cursor Agent に直接指示することでセッションを跨いだ知識共有ができます。
変更を小さな単位に分割し、頻繁に新しいチャットを作成する
大規模なコードベースを扱う Tips として以下の 2 点が紹介されています。
- 変更の範囲を絞り、一度に多くのことを行わないこと
- 頻繁に新しいチャットを作成すること
1 つのチャットで長いこと開発を進めてコンテキストウィンドウがいっぱいになってくると、Cursor は自動的に会話を要約します。
それにより、初期に与えたコンテキストが薄まったり、Cursor が悪戦苦闘した編集履歴が邪魔をしてしまい、Cursor Agent が生成するコードの品質が著しく低下することがあります。
そのため 1 コミットずつを目安に New Chat してコンテキストをリセットしています。
会話の履歴が長くなってくると、右下に「New Chat」ボタンが現れるので、それを押すと、現在の会話履歴を要約して次のプロンプトに加えた形で新しいチャットが開きます。
要約だけでは消えてしまうコンテキストについては前述した TODO リストを元に、セッション間の知識共有を行なっています。
参考: Large codebases
Rules の活用
応答品質全体の制御
@kinopee_aiさんが以下のリポジトリで公開されている global.mdc をプロジェクト向けにカスタマイズして利用しています。
この Rules を入れると、Cursor Agent がまずユーザータスク分析を行い、理解したタスクの内容・制約条件・実装のステップを検討してから実装に着手する様になるため、その時点で方向性が間違っていないかを確認できます。
また、Cursor Agent がやりがちだったリポジトリの中にある既存機能を重複して再実装や、必要のないライブラリを勝手にインストールするような暴走を防ぐことができ、always の設定で適用しています。
コンテキストの提供
我々のプロジェクトではモノレポを採用することが多く、各パッケージに関しての rules を別途定義しています。
具体的には以下の様な情報です。
- プロジェクト構成
- 技術スタック
- API 仕様や DB スキーマなど必要なドキュメント
- コーディングルールを実際の実装例を交えて説明
型安全性に関する制約
Cursor Agent にコードを書かせると、せっかく TypeScript を利用しているのに型エラーを握りつぶしたり、コンパイラを黙らせるためのコードを書いてくることが多々あります。
具体的には以下の様な実装です。
const result = data as any; // 安易にanyを利用、型アサーションを利用
const id = exercise.id ?? 0; // nullの場合に0代入
const user = users.find((u) => u.id === id)!; // 見つからない可能性を無視
1 つ目と 3 つ目は Lint ルールで禁じることができますが、2 つ目は TypeScript のコンパイラも Linter もすり抜けてしまいます。
上記を防ぐために型安全に関する Rules を定義したところ、Claude 4 Sonnet を利用して型エラーが綺麗に直せることが増えました。
# 型安全性ルール
## 基本原則
### 1. 型エラーの根本原因を理解する
- 型エラーが発生した際は、表面的な修正ではなく根本原因を調査する
- なぜその型が`null`や`undefined`になり得るのかを理解する
- データベースクエリの結果やAPIレスポンスの構造を確認する
### 2. 安易な型回避を禁止する
- `as any`や不適切なデフォルト値(`?? 0`など)による型エラー回避は禁止
- 特にID列に無関係な値(0など)を設定することは絶対に避ける
- データの整合性を破壊する可能性のある修正は行わない
### 3. 適切な型ガードとエラーハンドリング
- 必要に応じて適切な型ガードを実装する
- ビジネスロジックに基づいた適切なエラーハンドリングを行う
- Non-null assertionを使用する場合は、その根拠を明確にコメントで記載する
## 実装パターン
### 推奨パターン:適切なnon-null assertion
```typescript
// ✅ 根拠が明確な場合
// biome-ignore lint/style/noNonNullAssertion: jsonArrayFromしていてidがない場合空配列になるため値は必ず存在する
id: exercise.id!,
```
### 推奨パターン:型ガードの使用
```typescript
// ✅ 型ガードによる安全な処理
if (exercise.id == null) {
throw new Error("Exercise ID is required");
}
const id: number = exercise.id;
```
### 避けるべきパターン:不適切なデフォルト値
```typescript
// ❌ ID列に無関係な値を設定
id: exercise.id ?? 0, // データ整合性を破壊する危険性
// ❌ 意味のないデフォルト値
name: user.name ?? "unknown", // ビジネスロジック的に不適切
```
### 避けるべきパターン:型の強制キャスト
```typescript
// ❌ 根拠のない型キャスト
const id = exercise.id as number; // なぜnumberと断言できるのか不明
// ❌ any型による回避
const data = response as any; // 型安全性を完全に放棄
```
## 調査すべき項目
### データベースクエリの場合
- JOIN の条件と OUTER JOIN の可能性
- SELECT 句で選択している列の定義
- jsonArrayFrom などのヘルパー関数の動作
- テーブル間の関係性と外部キー制約
### API 呼び出しの場合
- API レスポンスの実際の構造
- オプショナルフィールドの存在
- エラーレスポンスの可能性
- バージョン違いによる構造変更
## 型エラー対応フロー
1. **エラーメッセージの詳細確認**
- どの型が期待されているか
- 実際にはどの型が来ているか
- なぜその型になるのか
2. **データソースの調査**
- データベーススキーマの確認
- クエリ結果の実際の値
- 外部 API の仕様書確認
3. **ビジネス要件の確認**
- その値が null の場合の適切な処理
- エラーとして扱うべきか
- デフォルト値が適切か
4. **適切な解決策の実装**
- 型ガード
- エラーハンドリング
- 根拠のある non-null assertion
@アノテーションを活用して Cursor Agent への的確なコンテキストを与える
ここまででは、Cursor Rules で共通のプロンプトを定義し、TODO リストの Markdown ファイルで前回のセッションまでの進捗状況や学びを管理する方法をご紹介しました。
タスクを遂行するにあたり必要な仕様や既存実装、ドキュメントといった特定のコンテキストを、@アノテーションで細かく与える方法をご紹介します。
Files
修正対象のファイルの指示、参考にする実装や仕様書を提供する際 @ファイル名
でファイルを指定可能です。
アノテーションをつけずに指示しても Cursor が grep してたどり着いてくれることが多いですが、認識相違による手戻りを防ぐためと、指示する際にファイル名を補完してくれて単純に便利なので必ず指定しています。
Folder
@フォルダ名
の参照には 2 つのモードがあります。
デフォルトでは指定されたフォルダ配下を、コード探索時の grep 対象にしてくれます。
v0.50 で追加されたFull Folder Content
を有効にすると、指定したフォルダ配下のファイルを可能な限りコンテキストに含めてくれる様になります。
コードベースを広く調査をしたい時はオフ、具体的に参考となる実装を与えたいときなどはオンと使い分けられそうですが、私は常にオンにして使っています。
Code
例えば@クラス名
、@メソッド名
などを指定することで、特定のコードブロックをコンテキストに含めることができます。
ファイル全体では情報量が大きすぎて、コンテキストを特定の箇所に限定したい場合などに有効です。
また、エディタ上で開いているコードをコピーしチャット欄にペーストする操作でも、コピーしたコードの参照がコンテキストに追加されます。
Docs
プロジェクトで利用しているフレームワークやライブラリなどを事前にインデックス化しておくと、@ドキュメント名
でドキュメントをコンテキストに含められる様になります。
Add Docs からドキュメントの URL を指定すると、
Cursor によるインデックス作成が開始され、@cdk
でドキュメントをコンテキストに指定できるようになります。
MCP でドキュメントが公開されていればツール経由で参照してもらう方法もありますが、MCP のツールを使ってくれるかどうかは Cursor Agent 次第なので、明らかに渡したいドキュメントが決まっている場合は@Docs
で指定する方が効率よく感じます。
Git
@Commit
で前回コミットからの差分を、@Branch
で main ブランチとの差異をコンテキストに含められます。
Cursor にコードレビューを依頼するときや、プルリクエストを作成するときなどに使用しています。
Web
Web 検索を交えて回答を生成したい時に、@Web
で検索結果をコンテキストに含めることができます。
Cursor Agent が自発的に Web 検索をしていることが多いのであまり明示的に指定することはありませんでしたが、Google 検索や Perplexity などツールを切り替えなくても Cursor Chat 内で検索ができるのは便利ですね。
Past Chat
@Past Chat
で過去のチャットの要約をコンテキストに含めることができます。
前述の通り、割と短いセッションで New Chat する使い方をしているので、過去のチャットでどんな会話をしたのかをコンテキストに含めたい時に仕様しています。
画像のペースト
リポジトリの中になかったり、テキストでは与えられないコンテキストはコピー&ペーストで画像で与えることができます。
例えば以下の様なケースで有効に使えます
- Excel 管理された API 仕様書のキャプチャを貼って OpenAPI 形式に変換
- Figma などから UI デザインの提供
- ローカルサーバーで起動しているアプリの画面のキャプチャ
2,3 は今だと MCP でできそうですね。
カスタムモードで定型作業を自動化する
まだベータ版ですが、カスタムモードを有効にすることで、Agent
、Ask
、Manual
に加えて独自のモードを作成できます。
カスタムモードでは以下を自由に選択できます。
- 利用するモデル
- 利用するツール
- 初期プロンプト
- Auto run の有効/無効
- Auto fix errors の有効/無効
- モードを起動する key binding
Cursor Rules でも AI の挙動をカスタマイズすることが可能ですが、より詳細に AI が持つ権限、機能を制御して、特定のタスクに特化したモードを作ることができます。
私は カスタムモードで Pull Request の作成を定型化しています。
初期プロンプトには以下を与えています。
PullRequest を作成のテンプレート@PULL_REQUEST_TEMPLATE.md を参照してください。
コミット漏れがないかを git state で確認し、必要に応じてユーザーに必要性を確認してください。
@Branch (Diff with Main Branch) を参照して、変更内容を簡潔にまとめてください。
PullRequest の作成については GitHub MCP を利用してください。
上記の設定だけで、モードを起動して「お願い」と依頼するだけで Pull Request を作成してくれます。
他にも、実装を終えたらコードレビュー特化のモードで Cursor にレビューを依頼するなど、使いこなすことで開発のワークフローを効率化できそうです。
まとめ
要件定義書や仕様書をしっかり書いて AI に提供すれば、Cursor Agent が要件を満たして動作するコードを一通り書いてくれます。
しかし、Vibe Coding で機能追加を重ねていくと、同じ機能が複数箇所で実装されたり、実装と修正を繰り返してゴミが大量に残ったり、型安全性が無く実行時までエラーに気づけなかったりと、動きはするが継続的に開発するのが難しいコードになっていく、というのがこれまでの Cursor Agent に対しての印象でした。
本記事では、そうした課題を解決するために意識していることとして、以下の Tips をご紹介しました。
- Ask モードで計画し Agent モードで実装する
- TODO リストによる進捗管理とセッション間の知識共有
- 変更を小さな単位に分け、頻繁な New Chat によるコンテキストのリセット
- Rules の活用方法
- @アノテーションの活用による的確なコンテキスト提供
- カスタムモードによる定型作業の自動化
この記事が誰かの役に立つと幸いです。
以上。リテールアプリ共創部のきんじょーでした。