🚀

Cursorで実践するスペック駆動開発:Kiro流フローを完全再現

に公開

はじめに

AI駆動開発ツールとして注目を集めているKiroの開発フローを、Cursorで再現できるか検証してみました。

今回の仕様駆動開発は、以下の記事を参考にしてみました。
https://qiita.com/tequila0725/items/241be34f5069d6e57aa3

Kiroでは、タスクの起票からGitHubのissue作成までを自動化していましたが、Cursorでも同様の仕様駆動開発フローを構築できることが分かりました。

スペック駆動開発で開発プロセスを Cursor Rules に定義

参考の記事を改変しつつ利用しています。
Cursorでは、 .cursor/rules/spec.mdc というファイルで定義しました。

プロジェクトごとにspecの仕様を変えることで、実装時に @spce 指定して利用します。

Issue作成結果

参考記事のように、開発プロセスを以下の5つのフェーズに分けて段階的に進めるようにしています。

  1. 要件定義フェーズ - 何を作るのかを明確化
  2. 設計フェーズ - どのようにつくるかを設計
  3. 実装計画フェーズ - タスクをユーザーストーリー単位に分解
  4. issue作成フェーズ - GitHubでプロジェクトとissueを作成
  5. 実行フェーズ - 実際の実装とプルリクエスト作成

Cursorでの実装方法

Cursorでは、KiroのようにVibeとSpecで別れていないため Cursor Rulesで定義して、チャット上のコンテキストを指定して利用する形になります。

1. Specファイルの作成

.cursor/rules/spec.mdcファイルを作成して、5段階フローのルールを定義します。

Specファイル(Rules)
---
alwaysApply: false
---

# タスク実行の5段階フロー

## 実行ルール 

### github認証情報
- owner: [Githubのオーナ名] # Github MCPを使用するのに必要。定義しないと毎回確認するようになります。

### ファイル操作
- 新規タスク開始時: 既存ファイル、既存issueの**内容を全て削除して白紙から書き直す** # 既存ファイルが存在する状態で再実行する場合に必要
- ファイル編集前に必ず現在の内容を確認

### フェーズ管理
- 各段階開始時: 「前段階のmdファイルを読み込みました」と報告
- 各段階の最後に、期待通りの結果になっているか確認
- `.cursor_workflow/specs/{指定したディレクトリ名}/requirements.md`が存在しない場合は必ず要件定義フェーズから始める

### 実行方針
- 段階的に進める: 一度に全てを変更せず、小さな変更を積み重ねること
- 複数のタスクを同時並行で進めないこと
- エラーが発生した場合は、報告すること
- エラーを解決してから次へ進むこと
- ユーザからの指示に無い機能を勝手に追加しないこと

## 1. 要件定義フェーズ
- ユーザからの指示に対して **「何を作るのか」を明確に定める。**
- 目的の明確化、現状分析、機能要件の定義、非機能要件の定義、制約条件の定義を実行する
- `.cursor_workflow/specs/{指定したディレクトリ名}/requirements.md`に文書化
- **必須確認**: 「要件定義フェーズが完了しました。設計フェーズに進んでよろしいですか?」

### 2. 設計フェーズ
- **必ず`.cursor_workflow/specs/{指定したディレクトリ名}/requirements.md`を読み込んでから開始すること**
- 要件定義フェーズで決定したことをもとに **「どのようにつくるか」「内部でどのように処理するか」を設計する**
- プログラマーが実際にコーディングできるレベルまで詳細化すること
- `.cursor_workflow/specs/{指定したディレクトリ名}/design.md`に文書化
- **必須確認**: 「設計フェーズが完了しました。実装計画フェーズに進んでよろしいですか?」

### 3. 実装計画フェーズ
- **必ず`.cursor_workflow/specs/{指定したディレクトリ名}/design.md`を読み込んでから開始すること**
- アジャイル・スクラム開発をすることを前提とする
- タスクを実行可能なユーザーストーリー単位に分解、優先順位を設定する
- `.cursor_workflow/specs/{指定したディレクトリ名}/implementations.md`に文書化
- **必須確認**: 「実装計画フェーズが完了しました。issue作成フェーズに進んでよろしいですか?」

### 4. issue作成フェーズ
- **必ず`.cursor_workflow/specs/{指定したディレクトリ名}/implementations.md`を読み込んでから開始すること**
- Github Projectを現在のリポジトリに紐づける形で新規作成する
- Github Issuesに各タスクの内容でIssueを作成する。Issueは先程作成したProjectに紐づける
- **必須確認**: 「issue作成フェーズが完了しました。実行フェーズに進んでよろしいですか?」

### 5. 実行フェーズ
- **必ずopenになっているissueの一覧を確認してから開始**
- **必ず対応するissueの詳細を確認してから開始**
- **issueの対応開始時に必ずローカル環境で作業ブランチを新規作成してチェックアウトする**
- 実装完了後に適切な単位でコミットメッセージを作成する
- 実装完了後にpull requestを作成する
- **必須対応** pull requestまで作成したら報告すること
- 次のissueに進む場合も実行フェーズを最初から繰り返すこと

