edsger 0.30.0 → 0.30.2

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 (116) hide show
  1. package/.claude/settings.local.json +28 -0
  2. package/.env.local +12 -0
  3. package/dist/api/features/__tests__/regression-prevention.test.d.ts +5 -0
  4. package/dist/api/features/__tests__/regression-prevention.test.js +338 -0
  5. package/dist/api/features/__tests__/status-updater.integration.test.d.ts +5 -0
  6. package/dist/api/features/__tests__/status-updater.integration.test.js +497 -0
  7. package/dist/commands/workflow/pipeline-runner.d.ts +17 -0
  8. package/dist/commands/workflow/pipeline-runner.js +393 -0
  9. package/dist/commands/workflow/runner.d.ts +26 -0
  10. package/dist/commands/workflow/runner.js +119 -0
  11. package/dist/commands/workflow/workflow-runner.d.ts +26 -0
  12. package/dist/commands/workflow/workflow-runner.js +119 -0
  13. package/dist/index.js +0 -0
  14. package/dist/phases/autonomous/index.js +9 -56
  15. package/dist/phases/code-implementation/analyzer-helpers.d.ts +28 -0
  16. package/dist/phases/code-implementation/analyzer-helpers.js +177 -0
  17. package/dist/phases/code-implementation/analyzer.d.ts +32 -0
  18. package/dist/phases/code-implementation/analyzer.js +629 -0
  19. package/dist/phases/code-implementation/branch-pr-creator.js +2 -58
  20. package/dist/phases/code-implementation/context-fetcher.d.ts +17 -0
  21. package/dist/phases/code-implementation/context-fetcher.js +86 -0
  22. package/dist/phases/code-implementation/index.js +9 -67
  23. package/dist/phases/code-implementation/mcp-server.d.ts +1 -0
  24. package/dist/phases/code-implementation/mcp-server.js +93 -0
  25. package/dist/phases/code-implementation/prompts-improvement.d.ts +5 -0
  26. package/dist/phases/code-implementation/prompts-improvement.js +108 -0
  27. package/dist/phases/code-implementation-verification/verifier.d.ts +31 -0
  28. package/dist/phases/code-implementation-verification/verifier.js +196 -0
  29. package/dist/phases/code-refine/analyzer.d.ts +41 -0
  30. package/dist/phases/code-refine/analyzer.js +561 -0
  31. package/dist/phases/code-refine/context-fetcher.d.ts +94 -0
  32. package/dist/phases/code-refine/context-fetcher.js +423 -0
  33. package/dist/phases/code-refine/index.js +6 -37
  34. package/dist/phases/code-refine-verification/analysis/llm-analyzer.d.ts +22 -0
  35. package/dist/phases/code-refine-verification/analysis/llm-analyzer.js +134 -0
  36. package/dist/phases/code-refine-verification/verifier.d.ts +47 -0
  37. package/dist/phases/code-refine-verification/verifier.js +597 -0
  38. package/dist/phases/code-review/analyzer.d.ts +29 -0
  39. package/dist/phases/code-review/analyzer.js +363 -0
  40. package/dist/phases/code-review/context-fetcher.d.ts +92 -0
  41. package/dist/phases/code-review/context-fetcher.js +296 -0
  42. package/dist/phases/code-review/index.js +1 -0
  43. package/dist/phases/feature-analysis/analyzer-helpers.d.ts +10 -0
  44. package/dist/phases/feature-analysis/analyzer-helpers.js +47 -0
  45. package/dist/phases/feature-analysis/analyzer.d.ts +11 -0
  46. package/dist/phases/feature-analysis/analyzer.js +208 -0
  47. package/dist/phases/feature-analysis/context-fetcher.d.ts +26 -0
  48. package/dist/phases/feature-analysis/context-fetcher.js +134 -0
  49. package/dist/phases/feature-analysis/http-fallback.d.ts +20 -0
  50. package/dist/phases/feature-analysis/http-fallback.js +95 -0
  51. package/dist/phases/feature-analysis/mcp-server.d.ts +1 -0
  52. package/dist/phases/feature-analysis/mcp-server.js +144 -0
  53. package/dist/phases/feature-analysis/prompts-improvement.d.ts +8 -0
  54. package/dist/phases/feature-analysis/prompts-improvement.js +109 -0
  55. package/dist/phases/feature-analysis-verification/verifier.d.ts +37 -0
  56. package/dist/phases/feature-analysis-verification/verifier.js +147 -0
  57. package/dist/phases/pr-execution/index.js +1 -0
  58. package/dist/phases/pr-execution/pr-executor.d.ts +2 -1
  59. package/dist/phases/pr-execution/pr-executor.js +9 -38
  60. package/dist/phases/pull-request/creator.js +9 -35
  61. package/dist/phases/technical-design/analyzer-helpers.d.ts +25 -0
  62. package/dist/phases/technical-design/analyzer-helpers.js +39 -0
  63. package/dist/phases/technical-design/analyzer.d.ts +21 -0
  64. package/dist/phases/technical-design/analyzer.js +461 -0
  65. package/dist/phases/technical-design/context-fetcher.d.ts +12 -0
  66. package/dist/phases/technical-design/context-fetcher.js +39 -0
  67. package/dist/phases/technical-design/http-fallback.d.ts +17 -0
  68. package/dist/phases/technical-design/http-fallback.js +151 -0
  69. package/dist/phases/technical-design/mcp-server.d.ts +1 -0
  70. package/dist/phases/technical-design/mcp-server.js +157 -0
  71. package/dist/phases/technical-design/prompts-improvement.d.ts +5 -0
  72. package/dist/phases/technical-design/prompts-improvement.js +93 -0
  73. package/dist/phases/technical-design-verification/verifier.d.ts +53 -0
  74. package/dist/phases/technical-design-verification/verifier.js +170 -0
  75. package/dist/services/feature-branches.d.ts +77 -0
  76. package/dist/services/feature-branches.js +205 -0
  77. package/dist/utils/git-branch-manager.d.ts +2 -0
  78. package/dist/utils/git-branch-manager.js +8 -35
  79. package/dist/utils/git-push.d.ts +43 -0
  80. package/dist/utils/git-push.js +127 -0
  81. package/dist/workflow-runner/config/phase-configs.d.ts +5 -0
  82. package/dist/workflow-runner/config/phase-configs.js +120 -0
  83. package/dist/workflow-runner/core/feature-filter.d.ts +16 -0
  84. package/dist/workflow-runner/core/feature-filter.js +46 -0
  85. package/dist/workflow-runner/core/index.d.ts +8 -0
  86. package/dist/workflow-runner/core/index.js +12 -0
  87. package/dist/workflow-runner/core/pipeline-evaluator.d.ts +24 -0
  88. package/dist/workflow-runner/core/pipeline-evaluator.js +32 -0
  89. package/dist/workflow-runner/core/state-manager.d.ts +24 -0
  90. package/dist/workflow-runner/core/state-manager.js +42 -0
  91. package/dist/workflow-runner/core/workflow-logger.d.ts +20 -0
  92. package/dist/workflow-runner/core/workflow-logger.js +65 -0
  93. package/dist/workflow-runner/executors/phase-executor.d.ts +8 -0
  94. package/dist/workflow-runner/executors/phase-executor.js +248 -0
  95. package/dist/workflow-runner/feature-workflow-runner.d.ts +26 -0
  96. package/dist/workflow-runner/feature-workflow-runner.js +119 -0
  97. package/dist/workflow-runner/index.d.ts +2 -0
  98. package/dist/workflow-runner/index.js +2 -0
  99. package/dist/workflow-runner/pipeline-runner.d.ts +17 -0
  100. package/dist/workflow-runner/pipeline-runner.js +393 -0
  101. package/dist/workflow-runner/workflow-processor.d.ts +54 -0
  102. package/dist/workflow-runner/workflow-processor.js +170 -0
  103. package/dist/workspace/workspace-manager.js +5 -3
  104. package/package.json +1 -1
  105. package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.d.ts +0 -4
  106. package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.js +0 -133
  107. package/dist/services/lifecycle-agent/__tests__/transition-rules.test.d.ts +0 -4
  108. package/dist/services/lifecycle-agent/__tests__/transition-rules.test.js +0 -336
  109. package/dist/services/lifecycle-agent/index.d.ts +0 -24
  110. package/dist/services/lifecycle-agent/index.js +0 -25
  111. package/dist/services/lifecycle-agent/phase-criteria.d.ts +0 -57
  112. package/dist/services/lifecycle-agent/phase-criteria.js +0 -335
  113. package/dist/services/lifecycle-agent/transition-rules.d.ts +0 -60
  114. package/dist/services/lifecycle-agent/transition-rules.js +0 -184
  115. package/dist/services/lifecycle-agent/types.d.ts +0 -190
  116. package/dist/services/lifecycle-agent/types.js +0 -12
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Feature Branches service for pipeline integration
3
+ * Allows phases to manage feature branches via MCP
4
+ */
5
+ import { callMcpEndpoint } from '../api/mcp-client.js';
6
+ /**
7
+ * List all branches for a feature
8
+ */
9
+ export async function getFeatureBranches(options) {
10
+ const { featureId, verbose } = options;
11
+ if (verbose) {
12
+ console.log(`📋 Fetching feature branches for feature: ${featureId}`);
13
+ }
14
+ const result = (await callMcpEndpoint('feature_branches/list', {
15
+ feature_id: featureId,
16
+ }));
17
+ const branches = result?.feature_branches || [];
18
+ if (verbose) {
19
+ console.log(`✅ Found ${branches.length} feature branches`);
20
+ }
21
+ return branches;
22
+ }
23
+ /**
24
+ * Get the current active branch for a feature
25
+ */
26
+ export async function getCurrentBranch(options) {
27
+ const { featureId, verbose } = options;
28
+ if (verbose) {
29
+ console.log(`📋 Getting current branch for feature: ${featureId}`);
30
+ }
31
+ const result = (await callMcpEndpoint('feature_branches/current', {
32
+ feature_id: featureId,
33
+ }));
34
+ return result?.current_branch || null;
35
+ }
36
+ /**
37
+ * Create feature branches
38
+ */
39
+ export async function createFeatureBranches(options, branches) {
40
+ const { featureId, verbose } = options;
41
+ if (verbose) {
42
+ console.log(`📋 Creating ${branches.length} feature branches for feature: ${featureId}`);
43
+ }
44
+ const result = (await callMcpEndpoint('feature_branches/create', {
45
+ feature_id: featureId,
46
+ branches: branches,
47
+ }));
48
+ const createdBranches = result?.created_branches || [];
49
+ if (verbose) {
50
+ console.log(`✅ Created ${createdBranches.length} feature branches`);
51
+ createdBranches.forEach((b, idx) => {
52
+ console.log(` ${idx + 1}. ${b.name} (status: ${b.status})`);
53
+ });
54
+ }
55
+ return createdBranches;
56
+ }
57
+ /**
58
+ * Update a feature branch
59
+ */
60
+ export async function updateFeatureBranch(branchId, updates, verbose) {
61
+ if (verbose) {
62
+ console.log(`📋 Updating feature branch: ${branchId}`);
63
+ }
64
+ const result = (await callMcpEndpoint('feature_branches/update', {
65
+ branch_id: branchId,
66
+ ...updates,
67
+ }));
68
+ if (verbose) {
69
+ console.log(`✅ Feature branch updated successfully`);
70
+ }
71
+ return result?.feature_branch;
72
+ }
73
+ /**
74
+ * Clear all branches for a feature (used before re-planning)
75
+ */
76
+ export async function clearFeatureBranches(options, force = false) {
77
+ const { featureId, verbose } = options;
78
+ if (verbose) {
79
+ console.log(`📋 Clearing feature branches for feature: ${featureId}`);
80
+ }
81
+ const result = (await callMcpEndpoint('feature_branches/clear', {
82
+ feature_id: featureId,
83
+ force: force,
84
+ }));
85
+ const deletedCount = result?.deleted_count || 0;
86
+ if (verbose) {
87
+ console.log(`✅ Cleared ${deletedCount} feature branches`);
88
+ }
89
+ return deletedCount;
90
+ }
91
+ /**
92
+ * Format feature branches for context (to include in prompts)
93
+ */
94
+ export function formatBranchesForContext(branches) {
95
+ if (!branches || branches.length === 0) {
96
+ return 'No feature branches defined yet.';
97
+ }
98
+ const branchList = branches
99
+ .map((b, idx) => {
100
+ const statusEmoji = b.status === 'merged'
101
+ ? '✅'
102
+ : b.status === 'in_progress'
103
+ ? '🔄'
104
+ : b.status === 'ready_for_review'
105
+ ? '👀'
106
+ : b.status === 'closed'
107
+ ? '❌'
108
+ : '⏳';
109
+ return `${idx + 1}. **${b.name}** ${statusEmoji}
110
+ - Status: ${b.status}
111
+ - Branch: ${b.branch_name || 'Not created'}
112
+ - PR: ${b.pull_request_url || 'Not created'}
113
+ - Description: ${b.description || 'No description'}`;
114
+ })
115
+ .join('\n\n');
116
+ return `# Feature Branches
117
+
118
+ ${branchList}
119
+
120
+ Total: ${branches.length} branches
121
+ Merged: ${branches.filter((b) => b.status === 'merged').length}
122
+ In Progress: ${branches.filter((b) => b.status === 'in_progress').length}
123
+ Pending: ${branches.filter((b) => b.status === 'pending').length}`;
124
+ }
125
+ /**
126
+ * Check if feature has multiple branches planned
127
+ */
128
+ export async function hasMultipleBranches(options) {
129
+ const branches = await getFeatureBranches(options);
130
+ return branches.length > 1;
131
+ }
132
+ /**
133
+ * Get next pending branch to work on
134
+ */
135
+ export async function getNextPendingBranch(options) {
136
+ const branches = await getFeatureBranches(options);
137
+ return branches.find((b) => b.status === 'pending') || null;
138
+ }
139
+ /**
140
+ * Check if all branches are completed
141
+ */
142
+ export async function allBranchesCompleted(options) {
143
+ const branches = await getFeatureBranches(options);
144
+ if (branches.length === 0)
145
+ return true;
146
+ return branches.every((b) => b.status === 'merged' || b.status === 'closed');
147
+ }
148
+ /**
149
+ * Get the base branch information for a feature branch.
150
+ * Returns:
151
+ * - If base_branch_id is set and that branch is merged: use main
152
+ * - If base_branch_id is set and that branch is not merged: use that branch's branch_name
153
+ * - If base_branch_id is null: use main
154
+ */
155
+ export async function getBaseBranchInfo(branch, allBranches, mainBranch = 'main') {
156
+ // No base branch - start from main
157
+ if (!branch.base_branch_id) {
158
+ return {
159
+ baseBranch: mainBranch,
160
+ needsRebase: false,
161
+ baseBranchMerged: true,
162
+ };
163
+ }
164
+ // Find the base branch
165
+ const baseBranch = allBranches.find((b) => b.id === branch.base_branch_id);
166
+ if (!baseBranch) {
167
+ // Base branch not found - fall back to main
168
+ return {
169
+ baseBranch: mainBranch,
170
+ needsRebase: false,
171
+ baseBranchMerged: true,
172
+ };
173
+ }
174
+ // Check if base branch is merged
175
+ if (baseBranch.status === 'merged') {
176
+ // Base branch is merged to main - we should base on main and rebase if needed
177
+ return { baseBranch: mainBranch, needsRebase: true, baseBranchMerged: true };
178
+ }
179
+ // Base branch is not merged - we should base on that branch
180
+ if (!baseBranch.branch_name) {
181
+ // Base branch doesn't have a git branch yet - fall back to main
182
+ return {
183
+ baseBranch: mainBranch,
184
+ needsRebase: false,
185
+ baseBranchMerged: false,
186
+ };
187
+ }
188
+ return {
189
+ baseBranch: baseBranch.branch_name,
190
+ needsRebase: false,
191
+ baseBranchMerged: false,
192
+ };
193
+ }
194
+ /**
195
+ * Get branch by ID
196
+ */
197
+ export async function getBranchById(branchId, verbose) {
198
+ if (verbose) {
199
+ console.log(`📋 Fetching feature branch: ${branchId}`);
200
+ }
201
+ const result = (await callMcpEndpoint('feature_branches/get', {
202
+ branch_id: branchId,
203
+ }));
204
+ return result?.feature_branch || null;
205
+ }
@@ -212,6 +212,8 @@ export interface RebaseWithConflictResolutionOptions {
212
212
  * GitHub to recalculate the PR diff.
213
213
  */
214
214
  forcePushAfterRebase?: boolean;
215
+ /** GitHub App installation token for authenticated push */
216
+ githubToken?: string;
215
217
  }
