@ronkovic/aad 0.3.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/LICENSE +21 -0
- package/README.md +312 -0
- package/bin/aad.js +2 -0
- package/package.json +78 -0
- package/src/__tests__/e2e/pipeline-e2e.test.ts +279 -0
- package/src/__tests__/e2e/resume-e2e.test.ts +200 -0
- package/src/__tests__/integration/cli-smoke.test.ts +175 -0
- package/src/__tests__/integration/pipeline.test.ts +346 -0
- package/src/bun-imports.d.ts +14 -0
- package/src/main.ts +52 -0
- package/src/modules/claude-provider/__tests__/claude-cli.adapter.test.ts +277 -0
- package/src/modules/claude-provider/__tests__/claude-sdk-real-env.test.ts +127 -0
- package/src/modules/claude-provider/__tests__/claude-sdk.adapter.test.ts +347 -0
- package/src/modules/claude-provider/__tests__/effort-strategy.test.ts +212 -0
- package/src/modules/claude-provider/__tests__/provider-registry.test.ts +251 -0
- package/src/modules/claude-provider/__tests__/retry.test.ts +201 -0
- package/src/modules/claude-provider/claude-cli.adapter.ts +156 -0
- package/src/modules/claude-provider/claude-provider.port.ts +35 -0
- package/src/modules/claude-provider/claude-sdk.adapter.ts +217 -0
- package/src/modules/claude-provider/effort-strategy.ts +94 -0
- package/src/modules/claude-provider/index.ts +32 -0
- package/src/modules/claude-provider/provider-registry.ts +92 -0
- package/src/modules/claude-provider/retry.ts +81 -0
- package/src/modules/cli/__tests__/app.test.ts +160 -0
- package/src/modules/cli/__tests__/cleanup.test.ts +111 -0
- package/src/modules/cli/__tests__/commands.test.ts +186 -0
- package/src/modules/cli/__tests__/output.test.ts +329 -0
- package/src/modules/cli/__tests__/resume.test.ts +324 -0
- package/src/modules/cli/__tests__/run.test.ts +168 -0
- package/src/modules/cli/__tests__/shutdown.test.ts +168 -0
- package/src/modules/cli/__tests__/status.test.ts +144 -0
- package/src/modules/cli/app.ts +241 -0
- package/src/modules/cli/commands/cleanup.ts +120 -0
- package/src/modules/cli/commands/resume.ts +156 -0
- package/src/modules/cli/commands/run.ts +322 -0
- package/src/modules/cli/commands/status.ts +101 -0
- package/src/modules/cli/index.ts +29 -0
- package/src/modules/cli/output.ts +256 -0
- package/src/modules/cli/shutdown.ts +122 -0
- package/src/modules/dashboard/__tests__/api-routes.test.ts +204 -0
- package/src/modules/dashboard/__tests__/file-watcher.test.ts +34 -0
- package/src/modules/dashboard/__tests__/server.test.ts +120 -0
- package/src/modules/dashboard/__tests__/sse-broadcaster.test.ts +163 -0
- package/src/modules/dashboard/__tests__/sse-routes.test.ts +58 -0
- package/src/modules/dashboard/__tests__/state-aggregator.test.ts +330 -0
- package/src/modules/dashboard/index.ts +8 -0
- package/src/modules/dashboard/routes/api.ts +84 -0
- package/src/modules/dashboard/routes/sse.ts +37 -0
- package/src/modules/dashboard/server.ts +111 -0
- package/src/modules/dashboard/services/file-watcher.ts +36 -0
- package/src/modules/dashboard/services/sse-broadcaster.ts +81 -0
- package/src/modules/dashboard/services/state-aggregator.ts +132 -0
- package/src/modules/dashboard/ui/dashboard.html +405 -0
- package/src/modules/git-workspace/__tests__/branch-manager.test.ts +335 -0
- package/src/modules/git-workspace/__tests__/git-exec.test.ts +91 -0
- package/src/modules/git-workspace/__tests__/memory-sync.test.ts +273 -0
- package/src/modules/git-workspace/__tests__/merge-service.test.ts +286 -0
- package/src/modules/git-workspace/__tests__/settings-merge.test.ts +163 -0
- package/src/modules/git-workspace/__tests__/worktree-manager.test.ts +247 -0
- package/src/modules/git-workspace/branch-manager.ts +191 -0
- package/src/modules/git-workspace/git-exec.ts +124 -0
- package/src/modules/git-workspace/index.ts +17 -0
- package/src/modules/git-workspace/memory-sync.ts +89 -0
- package/src/modules/git-workspace/merge-service.ts +156 -0
- package/src/modules/git-workspace/settings-merge.ts +95 -0
- package/src/modules/git-workspace/worktree-manager.ts +199 -0
- package/src/modules/logging/__tests__/log-store.test.ts +242 -0
- package/src/modules/logging/__tests__/logger.test.ts +81 -0
- package/src/modules/logging/__tests__/sse-transport.test.ts +93 -0
- package/src/modules/logging/index.ts +7 -0
- package/src/modules/logging/log-store.ts +80 -0
- package/src/modules/logging/logger.ts +55 -0
- package/src/modules/logging/transports/sse-transport.ts +28 -0
- package/src/modules/multi-repo/__tests__/multi-repo-planner.test.ts +93 -0
- package/src/modules/multi-repo/__tests__/repo-context.test.ts +79 -0
- package/src/modules/multi-repo/index.ts +12 -0
- package/src/modules/multi-repo/multi-repo-planner.ts +112 -0
- package/src/modules/multi-repo/repo-context.ts +71 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/file-lock.test.ts +141 -0
- package/src/modules/persistence/__tests__/index.test.ts +38 -0
- package/src/modules/persistence/__tests__/stores.test.ts +594 -0
- package/src/modules/persistence/file-lock.ts +158 -0
- package/src/modules/persistence/fs-run-store.ts +73 -0
- package/src/modules/persistence/fs-task-store.ts +152 -0
- package/src/modules/persistence/fs-worker-store.ts +116 -0
- package/src/modules/persistence/in-memory-stores.ts +98 -0
- package/src/modules/persistence/index.ts +60 -0
- package/src/modules/persistence/stores.port.ts +60 -0
- package/src/modules/planning/__tests__/file-conflict-validator.test.ts +256 -0
- package/src/modules/planning/__tests__/planning-service.test.ts +366 -0
- package/src/modules/planning/__tests__/project-detection.test.ts +707 -0
- package/src/modules/planning/file-conflict-validator.ts +135 -0
- package/src/modules/planning/index.ts +40 -0
- package/src/modules/planning/planning.service.ts +262 -0
- package/src/modules/planning/project-detection.ts +525 -0
- package/src/modules/plugin/__tests__/plugin-loader.test.ts +83 -0
- package/src/modules/plugin/__tests__/plugin-manager.test.ts +187 -0
- package/src/modules/plugin/index.ts +3 -0
- package/src/modules/plugin/plugin-loader.ts +46 -0
- package/src/modules/plugin/plugin-manager.ts +90 -0
- package/src/modules/plugin/plugin.types.ts +37 -0
- package/src/modules/process-manager/__tests__/process-manager.test.ts +210 -0
- package/src/modules/process-manager/__tests__/worker.test.ts +89 -0
- package/src/modules/process-manager/index.ts +5 -0
- package/src/modules/process-manager/process-manager.ts +193 -0
- package/src/modules/process-manager/worker.ts +106 -0
- package/src/modules/task-execution/__tests__/default-spawner.test.ts +154 -0
- package/src/modules/task-execution/__tests__/executor.test.ts +760 -0
- package/src/modules/task-execution/__tests__/implementer-green.test.ts +286 -0
- package/src/modules/task-execution/__tests__/merge-phase.test.ts +368 -0
- package/src/modules/task-execution/__tests__/reviewer.test.ts +302 -0
- package/src/modules/task-execution/__tests__/tester-red.test.ts +281 -0
- package/src/modules/task-execution/__tests__/tester-verify.test.ts +313 -0
- package/src/modules/task-execution/executor.ts +303 -0
- package/src/modules/task-execution/index.ts +45 -0
- package/src/modules/task-execution/phases/default-spawner.ts +49 -0
- package/src/modules/task-execution/phases/implementer-green.ts +100 -0
- package/src/modules/task-execution/phases/merge.ts +122 -0
- package/src/modules/task-execution/phases/reviewer.ts +160 -0
- package/src/modules/task-execution/phases/tester-red.ts +100 -0
- package/src/modules/task-execution/phases/tester-verify.ts +120 -0
- package/src/modules/task-queue/__tests__/dependency-resolver.test.ts +456 -0
- package/src/modules/task-queue/__tests__/dispatcher.test.ts +824 -0
- package/src/modules/task-queue/__tests__/task-plan.test.ts +122 -0
- package/src/modules/task-queue/__tests__/task.test.ts +130 -0
- package/src/modules/task-queue/dependency-resolver.ts +171 -0
- package/src/modules/task-queue/dispatcher.ts +372 -0
- package/src/modules/task-queue/index.ts +16 -0
- package/src/modules/task-queue/task-plan.ts +40 -0
- package/src/modules/task-queue/task.ts +67 -0
- package/src/shared/__tests__/config.test.ts +204 -0
- package/src/shared/__tests__/errors.test.ts +285 -0
- package/src/shared/__tests__/events.test.ts +496 -0
- package/src/shared/__tests__/types.test.ts +360 -0
- package/src/shared/config.ts +133 -0
- package/src/shared/errors.ts +128 -0
- package/src/shared/events.ts +171 -0
- package/src/shared/types.ts +143 -0
- package/tsconfig.json +30 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AAD Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# AAD - Autonomous Agent Development Orchestrator
|
|
2
|
+
|
|
3
|
+
TypeScript/Bun製マルチエージェント開発オーケストレーター。Claude CLIを活用し、TDDパイプラインによる並列タスク実行を自動化します。
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://bun.sh/)
|
|
8
|
+
[]()
|
|
9
|
+
[]()
|
|
10
|
+
|
|
11
|
+
## 主要機能
|
|
12
|
+
|
|
13
|
+
- **マルチエージェント並列実行**: 複数のClaudeエージェントが独立したタスクを並列処理
|
|
14
|
+
- **TDDパイプライン**: Red → Green → Verify → Review → Merge の自動化されたテスト駆動開発
|
|
15
|
+
- **マルチリポジトリ対応**: 複数リポジトリにまたがるタスクの統合管理 *(v0.3.0)*
|
|
16
|
+
- **プラグインシステム**: カスタムアダプター・パイプラインステップの拡張 *(v0.3.0)*
|
|
17
|
+
- **Git Workspace管理**: タスクごとに独立したworktreeを自動作成・管理
|
|
18
|
+
- **依存関係解決**: タスク間の依存関係を自動検出し、適切な順序で実行
|
|
19
|
+
- **リアルタイム監視**: Webダッシュボードで進捗・ログをリアルタイム表示
|
|
20
|
+
- **構造化ログ**: pinoによる構造化ログで詳細なトレーシングが可能
|
|
21
|
+
|
|
22
|
+
## アーキテクチャ
|
|
23
|
+
|
|
24
|
+
AADは12個のモジュールで構成された**Hexagonal-flavored Modular Monolith**です:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
cli (エントリポイント)
|
|
28
|
+
├── planning (要件→タスク分解) ★
|
|
29
|
+
├── task-queue (タスクライフサイクル管理)
|
|
30
|
+
├── task-execution (TDDパイプライン実行) ★
|
|
31
|
+
├── process-manager (ワーカープロセス管理)
|
|
32
|
+
├── git-workspace (worktree/branch管理)
|
|
33
|
+
├── claude-provider (SDK/CLI抽象化) ★
|
|
34
|
+
├── persistence (状態永続化) ★
|
|
35
|
+
├── logging (構造化ログ)
|
|
36
|
+
├── dashboard (Web UI)
|
|
37
|
+
├── multi-repo (マルチリポジトリ統合) ★
|
|
38
|
+
└── plugin (プラグインシステム) ★
|
|
39
|
+
|
|
40
|
+
★ = Ports & Adapters パターン適用
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 技術スタック
|
|
44
|
+
|
|
45
|
+
- **言語**: TypeScript (strict mode)
|
|
46
|
+
- **ランタイム**: Bun v1.2+
|
|
47
|
+
- **テストフレームワーク**: bun:test (Jest互換API)
|
|
48
|
+
- **Web Framework**: Hono
|
|
49
|
+
- **ロガー**: pino v9
|
|
50
|
+
- **バリデーション**: Zod
|
|
51
|
+
- **CLI**: Commander.js
|
|
52
|
+
|
|
53
|
+
## インストール
|
|
54
|
+
|
|
55
|
+
> ⚠️ **Bun専用** — AADはBunランタイムが必要です。Node.jsでは動作しません。
|
|
56
|
+
|
|
57
|
+
### 前提条件
|
|
58
|
+
|
|
59
|
+
- [Bun](https://bun.sh/) v1.2 以上
|
|
60
|
+
- Git
|
|
61
|
+
- Claude CLI (オプション: CLI adapter使用時)
|
|
62
|
+
|
|
63
|
+
### インストール不要で実行 (推奨)
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
bunx aad run requirements.md
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### グローバルインストール
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
bun install -g aad
|
|
73
|
+
aad run requirements.md
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### スタンドアロンバイナリ
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git clone https://github.com/ronkovic/aad.git
|
|
80
|
+
cd aad && bun install
|
|
81
|
+
bun run build # → ./aad バイナリを生成
|
|
82
|
+
./aad --version
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### ローカル開発
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
git clone https://github.com/ronkovic/aad.git
|
|
89
|
+
cd aad
|
|
90
|
+
bun install
|
|
91
|
+
bun run dev
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## クイックスタート
|
|
95
|
+
|
|
96
|
+
### 1. 要件ファイルの作成
|
|
97
|
+
|
|
98
|
+
`requirements.md` を作成してタスクを記述:
|
|
99
|
+
|
|
100
|
+
```markdown
|
|
101
|
+
# タスク一覧
|
|
102
|
+
|
|
103
|
+
## Task 1: ユーザー認証機能の実装
|
|
104
|
+
- ファイル: src/auth/login.ts
|
|
105
|
+
- テスト: src/auth/__tests__/login.test.ts
|
|
106
|
+
- 依存: なし
|
|
107
|
+
|
|
108
|
+
## Task 2: ダッシュボードUI作成
|
|
109
|
+
- ファイル: src/ui/dashboard.tsx
|
|
110
|
+
- テスト: src/ui/__tests__/dashboard.test.tsx
|
|
111
|
+
- 依存: Task 1
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 2. タスク実行
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
aad run requirements.md --workers 4
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
AADが自動的に:
|
|
121
|
+
1. タスクを分解・依存関係を解析
|
|
122
|
+
2. 各タスク用のworktreeを作成
|
|
123
|
+
3. 4つのワーカーで並列実行 (TDD: テスト作成 → 実装 → 検証 → レビュー)
|
|
124
|
+
4. 親ブランチへのマージ
|
|
125
|
+
|
|
126
|
+
### 3. 進捗確認
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# ステータス確認
|
|
130
|
+
aad status
|
|
131
|
+
|
|
132
|
+
# 実行の再開
|
|
133
|
+
aad resume <run_id>
|
|
134
|
+
|
|
135
|
+
# worktreeのクリーンアップ
|
|
136
|
+
aad cleanup
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 4. Webダッシュボード (オプション)
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
aad run requirements.md --workers 4 --dashboard
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
ブラウザで `http://localhost:7333` を開くと、リアルタイムで進捗・ログ・依存グラフを確認できます。
|
|
146
|
+
|
|
147
|
+
## 開発ガイド
|
|
148
|
+
|
|
149
|
+
### 環境セットアップ
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# 依存関係のインストール
|
|
153
|
+
bun install
|
|
154
|
+
|
|
155
|
+
# 型チェック
|
|
156
|
+
bun run typecheck
|
|
157
|
+
|
|
158
|
+
# テスト実行
|
|
159
|
+
bun test
|
|
160
|
+
|
|
161
|
+
# ウォッチモード
|
|
162
|
+
bun test --watch
|
|
163
|
+
|
|
164
|
+
# Lint
|
|
165
|
+
bun run lint
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### ビルド & 配布
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# 単一バイナリのビルド (self-contained実行ファイル)
|
|
172
|
+
bun build --compile ./src/main.ts --outfile aad
|
|
173
|
+
|
|
174
|
+
# 実行
|
|
175
|
+
./aad --version
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### モジュール境界ルール
|
|
179
|
+
|
|
180
|
+
各モジュールは `index.ts` でのみ公開APIをエクスポートします:
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
// ✅ Good
|
|
184
|
+
import { TaskQueue } from "@aad/task-queue";
|
|
185
|
+
|
|
186
|
+
// ❌ Bad: モジュール内部へのアクセス
|
|
187
|
+
import { Dispatcher } from "@aad/task-queue/dispatcher";
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### テストの書き方
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import { describe, test, expect } from "bun:test";
|
|
194
|
+
|
|
195
|
+
describe("MyModule", () => {
|
|
196
|
+
test("should do something", () => {
|
|
197
|
+
expect(true).toBe(true);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## 設定
|
|
203
|
+
|
|
204
|
+
`.aad/config.json` で動作をカスタマイズできます:
|
|
205
|
+
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"workers": {
|
|
209
|
+
"num": 4,
|
|
210
|
+
"max": 8
|
|
211
|
+
},
|
|
212
|
+
"models": {
|
|
213
|
+
"default": "sonnet",
|
|
214
|
+
"splitter": "sonnet",
|
|
215
|
+
"tester": "sonnet",
|
|
216
|
+
"implementer": "sonnet",
|
|
217
|
+
"reviewer": "sonnet",
|
|
218
|
+
"mergeResolver": "haiku"
|
|
219
|
+
},
|
|
220
|
+
"timeouts": {
|
|
221
|
+
"claude": 1200,
|
|
222
|
+
"test": 600
|
|
223
|
+
},
|
|
224
|
+
"dashboard": {
|
|
225
|
+
"enabled": true,
|
|
226
|
+
"port": 7333
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### 利用可能なモデル
|
|
232
|
+
|
|
233
|
+
`models`設定で指定できるモデル名:
|
|
234
|
+
|
|
235
|
+
| フルネーム | 短縮名 | 説明 |
|
|
236
|
+
|-----------|--------|------|
|
|
237
|
+
| claude-sonnet-4-5-20250929 | sonnet | Sonnet 4.5 - バランス型、推奨デフォルト |
|
|
238
|
+
| claude-opus-4-6 | opus | Opus 4.6 - 最高性能、高コスト |
|
|
239
|
+
| claude-haiku-4-5-20251001 | haiku | Haiku 4.5 - 高速・低コスト |
|
|
240
|
+
|
|
241
|
+
設定では短縮名(`sonnet`、`opus`、`haiku`)またはフルネームが使用できます。
|
|
242
|
+
|
|
243
|
+
## Claude Provider: SDK vs CLI
|
|
244
|
+
|
|
245
|
+
AADはClaudeとの通信に2つのadapterを提供:
|
|
246
|
+
|
|
247
|
+
- **SDK Adapter** (`claude-sdk.adapter.ts`): Agent SDK経由、構造化出力に最適
|
|
248
|
+
- **CLI Adapter** (`claude-cli.adapter.ts`): Claude CLI経由、重いファイル操作に最適
|
|
249
|
+
|
|
250
|
+
サービスごとに使い分け可能:
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
const providerConfig = {
|
|
254
|
+
default: 'cli',
|
|
255
|
+
overrides: {
|
|
256
|
+
splitter: 'sdk', // 構造化JSON出力
|
|
257
|
+
reviewer: 'sdk', // 構造化レビュー結果
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## トラブルシューティング
|
|
263
|
+
|
|
264
|
+
### ログの確認
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
# 構造化ログの確認
|
|
268
|
+
cat .aad/docs/<run_id>/logs/structured.jsonl | bunx pino-pretty
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Worktreeが残っている
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
aad cleanup --all
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### タスクがスタックした
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
# ステータス確認
|
|
281
|
+
aad status <run_id>
|
|
282
|
+
|
|
283
|
+
# 強制クリーンアップ
|
|
284
|
+
aad cleanup --run <run_id> --force
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## ライセンス
|
|
288
|
+
|
|
289
|
+
[MIT License](LICENSE)
|
|
290
|
+
|
|
291
|
+
## 貢献
|
|
292
|
+
|
|
293
|
+
コントリビューションを歓迎します! 詳細は [CONTRIBUTING.md](CONTRIBUTING.md) をご覧ください。
|
|
294
|
+
|
|
295
|
+
### 開発の流れ
|
|
296
|
+
|
|
297
|
+
1. このリポジトリをfork
|
|
298
|
+
2. feature branchを作成 (`git checkout -b feature/amazing-feature`)
|
|
299
|
+
3. 変更をcommit (`git commit -m 'feat: add amazing feature'`)
|
|
300
|
+
4. branchをpush (`git push origin feature/amazing-feature`)
|
|
301
|
+
5. Pull Requestを作成
|
|
302
|
+
|
|
303
|
+
## リンク
|
|
304
|
+
|
|
305
|
+
- [Architecture Documentation](docs/architecture.md)
|
|
306
|
+
- [Rewrite Plan (Internal)](docs/rewrite-plan/README.md)
|
|
307
|
+
- [Project Guidelines](CLAUDE.md)
|
|
308
|
+
- [Issue Tracker](https://github.com/ronkovic/aad/issues)
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
Built with ❤️ using [Bun](https://bun.sh/) and [Claude](https://www.anthropic.com/claude)
|
package/bin/aad.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ronkovic/aad",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Autonomous Agent Development Orchestrator - Multi-agent TDD pipeline powered by Claude",
|
|
5
|
+
"module": "src/main.ts",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"bin": {
|
|
9
|
+
"aad": "./bin/aad.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin",
|
|
13
|
+
"src",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"tsconfig.json"
|
|
17
|
+
],
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"bun": "./src/main.ts",
|
|
21
|
+
"import": "./src/main.ts",
|
|
22
|
+
"types": "./src/main.ts"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"dev": "bun run src/main.ts",
|
|
30
|
+
"build": "bun build --compile ./src/main.ts --outfile aad",
|
|
31
|
+
"test": "bun test",
|
|
32
|
+
"test:watch": "bun test --watch",
|
|
33
|
+
"test:coverage": "bun test --coverage",
|
|
34
|
+
"test:coverage:report": "bun test --coverage --coverage-reporter=html",
|
|
35
|
+
"typecheck": "tsc --noEmit",
|
|
36
|
+
"lint": "eslint src/",
|
|
37
|
+
"prepublishOnly": "bun run typecheck && bun run lint && bun test"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.34",
|
|
41
|
+
"hono": "^4.6",
|
|
42
|
+
"pino": "^9.6",
|
|
43
|
+
"pino-pretty": "^13.0",
|
|
44
|
+
"chokidar": "^4.0",
|
|
45
|
+
"commander": "^13.0",
|
|
46
|
+
"zod": "^3.24"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/bun": "latest",
|
|
50
|
+
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
51
|
+
"@typescript-eslint/parser": "^8.54.0",
|
|
52
|
+
"eslint": "^10.0.0",
|
|
53
|
+
"typescript": "^5.7"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"bun": ">=1.2.0"
|
|
57
|
+
},
|
|
58
|
+
"repository": {
|
|
59
|
+
"type": "git",
|
|
60
|
+
"url": "git+https://github.com/ronkovic/aad.git"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://github.com/ronkovic/aad#readme",
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://github.com/ronkovic/aad/issues"
|
|
65
|
+
},
|
|
66
|
+
"author": "ronkovic",
|
|
67
|
+
"keywords": [
|
|
68
|
+
"claude",
|
|
69
|
+
"agent",
|
|
70
|
+
"tdd",
|
|
71
|
+
"orchestrator",
|
|
72
|
+
"multi-agent",
|
|
73
|
+
"autonomous",
|
|
74
|
+
"development",
|
|
75
|
+
"bun"
|
|
76
|
+
],
|
|
77
|
+
"license": "MIT"
|
|
78
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E Pipeline Test — lightweight, mock-based
|
|
3
|
+
* Tests the full planning → dispatch → completion flow without real Claude API or git.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, test, expect, beforeEach } from "bun:test";
|
|
6
|
+
import { EventBus, type AADEvent } from "@aad/shared/events";
|
|
7
|
+
import { PlanningService } from "@aad/planning/planning.service";
|
|
8
|
+
import { Dispatcher } from "@aad/task-queue/dispatcher";
|
|
9
|
+
import { createStores } from "@aad/persistence/index";
|
|
10
|
+
import {
|
|
11
|
+
createRunId,
|
|
12
|
+
createTaskId,
|
|
13
|
+
createWorkerId,
|
|
14
|
+
type TaskId,
|
|
15
|
+
} from "@aad/shared/types";
|
|
16
|
+
import type { Config } from "@aad/shared/config";
|
|
17
|
+
import type {
|
|
18
|
+
ClaudeProvider,
|
|
19
|
+
ClaudeRequest,
|
|
20
|
+
ClaudeResponse,
|
|
21
|
+
} from "@aad/claude-provider/claude-provider.port";
|
|
22
|
+
import type { FileChecker } from "@aad/planning/project-detection";
|
|
23
|
+
import type pino from "pino";
|
|
24
|
+
|
|
25
|
+
// ── Helpers ──────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
class MockClaudeProvider implements ClaudeProvider {
|
|
28
|
+
mockResponse: ClaudeResponse = {
|
|
29
|
+
result: "",
|
|
30
|
+
exitCode: 0,
|
|
31
|
+
model: "claude-sonnet-4-5",
|
|
32
|
+
effortLevel: "medium",
|
|
33
|
+
duration: 100,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
async call(_req: ClaudeRequest): Promise<ClaudeResponse> {
|
|
37
|
+
return this.mockResponse;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function createMockLogger(): pino.Logger {
|
|
42
|
+
const noop = () => {};
|
|
43
|
+
return {
|
|
44
|
+
info: noop, warn: noop, error: noop, debug: noop, trace: noop, fatal: noop,
|
|
45
|
+
child: () => createMockLogger(),
|
|
46
|
+
} as unknown as pino.Logger;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function createMockConfig(): Config {
|
|
50
|
+
return {
|
|
51
|
+
workers: { num: 2, max: 4 },
|
|
52
|
+
models: {},
|
|
53
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
54
|
+
retry: { maxRetries: 2 },
|
|
55
|
+
debug: false,
|
|
56
|
+
adaptiveEffort: false,
|
|
57
|
+
teams: { splitter: false, reviewer: false },
|
|
58
|
+
memorySync: false,
|
|
59
|
+
dashboard: { enabled: false, port: 7333, host: "localhost" },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const mockFileChecker: FileChecker = {
|
|
64
|
+
exists: async () => false,
|
|
65
|
+
readText: async () => "",
|
|
66
|
+
glob: async () => [],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/** Collect specific event types into an array */
|
|
70
|
+
function collectEvents(eventBus: EventBus, ...types: string[]) {
|
|
71
|
+
const collected: AADEvent[] = [];
|
|
72
|
+
for (const t of types) {
|
|
73
|
+
eventBus.on(t as AADEvent["type"], (e: AADEvent) => collected.push(e));
|
|
74
|
+
}
|
|
75
|
+
return collected;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Wait until predicate is true (polling) */
|
|
79
|
+
function waitFor(predicate: () => boolean, timeoutMs = 5000): Promise<void> {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
const start = Date.now();
|
|
82
|
+
const check = () => {
|
|
83
|
+
if (predicate()) return resolve();
|
|
84
|
+
if (Date.now() - start > timeoutMs) return reject(new Error("waitFor timeout"));
|
|
85
|
+
setTimeout(check, 20);
|
|
86
|
+
};
|
|
87
|
+
check();
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ── Tests ────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
describe("E2E Pipeline", () => {
|
|
94
|
+
let eventBus: EventBus;
|
|
95
|
+
let mockProvider: MockClaudeProvider;
|
|
96
|
+
let logger: pino.Logger;
|
|
97
|
+
|
|
98
|
+
beforeEach(() => {
|
|
99
|
+
eventBus = new EventBus();
|
|
100
|
+
mockProvider = new MockClaudeProvider();
|
|
101
|
+
logger = createMockLogger();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("basic pipeline: plan → dispatch → complete", async () => {
|
|
105
|
+
const config = createMockConfig();
|
|
106
|
+
const stores = createStores("memory");
|
|
107
|
+
const runId = createRunId("e2e-run-1");
|
|
108
|
+
|
|
109
|
+
// 1) Planning — mock Claude to return a 2-task plan
|
|
110
|
+
mockProvider.mockResponse = {
|
|
111
|
+
result: JSON.stringify({
|
|
112
|
+
run_id: "e2e-run-1",
|
|
113
|
+
parent_branch: "main",
|
|
114
|
+
tasks: [
|
|
115
|
+
{ task_id: "task-1", title: "Task 1", description: "First", files_to_modify: ["a.ts"], depends_on: [], priority: 1 },
|
|
116
|
+
{ task_id: "task-2", title: "Task 2", description: "Second", files_to_modify: ["b.ts"], depends_on: [], priority: 2 },
|
|
117
|
+
],
|
|
118
|
+
}),
|
|
119
|
+
exitCode: 0,
|
|
120
|
+
model: "claude-sonnet-4-5",
|
|
121
|
+
effortLevel: "medium",
|
|
122
|
+
duration: 100,
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const planningService = new PlanningService(mockProvider, eventBus, config, logger, { fileChecker: mockFileChecker });
|
|
126
|
+
|
|
127
|
+
const events = collectEvents(eventBus,
|
|
128
|
+
"planning:started", "planning:completed", "task:dispatched", "run:completed",
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const taskPlan = await planningService.planTasks({
|
|
132
|
+
runId,
|
|
133
|
+
parentBranch: "main",
|
|
134
|
+
requirementsPath: "/fake/requirements.md",
|
|
135
|
+
targetDocsDir: "/fake/docs",
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
expect(taskPlan.tasks).toHaveLength(2);
|
|
139
|
+
|
|
140
|
+
// 2) Dispatch
|
|
141
|
+
// Register idle workers
|
|
142
|
+
const w1 = createWorkerId("worker-1");
|
|
143
|
+
const w2 = createWorkerId("worker-2");
|
|
144
|
+
await stores.workerStore.save({ workerId: w1, status: "idle", currentTask: null });
|
|
145
|
+
await stores.workerStore.save({ workerId: w2, status: "idle", currentTask: null });
|
|
146
|
+
|
|
147
|
+
const dispatcher = new Dispatcher({
|
|
148
|
+
taskStore: stores.taskStore,
|
|
149
|
+
workerStore: stores.workerStore,
|
|
150
|
+
runStore: stores.runStore,
|
|
151
|
+
eventBus,
|
|
152
|
+
config: { maxRetries: 2 },
|
|
153
|
+
logger,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
await dispatcher.initialize(taskPlan);
|
|
157
|
+
dispatcher.start();
|
|
158
|
+
|
|
159
|
+
// Wait for tasks to be dispatched
|
|
160
|
+
await waitFor(() => events.some(e => e.type === "task:dispatched"), 3000);
|
|
161
|
+
|
|
162
|
+
// 3) Simulate task completion
|
|
163
|
+
eventBus.emit({ type: "task:completed", taskId: createTaskId("task-1"), result: { taskId: createTaskId("task-1"), status: "completed", duration: 50 } });
|
|
164
|
+
eventBus.emit({ type: "task:completed", taskId: createTaskId("task-2"), result: { taskId: createTaskId("task-2"), status: "completed", duration: 60 } });
|
|
165
|
+
|
|
166
|
+
await waitFor(() => events.some(e => e.type === "run:completed"), 3000);
|
|
167
|
+
|
|
168
|
+
// Verify event flow
|
|
169
|
+
const types = events.map(e => e.type);
|
|
170
|
+
expect(types).toContain("planning:started");
|
|
171
|
+
expect(types).toContain("planning:completed");
|
|
172
|
+
expect(types).toContain("task:dispatched");
|
|
173
|
+
expect(types).toContain("run:completed");
|
|
174
|
+
|
|
175
|
+
dispatcher.stop();
|
|
176
|
+
}, 15_000);
|
|
177
|
+
|
|
178
|
+
test("error handling: Claude failure emits planning:failed", async () => {
|
|
179
|
+
const config = createMockConfig();
|
|
180
|
+
|
|
181
|
+
mockProvider.mockResponse = {
|
|
182
|
+
result: "API Error",
|
|
183
|
+
exitCode: 1,
|
|
184
|
+
model: "claude-sonnet-4-5",
|
|
185
|
+
effortLevel: "medium",
|
|
186
|
+
duration: 50,
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const planningService = new PlanningService(mockProvider, eventBus, config, logger, { fileChecker: mockFileChecker });
|
|
190
|
+
|
|
191
|
+
const events = collectEvents(eventBus, "planning:started", "planning:failed");
|
|
192
|
+
|
|
193
|
+
await expect(
|
|
194
|
+
planningService.planTasks({
|
|
195
|
+
runId: createRunId("e2e-err"),
|
|
196
|
+
parentBranch: "main",
|
|
197
|
+
requirementsPath: "/fake/req.md",
|
|
198
|
+
targetDocsDir: "/fake/docs",
|
|
199
|
+
})
|
|
200
|
+
).rejects.toThrow();
|
|
201
|
+
|
|
202
|
+
expect(events.some(e => e.type === "planning:failed")).toBe(true);
|
|
203
|
+
const failedEvent = events.find(e => e.type === "planning:failed") as Extract<AADEvent, { type: "planning:failed" }>;
|
|
204
|
+
expect(failedEvent.error).toContain("exit code 1");
|
|
205
|
+
}, 15_000);
|
|
206
|
+
|
|
207
|
+
test("task dependency: task-2 dispatched only after task-1 completes", async () => {
|
|
208
|
+
const config = createMockConfig();
|
|
209
|
+
const stores = createStores("memory");
|
|
210
|
+
|
|
211
|
+
// Plan with dependency: task-2 depends on task-1
|
|
212
|
+
mockProvider.mockResponse = {
|
|
213
|
+
result: JSON.stringify({
|
|
214
|
+
run_id: "e2e-dep",
|
|
215
|
+
parent_branch: "main",
|
|
216
|
+
tasks: [
|
|
217
|
+
{ task_id: "task-1", title: "Task 1", description: "First", files_to_modify: ["a.ts"], depends_on: [], priority: 1 },
|
|
218
|
+
{ task_id: "task-2", title: "Task 2", description: "Second", files_to_modify: ["b.ts"], depends_on: ["task-1"], priority: 2 },
|
|
219
|
+
],
|
|
220
|
+
}),
|
|
221
|
+
exitCode: 0,
|
|
222
|
+
model: "claude-sonnet-4-5",
|
|
223
|
+
effortLevel: "medium",
|
|
224
|
+
duration: 100,
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const planningService = new PlanningService(mockProvider, eventBus, config, logger, { fileChecker: mockFileChecker });
|
|
228
|
+
const taskPlan = await planningService.planTasks({
|
|
229
|
+
runId: createRunId("e2e-dep"),
|
|
230
|
+
parentBranch: "main",
|
|
231
|
+
requirementsPath: "/fake/req.md",
|
|
232
|
+
targetDocsDir: "/fake/docs",
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Set up workers
|
|
236
|
+
await stores.workerStore.save({ workerId: createWorkerId("w-1"), status: "idle", currentTask: null });
|
|
237
|
+
await stores.workerStore.save({ workerId: createWorkerId("w-2"), status: "idle", currentTask: null });
|
|
238
|
+
|
|
239
|
+
const dispatchedTasks: TaskId[] = [];
|
|
240
|
+
eventBus.on("task:dispatched", (e: AADEvent) => {
|
|
241
|
+
if (e.type === "task:dispatched") dispatchedTasks.push(e.taskId);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const dispatcher = new Dispatcher({
|
|
245
|
+
taskStore: stores.taskStore,
|
|
246
|
+
workerStore: stores.workerStore,
|
|
247
|
+
runStore: stores.runStore,
|
|
248
|
+
eventBus,
|
|
249
|
+
config: { maxRetries: 0 },
|
|
250
|
+
logger,
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
await dispatcher.initialize(taskPlan);
|
|
254
|
+
dispatcher.start();
|
|
255
|
+
|
|
256
|
+
// Wait for first dispatch
|
|
257
|
+
await waitFor(() => dispatchedTasks.length >= 1, 3000);
|
|
258
|
+
|
|
259
|
+
// Only task-1 should be dispatched (task-2 blocked by dependency)
|
|
260
|
+
expect(dispatchedTasks).toHaveLength(1);
|
|
261
|
+
expect(dispatchedTasks[0]).toBe(createTaskId("task-1"));
|
|
262
|
+
|
|
263
|
+
// Re-add worker as idle (it was marked busy)
|
|
264
|
+
await stores.workerStore.save({ workerId: createWorkerId("w-1"), status: "idle", currentTask: null });
|
|
265
|
+
|
|
266
|
+
// Complete task-1 → should unblock task-2
|
|
267
|
+
eventBus.emit({
|
|
268
|
+
type: "task:completed",
|
|
269
|
+
taskId: createTaskId("task-1"),
|
|
270
|
+
result: { taskId: createTaskId("task-1"), status: "completed", duration: 50 },
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
await waitFor(() => dispatchedTasks.length >= 2, 3000);
|
|
274
|
+
|
|
275
|
+
expect(dispatchedTasks[1]).toBe(createTaskId("task-2"));
|
|
276
|
+
|
|
277
|
+
dispatcher.stop();
|
|
278
|
+
}, 15_000);
|
|
279
|
+
});
|