生成された仕様

メモ機能追加 - 要件定義

メモ機能追加 - 要件定義

1. 目的の明確化

家計簿アプリにメモ機能を追加し、ユーザーが家計に関する記録や備考を自由に記録できるようにする。

2. 現状分析

既存機能

  • 収支管理(取引記録)
  • 月次集計・分析
  • カテゴリ管理
  • 口座管理
  • テンプレート管理
  • 一括取引登録
  • ユーザー設定

技術スタック

  • フロントエンド: Next.js + shadcn/ui
  • バックエンド: Nest.js + GraphQL
  • データベース: PostgreSQL (Prisma ORM)
  • 認証: ユーザー認証システム完備

3. 機能要件

3.1 基本機能

  • メモ作成: ユーザーが自由にメモを作成できる
  • メモ編集: 既存のメモを編集できる
  • メモ削除: 不要なメモを削除できる
  • メモ一覧表示: 作成日時順でメモ一覧を表示
  • メモ検索: タイトルや内容でメモを検索できる

3.2 メモの属性

  • タイトル: メモのタイトル(必須、最大100文字)
  • 内容: メモの本文(任意、最大5000文字)
  • 作成日時: メモの作成日時(自動設定)
  • 更新日時: メモの最終更新日時(自動更新)
  • ユーザーID: メモの所有者(認証されたユーザー)

3.3 UI要件

  • ホーム画面にメモ機能へのリンクを追加
  • メモ一覧画面(ページネーション対応)
  • メモ作成・編集画面(モーダルまたは別ページ)
  • メモ検索機能
  • レスポンシブデザイン対応

4. 非機能要件

4.1 パフォーマンス

  • メモ一覧の表示は1秒以内
  • メモの作成・編集・削除は2秒以内
  • 検索結果の表示は1秒以内

4.2 セキュリティ

  • ユーザーは自分のメモのみアクセス可能
  • 認証されていないユーザーはアクセス不可
  • 入力値のサニタイゼーション

4.3 ユーザビリティ

  • 直感的な操作感
  • 既存UIとの統一感
  • エラーメッセージの分かりやすさ

5. 制約条件

5.1 技術的制約

  • 既存の技術スタックを維持
  • 既存のデータベーススキーマに影響を与えない
  • 既存の認証システムを活用

5.2 ビジネス制約

  • 既存機能に影響を与えない
  • ユーザー体験の一貫性を保つ
  • 開発工数を最小限に抑える

6. 成功基準

  • ユーザーがメモを自由に作成・編集・削除できる
  • メモの検索が正常に動作する
  • 既存機能に影響がない
  • レスポンシブデザインが適切に動作する
  • パフォーマンス要件を満たす

7. 除外事項

  • メモの共有機能
  • メモのカテゴリ分類
  • メモの添付ファイル機能
  • メモのリマインダー機能
  • メモのエクスポート機能
メモ機能追加 - 設計書

メモ機能追加 - 設計書

1. システム概要

家計簿アプリにメモ機能を追加し、ユーザーが家計に関する記録や備考を自由に記録できるシステムを構築する。

2. アーキテクチャ設計

2.1 全体構成

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Frontend      │    │   Backend       │    │   Database      │
│   (Next.js)     │◄──►│   (Nest.js)     │◄──►│   (PostgreSQL)  │
│                 │    │   + GraphQL     │    │   + Prisma      │
└─────────────────┘    └─────────────────┘    └─────────────────┘

2.2 技術スタック

  • フロントエンド: Next.js 14 + shadcn/ui + Apollo Client
  • バックエンド: Nest.js + GraphQL + Prisma
  • データベース: PostgreSQL
  • 認証: JWT Token

3. データベース設計

3.1 メモテーブル (memos)

CREATE TABLE memos (
  id          VARCHAR(255) PRIMARY KEY DEFAULT (cuid()),
  title       VARCHAR(100) NOT NULL,
  content     TEXT,
  userId      VARCHAR(255) NOT NULL,
  createdAt   TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updatedAt   TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  
  FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE,
  INDEX idx_user_created (userId, createdAt DESC)
);

3.2 Prismaスキーマ

model Memo {
  id        String   @id @default(cuid())
  title     String   @db.VarChar(100)
  content   String?  @db.Text
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  userId String
  user   User   @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@map("memos")
}

3.3 Userモデルへの追加

model User {
  // ... existing fields ...
  memos Memo[]
}

4. バックエンド設計

4.1 ディレクトリ構造

service/src/memo/
├── memo.model.ts          # GraphQLモデル定義
├── memo.input.ts          # GraphQL入力型定義
├── memo.service.ts        # ビジネスロジック
├── memo.resolver.ts       # GraphQLリゾルバー
└── memo.module.ts         # モジュール定義

4.2 GraphQLモデル (memo.model.ts)