216
218
  /**
217
219
  * Switch to feature branch and rebase with base branch, with optional automatic conflict resolution
@@ -7,6 +7,7 @@ import * as fs from 'fs';
7
7
  import * as path from 'path';
8
8
  import { Octokit } from '@octokit/rest';
9
9
  import { logInfo, logError } from './logger.js';
10
+ import { gitForcePush } from './git-push.js';
10
11
  /**
11
12
  * Get current Git branch name
12
13
  */
@@ -689,7 +690,7 @@ export async function syncFeatBranchWithMain(featBranch, githubToken, owner, rep
689
690
  * @returns Object containing the previous branch name and conflict resolution result
690
691
  */
691
692
  export async function switchToFeatureBranchAndRebaseAsync(options) {
692
- const { featureBranch, baseBranch = 'main', rebaseTargetBranch, originalBaseBranch, verbose, resolveConflicts = false, conflictResolverConfig, forcePushAfterRebase = false, baseBranchCompleted = false, } = options;
693
+ const { featureBranch, baseBranch = 'main', rebaseTargetBranch, originalBaseBranch, verbose, resolveConflicts = false, conflictResolverConfig, forcePushAfterRebase = false, baseBranchCompleted = false, githubToken, } = options;
693
694
  // Determine the actual target branch for rebase
694
695
  // If rebaseTargetBranch is set (e.g., main), use it; otherwise use baseBranch
695
696
  const actualRebaseTarget = rebaseTargetBranch || baseBranch;
@@ -864,23 +865,9 @@ export async function switchToFeatureBranchAndRebaseAsync(options) {
864
865
  }
865
866
  // Force push after rebase to trigger GitHub to recalculate PR diff
866
867
  if (forcePushAfterRebase) {
867
- try {
868
- if (verbose) {
869
- logInfo(`📤 Force pushing ${featureBranch} to trigger PR diff recalculation...`);
870
- }
871
- execSync(`git push --force-with-lease origin ${featureBranch}`, {
872
- encoding: 'utf-8',
873
- stdio: verbose ? 'inherit' : 'pipe',
874
- });
875
- if (verbose) {
876
- logInfo(`✅ Successfully force pushed ${featureBranch}`);
877
- }
878
- }
879
- catch (pushError) {
880
- if (verbose) {
881
- logInfo(`⚠️ Could not force push ${featureBranch}: ${pushError instanceof Error ? pushError.message : String(pushError)}`);
882
- }
883
- // Don't fail the whole process if push fails
868
+ const pushResult = gitForcePush({ branchName: featureBranch, token: githubToken, verbose });
869
+ if (!pushResult.success && verbose) {
870
+ logInfo(`⚠️ Could not force push ${featureBranch}: ${pushResult.error}`);
884
871
  }
885
872
  }
886
873
  return { previousBranch };
@@ -908,23 +895,9 @@ export async function switchToFeatureBranchAndRebaseAsync(options) {
908
895
  }
909
896
  // Force push after rebase to trigger GitHub to recalculate PR diff
910
897
  if (forcePushAfterRebase) {
911
- try {
912
- if (verbose) {
913
- logInfo(`📤 Force pushing ${featureBranch} to trigger PR diff recalculation...`);
914
- }
915
- execSync(`git push --force-with-lease origin ${featureBranch}`, {
916
- encoding: 'utf-8',
917
- stdio: verbose ? 'inherit' : 'pipe',
918
- });
919
- if (verbose) {
920
- logInfo(`✅ Successfully force pushed ${featureBranch}`);
921
- }
922
- }
923
- catch (pushError) {
924
- if (verbose) {
925
- logInfo(`⚠️ Could not force push ${featureBranch}: ${pushError instanceof Error ? pushError.message : String(pushError)}`);
926
- }
927
- // Don't fail the whole process if push fails
898
+ const pushResult = gitForcePush({ branchName: featureBranch, token: githubToken, verbose });
899
+ if (!pushResult.success && verbose) {
900
+ logInfo(`⚠️ Could not force push ${featureBranch}: ${pushResult.error}`);
928
901
  }
929
902
  }
930
903
  return {
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Shared git push utilities with GitHub App credential helper support.
3
+ *
4
+ * Uses `git -c credential.helper=...` to pass the token securely per-command,
5
+ * matching the pattern established in workspace-manager.ts for clone/fetch.
6
+ * Uses `execFileSync` instead of `execSync` to avoid shell metacharacter injection.
7
+ */
8
+ export interface GitPushOptions {
9
+ branchName: string;
10
+ token?: string;
11
+ verbose?: boolean;
12
+ cwd?: string;
13
+ }
14
+ export interface GitPushResult {
15
+ success: boolean;
16
+ error?: string;
17
+ method?: 'push-u' | 'push' | 'force-with-lease';
18
+ }
19
+ /**
20
+ * Build git args with optional credential helper prefix.
21
+ * When a token is provided, prepends `-c credential.helper=...` so git
22
+ * authenticates with the GitHub App installation token.
23
+ */
24
+ export declare function buildCredentialArgs(token?: string): string[];
25
+ /**
26
+ * Push a branch to remote with three-level fallback:
27
+ * 1. git push -u origin <branch>
28
+ * 2. git push origin <branch>
29
+ * 3. git push --force-with-lease origin <branch>
30
+ *
31
+ * When `token` is provided, each command uses a credential helper for authentication.
32
+ */
33
+ export declare function gitPush(options: GitPushOptions): GitPushResult;
34
+ /**
35
+ * Force push a branch using --force-with-lease.
36
+ * Used after rebase operations to update the remote branch.
37
+ */
38
+ export declare function gitForcePush(options: GitPushOptions): GitPushResult;
39
+ /**
40
+ * Push the current branch to remote.
41
+ * Resolves the current branch name first, then delegates to gitPush.
42
+ */
43
+ export declare function gitPushCurrentBranch(options: Omit<GitPushOptions, 'branchName'>): GitPushResult;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Shared git push utilities with GitHub App credential helper support.
3
+ *
4
+ * Uses `git -c credential.helper=...` to pass the token securely per-command,
5
+ * matching the pattern established in workspace-manager.ts for clone/fetch.
6
+ * Uses `execFileSync` instead of `execSync` to avoid shell metacharacter injection.
7
+ */
8
+ import { execFileSync } from 'child_process';
9
+ import { logInfo } from './logger.js';
10
+ /**
11
+ * Build git args with optional credential helper prefix.
12
+ * When a token is provided, prepends `-c credential.helper=...` so git
13
+ * authenticates with the GitHub App installation token.
14
+ */
15
+ export function buildCredentialArgs(token) {
16
+ if (!token)
17
+ return [];
18
+ // First clear any existing credential helpers (e.g. osxkeychain) with an empty value,
19
+ // then set our custom helper. This ensures the token is used instead of stale keychain creds.
20
+ const credentialHelper = `!f() { echo "username=x-access-token"; echo "password=${token}"; }; f`;
21
+ return ['-c', 'credential.helper=', '-c', `credential.helper=${credentialHelper}`];
22
+ }
23
+ /**
24
+ * Push a branch to remote with three-level fallback:
25
+ * 1. git push -u origin <branch>
26
+ * 2. git push origin <branch>
27
+ * 3. git push --force-with-lease origin <branch>
28
+ *
29
+ * When `token` is provided, each command uses a credential helper for authentication.
30
+ */
31
+ export function gitPush(options) {
32
+ const { branchName, token, verbose, cwd } = options;
33
+ const credArgs = buildCredentialArgs(token);
34
+ const execOpts = {
35
+ cwd,
36
+ stdio: (verbose ? 'inherit' : 'pipe'),
37
+ };
38
+ if (verbose) {
39
+ logInfo(`📤 Pushing branch ${branchName} to remote...`);
40
+ }
41
+ // Try 1: push -u (set upstream tracking)
42
+ try {
43
+ execFileSync('git', [...credArgs, 'push', '-u', 'origin', branchName], execOpts);
44
+ return { success: true, method: 'push-u' };
45
+ }
46
+ catch {
47
+ // fall through
48
+ }
49
+ // Try 2: plain push
50
+ try {
51
+ execFileSync('git', [...credArgs, 'push', 'origin', branchName], execOpts);
52
+ return { success: true, method: 'push' };
53
+ }
54
+ catch {
55
+ // fall through
56
+ }
57
+ // Try 3: force push with lease
58
+ if (verbose) {
59
+ logInfo(`⚠️ Push rejected, attempting force push with lease...`);
60
+ }
61
+ try {
62
+ execFileSync('git', [...credArgs, 'push', '--force-with-lease', 'origin', branchName], execOpts);
63
+ if (verbose) {
64
+ logInfo(`✅ Successfully force pushed ${branchName}`);
65
+ }
66
+ return { success: true, method: 'force-with-lease' };
67
+ }
68
+ catch (error) {
69
+ return {
70
+ success: false,
71
+ error: error instanceof Error ? error.message : String(error),
72
+ };
73
+ }
74
+ }
75
+ /**
76
+ * Force push a branch using --force-with-lease.
77
+ * Used after rebase operations to update the remote branch.
78
+ */
79
+ export function gitForcePush(options) {
80
+ const { branchName, token, verbose, cwd } = options;
81
+ const credArgs = buildCredentialArgs(token);
82
+ const execOpts = {
83
+ cwd,
84
+ stdio: (verbose ? 'inherit' : 'pipe'),
85
+ };
86
+ if (verbose) {
87
+ logInfo(`📤 Force pushing ${branchName} to remote...`);
88
+ }
89
+ try {
90
+ execFileSync('git', [...credArgs, 'push', '--force-with-lease', 'origin', branchName], execOpts);
91
+ if (verbose) {
92
+ logInfo(`✅ Successfully force pushed ${branchName}`);
93
+ }
94
+ return { success: true, method: 'force-with-lease' };
95
+ }
96
+ catch (error) {
97
+ return {
98
+ success: false,
99
+ error: error instanceof Error ? error.message : String(error),
100
+ };
101
+ }
102
+ }
103
+ /**
104
+ * Push the current branch to remote.
105
+ * Resolves the current branch name first, then delegates to gitPush.
106
+ */
107
+ export function gitPushCurrentBranch(options) {
108
+ const { token, verbose, cwd } = options;
109
+ let branchName;
110
+ try {
111
+ branchName = execFileSync('git', ['branch', '--show-current'], {
112
+ encoding: 'utf-8',
113
+ cwd,
114
+ stdio: 'pipe',
115
+ }).trim();
116
+ }
117
+ catch (error) {
118
+ return {
119
+ success: false,
120
+ error: `Failed to resolve current branch: ${error instanceof Error ? error.message : String(error)}`,
121
+ };
122
+ }
123
+ if (!branchName) {
124
+ return { success: false, error: 'Could not determine current branch (detached HEAD?)' };
125
+ }
126
+ return gitPush({ branchName, token, verbose, cwd });
127
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Phase configurations for the pipeline runner
3
+ */
4
+ import { PhaseConfig } from '../../types/pipeline.js';
5
+ export declare const phaseConfigs: readonly PhaseConfig[];
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Phase configurations for the pipeline runner
3
+ */
4
+ import { analyzeFeatureWithMCP, checkFeatureAnalysisRequirements, } from '../../phases/feature-analysis/analyzer.js';
5
+ import { generateTechnicalDesign, checkTechnicalDesignRequirements, } from '../../phases/technical-design/analyzer.js';
6
+ import { implementFeatureCode, checkCodeImplementationRequirements, } from '../../phases/code-implementation/analyzer.js';
7
+ import { runFunctionalTesting, checkFunctionalTestingRequirements, } from '../../phases/functional-testing/analyzer.js';
8
+ import { writeCodeTests, checkCodeTestingRequirements, } from '../../phases/code-testing/analyzer.js';
9
+ import { refineCodeFromPRFeedback, checkCodeRefineRequirements, } from '../../phases/code-refine/analyzer.js';
10
+ import { verifyAndResolveComments, checkCodeRefineVerificationRequirements, } from '../../phases/code-refine-verification/verifier.js';
11
+ import { reviewPullRequest, checkCodeReviewRequirements, } from '../../phases/code-review/analyzer.js';
12
+ /**
13
+ * Wrapper for code-refine phase to inject GitHub token
14
+ */
15
+ const executeCodeRefine = async (options, config) => {
16
+ const githubToken = process.env.GITHUB_TOKEN || process.env.GITHUB_ACCESS_TOKEN;
17
+ if (!githubToken) {
18
+ return {
19
+ status: 'error',
20
+ message: 'GitHub token not found. Set GITHUB_TOKEN or GITHUB_ACCESS_TOKEN environment variable.',
21
+ };
22
+ }
23
+ return refineCodeFromPRFeedback({
24
+ featureId: options.featureId,
25
+ mcpServerUrl: options.mcpServerUrl,
26
+ mcpToken: options.mcpToken,
27
+ githubToken,
28
+ verbose: options.verbose,
29
+ }, config);
30
+ };
31
+ /**
32
+ * Wrapper for code-refine-verification phase to inject GitHub token
33
+ */
34
+ const executeCodeRefineVerification = async (options, config) => {
35
+ const githubToken = process.env.GITHUB_TOKEN || process.env.GITHUB_ACCESS_TOKEN;
36
+ if (!githubToken) {
37
+ return {
38
+ status: 'error',
39
+ message: 'GitHub token not found. Set GITHUB_TOKEN or GITHUB_ACCESS_TOKEN environment variable.',
40
+ };
41
+ }
42
+ return verifyAndResolveComments({
43
+ featureId: options.featureId,
44
+ mcpServerUrl: options.mcpServerUrl,
45
+ mcpToken: options.mcpToken,
46
+ githubToken,
47
+ config, // Add config parameter for LLM analysis
48
+ verbose: options.verbose,
49
+ });
50
+ };
51
+ /**
52
+ * Wrapper for code-review phase to inject GitHub token
53
+ */
54
+ const executeCodeReview = async (options, config) => {
55
+ const githubToken = process.env.GITHUB_TOKEN || process.env.GITHUB_ACCESS_TOKEN;
56
+ if (!githubToken) {
57
+ return {
58
+ status: 'error',
59
+ message: 'GitHub token not found. Set GITHUB_TOKEN or GITHUB_ACCESS_TOKEN environment variable.',
60
+ };
61
+ }
62
+ return reviewPullRequest({
63
+ featureId: options.featureId,
64
+ mcpServerUrl: options.mcpServerUrl,
65
+ mcpToken: options.mcpToken,
66
+ githubToken,
67
+ verbose: options.verbose,
68
+ }, config);
69
+ };
70
+ // Pipeline phase configurations
71
+ export const phaseConfigs = [
72
+ {
73
+ name: 'feature-analysis',
74
+ checkRequirements: checkFeatureAnalysisRequirements,
75
+ execute: analyzeFeatureWithMCP,
76
+ requirementsError: 'Feature analysis requirements not met. Install with: npm install @anthropic-ai/claude-code zod',
77
+ },
78
+ {
79
+ name: 'technical-design',
80
+ checkRequirements: checkTechnicalDesignRequirements,
81
+ execute: generateTechnicalDesign,
82
+ requirementsError: 'Technical design requirements not met. Install with: npm install @anthropic-ai/claude-code zod',
83
+ },
84
+ {
85
+ name: 'code-implementation',
86
+ checkRequirements: checkCodeImplementationRequirements,
87
+ execute: implementFeatureCode,
88
+ requirementsError: 'Code implementation requirements not met. Install with: npm install @anthropic-ai/claude-code zod',
89
+ },
90
+ {
91
+ name: 'functional-testing',
92
+ checkRequirements: checkFunctionalTestingRequirements,
93
+ execute: runFunctionalTesting,
94
+ requirementsError: 'Functional testing requirements not met. Install with: npm install @anthropic-ai/claude-code zod',
95
+ },
96
+ {
97
+ name: 'code-testing',
98
+ checkRequirements: checkCodeTestingRequirements,
99
+ execute: writeCodeTests,
100
+ requirementsError: 'Code testing requirements not met. Install with: npm install @anthropic-ai/claude-code zod',
101
+ },
102
+ {
103
+ name: 'code-refine',
104
+ checkRequirements: checkCodeRefineRequirements,
105
+ execute: executeCodeRefine,
106
+ requirementsError: 'Code refine requirements not met. Install with: npm install @anthropic-ai/claude-code @octokit/rest',
107
+ },
108
+ {
109
+ name: 'code-refine-verification',
110
+ checkRequirements: checkCodeRefineVerificationRequirements,
111
+ execute: executeCodeRefineVerification,
112
+ requirementsError: 'Code refine verification requirements not met. Install with: npm install @octokit/rest',
113
+ },
114
+ {
115
+ name: 'code-review',
116
+ checkRequirements: checkCodeReviewRequirements,
117
+ execute: executeCodeReview,
118
+ requirementsError: 'Code review requirements not met. Install with: npm install @anthropic-ai/claude-code @octokit/rest',
119
+ },
120
+ ];
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Feature filtering utilities for workflow processor
3
+ * Pure functions for filtering and selecting features for processing
4
+ */
5
+ import type { FeatureInfo } from '../../types/features.js';
6
+ import type { FeatureProcessingState } from './state-manager.js';
7
+ export declare const shouldProcessFeature: (maxRetries: number) => (feature: FeatureInfo, states: Map<string, FeatureProcessingState>) => boolean;
8
+ export declare const findNextFeature: (features: readonly FeatureInfo[], states: Map<string, FeatureProcessingState>, maxRetries: number) => FeatureInfo | undefined;
9
+ export declare const isFeatureProcessing: (featureId: string, states: Map<string, FeatureProcessingState>) => boolean;
10
+ export declare const isFeatureCompleted: (featureId: string, states: Map<string, FeatureProcessingState>) => boolean;
11
+ export declare const isFeatureFailed: (featureId: string, states: Map<string, FeatureProcessingState>) => boolean;
12
+ export declare const hasReachedMaxRetries: (featureId: string, states: Map<string, FeatureProcessingState>, maxRetries: number) => boolean;
13
+ export declare const filterProcessingFeatures: (features: readonly FeatureInfo[], states: Map<string, FeatureProcessingState>) => FeatureInfo[];
14
+ export declare const filterCompletedFeatures: (features: readonly FeatureInfo[], states: Map<string, FeatureProcessingState>) => FeatureInfo[];
15
+ export declare const filterFailedFeatures: (features: readonly FeatureInfo[], states: Map<string, FeatureProcessingState>) => FeatureInfo[];
16
+ export declare const filterAvailableFeatures: (features: readonly FeatureInfo[], states: Map<string, FeatureProcessingState>, maxRetries: number) => FeatureInfo[];
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Feature filtering utilities for workflow processor
3
+ * Pure functions for filtering and selecting features for processing
4
+ */
5
+ // Feature filtering functions (pure)
6
+ export const shouldProcessFeature = (maxRetries) => (feature, states) => {
7
+ const state = states.get(feature.id);
8
+ // If never processed, should process
9
+ if (!state)
10
+ return true;
11
+ // If feature was updated after last processing attempt, should reprocess
12
+ // This handles cases where user manually changes status back to ready_for_dev
13
+ if (feature.updated_at) {
14
+ const featureUpdatedTime = new Date(feature.updated_at).getTime();
15
+ const lastAttemptTime = state.lastAttempt.getTime();
16
+ if (featureUpdatedTime > lastAttemptTime) {
17
+ // Feature has been updated since last processing, reprocess it
18
+ return true;
19
+ }
20
+ }
21
+ // If failed and haven't exceeded retry limit, should retry
22
+ return state.status === 'failed' && state.retryCount < maxRetries;
23
+ };
24
+ export const findNextFeature = (features, states, maxRetries) => features.find((feature) => shouldProcessFeature(maxRetries)(feature, states));
25
+ // Feature status checking functions (pure)
26
+ export const isFeatureProcessing = (featureId, states) => {
27
+ const state = states.get(featureId);
28
+ return state?.status === 'processing';
29
+ };
30
+ export const isFeatureCompleted = (featureId, states) => {
31
+ const state = states.get(featureId);
32
+ return state?.status === 'completed';
33
+ };
34
+ export const isFeatureFailed = (featureId, states) => {
35
+ const state = states.get(featureId);
36
+ return state?.status === 'failed';
37
+ };
38
+ export const hasReachedMaxRetries = (featureId, states, maxRetries) => {
39
+ const state = states.get(featureId);
40
+ return state ? state.retryCount >= maxRetries : false;
41
+ };
42
+ // Feature collection filtering functions (pure)
43
+ export const filterProcessingFeatures = (features, states) => features.filter((feature) => isFeatureProcessing(feature.id, states));
44
+ export const filterCompletedFeatures = (features, states) => features.filter((feature) => isFeatureCompleted(feature.id, states));
45
+ export const filterFailedFeatures = (features, states) => features.filter((feature) => isFeatureFailed(feature.id, states));
46
+ export const filterAvailableFeatures = (features, states, maxRetries) => features.filter((feature) => shouldProcessFeature(maxRetries)(feature, states));
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Core workflow processor modules
3
+ * Centralized exports for all core functionality
4
+ */
5
+ export * from './state-manager.js';
6
+ export * from './feature-filter.js';
7
+ export * from './pipeline-evaluator.js';
8
+ export * from './workflow-logger.js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Core workflow processor modules
3
+ * Centralized exports for all core functionality
4
+ */
5
+ // State management
6
+ export * from './state-manager.js';
7
+ // Feature filtering
8
+ export * from './feature-filter.js';
9
+ // Pipeline evaluation
10
+ export * from './pipeline-evaluator.js';
11
+ // Workflow logging
12
+ export * from './workflow-logger.js';