aizuchi 0.3.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 +27 -1
- package/README.md +4 -3
- package/dist/bin/aizuchi.js +112 -2
- package/dist/web/assets/index-BYfu8Hdw.js +93 -0
- package/dist/web/assets/{index-B4RPR1mO.css → index-DiMXsTSP.css} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-DG0paXSh.js +0 -93
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
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
|
+
|
|
8
33
|
## [0.3.0] - 2026-06-16
|
|
9
34
|
|
|
10
35
|
UI を大幅に刷新し、協働ループ・自己診断・グローバル設定編集などを追加した節目のリリース。
|
|
@@ -115,7 +140,8 @@ UI をデザイントークン体系で刷新しました。
|
|
|
115
140
|
- **セキュリティ**: ローカル限定 (127.0.0.1)・書込パス許可リスト + symlink / トラバーサル拒否・
|
|
116
141
|
アトミック書込 + mtime 競合検知・秘匿情報のログ非出力・git 履歴連携 (read-only)。
|
|
117
142
|
|
|
118
|
-
[Unreleased]: https://github.com/megaphone-tokyo/AIzuchi/compare/v0.
|
|
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
|
|
119
145
|
[0.3.0]: https://github.com/megaphone-tokyo/AIzuchi/compare/v0.2.0...v0.3.0
|
|
120
146
|
[0.2.0]: https://github.com/megaphone-tokyo/AIzuchi/compare/v0.1.0...v0.2.0
|
|
121
147
|
[0.1.0]: https://github.com/megaphone-tokyo/AIzuchi/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -88,9 +88,10 @@ UI 表示言語は英語 / 日本語(en / ja・既定 en)を切り替えら
|
|
|
88
88
|
マーカー区切りで追記。AI は専用 API なしでファイルを読み書きするだけで参加できます。
|
|
89
89
|
- **提案ワークフロー**: `aiTaskCreation` が `proposal` のとき、AI 起票は `proposed` で入り、
|
|
90
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
|
-
|
|
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` 対応)。
|
|
94
95
|
|
|
95
96
|
### そのほか
|
|
96
97
|
|
package/dist/bin/aizuchi.js
CHANGED
|
@@ -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';
|
|
@@ -473,8 +474,117 @@ export async function runTaskCli(argv, io = {
|
|
|
473
474
|
const view = await ops.appendComment(positional[0] ?? '', opts.get('member') ?? 'claude', positional[1] ?? '');
|
|
474
475
|
return emit(view, `コメント追記: ${view.frontmatter.id}`);
|
|
475
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
|
+
}
|
|
476
586
|
default:
|
|
477
|
-
io.err('task サブコマンド: add / move <id> <status> / done <id> / block <id> --type --reason / revise <id> [--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');
|
|
478
588
|
return 1;
|
|
479
589
|
}
|
|
480
590
|
}
|