import { ObjectType, Field, ID } from '@nestjs/graphql';
import { User } from '../user/user.model';

@ObjectType()
export class Memo {
  @Field(() => ID)
  id: string;

  @Field()
  title: string;

  @Field({ nullable: true })
  content?: string;

  @Field()
  createdAt: Date;

  @Field()
  updatedAt: Date;

  @Field(() => User)
  user: User;

  @Field()
  userId: string;
}

4.3 GraphQL入力型 (memo.input.ts)

import { InputType, Field } from '@nestjs/graphql';

@InputType()
export class CreateMemoInput {
  @Field()
  title: string;

  @Field({ nullable: true })
  content?: string;

  @Field()
  userId: string;
}

@InputType()
export class UpdateMemoInput {
  @Field({ nullable: true })
  title?: string;

  @Field({ nullable: true })
  content?: string;
}

4.4 サービス層 (memo.service.ts)

@Injectable()
export class MemoService {
  constructor(private prisma: PrismaService) {}

  async findAll(userId: string): Promise<Memo[]> {
    return this.prisma.memo.findMany({
      where: { userId },
      orderBy: { createdAt: 'desc' },
      include: { user: true }
    });
  }

  async findOne(id: string): Promise<Memo | null> {
    return this.prisma.memo.findUnique({
      where: { id },
      include: { user: true }
    });
  }

  async create(input: CreateMemoInput): Promise<Memo> {
    return this.prisma.memo.create({
      data: input,
      include: { user: true }
    });
  }

  async update(id: string, input: UpdateMemoInput): Promise<Memo> {
    return this.prisma.memo.update({
      where: { id },
      data: input,
      include: { user: true }
    });
  }

  async remove(id: string): Promise<Memo> {
    return this.prisma.memo.delete({
      where: { id },
      include: { user: true }
    });
  }

  async search(userId: string, query: string): Promise<Memo[]> {
    return this.prisma.memo.findMany({
      where: {
        userId,
        OR: [
          { title: { contains: query, mode: 'insensitive' } },
          { content: { contains: query, mode: 'insensitive' } }
        ]
      },
      orderBy: { createdAt: 'desc' },
      include: { user: true }
    });
  }
}

4.5 GraphQLリゾルバー (memo.resolver.ts)

@Resolver(() => Memo)
export class MemoResolver {
  constructor(private readonly memoService: MemoService) {}

  @Query(() => [Memo])
  async memos(@Args('userId') userId: string) {
    return this.memoService.findAll(userId);
  }

  @Query(() => Memo, { nullable: true })
  async memo(@Args('id') id: string) {
    return this.memoService.findOne(id);
  }

  @Query(() => [Memo])
  async searchMemos(
    @Args('userId') userId: string,
    @Args('query') query: string
  ) {
    return this.memoService.search(userId, query);
  }

  @Mutation(() => Memo)
  async createMemo(@Args('input') input: CreateMemoInput) {
    return this.memoService.create(input);
  }

  @Mutation(() => Memo)
  async updateMemo(@Args('id') id: string, @Args('input') input: UpdateMemoInput) {
    return this.memoService.update(id, input);
  }

  @Mutation(() => Memo)
  async deleteMemo(@Args('id') id: string) {
    return this.memoService.remove(id);
  }
}

5. フロントエンド設計

5.1 ディレクトリ構造

app/src/
├── app/
│   └── memos/
│       ├── page.tsx              # メモ一覧ページ
│       ├── [id]/
│       │   └── page.tsx          # メモ詳細ページ
│       └── new/
│           └── page.tsx          # メモ作成ページ
├── components/
│   ├── MemoEditModal.tsx         # メモ編集モーダル
│   └── MemoCard.tsx              # メモカードコンポーネント
└── lib/
    └── graphql/
        ├── queries/
        │   └── memo.queries.ts   # メモ関連クエリ
        └── mutations/
            └── memo.mutations.ts # メモ関連ミューテーション

5.2 GraphQLクエリ・ミューテーション

// memo.queries.ts
export const GET_MEMOS = gql`
  query GetMemos($userId: String!) {
    memos(userId: $userId) {
      id
      title
      content
      createdAt
      updatedAt
    }
  }
`;

export const GET_MEMO = gql`
  query GetMemo($id: String!) {
    memo(id: $id) {
      id
      title
      content
      createdAt
      updatedAt
    }
  }
`;

export const SEARCH_MEMOS = gql`
  query SearchMemos($userId: String!, $query: String!) {
    searchMemos(userId: $userId, query: $query) {
      id
      title
      content
      createdAt
      updatedAt
    }
  }
`;

// memo.mutations.ts
export const CREATE_MEMO = gql`
  mutation CreateMemo($input: CreateMemoInput!) {
    createMemo(input: $input) {
      id
      title
      content
      createdAt
      updatedAt
    }
  }
`;

export const UPDATE_MEMO = gql`
  mutation UpdateMemo($id: String!, $input: UpdateMemoInput!) {
    updateMemo(id: $id, input: $input) {
      id
      title
      content
      createdAt
      updatedAt
    }
  }
`;

