@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.
Files changed (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +312 -0
  3. package/bin/aad.js +2 -0
  4. package/package.json +78 -0
  5. package/src/__tests__/e2e/pipeline-e2e.test.ts +279 -0
  6. package/src/__tests__/e2e/resume-e2e.test.ts +200 -0
  7. package/src/__tests__/integration/cli-smoke.test.ts +175 -0
  8. package/src/__tests__/integration/pipeline.test.ts +346 -0
  9. package/src/bun-imports.d.ts +14 -0
  10. package/src/main.ts +52 -0
  11. package/src/modules/claude-provider/__tests__/claude-cli.adapter.test.ts +277 -0
  12. package/src/modules/claude-provider/__tests__/claude-sdk-real-env.test.ts +127 -0
  13. package/src/modules/claude-provider/__tests__/claude-sdk.adapter.test.ts +347 -0
  14. package/src/modules/claude-provider/__tests__/effort-strategy.test.ts +212 -0
  15. package/src/modules/claude-provider/__tests__/provider-registry.test.ts +251 -0
  16. package/src/modules/claude-provider/__tests__/retry.test.ts +201 -0
  17. package/src/modules/claude-provider/claude-cli.adapter.ts +156 -0
  18. package/src/modules/claude-provider/claude-provider.port.ts +35 -0
  19. package/src/modules/claude-provider/claude-sdk.adapter.ts +217 -0
  20. package/src/modules/claude-provider/effort-strategy.ts +94 -0
  21. package/src/modules/claude-provider/index.ts +32 -0
  22. package/src/modules/claude-provider/provider-registry.ts +92 -0
  23. package/src/modules/claude-provider/retry.ts +81 -0
  24. package/src/modules/cli/__tests__/app.test.ts +160 -0
  25. package/src/modules/cli/__tests__/cleanup.test.ts +111 -0
  26. package/src/modules/cli/__tests__/commands.test.ts +186 -0
  27. package/src/modules/cli/__tests__/output.test.ts +329 -0
  28. package/src/modules/cli/__tests__/resume.test.ts +324 -0
  29. package/src/modules/cli/__tests__/run.test.ts +168 -0
  30. package/src/modules/cli/__tests__/shutdown.test.ts +168 -0
  31. package/src/modules/cli/__tests__/status.test.ts +144 -0
  32. package/src/modules/cli/app.ts +241 -0
  33. package/src/modules/cli/commands/cleanup.ts +120 -0
  34. package/src/modules/cli/commands/resume.ts +156 -0
  35. package/src/modules/cli/commands/run.ts +322 -0
  36. package/src/modules/cli/commands/status.ts +101 -0
  37. package/src/modules/cli/index.ts +29 -0
  38. package/src/modules/cli/output.ts +256 -0
  39. package/src/modules/cli/shutdown.ts +122 -0
  40. package/src/modules/dashboard/__tests__/api-routes.test.ts +204 -0
  41. package/src/modules/dashboard/__tests__/file-watcher.test.ts +34 -0
  42. package/src/modules/dashboard/__tests__/server.test.ts +120 -0
  43. package/src/modules/dashboard/__tests__/sse-broadcaster.test.ts +163 -0
  44. package/src/modules/dashboard/__tests__/sse-routes.test.ts +58 -0
  45. package/src/modules/dashboard/__tests__/state-aggregator.test.ts +330 -0
  46. package/src/modules/dashboard/index.ts +8 -0
  47. package/src/modules/dashboard/routes/api.ts +84 -0
  48. package/src/modules/dashboard/routes/sse.ts +37 -0
  49. package/src/modules/dashboard/server.ts +111 -0
  50. package/src/modules/dashboard/services/file-watcher.ts +36 -0
  51. package/src/modules/dashboard/services/sse-broadcaster.ts +81 -0
  52. package/src/modules/dashboard/services/state-aggregator.ts +132 -0
  53. package/src/modules/dashboard/ui/dashboard.html +405 -0
  54. package/src/modules/git-workspace/__tests__/branch-manager.test.ts +335 -0
  55. package/src/modules/git-workspace/__tests__/git-exec.test.ts +91 -0
  56. package/src/modules/git-workspace/__tests__/memory-sync.test.ts +273 -0
  57. package/src/modules/git-workspace/__tests__/merge-service.test.ts +286 -0
  58. package/src/modules/git-workspace/__tests__/settings-merge.test.ts +163 -0
  59. package/src/modules/git-workspace/__tests__/worktree-manager.test.ts +247 -0
  60. package/src/modules/git-workspace/branch-manager.ts +191 -0
  61. package/src/modules/git-workspace/git-exec.ts +124 -0
  62. package/src/modules/git-workspace/index.ts +17 -0
  63. package/src/modules/git-workspace/memory-sync.ts +89 -0
  64. package/src/modules/git-workspace/merge-service.ts +156 -0
  65. package/src/modules/git-workspace/settings-merge.ts +95 -0
  66. package/src/modules/git-workspace/worktree-manager.ts +199 -0
  67. package/src/modules/logging/__tests__/log-store.test.ts +242 -0
  68. package/src/modules/logging/__tests__/logger.test.ts +81 -0
  69. package/src/modules/logging/__tests__/sse-transport.test.ts +93 -0
  70. package/src/modules/logging/index.ts +7 -0
  71. package/src/modules/logging/log-store.ts +80 -0
  72. package/src/modules/logging/logger.ts +55 -0
  73. package/src/modules/logging/transports/sse-transport.ts +28 -0
  74. package/src/modules/multi-repo/__tests__/multi-repo-planner.test.ts +93 -0
  75. package/src/modules/multi-repo/__tests__/repo-context.test.ts +79 -0
  76. package/src/modules/multi-repo/index.ts +12 -0
  77. package/src/modules/multi-repo/multi-repo-planner.ts +112 -0
  78. package/src/modules/multi-repo/repo-context.ts +71 -0
  79. package/src/modules/persistence/__tests__/.tmp-stores-test-81991/progress.json +10 -0
  80. package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/completed/task-getall-2.json +10 -0
  81. package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/pending/task-1.json +13 -0
  82. package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/pending/task-getall-1.json +10 -0
  83. package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/pending/task-status-change.json +10 -0
  84. package/src/modules/persistence/__tests__/.tmp-stores-test-81991/workers/worker-1.json +5 -0
  85. package/src/modules/persistence/__tests__/.tmp-stores-test-81991/workers/worker-2.json +5 -0
  86. package/src/modules/persistence/__tests__/.tmp-stores-test-82469/progress.json +10 -0
  87. package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/completed/task-getall-2.json +10 -0
  88. package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/pending/task-1.json +13 -0
  89. package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/pending/task-getall-1.json +10 -0
  90. package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/pending/task-status-change.json +10 -0
  91. package/src/modules/persistence/__tests__/.tmp-stores-test-82469/workers/worker-1.json +5 -0
  92. package/src/modules/persistence/__tests__/.tmp-stores-test-82469/workers/worker-2.json +5 -0
  93. package/src/modules/persistence/__tests__/.tmp-stores-test-85301/progress.json +10 -0
  94. package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/completed/task-getall-2.json +10 -0
  95. package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/pending/task-1.json +13 -0
  96. package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/pending/task-getall-1.json +10 -0
  97. package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/pending/task-status-change.json +10 -0
  98. package/src/modules/persistence/__tests__/.tmp-stores-test-85301/workers/worker-1.json +5 -0
  99. package/src/modules/persistence/__tests__/.tmp-stores-test-85301/workers/worker-2.json +5 -0
  100. package/src/modules/persistence/__tests__/.tmp-stores-test-85759/progress.json +10 -0
  101. package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/completed/task-getall-2.json +10 -0
  102. package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/pending/task-1.json +13 -0
  103. package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/pending/task-getall-1.json +10 -0
  104. package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/pending/task-status-change.json +10 -0
  105. package/src/modules/persistence/__tests__/.tmp-stores-test-85759/workers/worker-1.json +5 -0
  106. package/src/modules/persistence/__tests__/.tmp-stores-test-85759/workers/worker-2.json +5 -0
  107. package/src/modules/persistence/__tests__/.tmp-stores-test-86184/progress.json +10 -0
  108. package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/completed/task-getall-2.json +10 -0
  109. package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/pending/task-1.json +13 -0
  110. package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/pending/task-getall-1.json +10 -0
  111. package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/pending/task-status-change.json +10 -0
  112. package/src/modules/persistence/__tests__/.tmp-stores-test-86184/workers/worker-1.json +5 -0
  113. package/src/modules/persistence/__tests__/.tmp-stores-test-86184/workers/worker-2.json +5 -0
  114. package/src/modules/persistence/__tests__/.tmp-stores-test-88026/progress.json +10 -0
  115. package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/completed/task-getall-2.json +10 -0
  116. package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/pending/task-1.json +13 -0
  117. package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/pending/task-getall-1.json +10 -0
  118. package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/pending/task-status-change.json +10 -0
  119. package/src/modules/persistence/__tests__/.tmp-stores-test-88026/workers/worker-1.json +5 -0
  120. package/src/modules/persistence/__tests__/.tmp-stores-test-88026/workers/worker-2.json +5 -0
  121. package/src/modules/persistence/__tests__/.tmp-stores-test-89475/progress.json +10 -0
  122. package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/completed/task-getall-2.json +10 -0
  123. package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/pending/task-1.json +13 -0
  124. package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/pending/task-getall-1.json +10 -0
  125. package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/pending/task-status-change.json +10 -0
  126. package/src/modules/persistence/__tests__/.tmp-stores-test-89475/workers/worker-1.json +5 -0
  127. package/src/modules/persistence/__tests__/.tmp-stores-test-89475/workers/worker-2.json +5 -0
  128. package/src/modules/persistence/__tests__/.tmp-stores-test-89924/progress.json +10 -0
  129. package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/completed/task-getall-2.json +10 -0
  130. package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/pending/task-1.json +13 -0
  131. package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/pending/task-getall-1.json +10 -0
  132. package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/pending/task-status-change.json +10 -0
  133. package/src/modules/persistence/__tests__/.tmp-stores-test-89924/workers/worker-1.json +5 -0
  134. package/src/modules/persistence/__tests__/.tmp-stores-test-89924/workers/worker-2.json +5 -0
  135. package/src/modules/persistence/__tests__/file-lock.test.ts +141 -0
  136. package/src/modules/persistence/__tests__/index.test.ts +38 -0
  137. package/src/modules/persistence/__tests__/stores.test.ts +594 -0
  138. package/src/modules/persistence/file-lock.ts +158 -0
  139. package/src/modules/persistence/fs-run-store.ts +73 -0
  140. package/src/modules/persistence/fs-task-store.ts +152 -0
  141. package/src/modules/persistence/fs-worker-store.ts +116 -0
  142. package/src/modules/persistence/in-memory-stores.ts +98 -0
  143. package/src/modules/persistence/index.ts +60 -0
  144. package/src/modules/persistence/stores.port.ts +60 -0
  145. package/src/modules/planning/__tests__/file-conflict-validator.test.ts +256 -0
  146. package/src/modules/planning/__tests__/planning-service.test.ts +366 -0
  147. package/src/modules/planning/__tests__/project-detection.test.ts +707 -0
  148. package/src/modules/planning/file-conflict-validator.ts +135 -0
  149. package/src/modules/planning/index.ts +40 -0
  150. package/src/modules/planning/planning.service.ts +262 -0
  151. package/src/modules/planning/project-detection.ts +525 -0
  152. package/src/modules/plugin/__tests__/plugin-loader.test.ts +83 -0
  153. package/src/modules/plugin/__tests__/plugin-manager.test.ts +187 -0
  154. package/src/modules/plugin/index.ts +3 -0
  155. package/src/modules/plugin/plugin-loader.ts +46 -0
  156. package/src/modules/plugin/plugin-manager.ts +90 -0
  157. package/src/modules/plugin/plugin.types.ts +37 -0
  158. package/src/modules/process-manager/__tests__/process-manager.test.ts +210 -0
  159. package/src/modules/process-manager/__tests__/worker.test.ts +89 -0
  160. package/src/modules/process-manager/index.ts +5 -0
  161. package/src/modules/process-manager/process-manager.ts +193 -0
  162. package/src/modules/process-manager/worker.ts +106 -0
  163. package/src/modules/task-execution/__tests__/default-spawner.test.ts +154 -0
  164. package/src/modules/task-execution/__tests__/executor.test.ts +760 -0
  165. package/src/modules/task-execution/__tests__/implementer-green.test.ts +286 -0
  166. package/src/modules/task-execution/__tests__/merge-phase.test.ts +368 -0
  167. package/src/modules/task-execution/__tests__/reviewer.test.ts +302 -0
  168. package/src/modules/task-execution/__tests__/tester-red.test.ts +281 -0
  169. package/src/modules/task-execution/__tests__/tester-verify.test.ts +313 -0
  170. package/src/modules/task-execution/executor.ts +303 -0
  171. package/src/modules/task-execution/index.ts +45 -0
  172. package/src/modules/task-execution/phases/default-spawner.ts +49 -0
  173. package/src/modules/task-execution/phases/implementer-green.ts +100 -0
  174. package/src/modules/task-execution/phases/merge.ts +122 -0
  175. package/src/modules/task-execution/phases/reviewer.ts +160 -0
  176. package/src/modules/task-execution/phases/tester-red.ts +100 -0
  177. package/src/modules/task-execution/phases/tester-verify.ts +120 -0
  178. package/src/modules/task-queue/__tests__/dependency-resolver.test.ts +456 -0
  179. package/src/modules/task-queue/__tests__/dispatcher.test.ts +824 -0
  180. package/src/modules/task-queue/__tests__/task-plan.test.ts +122 -0
  181. package/src/modules/task-queue/__tests__/task.test.ts +130 -0
  182. package/src/modules/task-queue/dependency-resolver.ts +171 -0
  183. package/src/modules/task-queue/dispatcher.ts +372 -0
  184. package/src/modules/task-queue/index.ts +16 -0
  185. package/src/modules/task-queue/task-plan.ts +40 -0
  186. package/src/modules/task-queue/task.ts +67 -0
  187. package/src/shared/__tests__/config.test.ts +204 -0
  188. package/src/shared/__tests__/errors.test.ts +285 -0
  189. package/src/shared/__tests__/events.test.ts +496 -0
  190. package/src/shared/__tests__/types.test.ts +360 -0
  191. package/src/shared/config.ts +133 -0
  192. package/src/shared/errors.ts +128 -0
  193. package/src/shared/events.ts +171 -0
  194. package/src/shared/types.ts +143 -0
  195. package/tsconfig.json +30 -0
@@ -0,0 +1,100 @@
1
+ import type { Task, WorkspaceInfo, EffortLevel } from "@aad/shared/types";
2
+ import type { ClaudeProvider } from "@aad/claude-provider";
3
+
4
+ export interface PhaseResult {
5
+ success: boolean;
6
+ output: string;
7
+ duration?: number;
8
+ }
9
+
10
+ export interface ImplementerGreenOptions {
11
+ effortLevel?: EffortLevel;
12
+ model?: string;
13
+ timeout?: number;
14
+ }
15
+
16
+ /**
17
+ * Build TDD Green phase prompt for implementer agent
18
+ */
19
+ export function buildGreenPhasePrompt(task: Task, workspace: WorkspaceInfo): string {
20
+ const codingConventions = getCodingConventions(workspace.language);
21
+
22
+ return `implementerエージェントとして、TDD Green フェーズを実行してください。
23
+
24
+ Task ID: ${task.taskId as string}
25
+ Task Title: ${task.title}
26
+ Task Description: ${task.description}
27
+
28
+ プロジェクト情報:
29
+ - Workspace: ${workspace.path}
30
+ - Language: ${workspace.language}
31
+ - Test Framework: ${workspace.testFramework}
32
+ - Package Manager: ${workspace.packageManager}
33
+ - Framework: ${workspace.framework}
34
+
35
+ 実行内容:
36
+ 1. 作成されたテストを確認する
37
+ 2. テストをパスするための最小限の実装を書く(${codingConventions})
38
+ 3. テストを実行してパスすることを確認する
39
+
40
+ 注意: 過度な最適化やリファクタリングは行わず、テストをパスするための最小限のコードを書いてください。`;
41
+ }
42
+
43
+ /**
44
+ * Get language-specific coding conventions
45
+ */
46
+ function getCodingConventions(language: string): string {
47
+ switch (language.toLowerCase()) {
48
+ case "go":
49
+ case "golang":
50
+ return "effective Go, gofmt規約に従う";
51
+
52
+ case "python":
53
+ return "PEP 8, Black/Ruff規約に従う";
54
+
55
+ case "rust":
56
+ return "Rust API guidelines, rustfmt規約に従う";
57
+
58
+ case "typescript":
59
+ case "javascript":
60
+ return "ESLint/Prettier規約に従う";
61
+
62
+ case "java":
63
+ return "Google Java Style Guideに従う";
64
+
65
+ case "c#":
66
+ case "csharp":
67
+ return "C# Coding Conventionsに従う";
68
+
69
+ default:
70
+ return "言語別のコーディング規約に従う";
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Run TDD Green phase: implement minimal code to pass tests
76
+ */
77
+ export async function runImplementerGreen(
78
+ task: Task,
79
+ workspace: WorkspaceInfo,
80
+ provider: ClaudeProvider,
81
+ options: ImplementerGreenOptions = {}
82
+ ): Promise<PhaseResult> {
83
+ const prompt = buildGreenPhasePrompt(task, workspace);
84
+
85
+ const response = await provider.call({
86
+ prompt,
87
+ cwd: workspace.path,
88
+ allowedTools: ["Read", "Write", "Edit", "Glob", "Grep", "Bash"],
89
+ permissionMode: "bypassPermissions",
90
+ effortLevel: options.effortLevel ?? "medium",
91
+ model: options.model,
92
+ timeout: options.timeout,
93
+ });
94
+
95
+ return {
96
+ success: response.exitCode === 0,
97
+ output: response.result,
98
+ duration: response.duration,
99
+ };
100
+ }
@@ -0,0 +1,122 @@
1
+ import type { Task, WorkspaceInfo } from "@aad/shared/types";
2
+ import type { ClaudeProvider } from "@aad/claude-provider";
3
+ import type { MergeService } from "@aad/git-workspace";
4
+
5
+ export interface MergePhaseResult {
6
+ success: boolean;
7
+ output: string;
8
+ hadConflict: boolean;
9
+ duration?: number;
10
+ }
11
+
12
+ export interface MergePhaseOptions {
13
+ mergeResolverModel?: string;
14
+ timeout?: number;
15
+ }
16
+
17
+ /**
18
+ * Build conflict resolution prompt for merge-resolver agent
19
+ */
20
+ export function buildConflictResolutionPrompt(
21
+ task: Task,
22
+ conflicts: string[]
23
+ ): string {
24
+ const conflictList = conflicts.map((f) => `- ${f}`).join("\n");
25
+
26
+ return `merge-resolverエージェントとして、マージ競合を解決してください。
27
+
28
+ Task ID: ${task.taskId as string}
29
+ Task Title: ${task.title}
30
+
31
+ 競合ファイル:
32
+ ${conflictList}
33
+
34
+ 実行内容:
35
+ 1. 競合マーカー (<<<<<<<, =======, >>>>>>>) を確認
36
+ 2. 両方の変更を理解し、適切にマージする
37
+ 3. 競合マーカーを削除する
38
+ 4. 解決したファイルを git add する
39
+
40
+ 注意:
41
+ - 両方の変更の意図を尊重し、機能を壊さないように解決してください
42
+ - 解決後は必ず git add <ファイル> を実行してください`;
43
+ }
44
+
45
+ /**
46
+ * Run merge phase: merge task branch to parent branch
47
+ * If conflicts occur, use Claude to resolve them
48
+ */
49
+ export async function runMergePhase(
50
+ task: Task,
51
+ _workspace: WorkspaceInfo,
52
+ taskBranch: string,
53
+ parentBranch: string,
54
+ parentWorktree: string,
55
+ mergeService: MergeService,
56
+ provider: ClaudeProvider,
57
+ options: MergePhaseOptions = {}
58
+ ): Promise<MergePhaseResult> {
59
+ const startTime = Date.now();
60
+
61
+ // Try to merge
62
+ const mergeResult = await mergeService.mergeToParent(
63
+ task.taskId,
64
+ taskBranch,
65
+ parentBranch,
66
+ parentWorktree
67
+ );
68
+
69
+ if (mergeResult.success) {
70
+ // Merge succeeded
71
+ return {
72
+ success: true,
73
+ output: mergeResult.message ?? "Merge completed successfully",
74
+ hadConflict: false,
75
+ duration: Date.now() - startTime,
76
+ };
77
+ }
78
+
79
+ // Merge failed with conflicts
80
+ const conflicts = mergeResult.conflicts ?? [];
81
+
82
+ if (conflicts.length === 0) {
83
+ // Merge failed but no conflicts detected (unexpected error)
84
+ return {
85
+ success: false,
86
+ output: mergeResult.message ?? "Merge failed (unknown reason)",
87
+ hadConflict: false,
88
+ duration: Date.now() - startTime,
89
+ };
90
+ }
91
+
92
+ // Resolve conflicts with Claude
93
+ const prompt = buildConflictResolutionPrompt(task, conflicts);
94
+
95
+ const response = await provider.call({
96
+ prompt,
97
+ cwd: parentWorktree,
98
+ allowedTools: ["Read", "Write", "Edit", "Glob", "Grep", "Bash"],
99
+ permissionMode: "bypassPermissions",
100
+ effortLevel: "low",
101
+ model: options.mergeResolverModel ?? "claude-haiku-4-5",
102
+ timeout: options.timeout,
103
+ });
104
+
105
+ const duration = Date.now() - startTime;
106
+
107
+ if (response.exitCode === 0) {
108
+ return {
109
+ success: true,
110
+ output: response.result,
111
+ hadConflict: true,
112
+ duration,
113
+ };
114
+ }
115
+
116
+ return {
117
+ success: false,
118
+ output: response.result,
119
+ hadConflict: true,
120
+ duration,
121
+ };
122
+ }
@@ -0,0 +1,160 @@
1
+ import type { Task, WorkspaceInfo, RunId } from "@aad/shared/types";
2
+ import type { ClaudeProvider, SubagentConfig } from "@aad/claude-provider";
3
+
4
+ export interface PhaseResult {
5
+ success: boolean;
6
+ output: string;
7
+ duration?: number;
8
+ }
9
+
10
+ export interface ReviewerOptions {
11
+ model?: string;
12
+ timeout?: number;
13
+ }
14
+
15
+ export interface ReviewConfig {
16
+ teams: {
17
+ reviewer: boolean;
18
+ };
19
+ }
20
+
21
+ /**
22
+ * Build solo review prompt
23
+ */
24
+ export function buildReviewPrompt(task: Task): string {
25
+ return `reviewerエージェントとして、コードレビューを実行してください。
26
+
27
+ Task ID: ${task.taskId as string}
28
+ Task Title: ${task.title}
29
+
30
+ 実行内容:
31
+ 1. 作成されたコードとテストを確認する
32
+ 2. 品質・セキュリティ・パフォーマンスの観点からレビューする
33
+ 3. 必要に応じて軽微な修正を行う
34
+ 4. 重大な問題があれば報告する
35
+
36
+ 注意: 軽微なスタイル問題は修正しても良いですが、機能を壊さないように注意してください。`;
37
+ }
38
+
39
+ /**
40
+ * Build Agent Teams parallel review prompt
41
+ */
42
+ export function buildTeamsReviewPrompt(task: Task, runId: RunId): string {
43
+ return `reviewerチームリーダーとして、Agent Teamsを使った並列コードレビューを実行してください。
44
+
45
+ Task ID: ${task.taskId as string}
46
+ Task Title: ${task.title}
47
+
48
+ 実行内容:
49
+ 1. TeamCreateツールを使用してレビューチームを作成
50
+ チーム名: review-${task.taskId as string}
51
+
52
+ 2. 以下の3人のチームメイトをTaskツールで作成(すべてsubagent_type: general-purposeを使用):
53
+ a) 品質レビュアー (名前: quality-reviewer)
54
+ - コードの可読性、保守性、DRY原則の遵守
55
+ - テストカバレッジと品質
56
+ - ドキュメントの充実度
57
+
58
+ b) セキュリティレビュアー (名前: security-reviewer)
59
+ - SQLインジェクション、XSS、CSRF対策
60
+ - 認証・認可の適切な実装
61
+ - 機密情報の扱い
62
+
63
+ c) パフォーマンスレビュアー (名前: performance-reviewer)
64
+ - N+1クエリ問題
65
+ - 不要なメモリアロケーション
66
+ - 適切なアルゴリズム選択
67
+
68
+ 3. 各チームメイトにタスクを割り当て(TaskCreateとTaskUpdate使用)
69
+
70
+ 4. 各チームメイトからのレビュー結果を収集
71
+
72
+ 5. 結果を統合してcode_review.mdを生成:
73
+ .aad/docs/${runId as string}/${task.taskId as string}/code_review.md
74
+
75
+ 6. TeamDeleteでチームをクリーンアップ
76
+
77
+ 重要な制約:
78
+ - teammateMode: "in-process" を使用してtmux干渉を避ける
79
+ - 各レビュアーの結果を必ず統合してcode_review.mdに保存
80
+ - 重大な問題が見つかった場合は明確にマーク
81
+
82
+ 注意: 軽微なスタイル問題は修正しても良いですが、機能を壊さないように注意してください。`;
83
+ }
84
+
85
+ /**
86
+ * Build subagent configs for reviewer teams mode
87
+ */
88
+ export function buildReviewerSubagents(): SubagentConfig[] {
89
+ return [
90
+ {
91
+ name: "quality-reviewer",
92
+ prompt: `品質レビューの専門家として、コードレビューを実施してください。
93
+ 以下の観点で評価してください:
94
+ - コードの可読性と保守性
95
+ - DRY原則の遵守
96
+ - テストカバレッジと品質
97
+ - ドキュメントの充実度
98
+ - 命名規約の一貫性`,
99
+ },
100
+ {
101
+ name: "security-reviewer",
102
+ prompt: `セキュリティレビューの専門家として、コードレビューを実施してください。
103
+ 以下の観点で評価してください:
104
+ - SQLインジェクション、XSS、CSRF対策
105
+ - 認証・認可の適切な実装
106
+ - 機密情報の扱い(ハードコードされた秘密鍵等)
107
+ - 入力バリデーション
108
+ - 依存パッケージの脆弱性`,
109
+ },
110
+ {
111
+ name: "performance-reviewer",
112
+ prompt: `パフォーマンスレビューの専門家として、コードレビューを実施してください。
113
+ 以下の観点で評価してください:
114
+ - N+1クエリ問題
115
+ - 不要なメモリアロケーション
116
+ - 適切なアルゴリズム選択
117
+ - キャッシュの活用
118
+ - 非同期処理の適切な使用`,
119
+ },
120
+ ];
121
+ }
122
+
123
+ /**
124
+ * Run code review phase (solo or teams mode)
125
+ * Note: Review failures are non-fatal; execution continues even on error
126
+ */
127
+ export async function runReviewer(
128
+ task: Task,
129
+ workspace: WorkspaceInfo,
130
+ runId: RunId,
131
+ config: ReviewConfig,
132
+ provider: ClaudeProvider,
133
+ options: ReviewerOptions = {}
134
+ ): Promise<PhaseResult> {
135
+ const useTeams = config.teams.reviewer;
136
+
137
+ const prompt = useTeams
138
+ ? buildTeamsReviewPrompt(task, runId)
139
+ : buildReviewPrompt(task);
140
+
141
+ const effortLevel = useTeams ? "high" : "medium";
142
+ const subagents = useTeams ? buildReviewerSubagents() : undefined;
143
+
144
+ const response = await provider.call({
145
+ prompt,
146
+ cwd: workspace.path,
147
+ allowedTools: ["Read", "Write", "Edit", "Glob", "Grep", "Bash"],
148
+ permissionMode: "bypassPermissions",
149
+ effortLevel,
150
+ model: options.model,
151
+ timeout: options.timeout,
152
+ ...(subagents ? { subagents } : {}),
153
+ });
154
+
155
+ return {
156
+ success: response.exitCode === 0,
157
+ output: response.result,
158
+ duration: response.duration,
159
+ };
160
+ }
@@ -0,0 +1,100 @@
1
+ import type { Task, WorkspaceInfo, EffortLevel } from "@aad/shared/types";
2
+ import type { ClaudeProvider } from "@aad/claude-provider";
3
+
4
+ export interface PhaseResult {
5
+ success: boolean;
6
+ output: string;
7
+ duration?: number;
8
+ }
9
+
10
+ export interface TesterRedOptions {
11
+ effortLevel?: EffortLevel;
12
+ model?: string;
13
+ timeout?: number;
14
+ }
15
+
16
+ /**
17
+ * Build TDD Red phase prompt for tester agent
18
+ */
19
+ export function buildRedPhasePrompt(task: Task, workspace: WorkspaceInfo): string {
20
+ const languagePatterns = getLanguageTestPatterns(workspace.language);
21
+
22
+ return `testerエージェントとして、TDD Red フェーズを実行してください。
23
+
24
+ Task ID: ${task.taskId as string}
25
+ Task Title: ${task.title}
26
+ Task Description: ${task.description}
27
+
28
+ プロジェクト情報:
29
+ - Workspace: ${workspace.path}
30
+ - Language: ${workspace.language}
31
+ - Test Framework: ${workspace.testFramework}
32
+ - Package Manager: ${workspace.packageManager}
33
+ - Framework: ${workspace.framework}
34
+
35
+ 実行内容:
36
+ 1. タスクの要件を理解する
37
+ 2. 失敗するテストを作成する(言語に応じた適切なパターンを使用)
38
+ ${languagePatterns}
39
+ 3. テストを実行して失敗することを確認する
40
+
41
+ 注意: このフェーズでは実装コードは書かないでください。テストのみ作成してください。`;
42
+ }
43
+
44
+ /**
45
+ * Get language-specific test patterns
46
+ */
47
+ function getLanguageTestPatterns(language: string): string {
48
+ switch (language.toLowerCase()) {
49
+ case "go":
50
+ case "golang":
51
+ return "- Go: テーブル駆動テスト";
52
+
53
+ case "python":
54
+ return "- Python: pytest parametrize";
55
+
56
+ case "rust":
57
+ return "- Rust: テストケースベクター";
58
+
59
+ case "typescript":
60
+ case "javascript":
61
+ return "- TypeScript/JavaScript: it.each (Vitest/Jest/Bun)";
62
+
63
+ case "terraform":
64
+ return "- Terraform: terraform validate";
65
+
66
+ case "java":
67
+ return "- Java: JUnit parametrized tests";
68
+
69
+ default:
70
+ return "- 言語に応じた適切なテストパターンを使用";
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Run TDD Red phase: create failing tests
76
+ */
77
+ export async function runTesterRed(
78
+ task: Task,
79
+ workspace: WorkspaceInfo,
80
+ provider: ClaudeProvider,
81
+ options: TesterRedOptions = {}
82
+ ): Promise<PhaseResult> {
83
+ const prompt = buildRedPhasePrompt(task, workspace);
84
+
85
+ const response = await provider.call({
86
+ prompt,
87
+ cwd: workspace.path,
88
+ allowedTools: ["Read", "Write", "Edit", "Glob", "Grep", "Bash"],
89
+ permissionMode: "bypassPermissions",
90
+ effortLevel: options.effortLevel ?? "medium",
91
+ model: options.model,
92
+ timeout: options.timeout,
93
+ });
94
+
95
+ return {
96
+ success: response.exitCode === 0,
97
+ output: response.result,
98
+ duration: response.duration,
99
+ };
100
+ }
@@ -0,0 +1,120 @@
1
+ import type { WorkspaceInfo } from "@aad/shared/types";
2
+ import { TestRunnerError } from "@aad/shared/errors";
3
+
4
+ export interface ProcessResult {
5
+ exitCode: number;
6
+ stdout: string;
7
+ stderr: string;
8
+ }
9
+
10
+ export interface ProcessSpawner {
11
+ spawn(
12
+ cmd: string,
13
+ args: string[],
14
+ opts: { cwd: string; timeout?: number }
15
+ ): Promise<ProcessResult>;
16
+ }
17
+
18
+ export interface TestResult {
19
+ success: boolean;
20
+ output: string;
21
+ error?: string;
22
+ }
23
+
24
+ /**
25
+ * Build test command based on detected test framework
26
+ */
27
+ export function buildTestCommand(workspace: WorkspaceInfo): string[] {
28
+ const { testFramework, packageManager } = workspace;
29
+
30
+ switch (testFramework) {
31
+ case "bun-test":
32
+ return ["bun", "test"];
33
+
34
+ case "vitest":
35
+ if (packageManager === "npm") return ["npm", "run", "test"];
36
+ if (packageManager === "yarn") return ["yarn", "test"];
37
+ if (packageManager === "pnpm") return ["pnpm", "test"];
38
+ return ["npx", "vitest", "run"];
39
+
40
+ case "jest":
41
+ if (packageManager === "npm") return ["npm", "test"];
42
+ if (packageManager === "yarn") return ["yarn", "test"];
43
+ if (packageManager === "pnpm") return ["pnpm", "test"];
44
+ return ["npx", "jest"];
45
+
46
+ case "mocha":
47
+ if (packageManager === "npm") return ["npm", "test"];
48
+ if (packageManager === "yarn") return ["yarn", "test"];
49
+ return ["npx", "mocha"];
50
+
51
+ case "pytest":
52
+ return ["pytest", "-v"];
53
+
54
+ case "go-test":
55
+ return ["go", "test", "./..."];
56
+
57
+ case "cargo":
58
+ return ["cargo", "test"];
59
+
60
+ case "maven":
61
+ return ["mvn", "test"];
62
+
63
+ case "gradle":
64
+ return ["./gradlew", "test"];
65
+
66
+ case "unknown":
67
+ throw new TestRunnerError(
68
+ "Unsupported test framework: unknown",
69
+ { testFramework }
70
+ );
71
+
72
+ default: {
73
+ const exhaustive: never = testFramework;
74
+ throw new TestRunnerError(
75
+ `Unsupported test framework: ${exhaustive}`,
76
+ { testFramework }
77
+ );
78
+ }
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Run tests in workspace and return result
84
+ * @param workspace - Workspace information with test framework
85
+ * @param spawner - Optional custom process spawner (for testing)
86
+ * @param timeout - Test timeout in milliseconds (default: 300000 = 5min)
87
+ */
88
+ export async function runTests(
89
+ workspace: WorkspaceInfo,
90
+ spawner?: ProcessSpawner,
91
+ timeout = 300000
92
+ ): Promise<TestResult> {
93
+ const command = buildTestCommand(workspace);
94
+ const [cmd, ...args] = command;
95
+
96
+ if (!cmd) {
97
+ throw new TestRunnerError("Invalid test command: empty", { workspace });
98
+ }
99
+
100
+ const actualSpawner =
101
+ spawner ?? (await import("./default-spawner")).createDefaultSpawner();
102
+
103
+ const result = await actualSpawner.spawn(cmd, args, {
104
+ cwd: workspace.path,
105
+ timeout,
106
+ });
107
+
108
+ if (result.exitCode === 0) {
109
+ return {
110
+ success: true,
111
+ output: result.stdout,
112
+ };
113
+ }
114
+
115
+ return {
116
+ success: false,
117
+ output: result.stdout,
118
+ error: result.stderr,
119
+ };
120
+ }