aizuchi 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,147 @@
1
+ # 変更履歴 (Changelog)
2
+
3
+ このファイルの記法は [Keep a Changelog](https://keepachangelog.com/ja/1.1.0/) に、
4
+ バージョン番号は [セマンティックバージョニング](https://semver.org/lang/ja/) に従います。
5
+
6
+ ## [Unreleased]
7
+
8
+ ## [0.4.0] - 2026-06-16
9
+
10
+ 「今どうなっているか・次に何をすべきか」の見通しと、堅牢性・操作性を高めるリリース。
11
+
12
+ ### Added (追加)
13
+
14
+ - **ホーム「次の一手」ビュー**(既定の最初のタブ): 判断待ち・停滞・クリティカルパスの律速・WIP 超過・新着を
15
+ 1 画面に集約し、開いた瞬間に「今プロジェクトを止めているもの」「次に着手すべきタスク」が分かる。
16
+ 既存の集計ロジックを束ねるだけで、新しい保存先や通信は増やさない。タブの並べ替え・非表示で従来どおり
17
+ ボードを最初のタブに戻せる。
18
+ - **タスク詳細のタイムラインタブ**: 作業ログ・やりとり・コミットを 1 本の時系列にまとめて表示し、
19
+ 「このタスクで何がいつ起きたか」を一目で追える。
20
+ - **やりとり / 作業ログ / タイムラインの `@member` ハイライト**: 登録メンバー宛のメンションを強調表示。
21
+ やりとりタブでは投稿者(=あなた)宛のメンションをさらに強調する。
22
+ - **AI 用 CLI に読取系コマンドを追加**(read-only): `aizuchi task list`(一覧・`--status` / `--assignee` 絞り込み)、
23
+ `task show <id>`(1 タスクの詳細)、`task next`(依存が解決済みで着手できるタスクをクリティカルパス優先で提示)。
24
+ AI が「今の状態」「次の一手」をファイル経由で引ける。いずれも `--json` 対応。
25
+ - **キーボードショートカットのヘルプ**: `?` キーまたはヘッダーの「?」ボタンで、⌘K・カードの移動(Space で掴む /
26
+ 矢印で移動)・Enter で詳細・Esc などの一覧を表示。
27
+
28
+ ### Changed (変更)
29
+
30
+ - 各ビューを Error Boundary で隔離し、1 つのビューの表示エラーがアプリ全体を白画面にしないように
31
+ (他のタブへ切り替える / 再試行で復帰できる)。
32
+
33
+ ## [0.3.0] - 2026-06-16
34
+
35
+ UI を大幅に刷新し、協働ループ・自己診断・グローバル設定編集などを追加した節目のリリース。
36
+
37
+ ### Added (追加)
38
+
39
+ - キーボードによるカード移動 (KeyboardSensor): カードにフォーカスして **Space で掴み**、矢印でカラム間移動、
40
+ Space で確定 (Enter はカード詳細を開く)。ポインタ D&D と同じ status 更新経路。実機キーボードで
41
+ 掴む→矢印→確定→永続化まで動作確認済み。
42
+ - モーダルの focus trap (Tab/Shift+Tab フォーカスをダイアログ内に閉じ込める・全ダイアログ共通)
43
+ - 空ボード時のオンボーディング案内 (「最初のタスクを起票」+ AI 指示コピー導線)
44
+ - AI 設定ファイルの保存前に変更差分 (diff) を確認するステップを追加 (F-4-1)。JSON 構文チェック後、
45
+ 現行内容と編集内容の差分を提示してから保存する。
46
+ - 設定画面でカラムと AI 設定ファイルパスを GUI で編集できるように (F-5-1)。カラムは id / 表示名 /
47
+ WIP 上限 / 空のとき隠す + 並べ替え・追加・削除、パスは settings / browse / root の 3 種を行編集。
48
+ 「詳細 (生 JSON)」に残るのはマイルストーン定義のみ。
49
+ - 設定画面でビュー切替タブ ([ボード][クリティカルパス] など) の**表示順の並べ替え**と**表示/非表示**を
50
+ 切り替えられるように (`config.views`)。タブ・コマンドパレット (⌘K) の双方に反映。すべて隠した場合は
51
+ ボードへフォールバックして締め出しを防ぐ。
52
+ - AI 設定のカタログ (skill / agent) を**スキルディレクトリ単位でグループ表示**に。`references/*.md` 等の
53
+ 補助ファイルが「名前なし」で散らばらず、`figma-to-html` のように 1 つの折りたたみグループにまとまる。
54
+ name を持たないファイルはファイル名 (相対パス) で表示。
55
+ - AI 設定画面で**グローバル (`~/.claude/skills`, `~/.claude/agents`) の skill / agent も閲覧・編集**できるように。
56
+ 対象は skills / agents のみ (settings.json 等は対象外)。グローバルファイルの**保存時は「全プロジェクトに影響する」
57
+ 警告ダイアログ**を表示。パス検証はプロジェクト用と同一の許可リスト・deny-list・symlink 拒否を `~/.claude` に
58
+ 限定して再利用 (反証セキュリティレビュー済・skills/agents 外への読み書きは全経路で拒否)。
59
+
60
+ ### Changed (変更)
61
+
62
+ - クリティカルパスビューをボードと同じカラム型レイアウトに刷新。トポロジカル深さ (レベル) ごとに
63
+ カラムを分け、各カラムにヘッダー (レベル名 + 件数) を付けてレベルの区切りを明確化。カード表示・
64
+ クリティカルパス強調・循環警告は従来どおり。
65
+ - `README.md` を ship 済み機能の実態に合わせて全面更新 (旧「Phase 1 / MVP」「Phase 2 以降」章を撤廃し、
66
+ 機能群で再編)。`CHANGELOG.md` を新設し README からリンク。
67
+
68
+ ### Fixed (修正)
69
+
70
+ - 狭幅 (画面分割など) でのレイアウト崩れを修正: ヘッダーの操作群が折り返し、タブが多くても潰れず横スクロール、
71
+ 2 ペイン系ビュー (ドキュメント / アーカイブ / ごみ箱 / マイルストーン / 日次ダイジェスト / ファイル) の
72
+ 左サイドバーが幅に応じて縮小、ボードの絞り込みバーが折り返すように。
73
+ - やり直し依頼 (差し戻し) で、競合以外の理由 (差し戻せない状態など) でも差分ダイアログが開こうとして
74
+ 画面が固まる不具合を修正。競合以外はエラー表示で返すように。
75
+ - 停滞アラート / 統計の完了集計が作業ログを正しく読むように修正: (1) 作業ログが時系列でない (古い行の追記)
76
+ 場合でも最新の日時で判定、(2) 「やりとり」セクションのコメント行を作業ログと取り違えない。
77
+ - 複数タスクの一括指示コピーで、選択中に他端末や AI がタスクを移動・削除すると選択件数がずれる不具合を修正。
78
+ - アクセシビリティ改善: 進捗バーにスクリーンリーダー用の名前を付与、ビュー本体を `main` ランドマーク化
79
+ (Lighthouse アクセシビリティ 94 → 100)。
80
+ - キーボード D&D が一度も掴めなかった不具合を修正: カード独自の `onKeyDown` (Enter/Space で詳細を開く) が
81
+ dnd-kit の KeyboardSensor 起動を上書きしていた。Space を dnd-kit へ委譲し Enter を詳細表示に役割分担。
82
+ - タスク詳細パネルが Escape で閉じなかった不具合を修正 (他のダイアログと挙動を統一)。編集・コメント入力中や
83
+ 入れ子のダイアログ表示中は誤って閉じないよう配慮。
84
+ - コマンドパレット (⌘K) とタスク詳細パネルにも focus trap を追加 (Tab がダイアログ外へ抜けないように・a11y)。
85
+ - AI 設定ファイル管理で、ファイルツリー / カタログの読み込み失敗が無音で空表示になっていたのを通知するように。
86
+ - AI 設定ファイルで親ディレクトリが未作成のとき、読込が 403 ではなく 404 (新規作成扱い) を返すように修正。
87
+ 保存時に親ディレクトリを自動作成するため、新規設定ファイルの作成 (読込→編集→保存) が成立する。
88
+ パストラバーサル / symlink による範囲外アクセスは従来どおり拒否 (最も近い実在祖先で containment 判定)。
89
+ - コメント / 作業ログ追記でも、上書き直前に旧版を `.kanban/.trash` へ退避するように (他の書込操作と統一)。
90
+ 外部 (AI / エディタ) の同時編集と交錯して上書きで失われても、ごみ箱から復元できる。
91
+
92
+ ## [0.2.0] - 2026-06-14
93
+
94
+ 協働ループの強化と、誤操作からの回復手段 (アーカイブ復元 / ごみ箱) を追加。
95
+ UI をデザイントークン体系で刷新しました。
96
+
97
+ ### Added (追加)
98
+
99
+ - **協働ループ 3 機能**: 差し戻し導線 (やり直しを依頼)、判断待ちセンチネル (pending + decision の常時集計)、
100
+ 停滞アラート (status 別閾値超過の検知)。
101
+ - **アーカイブ検索 + 復元 (unarchive)**: archive のタスクを一覧・検索し、ボードへ戻せるように。
102
+ - **ごみ箱 (.kanban/.trash 自動退避 + 復元)**: タスクの破壊的書込の直前に旧版を退避する安全網。
103
+ - **Saved Views (絞り込みプリセット)**: ボードの絞り込みを config に名前付きで保存しワンクリック適用。
104
+ - **新着インジケータ (board-diff)**: 見ていない間の AI による変更をカードのハイライトで可視化。
105
+ - **複数タスク一括指示コピー**: 選択した複数タスクを依存順に 1 つの指示文へまとめてコピー。
106
+ - **member 移譲**: member のスラッグ (id) と表示名 (label) を分離し、スラッグの変更・削除時に
107
+ 参照タスクの `created_by` / `assignee` を付け替え。
108
+
109
+ ### Changed (変更)
110
+
111
+ - **UI デザイン刷新**: デザイントークン体系を導入し、ニュートラルで高密度なツール系の見た目へ。
112
+ - マイルストーン / 日次ダイジェストを関連資料と同じ 2 ペイン構成に統一。統計を横幅 100% 化・
113
+ 2 カラムグリッド化 (1200px 以下は 1 カラム)。
114
+ - 言語設定を config に永続化し、カラム名が表示言語に追随するように。ボードにドラッグ横パンを追加。
115
+
116
+ ### Fixed (修正)
117
+
118
+ - デザイン QA 指摘の修正 (コントラスト / カラム輪郭 / スクリムの 3 件 + トークンの役割ずれ)。
119
+ - 既定テンプレートの member 例を実名から汎用名 (member1 / member2) に変更。
120
+
121
+ ## [0.1.0] - 2026-06-12
122
+
123
+ 初公開。npm パッケージ `aizuchi` を公開しました。
124
+
125
+ ### Added (追加)
126
+
127
+ - **カンバンボード (MVP)**: config の `columns` どおりのカラム表示、カード (ID / タイトル / 担当 /
128
+ 優先度 / ラベル / 経過時間)、ドラッグ&ドロップ (status のピンポイント書換)、ライブ反映 (watcher → SSE)、
129
+ タスク追加・詳細 (Markdown プレビュー / 編集 / 競合検知)、警告バナー。
130
+ - **意思疎通コア**: 指示コピー (instructionTemplate 展開)、提案ワークフロー (承認 / 却下 / モード違反検知)、
131
+ 設定画面、i18n (en / ja・既定 en)。
132
+ - **見通し**: クリティカルパスビュー、マイルストーン、日次ダイジェスト、受け入れ条件の進捗バー、
133
+ WIP 超過警告。
134
+ - **管理と往復**: AI 設定ファイル管理、検索 / フィルタ、アーカイブ、重複 ID リネーム支援、タスクコメント (往復)。
135
+ - **自動化 / 分析**: AI 用 CLI (`task add / move / done / block / log / comment`)、統計ビュー。
136
+ - **体験 / 射程**: コマンドパレット ⌘K、タスクテンプレート、複数案件横断 `aizuchi overview` (read-only)、
137
+ 関連資料機能 (タスク ⇄ 設計資料の相互リンク)。
138
+ - **CLI**: `init` (`.kanban/` 生成 + CLAUDE.md へ規約追記)、`serve` (127.0.0.1 限定起動)、
139
+ `task`、`overview`。
140
+ - **セキュリティ**: ローカル限定 (127.0.0.1)・書込パス許可リスト + symlink / トラバーサル拒否・
141
+ アトミック書込 + mtime 競合検知・秘匿情報のログ非出力・git 履歴連携 (read-only)。
142
+
143
+ [Unreleased]: https://github.com/megaphone-tokyo/AIzuchi/compare/v0.4.0...HEAD
144
+ [0.4.0]: https://github.com/megaphone-tokyo/AIzuchi/compare/v0.3.0...v0.4.0
145
+ [0.3.0]: https://github.com/megaphone-tokyo/AIzuchi/compare/v0.2.0...v0.3.0
146
+ [0.2.0]: https://github.com/megaphone-tokyo/AIzuchi/compare/v0.1.0...v0.2.0
147
+ [0.1.0]: https://github.com/megaphone-tokyo/AIzuchi/releases/tag/v0.1.0
package/README.md CHANGED
@@ -13,6 +13,8 @@ AI も人間も通常のファイル読み書きだけで参加できます。
13
13
  - **ファイルが唯一の正**: DB を使いません。git 管理でき、サーバー停止中も読めます。
14
14
  - **規約ベースの AI 連携**: AI は専用 API なしで、ファイル読み書きだけで参加します。
15
15
 
16
+ 変更履歴は [CHANGELOG.md](CHANGELOG.md) を参照してください。
17
+
16
18
  ## 必要環境
17
19
 
18
20
  - Node.js **20.1 以上**(`fs.readdir` の `recursive` を使うため)
@@ -39,22 +41,66 @@ npx aizuchi overview <dir...> # 例: npx aizuchi overview ~/proj-a ~/proj-b
39
41
  serve(5151)と衝突しない 5152 で、`--port <番号>` で変更できます。
40
42
  - `127.0.0.1` 以外にはバインドせず、外部へのネットワーク送信は一切行いません。
41
43
 
42
- ## できること(Phase 1 / MVP)
44
+ ## できること
45
+
46
+ ブラウザのボードで AI への委任を一望・操作します。エディタや AI がファイルを書き換えると、
47
+ watcher → SSE で開いているボードへ約 1 秒以内に反映されます(ライブ更新)。
48
+ UI 表示言語は英語 / 日本語(en / ja・既定 en)を切り替えられます。
49
+
50
+ ### ボード運用
43
51
 
44
- - **カンバンボード**: config の `columns` どおりにカラム表示。カードに ID・タイトル・担当(human/ai の種別を
45
- 色で区別)・優先度・ラベル・最終更新からの経過時間を表示。
46
- カラム名は `columns[].label` が無ければ UI の表示言語に追随します(`label` を書けばカスタム名として固定。
47
- 既存の config で日本語表示にしたい場合は `label` 行を削除してください)。
48
52
  - **ドラッグ&ドロップ**: カラム移動で frontmatter の `status` / `updated` だけをピンポイント書換(本文は保全)。
49
53
  Pending へ移すと種別(decision / external / dependency)と理由を尋ねるダイアログが出ます。
50
- - **ライブ反映**: エディタや AI がファイルを変更すると、watcher SSE で開いているボードへ約 1 秒以内に反映。
51
- - **タスク追加**: モーダルからタイトル・起票者・担当・優先度・ラベルを入力して起票(ID はサーバーが採番)。
52
- - **タスク詳細**: Markdown プレビュー / 本文編集(保存時に競合検知 差分提示)/ 作業ログのタイムライン表示。
54
+ - **検索フィルタ**: タイトル / ラベル / 担当でカードを絞り込み。
55
+ - **Saved Views(保存済みビュー)**: 絞り込み条件を config に名前付きで保存し、chip からワンクリックで適用。
56
+ - **新着ハイライト**: 画面を見ていない間に AI が動かしたタスク(status 変化 / 作業ログ追記 / 新規)を
57
+ カードのハイライトと「新着」バッジで可視化。カードを開くと既読化、ヘッダから一括既読も可能。
58
+ - **停滞バッジ**: 一定期間更新が止まったタスクにバッジを表示(閾値は status 別に設定可能)。
59
+ - **差し戻し(やり直しを依頼)**: カード / 詳細から理由を添えて `todo` へ戻し、作業ログに記録。
60
+ 直近の差し戻し理由は再依頼の指示コピーに自動で載ります。
61
+ - **複数選択の一括指示コピー**: 複数選択モードでカードを選び、依存関係のトポロジカル順に並べた
62
+ 1 つの指示文へまとめてクリップボードへコピー(AI へのバッチ委任を 1 操作で)。
63
+ - **ドラッグ横パン**: カラムの空白部分を掴んで横スクロール(カード等のインタラクティブ要素からの操作は除外)。
64
+
65
+ ### 見通し
66
+
67
+ - **クリティカルパス**: `depends_on` の DAG をトポロジカル深さで列配置し、クリティカルパスを強調。
68
+ 循環は検出して警告し、再編成の指示コピーも提供。
69
+ - **マイルストーン**: 予定表(milestone 別・done/total 付き)と対応履歴(月別)の 2 ペイン表示。
70
+ - **日次ダイジェスト**: 日付別に作業ログをまとめて表示。
71
+ - **統計**: 完了数推移 / サイクルタイム / ステータス滞留 / member 別完了数 / ボトルネック /
72
+ 停滞中(静かな)タスクの一覧。
73
+
74
+ ### 管理と健全性
75
+
76
+ - **アーカイブ検索 + 復元**: done タスクを archive へ退避し、一覧・検索・プレビュー。
77
+ 「ボードに戻す」で `tasks/` へ復元(unarchive)。
78
+ - **ごみ箱(.trash 退避 + 復元)**: タスクを破壊的に書き換える直前に旧版を `.kanban/.trash/` へ
79
+ 自動退避する安全網。一覧・現行との差分プレビュー・復元が可能(誤上書きからの回復)。
80
+ - **AI 設定ファイル管理**: `aiConfigPaths` で許可したファイル(CLAUDE.md / settings 等)を
81
+ GUI から編集(JSON 構文チェック・`.bak` バックアップ・パーミッション保全)。
82
+ - **git 履歴連携(read-only)**: 詳細パネルのコミットタブで、タスク ID を含むコミット履歴を表示。
83
+ `git log` を read-only で実行するだけで、fetch / pull 等の外部通信はしません。
84
+
85
+ ### AI 連携規約 + CLI
86
+
87
+ - **規約ベースの連携**: `init` が CLAUDE.md に運用規約(着手 / 進捗 / 完了 / ブロック / 起票 / 書式)を
88
+ マーカー区切りで追記。AI は専用 API なしでファイルを読み書きするだけで参加できます。
89
+ - **提案ワークフロー**: `aiTaskCreation` が `proposal` のとき、AI 起票は `proposed` で入り、
90
+ 承認 / 却下を経て `todo` へ。モード違反は警告で検知。
91
+ - **AI 用 CLI(必須でない安全弁)**: 書込系 `aizuchi task add / move <id> <status> / done <id> /
92
+ block <id> --type --reason / log <id> "msg" / comment <id> "msg" / revise <id> --reason` と、
93
+ 読取系 `list [--status S] [--assignee M] / show <id> / next`(着手可能なタスクを依存解決順に提示)を提供。
94
+ いずれも既存のスキーマ検証・採番直列化・行置換検証を再利用します(生ファイル編集も従来どおり可・`--json` 対応)。
95
+
96
+ ### そのほか
97
+
98
+ - **複数案件横断 overview**: `aizuchi overview <dir...>` で複数 `.kanban` を read-only 集約表示。
99
+ - **コマンドパレット ⌘K**: ビュー切替 / タスク検索ジャンプ / 起票 / 設定を 1 操作で。
100
+ - **タスク詳細**: Markdown プレビュー / 本文編集(保存時に競合検知 → 行差分提示)/
101
+ 作業ログ・やりとり(双方向コメント)のタイムライン。
53
102
  - **警告バナー**: 壊れた frontmatter・重複 ID・未登録メンバー・不正な config を表示(ボードは落ちません)。
54
103
 
55
- > Phase 2 以降: 提案ワークフロー・設定画面・AI 設定ファイル管理・検索/フィルタ/アーカイブ UI・
56
- > 指示コピー・重複 ID リネーム支援・多言語対応(i18n)・マイルストーン/ガント。
57
-
58
104
  ## 開発
59
105
 
60
106
  ```bash
@@ -78,7 +124,7 @@ npm run typecheck # tsc(server)+ tsc -p web/tsconfig.json(web)
78
124
  (React SPA) ◀SSE─│ server/static.ts server/app.ts events │──watch/read/write──▶ .kanban/
79
125
  │ chokidar watcher (server/watcher) │ (プレーンテキスト)
80
126
  └────────────────────────────────────────────┘ ▲
81
- AI / 人間が直接編集
127
+ AI / 人間が直接編集 or CLI
82
128
  ```
83
129
 
84
130
  ```
@@ -90,7 +136,7 @@ aizuchi/
90
136
  │ ├── board.ts # .kanban 読込・zod 検証・警告生成
91
137
  │ ├── watcher.ts # chokidar(debounce 200ms)→ SSE
92
138
  │ ├── static.ts # ビルド済み SPA の配信(トラバーサル拒否)
93
- │ └── lib/ # task-file / pinpoint / id / fs-safe / time
139
+ │ └── lib/ # task-file / pinpoint / id / fs-safe / time / git / trash / task-ops
94
140
  ├── web/ # Vite + React SPA(ビルド成果物を dist/web に同梱)
95
141
  │ └── src/ # App / useBoard / components / lib
96
142
  ├── shared/ # task / config の zod スキーマ・型・行 LCS diff(server / web 共有)
@@ -102,11 +148,11 @@ aizuchi/
102
148
 
103
149
  - タスク = `.kanban/tasks/<ID>.md`(1 タスク 1 ファイル、**ID=ファイル名**)。
104
150
  - frontmatter: `id` / `title` / `status` / `assignee` / `created_by` / `priority` / `labels` /
105
- `blocked_type` / `blocked_reason` / `depends_on` / `created` / `updated`。
151
+ `blocked_type` / `blocked_reason` / `depends_on` / `milestone` / `created` / `updated`。
106
152
  - `status`: `proposed` / `backlog` / `todo` / `in_progress` / `pending` / `done`。
107
153
  - 日時は ISO 8601(タイムゾーン付き、例: `2026-06-10T10:00:00+09:00`)。UTC へ変換しません。
108
- - 設定は `.kanban/config.json`(プロジェクト名・ID 接頭辞・ポート・メンバー・カラム ほか)。
109
- 設定ゼロでも既定値で動作します。
154
+ - 設定は `.kanban/config.json`(プロジェクト名・ID 接頭辞・ポート・メンバー・カラム・マイルストーン・
155
+ タスクテンプレート・保存済みビュー・停滞閾値・言語 ほか)。設定ゼロでも既定値で動作します。
110
156
 
111
157
  各フィールドの enum 値・必須条件など完全な frontmatter スキーマは、`aizuchi init` が `CLAUDE.md` に追記する運用規約(frontmatter スキーマ表)に記載されます。init 後すぐ手元で確認できます。
112
158
 
@@ -128,10 +174,24 @@ aizuchi/
128
174
  - [ ] **警告**: frontmatter を壊したファイルを置くと警告バナーに表示され、ボード自体は落ちない。
129
175
  - [ ] **詳細**: タスク詳細でプレビュー・編集・作業ログのタイムラインが使える。保存時に外部変更と競合したら
130
176
  上書き確認(差分提示)が出る。
177
+ - [ ] **アーカイブ / ごみ箱**: done をアーカイブ → アーカイブビューから復元できる。破壊的書換の前に
178
+ `.kanban/.trash/` へ退避され、ごみ箱から復元できる。
131
179
  - [ ] **ネットワーク**: 動作中、外部へのネットワーク通信が一切発生しない。
132
180
  - [ ] **終了**: ブラウザでボードを開いたまま `Ctrl+C` で serve が速やかに終了する。
133
181
  - [ ] **再現性**: 本 README の手順だけで第三者が環境構築〜起動まで再現できる。
134
182
 
183
+ ## アクセシビリティ
184
+
185
+ キーボードとスクリーンリーダーでの操作を考慮しています。
186
+
187
+ - **キーボード操作**: フォームやボタンは Tab で移動・操作できます。コマンドパレット(⌘K / Ctrl+K)から
188
+ ビュー切替・タスク検索ジャンプ・起票・設定をキーボードだけで実行できます。
189
+ - **キーボードでのカード移動**: カードにフォーカスして **Space で掴み**、矢印キーでカラム間を移動、
190
+ 再度 Space で確定(Enter はカード詳細を開く)。ポインタ D&D と同じ status 更新経路・Pending 行きは保留ダイアログを起動。
191
+ - **モーダル**: 開いた時に内部へフォーカスを移し、Tab / Shift+Tab はダイアログ内をループ(focus trap)、
192
+ Escape で閉じ、閉じたら元の要素へフォーカスを戻します。
193
+ - **ARIA / 役割**: カラムやカード、ツールバー、ダイアログに `role` と `aria-label` を付与しています。
194
+
135
195
  ## 技術スタック
136
196
 
137
197
  Node.js 20.1+ / Hono + @hono/node-server / chokidar / SSE / React 18 + TypeScript + Vite /
@@ -142,7 +202,7 @@ Node.js 20.1+ / Hono + @hono/node-server / chokidar / SSE / React 18 + TypeScrip
142
202
  - **@hono/node-server** — Hono を Node ランタイムで起動し、ビルド済み SPA を静的配信するため
143
203
  - **js-yaml (+ @types/js-yaml)** — gray-matter に `JSON_SCHEMA` エンジンを注入し、`+09:00` 付き日時を Date 化せず文字列のまま往復させるため(gray-matter 内蔵の旧 3.x に transitive 依存しない)
144
204
  - **@vitejs/plugin-react** — Vite で React(JSX / Fast Refresh)を使うため
145
- - **@dnd-kit/utilities** — @dnd-kit ファミリ。D&D の CSS transform 補助(sortable はカラム内並べ替えが入る Phase 2 で追加)
205
+ - **@dnd-kit/utilities** — @dnd-kit ファミリ。D&D の CSS transform 補助
146
206
  - **tsx** — 開発時に TypeScript サーバーを直接実行するため(本番は dist のコンパイル済み JS を使用)
147
207
  - **typescript / vitest / @types/\*** — 型安全とテストの基盤
148
208
 
@@ -5,8 +5,9 @@ import readline from 'node:readline';
5
5
  import { fileURLToPath, pathToFileURL } from 'node:url';
6
6
  import { serve } from '@hono/node-server';
7
7
  import { DEFAULT_OVERVIEW_PORT, DEFAULT_PORT } from '../shared/schema.js';
8
+ import { analyzeCriticalPath } from '../shared/critical-path.js';
8
9
  import { createApp } from '../server/app.js';
9
- import { loadConfig } from '../server/board.js';
10
+ import { loadArchive, loadBoard, loadConfig } from '../server/board.js';
10
11
  import { createEventBus } from '../server/events.js';
11
12
  import { cleanupStaleTmpFiles, resolveKanbanReal, writeFileAtomic } from '../server/lib/fs-safe.js';
12
13
  import { createOverviewApp } from '../server/overview.js';
@@ -28,6 +29,9 @@ const KANBAN_README = `# .kanban — AIzuchi タスクボード
28
29
  - \`config.json\` — ボード設定 (メンバー・カラム・ID 接頭辞・指示テンプレート)
29
30
  - \`tasks/\` — アクティブなタスク (1 タスク = 1 Markdown ファイル、ID = ファイル名)
30
31
  - \`archive/\` — 完了・却下タスク (アーカイブ実行月の月別フォルダ)
32
+ - \`.trash/\` — 破壊的書込 (編集保存・D&D・差し戻し等) の直前に自動退避した旧版 (\`<ID>.<epoch_ms>.md\`)。
33
+ ダッシュボードの「ごみ箱」から復元できます。世代は \`config.json\` の \`trashKeep\` 件まで保持し、古いものは自動削除。
34
+ 履歴に含めたくない場合は \`.gitignore\` に \`.kanban/.trash/\` を追加してください。
31
35
 
32
36
  タスクの書式・運用規約はプロジェクトの CLAUDE.md「AIzuchi 運用規約」を参照してください。
33
37
  `;
@@ -457,6 +461,11 @@ export async function runTaskCli(argv, io = {
457
461
  });
458
462
  return emit(view, `保留: ${view.frontmatter.id} (${view.frontmatter.blocked_type ?? ''})`);
459
463
  }
464
+ case 'revise': {
465
+ // やり直し依頼 (差し戻し): done/in_progress/pending → todo へ戻し、理由を作業ログに残す
466
+ const view = await ops.revise(positional[0] ?? '', { reason: opts.get('reason') });
467
+ return emit(view, `差し戻し: ${view.frontmatter.id} → ${view.frontmatter.status}`);
468
+ }
460
469
  case 'log': {
461
470
  const view = await ops.appendLog(positional[0] ?? '', opts.get('member') ?? 'claude', positional[1] ?? '');
462
471
  return emit(view, `作業ログ追記: ${view.frontmatter.id}`);
@@ -465,8 +474,117 @@ export async function runTaskCli(argv, io = {
465
474
  const view = await ops.appendComment(positional[0] ?? '', opts.get('member') ?? 'claude', positional[1] ?? '');
466
475
  return emit(view, `コメント追記: ${view.frontmatter.id}`);
467
476
  }
477
+ // --- 読取系 (read-only。AI が「今の状態」「次の一手」をファイル経由で引ける) ---
478
+ case 'list': {
479
+ // アクティブなタスク一覧。--status / --assignee で絞り込み。書込なし
480
+ const board = await loadBoard(root);
481
+ const statusFilter = opts.get('status');
482
+ const assigneeFilter = opts.get('assignee');
483
+ const rows = board.tasks
484
+ .filter((t) => statusFilter === undefined || t.frontmatter.status === statusFilter)
485
+ .filter((t) => assigneeFilter === undefined || t.frontmatter.assignee === assigneeFilter)
486
+ .map((t) => ({
487
+ id: t.frontmatter.id,
488
+ status: t.frontmatter.status,
489
+ assignee: t.frontmatter.assignee,
490
+ priority: t.frontmatter.priority,
491
+ title: t.frontmatter.title,
492
+ labels: t.frontmatter.labels,
493
+ }))
494
+ .sort((a, b) => a.id.localeCompare(b.id, 'en', { numeric: true }));
495
+ if (json) {
496
+ io.out(JSON.stringify(rows));
497
+ }
498
+ else if (rows.length === 0) {
499
+ io.out('(該当タスクなし)');
500
+ }
501
+ else {
502
+ for (const r of rows)
503
+ io.out(`${r.id}\t${r.status}\t${r.assignee ?? '-'}\t${r.title}`);
504
+ }
505
+ return 0;
506
+ }
507
+ case 'show': {
508
+ // 1 タスクの詳細 (frontmatter 要約 + 本文)。アクティブ → 無ければ archive を探す
509
+ const id = positional[0] ?? '';
510
+ if (id === '') {
511
+ io.err(json ? JSON.stringify({ error: 'id required', code: 'invalid_input' }) : 'show には <id> が必要です');
512
+ return 1;
513
+ }
514
+ const board = await loadBoard(root);
515
+ const found = board.tasks.find((t) => t.frontmatter.id === id);
516
+ if (found !== undefined) {
517
+ if (json) {
518
+ io.out(JSON.stringify(found));
519
+ }
520
+ else {
521
+ const f = found.frontmatter;
522
+ io.out(`${f.id} [${f.status}] ${f.title}`);
523
+ io.out(`assignee: ${f.assignee ?? '-'} priority: ${f.priority} labels: ${f.labels.join(', ') || '-'}`);
524
+ if (f.depends_on.length > 0)
525
+ io.out(`depends_on: ${f.depends_on.join(', ')}`);
526
+ if (f.status === 'pending')
527
+ io.out(`blocked: ${f.blocked_type ?? '-'} — ${f.blocked_reason ?? '-'}`);
528
+ io.out('');
529
+ io.out(found.body.trim());
530
+ }
531
+ return 0;
532
+ }
533
+ const archived = (await loadArchive(root)).find((a) => a.id === id);
534
+ if (archived !== undefined) {
535
+ if (json) {
536
+ io.out(JSON.stringify({ ...archived, archived: true }));
537
+ }
538
+ else {
539
+ io.out(`${archived.id} [archived]${archived.title ? ` ${archived.title}` : ''}`);
540
+ io.out('');
541
+ io.out((archived.body ?? '').trim());
542
+ }
543
+ return 0;
544
+ }
545
+ io.err(json ? JSON.stringify({ error: 'not found', code: 'not_found', id }) : `タスクが見つかりません: ${id}`);
546
+ return 2;
547
+ }
548
+ case 'next': {
549
+ // 「次に着手できるタスク」= 未完了かつ依存が全て解決済み (active な先行依存なし) の
550
+ // backlog/todo/in_progress。クリティカルパス上を優先表示する。書込なし
551
+ const board = await loadBoard(root);
552
+ const active = board.tasks.filter((t) => t.frontmatter.status !== 'done');
553
+ const cp = analyzeCriticalPath(active.map((t) => ({ id: t.frontmatter.id, dependsOn: t.frontmatter.depends_on })));
554
+ const onCp = new Set(cp.criticalPath);
555
+ const activeIds = new Set(active.map((t) => t.frontmatter.id));
556
+ const ACTIONABLE = new Set(['backlog', 'todo', 'in_progress']);
557
+ const ready = active
558
+ .filter((t) => ACTIONABLE.has(t.frontmatter.status) &&
559
+ t.frontmatter.depends_on.every((d) => !activeIds.has(d)))
560
+ .map((t) => ({
561
+ id: t.frontmatter.id,
562
+ status: t.frontmatter.status,
563
+ title: t.frontmatter.title,
564
+ onCriticalPath: onCp.has(t.frontmatter.id),
565
+ }))
566
+ .sort((a, b) => {
567
+ if (a.onCriticalPath !== b.onCriticalPath)
568
+ return a.onCriticalPath ? -1 : 1;
569
+ return a.id.localeCompare(b.id, 'en', { numeric: true });
570
+ });
571
+ if (json) {
572
+ io.out(JSON.stringify({ ready, criticalPath: cp.criticalPath, hasCycle: cp.cycleNodes.length > 0 }));
573
+ return 0;
574
+ }
575
+ if (cp.cycleNodes.length > 0)
576
+ io.out(`⚠ 依存に循環があります: ${cp.cycleNodes.join(', ')}`);
577
+ if (ready.length === 0) {
578
+ io.out('着手可能なタスクはありません (全て完了 / 依存待ち / ブロック中)');
579
+ return 0;
580
+ }
581
+ io.out('次に着手できるタスク (依存解決済み):');
582
+ for (const r of ready)
583
+ io.out(`- ${r.id} (${r.status})${r.onCriticalPath ? ' ★CP' : ''} ${r.title}`);
584
+ return 0;
585
+ }
468
586
  default:
469
- io.err('task サブコマンド: add / move <id> <status> / done <id> / block <id> --type --reason / log <id> "msg" / comment <id> "msg"');
587
+ io.err('task サブコマンド: add / move <id> <status> / done <id> / block <id> --type --reason / revise <id> [--reason "..."] / log <id> "msg" / comment <id> "msg" / list [--status S] [--assignee M] / show <id> / next');
470
588
  return 1;
471
589
  }
472
590
  }