export const DELETE_MEMO = gql`
  mutation DeleteMemo($id: String!) {
    deleteMemo(id: $id) {
      id
    }
  }
`;

5.3 ページ設計

5.3.1 ホーム画面への追加

// page.tsx に追加
<Link href="/memos">
  <Card className="hover:shadow-md transition-shadow cursor-pointer">
    <CardHeader className="flex flex-row items-center space-y-0 pb-2">
      <CardTitle className="text-sm font-medium">メモ</CardTitle>
      <FileText className="h-4 w-4 text-muted-foreground ml-auto" />
    </CardHeader>
    <CardContent>
      <p className="text-xs text-muted-foreground">
        家計に関するメモの記録・管理
      </p>
    </CardContent>
  </Card>
</Link>

5.3.2 メモ一覧ページ (memos/page.tsx)

  • メモ一覧の表示(作成日時順)
  • 検索機能
  • 新規作成ボタン
  • メモカードの表示
  • ページネーション(必要に応じて)

5.3.3 メモ作成ページ (memos/new/page.tsx)

  • タイトル入力(必須、最大100文字)
  • 内容入力(任意、最大5000文字)
  • 保存・キャンセルボタン

5.3.4 メモ詳細ページ (memos/[id]/page.tsx)

  • メモの詳細表示
  • 編集・削除ボタン
  • 編集モーダルの表示

5.4 コンポーネント設計

5.4.1 MemoCardコンポーネント

interface MemoCardProps {
  memo: {
    id: string;
    title: string;
    content?: string;
    createdAt: Date;
    updatedAt: Date;
  };
  onEdit: (id: string) => void;
  onDelete: (id: string) => void;
}

5.4.2 MemoEditModalコンポーネント

interface MemoEditModalProps {
  isOpen: boolean;
  onClose: () => void;
  memo?: {
    id: string;
    title: string;
    content?: string;
  };
  onSave: (data: { title: string; content?: string }) => void;
}

6. セキュリティ設計

6.1 認証・認可

  • JWT Tokenによる認証
  • ユーザーは自分のメモのみアクセス可能
  • 認証されていないユーザーはアクセス不可

6.2 入力値検証

  • タイトル: 必須、最大100文字
  • 内容: 任意、最大5000文字
  • HTMLタグのサニタイゼーション

6.3 データベースセキュリティ

  • 外部キー制約による整合性保証
  • カスケード削除によるデータ整合性

7. パフォーマンス設計

7.1 データベース最適化

  • インデックス: (userId, createdAt DESC)
  • ページネーション対応
  • 検索インデックス(必要に応じて)

7.2 フロントエンド最適化

  • Apollo Client キャッシュ活用
  • 遅延読み込み
  • 検索のデバウンス処理

8. エラーハンドリング

8.1 バックエンド

  • バリデーションエラー
  • データベースエラー
  • 認証エラー

8.2 フロントエンド

  • ネットワークエラー
  • バリデーションエラー
  • ユーザーフレンドリーなエラーメッセージ

9. テスト設計

9.1 バックエンドテスト

  • ユニットテスト(サービス層)
  • 統合テスト(リゾルバー層)
  • E2Eテスト(GraphQL API)

9.2 フロントエンドテスト

  • コンポーネントテスト
  • ページテスト
  • E2Eテスト(ユーザーフロー)

10. デプロイメント設計

10.1 データベースマイグレーション

  • Prismaマイグレーション
  • 既存データへの影響なし

10.2 段階的リリース

  • 機能フラグによる段階的公開
  • ロールバック計画

11. 運用設計

11.1 監視

  • パフォーマンス監視
  • エラー監視
  • ユーザー行動分析

11.2 メンテナンス

  • データベース最適化
  • ログローテーション
  • セキュリティアップデート
メモ機能追加 - 実装計画

メモ機能追加 - 実装計画

1. プロジェクト概要

家計簿アプリにメモ機能を追加するプロジェクトを、アジャイル・スクラム開発手法に基づいて実行可能なユーザーストーリー単位に分解し、優先順位を設定する。

2. スプリント構成

スプリント1: データベース基盤構築

期間: 1日
目標: メモ機能のデータベース基盤を構築

スプリント2: バックエンドAPI実装

期間: 2日
目標: GraphQL APIの実装とテスト

スプリント3: フロントエンド基本機能実装

期間: 2日
目標: メモ一覧・作成・編集・削除の基本機能

スプリント4: 検索機能とUI改善

期間: 1日
目標: 検索機能とユーザビリティの向上

3. ユーザーストーリー

スプリント1: データベース基盤構築

US-001: メモテーブルの作成

As a 開発者
I want to メモデータを保存するためのデータベーステーブルを作成する
So that メモ機能の基盤を構築できる

受け入れ条件:

  • PrismaスキーマにMemoモデルを追加
  • Userモデルにmemosリレーションを追加
  • データベースマイグレーションを実行
  • 既存データに影響がないことを確認

