From 327963d9ddd51104fce5fa51c9156854fc03d970 Mon Sep 17 00:00:00 2001 From: kou Date: Wed, 25 Mar 2026 11:00:40 +0900 Subject: [PATCH] =?UTF-8?q?MVP=E6=98=A8=E6=97=A5=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .task/tasks.json | 69 ++++ .task/tasks.json.bak | 62 +++ docs/architecture.md | 215 ++++++++++ docs/development-guidelines.md | 515 ++++++++++++++++++++++++ docs/functional-design.md | 581 +++++++++++++++++++++++++++ docs/glossary.md | 452 +++++++++++++++++++++ docs/product-requirements.md | 314 +++++++++++++++ docs/repository-structure.md | 371 +++++++++++++++++ output.jsonl | 561 ++++++++++++++++++++++++++ package-lock.json | 173 ++++++-- package.json | 20 +- src/cli/commands/add.ts | 53 +++ src/cli/commands/delete.ts | 137 +++++++ src/cli/commands/done.ts | 149 +++++++ src/cli/commands/list.ts | 110 +++++ src/cli/commands/show.ts | 58 +++ src/cli/commands/start.ts | 123 ++++++ src/cli/formatters/ErrorFormatter.ts | 190 +++++++++ src/cli/formatters/TaskFormatter.ts | 234 +++++++++++ src/cli/index.ts | 148 +++++++ src/services/GitService.ts | 166 ++++++++ src/services/TaskService.ts | 243 +++++++++++ src/storage/FileStorage.ts | 213 ++++++++++ src/types/Config.ts | 42 ++ src/types/Task.ts | 98 +++++ src/types/errors.ts | 59 +++ tsconfig.json | 2 +- 27 files changed, 5321 insertions(+), 37 deletions(-) create mode 100644 .task/tasks.json create mode 100644 .task/tasks.json.bak create mode 100644 docs/architecture.md create mode 100644 docs/development-guidelines.md create mode 100644 docs/functional-design.md create mode 100644 docs/glossary.md create mode 100644 docs/product-requirements.md create mode 100644 docs/repository-structure.md create mode 100644 output.jsonl create mode 100644 src/cli/commands/add.ts create mode 100644 src/cli/commands/delete.ts create mode 100644 src/cli/commands/done.ts create mode 100644 src/cli/commands/list.ts create mode 100644 src/cli/commands/show.ts create mode 100644 src/cli/commands/start.ts create mode 100644 src/cli/formatters/ErrorFormatter.ts create mode 100644 src/cli/formatters/TaskFormatter.ts create mode 100644 src/cli/index.ts create mode 100644 src/services/GitService.ts create mode 100644 src/services/TaskService.ts create mode 100644 src/storage/FileStorage.ts create mode 100644 src/types/Config.ts create mode 100644 src/types/Task.ts create mode 100644 src/types/errors.ts diff --git a/.task/tasks.json b/.task/tasks.json new file mode 100644 index 0000000..622ec7d --- /dev/null +++ b/.task/tasks.json @@ -0,0 +1,69 @@ +[ + { + "id": 1, + "title": "テスト用タスク", + "status": "completed", + "createdAt": "2026-03-25T01:35:05.059Z", + "updatedAt": "2026-03-25T01:36:44.220Z", + "statusHistory": [ + { + "from": "open", + "to": "in_progress", + "changedAt": "2026-03-25T01:36:02.973Z" + }, + { + "from": "in_progress", + "to": "completed", + "changedAt": "2026-03-25T01:36:44.220Z" + } + ], + "branchName": "feature/task-1-no-title" + }, + { + "id": 2, + "title": "Git環境なしテスト", + "status": "in_progress", + "createdAt": "2026-03-25T01:36:25.215Z", + "updatedAt": "2026-03-25T01:36:28.659Z", + "statusHistory": [ + { + "from": "open", + "to": "in_progress", + "changedAt": "2026-03-25T01:36:28.659Z" + } + ], + "branchName": "feature/task-2-git" + }, + { + "id": 3, + "title": "警告テスト用タスク", + "status": "open", + "createdAt": "2026-03-25T01:36:47.965Z", + "updatedAt": "2026-03-25T01:36:47.965Z", + "statusHistory": [] + }, + { + "id": 4, + "title": "テスト用タスク", + "status": "open", + "createdAt": "2026-03-25T01:38:08.014Z", + "updatedAt": "2026-03-25T01:38:08.014Z", + "statusHistory": [] + }, + { + "id": 5, + "title": "TaskCLI P1機能の実装", + "description": "GitHub Issues連携・検索・優先度管理を実装する", + "status": "in_progress", + "createdAt": "2026-03-25T01:54:14.616Z", + "updatedAt": "2026-03-25T01:54:21.778Z", + "statusHistory": [ + { + "from": "open", + "to": "in_progress", + "changedAt": "2026-03-25T01:54:21.778Z" + } + ], + "branchName": "feature/task-5-taskcli-p1" + } +] \ No newline at end of file diff --git a/.task/tasks.json.bak b/.task/tasks.json.bak new file mode 100644 index 0000000..3eaac58 --- /dev/null +++ b/.task/tasks.json.bak @@ -0,0 +1,62 @@ +[ + { + "id": 1, + "title": "テスト用タスク", + "status": "completed", + "createdAt": "2026-03-25T01:35:05.059Z", + "updatedAt": "2026-03-25T01:36:44.220Z", + "statusHistory": [ + { + "from": "open", + "to": "in_progress", + "changedAt": "2026-03-25T01:36:02.973Z" + }, + { + "from": "in_progress", + "to": "completed", + "changedAt": "2026-03-25T01:36:44.220Z" + } + ], + "branchName": "feature/task-1-no-title" + }, + { + "id": 2, + "title": "Git環境なしテスト", + "status": "in_progress", + "createdAt": "2026-03-25T01:36:25.215Z", + "updatedAt": "2026-03-25T01:36:28.659Z", + "statusHistory": [ + { + "from": "open", + "to": "in_progress", + "changedAt": "2026-03-25T01:36:28.659Z" + } + ], + "branchName": "feature/task-2-git" + }, + { + "id": 3, + "title": "警告テスト用タスク", + "status": "open", + "createdAt": "2026-03-25T01:36:47.965Z", + "updatedAt": "2026-03-25T01:36:47.965Z", + "statusHistory": [] + }, + { + "id": 4, + "title": "テスト用タスク", + "status": "open", + "createdAt": "2026-03-25T01:38:08.014Z", + "updatedAt": "2026-03-25T01:38:08.014Z", + "statusHistory": [] + }, + { + "id": 5, + "title": "TaskCLI P1機能の実装", + "description": "GitHub Issues連携・検索・優先度管理を実装する", + "status": "open", + "createdAt": "2026-03-25T01:54:14.616Z", + "updatedAt": "2026-03-25T01:54:14.616Z", + "statusHistory": [] + } +] \ No newline at end of file diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..8c2ea54 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,215 @@ +# 技術仕様書 (Architecture Design Document) + +## テクノロジースタック + +### 言語・ランタイム + +| 技術 | バージョン | 理由 | +|------|-----------|------| +| Node.js | v18+ (推奨: v24.11.0) | 非同期I/Oに優れた実績あるランタイム。npm v24.11.0はdevcontainerに標準搭載済み | +| TypeScript | 5.x | 静的型付けによるコンパイル時バグ検出と IDE 補完で保守性を向上 | +| npm | 11.x | Node.js v24.11.0 に同梱。`package-lock.json` により依存関係を厳密に管理 | + +### フレームワーク・ライブラリ(本番依存) + +| 技術 | バージョン | 用途 | 選定理由 | +|------|-----------|------|----------| +| commander | ^11.0.0 | CLIコマンドパース | 学習コストが低く機能が十分。npmダウンロード数トップクラスで実績あり | +| chalk | ^5.3.0 | ターミナルカラー出力 | ESM対応の最新版。シンプルなAPIで色・スタイル指定が直感的 | +| cli-table3 | ^0.6.3 | テーブル表示 | CLIでの表形式出力に特化したシンプルなライブラリ | +| simple-git | ^3.22.0 | Git操作 | Node.jsからGitを操作する事実上の標準ライブラリ。型定義が充実 | +| @octokit/rest | ^20.0.0 | GitHub API | GitHubの公式Node.jsクライアント。TypeScript対応 | + +### 開発ツール + +| 技術 | バージョン | 用途 | 選定理由 | +|------|-----------|------|----------| +| vitest | ^1.0.0 | テストフレームワーク | Viteベースで高速。TypeScriptをネイティブサポート | +| eslint | ^9.0.0 | 静的解析 | TypeScript対応のlinter標準 | +| prettier | ^3.0.0 | コードフォーマット | opinionatedなフォーマッターで議論を排除 | +| tsx | ^4.0.0 | TypeScript実行(開発時) | `ts-node` より高速なTypeScript実行環境 | +| @types/node | ^20.0.0 | Node.js型定義 | TypeScriptからNode.js APIを型安全に利用 | + +--- + +## アーキテクチャパターン + +### レイヤードアーキテクチャ + +``` +┌───────────────────────────────────────┐ +│ CLIレイヤー(src/cli/) │ ← コマンドパース・入力検証・表示 +│ Commander.js によるコマンドルーティング │ +├───────────────────────────────────────┤ +│ サービスレイヤー(src/services/) │ ← ビジネスロジック +│ TaskService / GitService / │ +│ GitHubService │ +├───────────────────────────────────────┤ +│ データレイヤー(src/storage/) │ ← データ永続化 +│ FileStorage(JSON読み書き) │ +└───────────────────────────────────────┘ + ↕ +┌───────────────────────────────────────┐ +│ 外部システム │ +│ Git(simple-git)/ GitHub API │ +│ (@octokit/rest) │ +└───────────────────────────────────────┘ +``` + +#### CLIレイヤー(`src/cli/`) + +- **責務**: ユーザー入力の受付、バリデーション、結果の表示 +- **許可される操作**: サービスレイヤーの呼び出し +- **禁止される操作**: データレイヤー(FileStorage)への直接アクセス + +#### サービスレイヤー(`src/services/`) + +- **責務**: ビジネスロジックの実装、Gitブランチ管理、GitHub API呼び出し +- **許可される操作**: データレイヤーの呼び出し、外部APIの呼び出し +- **禁止される操作**: CLIレイヤーへの依存、console.logによる直接出力 + +#### データレイヤー(`src/storage/`) + +- **責務**: データの永続化(`.task/tasks.json` / `.task/config.json`)、バックアップ +- **許可される操作**: ファイルシステムへのアクセス +- **禁止される操作**: ビジネスロジックの実装 + +--- + +## データ永続化戦略 + +### ストレージ方式 + +| データ種別 | ストレージ | フォーマット | 理由 | +|-----------|----------|-------------|------| +| タスクデータ | ローカルファイル(`.task/tasks.json`) | JSON | 特別なソフト不要・Gitで管理可能・可読性が高い | +| アプリ設定 | ローカルファイル(`.task/config.json`) | JSON | 同上。GitHub設定などをプロジェクト単位で管理 | +| エラーログ | ローカルファイル(`.task/error.log`) | テキスト | デバッグ用の追記ログ | + +**MVP後の移行パス**: タスク数が1,000件を超えた場合、またはチーム機能を実装する段階でSQLite(`better-sqlite3`)への移行を検討。データモデルは移行を考慮した設計とする。 + +### バックアップ戦略 + +- **タイミング**: `saveTasks()` 呼び出し時(書き込み前に毎回) +- **保存先**: `.task/tasks.json.bak`(直前の1世代のみ) +- **復元方法**: `tasks.json` の読み込みに失敗した際、`.bak` から自動復元を試みる +- **手動復元**: `cp .task/tasks.json.bak .task/tasks.json` + +--- + +## パフォーマンス要件 + +### レスポンスタイム + +| 操作 | 目標時間 | 測定環境 | +|------|---------|---------| +| `task add` / `task done` など基本コマンド | 100ms以内 | CPU Core i5相当、メモリ8GB、SSD | +| `task list`(1,000件) | 1秒以内 | 同上 | +| `task sync`(GitHub API) | 5秒以内(タイムアウト設定) | ネットワーク込み | + +**測定方法**: `console.time` でCLI起動から結果表示まで計測。CIのE2Eテストで1,000件のダミーデータを使って自動計測。 + +### リソース使用量 + +| リソース | 上限 | 理由 | +|---------|------|------| +| メモリ | 128MB | タスク1,000件のJSONを全件ロードしても十分な余裕 | +| 起動時メモリ | 50MB | Node.js最小構成での起動コスト | +| ディスク(`.task/`) | 10MB | タスク10,000件 × 平均1KB = 10MB | + +--- + +## セキュリティアーキテクチャ + +### データ保護 + +- **ファイルパーミッション**: `.task/config.json` 作成時に `chmod 600` を適用(所有者のみ読み書き可) +- **機密情報管理**: GitHub Personal Access Token は `GITHUB_TOKEN` 環境変数から取得。`config.json` には保存しない。`.task/` ディレクトリを `.gitignore` に追加するよう `task init` 実行時に案内する + +### 入力検証 + +- **バリデーション**: CLIレイヤーで実施 + - タイトル: 1文字以上200文字以内 + - タスクID: 正の整数 + - 期限: `YYYY-MM-DD` 形式、過去日付は警告(エラーではない) + - 優先度: `high` / `medium` / `low` のいずれか +- **サニタイゼーション**: ブランチ名に使用するslugから英数字とハイフン以外の文字を除去(パスインジェクション対策) +- **エラーハンドリング**: スタックトレースはエラーログに記録し、ユーザーには原因と解決策のみ表示 + +--- + +## スケーラビリティ設計 + +### データ増加への対応 + +- **想定データ量(MVP)**: タスク1,000件以内(JSONファイルで十分対応可能) +- **パフォーマンス劣化対策**: `task list` の表示件数をデフォルト50件に制限し、`--all` フラグで全件表示 +- **アーカイブ戦略**: `status: archived` のタスクは `task list` からデフォルト除外。将来的に `.task/archive.json` へ移動する機能を追加 + +### 機能拡張性 + +- **設定のカスタマイズ**: `config.json` への設定追加でプラグイン的な拡張を可能にする設計(例: カスタムブランチプレフィックス、デフォルト優先度) +- **API拡張性**: GitService / GitHubService を抽象化し、将来的なGitLab・Bitbucket対応を考慮したインターフェース設計 + +--- + +## テスト戦略 + +### ユニットテスト(vitest) + +- **フレームワーク**: vitest +- **対象**: `TaskService`・`GitService`・`FileStorage` の各メソッド +- **カバレッジ目標**: 80%以上 +- **方針**: GitService のGit操作は `simple-git` をモックし、ファイルI/Oは一時ディレクトリを使用 + +### 統合テスト + +- **方法**: 実際の一時ディレクトリに対してFileStorageを通じたE2Eフロー確認 +- **対象**: `task add` → `task start` → `task done` の一連のフロー + +### E2Eテスト + +- **ツール**: vitest + `child_process.exec` +- **シナリオ**: CLIコマンドを実際に実行してstdout / stderrを検証 +- **テスト用データ**: `.task-test/` ディレクトリを使用して本番データと分離(テスト後に削除) + +--- + +## 技術的制約 + +### 環境要件 + +- **OS**: macOS・Linux・Windows(Git Bash) +- **Node.js**: v18以上(LTS推奨: v24.11.0) +- **Git**: v2.x以上(Gitブランチ連携を使用する場合) +- **必要な外部依存**: インターネット接続(GitHub API連携時のみ) + +### パフォーマンス制約 + +- JSONファイル全件ロード方式のため、タスク数が10,000件を超えると `task list` が1秒超になる可能性がある(その段階でSQLite移行を推奨) +- GitHub API のレート制限: 認証ありで5,000リクエスト/時間 + +### セキュリティ制約 + +- GitHub Token の保存はファイルシステムの環境変数のみ(設定ファイルへの永続化は行わない) +- `.task/` ディレクトリをGit管理下に置く場合は機密情報(token等)が含まれないことをユーザーが確認する + +--- + +## 依存関係管理 + +| ライブラリ | 用途 | バージョン管理方針 | +|-----------|------|-------------------| +| commander | CLIパース | `^11.0.0`(マイナーバージョン自動) | +| chalk | カラー出力 | `^5.3.0`(ESM版。メジャー固定) | +| cli-table3 | テーブル表示 | `^0.6.3`(マイナーバージョン自動) | +| simple-git | Git操作 | `^3.22.0`(マイナーバージョン自動) | +| @octokit/rest | GitHub API | `^20.0.0`(マイナーバージョン自動) | +| vitest | テスト | `^1.0.0`(開発依存・マイナー自動) | +| typescript | 型チェック | `~5.3.0`(devDep・パッチのみ自動) | +| eslint | 静的解析 | `^9.0.0`(devDep・マイナー自動) | + +**方針**: +- 本番依存は `^`(マイナーバージョンアップ自動)を基本とし、`package-lock.json` で実際のバージョンを固定 +- メジャーバージョンアップは手動で検証してから更新 +- `npm audit` をCIで実行し、脆弱性を定期検知 diff --git a/docs/development-guidelines.md b/docs/development-guidelines.md new file mode 100644 index 0000000..68ff43c --- /dev/null +++ b/docs/development-guidelines.md @@ -0,0 +1,515 @@ +# 開発ガイドライン (Development Guidelines) + +## コーディング規約 + +### 命名規則 + +#### 変数・関数 + +```typescript +// ✅ 良い例 +const taskList = loadTasks(); +const isGitRepository = await gitService.isGitRepository(); +function generateBranchName(taskId: number, title: string): string { } + +// ❌ 悪い例 +const data = load(); +const flag = await git.check(); +function gen(id: number, t: string): string { } +``` + +**原則**: +- 変数・プロパティ: camelCase、名詞または名詞句 +- 関数・メソッド: camelCase、動詞で始める +- 定数: UPPER_SNAKE_CASE(例: `DEFAULT_STORAGE_DIR = '.task'`) +- Boolean変数: `is`、`has`、`should`、`can` で始める + +#### クラス・インターフェース + +```typescript +// クラス: PascalCase + 役割接尾辞 +class TaskService { } +class GitService { } +class FileStorage { } + +// インターフェース・型エイリアス: PascalCase(I接頭辞なし) +interface Task { } +interface CreateTaskInput { } +type TaskStatus = 'open' | 'in_progress' | 'completed' | 'archived'; +type TaskPriority = 'high' | 'medium' | 'low'; +``` + +--- + +### コードフォーマット + +- **インデント**: 2スペース(Prettierで自動整形) +- **行の長さ**: 最大100文字 +- **セミコロン**: あり(TypeScript標準) +- **引用符**: シングルクォート(`'`) + +フォーマットはPrettierが自動適用するため、手動整形は不要です。 + +--- + +### コメント規約 + +**公開メソッド・クラスにはTSDocを記述**: + +```typescript +/** + * タスクのステータスを変更し、履歴を記録する + * + * @param id - 変更対象のタスクID + * @param newStatus - 変更後のステータス + * @returns 更新されたタスク + * @throws {NotFoundError} 指定されたIDのタスクが存在しない場合 + */ +changeStatus(id: number, newStatus: TaskStatus): Task { } +``` + +**インラインコメントは「なぜ」を説明する**: + +```typescript +// ✅ 良い例: 理由を説明 +// 書き込み前にバックアップを作成し、中断時のデータ損失を防ぐ +this.backupTasks(); +fs.writeFileSync(this.tasksPath, JSON.stringify(data, null, 2)); + +// ❌ 悪い例: 何をしているか(コードを見れば分かる) +// バックアップを作成する +this.backupTasks(); +``` + +--- + +### エラーハンドリング + +**プロジェクト固有のカスタムエラークラスを使用**: + +```typescript +// src/types/errors.ts +class TaskNotFoundError extends Error { + constructor(public taskId: number) { + super(`タスク #${taskId} が見つかりません`); + this.name = 'TaskNotFoundError'; + } +} + +class ValidationError extends Error { + constructor( + message: string, + public field: string, + public value: unknown + ) { + super(message); + this.name = 'ValidationError'; + } +} + +class GitOperationError extends Error { + constructor(message: string, public cause?: Error) { + super(message); + this.name = 'GitOperationError'; + } +} +``` + +**CLIレイヤーでエラーをキャッチし、ユーザーフレンドリーなメッセージに変換**: + +```typescript +// src/cli/commands/start.ts +try { + const task = taskService.getTask(id); + await gitService.createAndCheckoutBranch(branchName); +} catch (error) { + if (error instanceof TaskNotFoundError) { + console.error(`エラー: タスク #${id} が見つかりません`); + process.exit(1); + } + if (error instanceof GitOperationError) { + // Git失敗でもタスク操作は完了させる + console.warn(`警告: ブランチ作成に失敗しました。タスクのステータスのみ更新します`); + } else { + // 予期しないエラーはログに記録して上位へ + logger.error(error); + throw error; + } +} +``` + +--- + +### 型定義のルール + +**`any` の使用禁止**: + +```typescript +// ❌ 悪い例 +function parseData(data: any): any { } + +// ✅ 良い例 +function parseTaskData(data: unknown): Task { + if (!isValidTaskData(data)) { + throw new ValidationError('無効なタスクデータ', 'data', data); + } + return data as Task; +} +``` + +**オプショナルフィールドには `?` を使用**(`| undefined` より簡潔): + +```typescript +interface Task { + id: number; + title: string; + description?: string; // ✅ オプショナル + dueDate?: string; // ✅ オプショナル +} +``` + +--- + +## Git運用ルール + +### ブランチ戦略(Git Flow) + +``` +main(本番環境・安定版) +└── develop(開発統合ブランチ) + ├── feature/[機能名] ← 新機能開発 + ├── fix/[修正内容] ← バグ修正 + └── refactor/[対象] ← リファクタリング +``` + +**運用ルール**: +- `main`: タグでバージョン管理。直接コミット禁止 +- `develop`: CIが自動テストを実行。直接コミット禁止 +- `feature/*` / `fix/*`: `develop` から分岐し、PRでマージ +- **マージ方針**: `feature` → `develop` は squash merge、`develop` → `main` は merge commit + +**TaskCLI専用ブランチ(Git連携機能で自動作成)**: +- `feature/task-{id}-{slug}`: `task start ` で自動生成 + +--- + +### コミットメッセージ規約(Conventional Commits) + +**フォーマット**: +``` +(): + +(任意) + +