@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
|
@@ -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
|
+
}
|