タスク:

  1. Prismaスキーマの更新
  2. データベースマイグレーションの実行
  3. 既存データの整合性確認

見積もり: 2時間


US-002: データベースインデックスの最適化

As a 開発者
I want to メモ検索と一覧表示のパフォーマンスを最適化する
So that ユーザーが快適にメモ機能を利用できる

受け入れ条件:

  • userId + createdAtの複合インデックスを作成
  • 検索パフォーマンスのテスト
  • インデックスサイズの確認

タスク:

  1. インデックス定義の追加
  2. パフォーマンステストの実行
  3. インデックス最適化の確認

見積もり: 1時間


スプリント2: バックエンドAPI実装

US-003: メモモデルと入力型の定義

As a 開発者
I want to GraphQLのモデルと入力型を定義する
So that メモデータの構造を明確にできる

受け入れ条件:

  • Memo GraphQLモデルの実装
  • CreateMemoInput, UpdateMemoInputの実装
  • 型安全性の確保
  • GraphQLスキーマの生成確認

タスク:

  1. memo.model.tsの実装
  2. memo.input.tsの実装
  3. GraphQLスキーマの確認

見積もり: 2時間


US-004: メモサービスの実装

As a 開発者
I want to メモのCRUD操作と検索機能を実装する
So that メモデータの操作ができる

受け入れ条件:

  • メモの作成・読み取り・更新・削除機能
  • ユーザー別のメモ取得機能
  • メモ検索機能(タイトル・内容)
  • エラーハンドリングの実装

タスク:

  1. memo.service.tsの実装
  2. CRUD操作の実装
  3. 検索機能の実装
  4. エラーハンドリングの追加

見積もり: 4時間


US-005: GraphQLリゾルバーの実装

As a 開発者
I want to GraphQL APIエンドポイントを実装する
So that フロントエンドからメモ機能を利用できる

受け入れ条件:

  • メモ一覧取得クエリ
  • メモ詳細取得クエリ
  • メモ検索クエリ
  • メモ作成・更新・削除ミューテーション
  • 認証ガードの適用

タスク:

  1. memo.resolver.tsの実装
  2. クエリ・ミューテーションの実装
  3. 認証ガードの適用
  4. GraphQL Playgroundでのテスト

見積もり: 3時間


US-006: メモモジュールの統合

As a 開発者
I want to メモモジュールをアプリケーションに統合する
So that メモ機能が利用可能になる

受け入れ条件:

  • memo.module.tsの実装
  • app.module.tsへの追加
  • 依存関係の解決
  • アプリケーション起動の確認

タスク:

  1. memo.module.tsの実装
  2. app.module.tsの更新
  3. 依存関係の確認
  4. アプリケーション起動テスト

見積もり: 1時間


スプリント3: フロントエンド基本機能実装

US-007: GraphQLクエリ・ミューテーションの実装

As a フロントエンド開発者
I want to メモ機能用のGraphQLクエリ・ミューテーションを実装する
So that バックエンドAPIと通信できる

受け入れ条件:

  • メモ一覧取得クエリ
  • メモ詳細取得クエリ
  • メモ検索クエリ
  • メモ作成・更新・削除ミューテーション
  • TypeScript型定義の生成

タスク:

  1. memo.queries.tsの実装
  2. memo.mutations.tsの実装
  3. 型定義の確認
  4. Apollo Clientでのテスト

見積もり: 2時間


US-008: メモカードコンポーネントの実装

As a ユーザー
I want to メモ一覧でメモの概要を確認できる
So that メモの内容を素早く把握できる

受け入れ条件:

  • メモタイトルの表示
  • メモ内容のプレビュー(最大100文字)
  • 作成日時の表示
  • 編集・削除ボタン
  • レスポンシブデザイン

タスク:

  1. MemoCard.tsxの実装
  2. スタイリングの適用
  3. レスポンシブデザインの実装
  4. コンポーネントテスト

見積もり: 3時間


US-009: メモ一覧ページの実装

As a ユーザー
I want to メモ一覧を表示して管理できる
So that 作成したメモを確認・操作できる

受け入れ条件:

  • メモ一覧の表示(作成日時順)
  • 新規作成ボタン
  • メモカードの表示
  • ローディング状態の表示
  • エラーハンドリング

タスク:

  1. memos/page.tsxの実装
  2. メモ一覧の表示ロジック
  3. 新規作成ボタンの実装
  4. ローディング・エラー状態の実装

見積もり: 4時間


US-010: メモ作成ページの実装

As a ユーザー
I want to 新しいメモを作成できる
So that 家計に関する記録を保存できる

受け入れ条件:

  • タイトル入力(必須、最大100文字)
  • 内容入力(任意、最大5000文字)
  • バリデーション機能
  • 保存・キャンセルボタン
  • 成功・エラーメッセージ

タスク:

  1. memos/new/page.tsxの実装
  2. フォームの実装
  3. バリデーションの実装
  4. 保存処理の実装

見積もり: 3時間


US-011: メモ編集モーダルの実装

As a ユーザー
I want to 既存のメモを編集できる
So that メモの内容を更新できる

受け入れ条件:

  • モーダルでの編集画面
  • 既存データの表示
  • タイトル・内容の編集
  • 保存・キャンセル機能
  • バリデーション機能

タスク:

  1. MemoEditModal.tsxの実装
  2. 編集フォームの実装
  3. 保存処理の実装
  4. モーダルの制御

見積もり: 3時間


US-012: メモ削除機能の実装

As a ユーザー
I want to 不要なメモを削除できる
So that メモを整理できる

受け入れ条件:

  • 削除確認ダイアログ
  • 削除処理の実行
  • 削除後の一覧更新
  • エラーハンドリング

タスク:

  1. 削除確認ダイアログの実装
  2. 削除処理の実装
  3. 一覧更新の実装
  4. エラーハンドリング

見積もり: 2時間


US-013: ホーム画面へのメモ機能追加

As a ユーザー
I want to ホーム画面からメモ機能にアクセスできる
So that メモ機能を簡単に利用できる

受け入れ条件:

  • メモ機能へのリンクカード
  • 適切なアイコンの表示
  • 既存デザインとの統一
  • レスポンシブデザイン

タスク:

  1. page.tsxの更新
  2. メモカードの追加
  3. アイコンの追加
  4. スタイリングの調整

見積もり: 1時間


スプリント4: 検索機能とUI改善

US-014: メモ検索機能の実装

As a ユーザー
I want to メモを検索できる
So that 特定のメモを素早く見つけられる

受け入れ条件:

  • 検索入力フィールド
  • タイトル・内容での検索
  • リアルタイム検索(デバウンス)
  • 検索結果の表示
  • 検索クリア機能

タスク:

  1. 検索入力フィールドの実装
  2. 検索ロジックの実装
  3. デバウンス処理の実装
  4. 検索結果の表示

見積もり: 3時間


US-015: エラーハンドリングとユーザビリティ改善

As a ユーザー
I want to エラーが発生した時に適切なメッセージが表示される
So that 問題を理解して対処できる

受け入れ条件:

  • ネットワークエラーの表示
  • バリデーションエラーの表示
  • ローディング状態の表示
  • 成功メッセージの表示
  • ユーザーフレンドリーなメッセージ

タスク:

  1. エラーハンドリングの実装
  2. メッセージコンポーネントの実装
  3. ローディング状態の改善
  4. ユーザビリティの向上

見積もり: 2時間


US-016: レスポンシブデザインの最適化

As a ユーザー
I want to モバイルデバイスでも快適にメモ機能を利用できる
So that どこからでもメモを管理できる

受け入れ条件:

  • モバイル画面での表示最適化
  • タッチ操作の最適化
  • フォントサイズの調整
  • レイアウトの調整

タスク:

  1. モバイル表示の確認
  2. レスポンシブデザインの調整
  3. タッチ操作の最適化
  4. ユーザビリティテスト

見積もり: 2時間


4. 優先順位

高優先度(MVP)

  1. US-001: メモテーブルの作成
  2. US-003: メモモデルと入力型の定義
  3. US-004: メモサービスの実装
  4. US-005: GraphQLリゾルバーの実装
  5. US-006: メモモジュールの統合
  6. US-007: GraphQLクエリ・ミューテーションの実装
  7. US-008: メモカードコンポーネントの実装
  8. US-009: メモ一覧ページの実装
  9. US-010: メモ作成ページの実装
  10. US-011: メモ編集モーダルの実装
  11. US-012: メモ削除機能の実装
  12. US-013: ホーム画面へのメモ機能追加

中優先度(機能拡張)

  1. US-002: データベースインデックスの最適化
  2. US-014: メモ検索機能の実装

低優先度(品質向上)

  1. US-015: エラーハンドリングとユーザビリティ改善
  2. US-016: レスポンシブデザインの最適化

5. リスク管理

技術的リスク

  • データベースマイグレーションの失敗: 既存データのバックアップを取得
  • GraphQLスキーマの競合: 段階的な実装とテスト
  • パフォーマンス問題: インデックスの最適化とテスト

スケジュールリスク

  • 実装時間の超過: バッファ時間の確保
  • テスト時間の不足: 自動テストの導入検討

6. 成功基準

機能要件

  • メモの作成・編集・削除が正常に動作する
  • メモ一覧が作成日時順で表示される
  • 検索機能が正常に動作する
  • 既存機能に影響がない

非機能要件

  • メモ一覧の表示が1秒以内
  • メモのCRUD操作が2秒以内
  • レスポンシブデザインが適切に動作する
  • エラーハンドリングが適切に動作する

7. 完了定義

スプリント完了条件

  • 全てのユーザーストーリーが完了している
  • 受け入れ条件が満たされている
  • テストが通っている
  • コードレビューが完了している

プロジェクト完了条件

  • 全てのスプリントが完了している
  • 本番環境での動作確認が完了している
  • ドキュメントが更新されている
  • ユーザー受け入れテストが完了している

2. GitHub CLIのインストール

GitHubとの連携にはghコマンド(GitHub CLI)が必要です。公式ドキュメント:
https://github.com/cli/cli#installation

macOS(Homebrew)

brew update
brew install gh
# 既に入っている場合のアップグレード
brew upgrade gh

アンインストール:

brew uninstall gh

Windows(WSL: Ubuntu/Debian系)

WSL上で以下を実行します。詳細手順:
https://github.com/cli/cli/blob/trunk/docs/install_linux.md#debian

sudo apt update
sudo apt install gh

アンインストール:

sudo apt remove gh    # 設定も消す場合は apt purge gh

その他Linux(例: Fedora/CentOS/RHEL)

ディストリビューションのパッケージマネージャでインストールできます。

# Fedora / RHEL系(dnf)
sudo dnf install gh || sudo yum install gh

初期設定(はじめて使う場合)

  1. バージョン確認:
gh --version
  1. GitHubへログイン:ブラウザでのWebフローが簡単です。
gh auth login --web --hostname github.com

ガイドに従って、GitHub.com → HTTPS → ブラウザ認証を選択します(2FAも対応)。

  1. 認証状態の確認:
gh auth status
  1. 便利な初期設定(任意):
# `gh`から作るGitリモートのデフォルトをHTTPSに
gh config set git_protocol https
  1. 動作確認:
# 自分のリポジトリ一覧(Public/Privateは権限による)
gh repo list --limit 5

# Issue一覧(GitHubリポジトリ直下で)
gh issue status || true

参考記事

https://qiita.com/kikutch/items/188462fdda893a520d9e

3. 実際の使用例

家計簿アプリにメモ機能を追加する例で、スペック駆動開発を試してみました。

プロンプト例

今回は、実装までは行わず家計簿アプリにメモ機能を要件にプロンプトを投げました。

メモ機能を追加したい

実行結果

計画フェーズからissue作成フェーズまで行うと、リポジトリのissueに仕様で作成したタスクが自動的にissueを作成されます。

Cursorでの実行画面

  1. 要件定義フェーズ:メモ機能の要件を明確化
  2. 設計フェーズ:UI/UXとデータ構造を設計
  3. 実装計画フェーズ:タスクをユーザーストーリー単位に分解
  4. issue作成フェーズ:GitHubプロジェクトとissueを自動作成
  5. 実行フェーズ:実装とプルリクエスト作成

以下は、gh コマンド issue の listを一覧で出して表示した結果です。

GitHubプロジェクト作成

実行コマンド(リポジトリ直下で実行):

gh issue list --limit 10 --state all

メリット

  • 構造化された開発プロセス:5段階のフローで開発を体系化
  • 自動化されたドキュメント生成:要件定義から実装計画まで自動で文書化
  • GitHub連携:issueとプルリクエストの自動作成
  • 再現性:同じフローで複数のプロジェクトを管理可能
  • 仕様ソースコード上に上がってるためDevinやClaude Codeも参照できる

デメリット

  • issueとspec上で生成されたタスクの二重管理になる
    • 常に更新するときは、specのimplementations.mdとissueを同時にAIに編集して貰う必要がある
  • 仕様書やタスクのファイルが共有しているため、ファイルが肥大化する可能性あり

Notion MCPを使う

最近のNotionのアップデートで、リモートMCPが提供され、ブラウザのOAuthのみでセットアップできるようになりました。

Cursor公式 MCP サイト

https://docs.cursor.com/en/tools/mcp

Notion MCPを使うと、プロンプトから指定したデータベースにタスクを追加できます。

Cursorでの設定手順

  1. Cursor MCP Directory にアクセス
  2. Notion を追加 → 指示に従いOAuthでログイン
  3. Cursorの Integrations から Notion を有効化
  4. MCPで読み込むチームスペース/ページを選択(権限のある範囲のみ)

Notion向けのSpec例(Cursor Rules)

Notion向けのSpecファイル(Rules)
---
alwaysApply: false
---

# タスク実行の5段階フロー

## 実行ルール 

### ファイル操作
- 新規タスク開始時: 既存ファイル、既存issueの**内容を全て削除して白紙から書き直す** # 既存ファイルが存在する状態で再実行する場合に必要
- ファイル編集前に必ず現在の内容を確認

### フェーズ管理
- 各段階開始時: 「前段階のmdファイルを読み込みました」と報告
- 各段階の最後に、期待通りの結果になっているか確認
- `.cursor_workflow/specs/{指定したディレクトリ名}/requirements.md`が存在しない場合は必ず要件定義フェーズから始める

### 実行方針
- 段階的に進める: 一度に全てを変更せず、小さな変更を積み重ねること
- 複数のタスクを同時並行で進めないこと
- エラーが発生した場合は、報告すること
- エラーを解決してから次へ進むこと
- ユーザからの指示に無い機能を勝手に追加しないこと

## 1. 要件定義フェーズ
- ユーザからの指示に対して **「何を作るのか」を明確に定める。**
- 目的の明確化、現状分析、機能要件の定義、非機能要件の定義、制約条件の定義を実行する
- `.cursor_workflow/specs/{指定したディレクトリ名}/requirements.md`に文書化
- **必須確認**: 「要件定義フェーズが完了しました。設計フェーズに進んでよろしいですか?」

### 2. 設計フェーズ
- **必ず`.cursor_workflow/specs/{指定したディレクトリ名}/requirements.md`を読み込んでから開始すること**
- 要件定義フェーズで決定したことをもとに **「どのようにつくるか」「内部でどのように処理するか」を設計する**
- プログラマーが実際にコーディングできるレベルまで詳細化すること
- `.cursor_workflow/specs/{指定したディレクトリ名}/design.md`に文書化
- **必須確認**: 「設計フェーズが完了しました。実装計画フェーズに進んでよろしいですか?」

### 3. 実装計画フェーズ
- **必ず`.cursor_workflow/specs/{指定したディレクトリ名}/design.md`を読み込んでから開始すること**
- アジャイル・スクラム開発をすることを前提とする
- タスクを実行可能なユーザーストーリー単位に分解、優先順位を設定する
- `.cursor_workflow/specs/{指定したディレクトリ名}/implementations.md`に文書化
- **必須確認**: 「実装計画フェーズが完了しました。issue作成フェーズに進んでよろしいですか?」

### 4. issue作成フェーズ
- **必ず`.cursor_workflow/specs/{指定したディレクトリ名}/implementations.md`を読み込んでから開始すること**
- Notion MCP を使って、implementations.md に定義したタスクをデータベース(例: Issue Tables)へ追加する
- タスク管理ページのデータベースに各タスクを登録する
- **必須確認**: 「issue作成フェーズが完了しました。実行フェーズに進んでよろしいですか?」

### 5. 実行フェーズ
- **必ずNotionの タスク管理ページ Issue Tablesの一覧を確認してから開始**
- **必ず対応するIssue Tablesの詳細を確認してから開始**
- **Issue Tablesの対応開始時に必ずローカル環境で作業ブランチを新規作成してチェックアウトする**
- 実装完了後に適切な単位でコミットメッセージを作成する
- 実装完了後にpull requestを作成する
- **必須対応** pull requestまで作成したら報告すること
- 次のissueに進む場合も実行フェーズを最初から繰り返すこと

::

生成されたタスク一覧

生成されたタスクがNotionにも反映されていることを確認できました。

Notionで指定したSpecを @spec-notion を追加して、GitHubのissueと同様に進めます。
フェーズごとに要件定義からタスク起票までを行うことができます。

いままでのNotion MCPはオープンソースのMCPサーバーでしたが、リモート版のMCPサーバーがリリースされました。
公式のサイトにもあるように、OAuthでユーザーレベルの認証が可能になったことで簡単に扱えるようになりました。

また、動作も従来よりも早く・軽いため個人のタスク管理として活用できるMCPとなっていました。
雑に始めたところもあって暴走気味になったので、トライアンドエラーで工夫は必要でそうです。

参考記事

https://notion.notion.site/Notion-MCP-1d0efdeead058054a339ffe6b38649e1
https://info.nextmode.co.jp/blog/notion-mcp-to-cursor

よくあるつまずき
  • Notion側の権限不足でデータベースが表示されない → ワークスペース/ページの権限を確認
  • MCPに読み込ませるページの選択漏れ → Integrations で対象ページを追加
  • データベースのプロパティ名不一致 → Spec側の項目名とNotion側のカラム名を合わせる

Jiraとの連携も可能

個人のプロジェクトでJiraは使っていないですが、仕事ではJiraを使う頻度は高いためCursor から Jiraの連携もできるため活用できそうです。
どのように運用するのかどのような形のRulesが良いか検討が必要ですが、つなげてみて活用方法のガードレールは作っていきたいです。

Jiraと連携方法は別途記事にしたいと思いました。

テスト設計にも活用できそう

今回は試していないですが、AI伴走型テスト設計という形も面白いと感じたため、プロダクトで活用していきたいと感じました。

https://zenn.dev/globis/articles/24cc3f0ded2987

まとめ

CursorとGitHub CLIを組み合わせることで、Kiroのようなスペック駆動開発フローを実現できました。
また、他のタスク管理ツールだったりも組みわせるときは、公式が提供しているMCPを活用することで利用してる管理ツールでワークフローに組み込むことができます。

特に、AIアシスタントに構造化された指示を出すことで、一貫性のある開発プロセスを維持できる点が大きなメリットです。
より仕様をベースに構築できることとエビデンスとして残していくことで、過去の経緯ややったことを参照できるのは個人に良いと感じました。

今後は、より複雑なプロジェクトでもこのフローを適用してナレッジをためていき、開発効率の向上を図っていきたいと感じました。

Discussion