edsger 0.25.0 ā 0.26.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/dist/commands/workflow/config/phase-configs.js +5 -0
- package/dist/commands/workflow/executors/phase-executor.d.ts +2 -2
- package/dist/commands/workflow/executors/phase-executor.js +2 -2
- package/dist/commands/workflow/phase-orchestrator.js +58 -1
- package/dist/config/feature-status.js +5 -0
- package/dist/index.js +0 -0
- package/dist/phases/pr-splitting/context.d.ts +14 -0
- package/dist/phases/pr-splitting/context.js +41 -0
- package/dist/phases/pr-splitting/index.d.ts +29 -0
- package/dist/phases/pr-splitting/index.js +361 -0
- package/dist/phases/pr-splitting/outcome.d.ts +24 -0
- package/dist/phases/pr-splitting/outcome.js +113 -0
- package/dist/phases/pr-splitting/prompts.d.ts +43 -0
- package/dist/phases/pr-splitting/prompts.js +209 -0
- package/dist/phases/test-cases-analysis/context.d.ts +1 -0
- package/dist/phases/test-cases-analysis/context.js +11 -3
- package/dist/phases/test-cases-analysis/prompts.d.ts +1 -0
- package/dist/phases/test-cases-analysis/prompts.js +32 -0
- package/dist/phases/user-stories-analysis/context.d.ts +1 -0
- package/dist/phases/user-stories-analysis/context.js +11 -3
- package/dist/phases/user-stories-analysis/prompts.d.ts +1 -0
- package/dist/phases/user-stories-analysis/prompts.js +32 -0
- package/dist/services/audit-logs.d.ts +2 -2
- package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.d.ts +4 -0
- package/dist/services/lifecycle-agent/__tests__/phase-criteria.test.js +133 -0
- package/dist/services/lifecycle-agent/__tests__/transition-rules.test.d.ts +4 -0
- package/dist/services/lifecycle-agent/__tests__/transition-rules.test.js +336 -0
- package/dist/services/lifecycle-agent/index.d.ts +24 -0
- package/dist/services/lifecycle-agent/index.js +25 -0
- package/dist/services/lifecycle-agent/phase-criteria.d.ts +57 -0
- package/dist/services/lifecycle-agent/phase-criteria.js +335 -0
- package/dist/services/lifecycle-agent/transition-rules.d.ts +60 -0
- package/dist/services/lifecycle-agent/transition-rules.js +184 -0
- package/dist/services/lifecycle-agent/types.d.ts +190 -0
- package/dist/services/lifecycle-agent/types.js +12 -0
- package/dist/services/pull-requests.d.ts +56 -0
- package/dist/services/pull-requests.js +125 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/pipeline.d.ts +1 -1
- package/package.json +1 -1
- package/.claude/settings.local.json +0 -28
- package/.env.local +0 -12
- package/dist/api/features/__tests__/regression-prevention.test.d.ts +0 -5
- package/dist/api/features/__tests__/regression-prevention.test.js +0 -338
- package/dist/api/features/__tests__/status-updater.integration.test.d.ts +0 -5
- package/dist/api/features/__tests__/status-updater.integration.test.js +0 -497
- package/dist/commands/workflow/pipeline-runner.d.ts +0 -17
- package/dist/commands/workflow/pipeline-runner.js +0 -393
- package/dist/commands/workflow/runner.d.ts +0 -26
- package/dist/commands/workflow/runner.js +0 -119
- package/dist/commands/workflow/workflow-runner.d.ts +0 -26
- package/dist/commands/workflow/workflow-runner.js +0 -119
- package/dist/phases/code-implementation/analyzer-helpers.d.ts +0 -28
- package/dist/phases/code-implementation/analyzer-helpers.js +0 -177
- package/dist/phases/code-implementation/analyzer.d.ts +0 -32
- package/dist/phases/code-implementation/analyzer.js +0 -629
- package/dist/phases/code-implementation/context-fetcher.d.ts +0 -17
- package/dist/phases/code-implementation/context-fetcher.js +0 -86
- package/dist/phases/code-implementation/mcp-server.d.ts +0 -1
- package/dist/phases/code-implementation/mcp-server.js +0 -93
- package/dist/phases/code-implementation/prompts-improvement.d.ts +0 -5
- package/dist/phases/code-implementation/prompts-improvement.js +0 -108
- package/dist/phases/code-implementation-verification/verifier.d.ts +0 -31
- package/dist/phases/code-implementation-verification/verifier.js +0 -196
- package/dist/phases/code-refine/analyzer.d.ts +0 -41
- package/dist/phases/code-refine/analyzer.js +0 -561
- package/dist/phases/code-refine/context-fetcher.d.ts +0 -94
- package/dist/phases/code-refine/context-fetcher.js +0 -423
- package/dist/phases/code-refine-verification/analysis/llm-analyzer.d.ts +0 -22
- package/dist/phases/code-refine-verification/analysis/llm-analyzer.js +0 -134
- package/dist/phases/code-refine-verification/verifier.d.ts +0 -47
- package/dist/phases/code-refine-verification/verifier.js +0 -597
- package/dist/phases/code-review/analyzer.d.ts +0 -29
- package/dist/phases/code-review/analyzer.js +0 -363
- package/dist/phases/code-review/context-fetcher.d.ts +0 -92
- package/dist/phases/code-review/context-fetcher.js +0 -296
- package/dist/phases/feature-analysis/analyzer-helpers.d.ts +0 -10
- package/dist/phases/feature-analysis/analyzer-helpers.js +0 -47
- package/dist/phases/feature-analysis/analyzer.d.ts +0 -11
- package/dist/phases/feature-analysis/analyzer.js +0 -208
- package/dist/phases/feature-analysis/context-fetcher.d.ts +0 -26
- package/dist/phases/feature-analysis/context-fetcher.js +0 -134
- package/dist/phases/feature-analysis/http-fallback.d.ts +0 -20
- package/dist/phases/feature-analysis/http-fallback.js +0 -95
- package/dist/phases/feature-analysis/mcp-server.d.ts +0 -1
- package/dist/phases/feature-analysis/mcp-server.js +0 -144
- package/dist/phases/feature-analysis/prompts-improvement.d.ts +0 -8
- package/dist/phases/feature-analysis/prompts-improvement.js +0 -109
- package/dist/phases/feature-analysis-verification/verifier.d.ts +0 -37
- package/dist/phases/feature-analysis-verification/verifier.js +0 -147
- package/dist/phases/technical-design/analyzer-helpers.d.ts +0 -25
- package/dist/phases/technical-design/analyzer-helpers.js +0 -39
- package/dist/phases/technical-design/analyzer.d.ts +0 -21
- package/dist/phases/technical-design/analyzer.js +0 -461
- package/dist/phases/technical-design/context-fetcher.d.ts +0 -12
- package/dist/phases/technical-design/context-fetcher.js +0 -39
- package/dist/phases/technical-design/http-fallback.d.ts +0 -17
- package/dist/phases/technical-design/http-fallback.js +0 -151
- package/dist/phases/technical-design/mcp-server.d.ts +0 -1
- package/dist/phases/technical-design/mcp-server.js +0 -157
- package/dist/phases/technical-design/prompts-improvement.d.ts +0 -5
- package/dist/phases/technical-design/prompts-improvement.js +0 -93
- package/dist/phases/technical-design-verification/verifier.d.ts +0 -53
- package/dist/phases/technical-design-verification/verifier.js +0 -170
- package/dist/services/feature-branches.d.ts +0 -77
- package/dist/services/feature-branches.js +0 -205
- package/dist/workflow-runner/config/phase-configs.d.ts +0 -5
- package/dist/workflow-runner/config/phase-configs.js +0 -120
- package/dist/workflow-runner/core/feature-filter.d.ts +0 -16
- package/dist/workflow-runner/core/feature-filter.js +0 -46
- package/dist/workflow-runner/core/index.d.ts +0 -8
- package/dist/workflow-runner/core/index.js +0 -12
- package/dist/workflow-runner/core/pipeline-evaluator.d.ts +0 -24
- package/dist/workflow-runner/core/pipeline-evaluator.js +0 -32
- package/dist/workflow-runner/core/state-manager.d.ts +0 -24
- package/dist/workflow-runner/core/state-manager.js +0 -42
- package/dist/workflow-runner/core/workflow-logger.d.ts +0 -20
- package/dist/workflow-runner/core/workflow-logger.js +0 -65
- package/dist/workflow-runner/executors/phase-executor.d.ts +0 -8
- package/dist/workflow-runner/executors/phase-executor.js +0 -248
- package/dist/workflow-runner/feature-workflow-runner.d.ts +0 -26
- package/dist/workflow-runner/feature-workflow-runner.js +0 -119
- package/dist/workflow-runner/index.d.ts +0 -2
- package/dist/workflow-runner/index.js +0 -2
- package/dist/workflow-runner/pipeline-runner.d.ts +0 -17
- package/dist/workflow-runner/pipeline-runner.js +0 -393
- package/dist/workflow-runner/workflow-processor.d.ts +0 -54
- package/dist/workflow-runner/workflow-processor.js +0 -170
|
@@ -6,6 +6,7 @@ import { analyseUserStories } from '../../../phases/user-stories-analysis/index.
|
|
|
6
6
|
import { analyseTestCases } from '../../../phases/test-cases-analysis/index.js';
|
|
7
7
|
import { generateTechnicalDesign } from '../../../phases/technical-design/index.js';
|
|
8
8
|
import { planFeatureBranches } from '../../../phases/branch-planning/index.js';
|
|
9
|
+
import { splitFeatureIntoPRs } from '../../../phases/pr-splitting/index.js';
|
|
9
10
|
import { implementFeatureCode } from '../../../phases/code-implementation/index.js';
|
|
10
11
|
import { runFunctionalTesting } from '../../../phases/functional-testing/analyzer.js';
|
|
11
12
|
import { writeCodeTests } from '../../../phases/code-testing/analyzer.js';
|
|
@@ -95,6 +96,10 @@ export const phaseConfigs = [
|
|
|
95
96
|
name: 'code-implementation',
|
|
96
97
|
execute: implementFeatureCode,
|
|
97
98
|
},
|
|
99
|
+
{
|
|
100
|
+
name: 'pr-splitting',
|
|
101
|
+
execute: splitFeatureIntoPRs,
|
|
102
|
+
},
|
|
98
103
|
{
|
|
99
104
|
name: 'functional-testing',
|
|
100
105
|
execute: runFunctionalTesting,
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
import { EdsgerConfig } from '../../../types/index.js';
|
|
5
5
|
import { PipelinePhaseOptions, PipelineResult, PhaseConfig } from '../../../types/pipeline.js';
|
|
6
6
|
export declare const createPhaseRunner: (phaseConfig: PhaseConfig) => (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>;
|
|
7
|
-
declare const runFeatureAnalysisPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runUserStoriesAnalysisPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runTestCasesAnalysisPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runTechnicalDesignPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runBranchPlanningPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeImplementationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runFunctionalTestingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeTestingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefinePhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefineVerificationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeReviewPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runAutonomousPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>;
|
|
8
|
-
export { runFeatureAnalysisPhase, runUserStoriesAnalysisPhase, runTestCasesAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase, };
|
|
7
|
+
declare const runFeatureAnalysisPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runUserStoriesAnalysisPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runTestCasesAnalysisPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runTechnicalDesignPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runBranchPlanningPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeImplementationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runPRSplittingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runFunctionalTestingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeTestingPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefinePhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeRefineVerificationPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runCodeReviewPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>, runAutonomousPhase: (options: PipelinePhaseOptions, config: EdsgerConfig) => Promise<PipelineResult>;
|
|
8
|
+
export { runFeatureAnalysisPhase, runUserStoriesAnalysisPhase, runTestCasesAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runPRSplittingPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase, };
|
|
@@ -262,6 +262,6 @@ export const createPhaseRunner = (phaseConfig) => async (options, config) => {
|
|
|
262
262
|
}
|
|
263
263
|
};
|
|
264
264
|
// Create phase runners using the configuration
|
|
265
|
-
const [runFeatureAnalysisPhase, runUserStoriesAnalysisPhase, runTestCasesAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase,] = phaseConfigs.map(createPhaseRunner);
|
|
265
|
+
const [runFeatureAnalysisPhase, runUserStoriesAnalysisPhase, runTestCasesAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runPRSplittingPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase,] = phaseConfigs.map(createPhaseRunner);
|
|
266
266
|
// Export individual phase runners for granular control
|
|
267
|
-
export { runFeatureAnalysisPhase, runUserStoriesAnalysisPhase, runTestCasesAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase, };
|
|
267
|
+
export { runFeatureAnalysisPhase, runUserStoriesAnalysisPhase, runTestCasesAnalysisPhase, runTechnicalDesignPhase, runBranchPlanningPhase, runCodeImplementationPhase, runPRSplittingPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeRefinePhase, runCodeRefineVerificationPhase, runCodeReviewPhase, runAutonomousPhase, };
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* Uses functional programming principles for composability
|
|
15
15
|
*/
|
|
16
16
|
import { updateFeatureStatusForPhase, markWorkflowPhaseCompleted, } from '../../api/features/index.js';
|
|
17
|
-
import { runFeatureAnalysisPhase, runUserStoriesAnalysisPhase, runTestCasesAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeReviewPhase, } from './executors/phase-executor.js';
|
|
17
|
+
import { runFeatureAnalysisPhase, runUserStoriesAnalysisPhase, runTestCasesAnalysisPhase, runTechnicalDesignPhase, runCodeImplementationPhase, runPRSplittingPhase, runFunctionalTestingPhase, runCodeTestingPhase, runCodeReviewPhase, } from './executors/phase-executor.js';
|
|
18
18
|
import { logPipelineStart, logPhaseResult, logPipelineComplete, shouldContinuePipeline, } from '../../utils/pipeline-logger.js';
|
|
19
19
|
import { handleTestFailuresWithRetry } from '../../phases/functional-testing/test-retry-handler.js';
|
|
20
20
|
import { handleCodeRefineWithRetry } from '../../phases/code-refine/retry-handler.js';
|
|
@@ -78,6 +78,11 @@ export const runPipelineByMode = async (options, config, executionMode) => {
|
|
|
78
78
|
return await runOnlyCodeImplementation(options, config);
|
|
79
79
|
case 'from_code_implementation':
|
|
80
80
|
return await runFromCodeImplementation(options, config);
|
|
81
|
+
// PR Splitting
|
|
82
|
+
case 'only_pr_splitting':
|
|
83
|
+
return await runOnlyPRSplitting(options, config);
|
|
84
|
+
case 'from_pr_splitting':
|
|
85
|
+
return await runFromPRSplitting(options, config);
|
|
81
86
|
// Functional Testing
|
|
82
87
|
case 'only_functional_testing':
|
|
83
88
|
return await runOnlyFunctionalTesting(options, config);
|
|
@@ -310,6 +315,58 @@ const runOnlyCodeImplementation = async (options, config) => {
|
|
|
310
315
|
}
|
|
311
316
|
return finalizePipelineExecution(options, results, verbose);
|
|
312
317
|
};
|
|
318
|
+
/**
|
|
319
|
+
* Run only PR splitting phase
|
|
320
|
+
*/
|
|
321
|
+
const runOnlyPRSplitting = async (options, config) => {
|
|
322
|
+
const { featureId, verbose } = options;
|
|
323
|
+
logPipelineStart(featureId, verbose);
|
|
324
|
+
const results = [];
|
|
325
|
+
const prSplittingResult = await runPRSplittingPhase(options, config);
|
|
326
|
+
results.push(await logAndMarkPhaseCompleted(prSplittingResult, verbose));
|
|
327
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
328
|
+
};
|
|
329
|
+
/**
|
|
330
|
+
* Run from PR splitting to end
|
|
331
|
+
* Flow: pr-splitting ā functional-testing ā ... ā code-review ā code-refine
|
|
332
|
+
*/
|
|
333
|
+
const runFromPRSplitting = async (options, config) => {
|
|
334
|
+
const { featureId, verbose } = options;
|
|
335
|
+
logPipelineStart(featureId, verbose);
|
|
336
|
+
const results = [];
|
|
337
|
+
// 1. PR Splitting
|
|
338
|
+
const prSplittingResult = await runPRSplittingPhase(options, config);
|
|
339
|
+
results.push(await logAndMarkPhaseCompleted(prSplittingResult, verbose));
|
|
340
|
+
if (!shouldContinuePipeline(results)) {
|
|
341
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
342
|
+
}
|
|
343
|
+
// 2. Functional Testing with retry loop for bug fixes
|
|
344
|
+
const testingResult = await handleTestFailuresWithRetry({
|
|
345
|
+
options,
|
|
346
|
+
config,
|
|
347
|
+
results,
|
|
348
|
+
verbose,
|
|
349
|
+
});
|
|
350
|
+
const finalStatus = testingResult.status === 'success' ? 'testing_passed' : 'testing_failed';
|
|
351
|
+
await updateFeatureStatusForPhase(featureId, finalStatus === 'testing_passed' ? 'testing-passed' : 'testing-failed', verbose);
|
|
352
|
+
if (testingResult.status === 'success') {
|
|
353
|
+
const prCreated = await handlePullRequestCreation({
|
|
354
|
+
featureId,
|
|
355
|
+
results,
|
|
356
|
+
testingResult,
|
|
357
|
+
verbose,
|
|
358
|
+
});
|
|
359
|
+
if (prCreated) {
|
|
360
|
+
await continueWithCodeReviewAndRefine(options, config, results, verbose);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
if (verbose) {
|
|
364
|
+
logInfo('ā ļø Skipping code review and refine workflow: pull request creation failed');
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return finalizePipelineExecution(options, results, verbose);
|
|
369
|
+
};
|
|
313
370
|
/**
|
|
314
371
|
* Run only functional testing phase
|
|
315
372
|
*/
|
|
@@ -36,6 +36,8 @@ export const STATUS_PROGRESSION_ORDER = [
|
|
|
36
36
|
'branch_planning_verification',
|
|
37
37
|
'code_implementation',
|
|
38
38
|
'code_implementation_verification',
|
|
39
|
+
'pr_splitting',
|
|
40
|
+
'pr_splitting_verification',
|
|
39
41
|
'code_refine',
|
|
40
42
|
'code_refine_verification',
|
|
41
43
|
'bug_fixing',
|
|
@@ -73,6 +75,8 @@ export const PHASE_STATUS_MAP = {
|
|
|
73
75
|
'branch-planning-verification': 'branch_planning_verification',
|
|
74
76
|
'code-implementation': 'code_implementation',
|
|
75
77
|
'code-implementation-verification': 'code_implementation_verification',
|
|
78
|
+
'pr-splitting': 'pr_splitting',
|
|
79
|
+
'pr-splitting-verification': 'pr_splitting_verification',
|
|
76
80
|
'code-refine': 'code_refine',
|
|
77
81
|
'code-refine-verification': 'code_refine_verification',
|
|
78
82
|
'bug-fixing': 'bug_fixing',
|
|
@@ -105,6 +109,7 @@ export const HUMAN_SELECTABLE_STATUSES = [
|
|
|
105
109
|
'technical_design',
|
|
106
110
|
'branch_planning',
|
|
107
111
|
'code_implementation',
|
|
112
|
+
'pr_splitting',
|
|
108
113
|
'code_refine',
|
|
109
114
|
'bug_fixing',
|
|
110
115
|
'code_review',
|
package/dist/index.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FeatureInfo } from '../../types/features.js';
|
|
2
|
+
import { type ProductInfo } from '../../api/products.js';
|
|
3
|
+
import { type Branch } from '../../services/branches.js';
|
|
4
|
+
import { type PullRequest } from '../../services/pull-requests.js';
|
|
5
|
+
export interface PRSplittingContext {
|
|
6
|
+
feature: FeatureInfo;
|
|
7
|
+
product: ProductInfo;
|
|
8
|
+
existing_branches: Branch[];
|
|
9
|
+
existing_pull_requests: PullRequest[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Fetch all context information needed for PR splitting via MCP endpoints
|
|
13
|
+
*/
|
|
14
|
+
export declare function fetchPRSplittingContext(featureId: string, verbose?: boolean): Promise<PRSplittingContext>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { logInfo, logError } from '../../utils/logger.js';
|
|
2
|
+
import { getFeature } from '../../api/features/index.js';
|
|
3
|
+
import { getProduct } from '../../api/products.js';
|
|
4
|
+
import { getBranches } from '../../services/branches.js';
|
|
5
|
+
import { getPullRequests, } from '../../services/pull-requests.js';
|
|
6
|
+
/**
|
|
7
|
+
* Fetch all context information needed for PR splitting via MCP endpoints
|
|
8
|
+
*/
|
|
9
|
+
export async function fetchPRSplittingContext(featureId, verbose) {
|
|
10
|
+
try {
|
|
11
|
+
if (verbose) {
|
|
12
|
+
logInfo(`Fetching PR splitting context for feature: ${featureId}`);
|
|
13
|
+
}
|
|
14
|
+
// Fetch all required data in parallel for better performance
|
|
15
|
+
const [feature, existing_branches, existing_pull_requests] = await Promise.all([
|
|
16
|
+
getFeature(featureId, verbose),
|
|
17
|
+
getBranches({ featureId, verbose }).catch(() => []),
|
|
18
|
+
getPullRequests({ featureId, verbose }).catch(() => []),
|
|
19
|
+
]);
|
|
20
|
+
const product = await getProduct(feature.product_id, verbose);
|
|
21
|
+
if (verbose) {
|
|
22
|
+
logInfo(`ā
PR splitting context fetched successfully:`);
|
|
23
|
+
logInfo(` Feature: ${feature.name}`);
|
|
24
|
+
logInfo(` Product: ${product.name}`);
|
|
25
|
+
logInfo(` Technical Design: ${feature.technical_design ? 'Yes' : 'No'}`);
|
|
26
|
+
logInfo(` Existing Branches: ${existing_branches.length}`);
|
|
27
|
+
logInfo(` Existing Pull Requests: ${existing_pull_requests.length}`);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
feature,
|
|
31
|
+
product,
|
|
32
|
+
existing_branches,
|
|
33
|
+
existing_pull_requests,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
38
|
+
logError(`Failed to fetch PR splitting context: ${errorMessage}`);
|
|
39
|
+
throw new Error(`Context fetch failed: ${errorMessage}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { EdsgerConfig } from '../../types/index.js';
|
|
2
|
+
export interface PRSplittingOptions {
|
|
3
|
+
featureId: string;
|
|
4
|
+
verbose?: boolean;
|
|
5
|
+
replaceExisting?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface PlannedPullRequest {
|
|
8
|
+
sequence: number;
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
branch_name?: string;
|
|
12
|
+
depends_on_branch_name?: string | null;
|
|
13
|
+
files?: Array<{
|
|
14
|
+
path: string;
|
|
15
|
+
change_type: string;
|
|
16
|
+
}>;
|
|
17
|
+
scope?: string[];
|
|
18
|
+
acceptance_criteria?: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface PRSplittingResult {
|
|
21
|
+
featureId: string;
|
|
22
|
+
pullRequests: PlannedPullRequest[];
|
|
23
|
+
status: 'success' | 'error';
|
|
24
|
+
summary: string;
|
|
25
|
+
rationale?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare const splitFeatureIntoPRs: (options: PRSplittingOptions, config: EdsgerConfig) => Promise<PRSplittingResult>;
|
|
28
|
+
export { fetchPRSplittingContext } from './context.js';
|
|
29
|
+
export type { PRSplittingContext } from './context.js';
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
2
|
+
import { DEFAULT_MODEL } from '../../constants.js';
|
|
3
|
+
import { logInfo, logError } from '../../utils/logger.js';
|
|
4
|
+
import { fetchPRSplittingContext } from './context.js';
|
|
5
|
+
import { createPRSplittingSystemPrompt, createPRSplittingPromptWithContext, createImprovementPrompt, formatContextForPrompt, formatExistingPRsForPrompt, } from './prompts.js';
|
|
6
|
+
import { buildSuccessResult, buildErrorResult, buildNoChangeResult, validatePlannedPRs, sortPRsByDependency, } from './outcome.js';
|
|
7
|
+
import { createPullRequests, clearPullRequests, } from '../../services/pull-requests.js';
|
|
8
|
+
import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
|
|
9
|
+
import { getFeedbacksForPhase, formatFeedbacksForContext, } from '../../services/feedbacks.js';
|
|
10
|
+
function userMessage(content) {
|
|
11
|
+
return {
|
|
12
|
+
type: 'user',
|
|
13
|
+
message: { role: 'user', content: content },
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
async function* prompt(analysisPrompt) {
|
|
17
|
+
yield userMessage(analysisPrompt);
|
|
18
|
+
await new Promise((res) => setTimeout(res, 10000));
|
|
19
|
+
}
|
|
20
|
+
export const splitFeatureIntoPRs = async (options, config) => {
|
|
21
|
+
const { featureId, verbose, replaceExisting } = options;
|
|
22
|
+
if (verbose) {
|
|
23
|
+
logInfo(`Starting PR splitting for feature ID: ${featureId}`);
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
// Log phase start
|
|
27
|
+
await logFeaturePhaseEvent({
|
|
28
|
+
featureId,
|
|
29
|
+
eventType: 'phase_started',
|
|
30
|
+
phase: 'pr_splitting',
|
|
31
|
+
result: 'info',
|
|
32
|
+
metadata: {
|
|
33
|
+
replace_existing: replaceExisting || false,
|
|
34
|
+
timestamp: new Date().toISOString(),
|
|
35
|
+
},
|
|
36
|
+
}, verbose);
|
|
37
|
+
// Fetch context
|
|
38
|
+
const context = await fetchPRSplittingContext(featureId, verbose);
|
|
39
|
+
// Fetch feedbacks for pr-splitting phase
|
|
40
|
+
let feedbacksContext = null;
|
|
41
|
+
let hasFeedbacks = false;
|
|
42
|
+
try {
|
|
43
|
+
feedbacksContext = await getFeedbacksForPhase({ featureId, verbose }, 'pr-splitting');
|
|
44
|
+
hasFeedbacks = feedbacksContext.feedbacks.length > 0;
|
|
45
|
+
if (verbose) {
|
|
46
|
+
logInfo(`š Feedbacks fetched: ${feedbacksContext.feedbacks.length} feedbacks found`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
if (verbose) {
|
|
51
|
+
logError(`Failed to fetch feedbacks: ${error instanceof Error ? error.message : String(error)}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Determine operation mode
|
|
55
|
+
const hasExistingPRs = context.existing_pull_requests.length > 0;
|
|
56
|
+
const isIncrementalUpdate = hasExistingPRs && hasFeedbacks && !replaceExisting;
|
|
57
|
+
if (verbose) {
|
|
58
|
+
logInfo('\nš PR Splitting Mode Decision:');
|
|
59
|
+
logInfo(` - Has existing PRs: ${hasExistingPRs} (${context.existing_pull_requests.length} PRs)`);
|
|
60
|
+
logInfo(` - Has feedbacks: ${hasFeedbacks} (${feedbacksContext?.feedbacks.length || 0} feedbacks)`);
|
|
61
|
+
logInfo(` - Replace existing: ${replaceExisting || false}`);
|
|
62
|
+
logInfo(` - Mode: ${isIncrementalUpdate ? 'š Incremental Update' : hasExistingPRs && !replaceExisting ? 'ā
No Change Needed' : '⨠New Planning'}`);
|
|
63
|
+
}
|
|
64
|
+
// Check if there are existing PRs and no feedbacks - no change needed
|
|
65
|
+
if (hasExistingPRs && !hasFeedbacks && !replaceExisting) {
|
|
66
|
+
if (verbose) {
|
|
67
|
+
logInfo(`Feature already has ${context.existing_pull_requests.length} PRs planned and no feedbacks to address.`);
|
|
68
|
+
logInfo('Use replaceExisting: true to re-plan.');
|
|
69
|
+
}
|
|
70
|
+
return buildNoChangeResult(featureId, context.existing_pull_requests.length);
|
|
71
|
+
}
|
|
72
|
+
// Clear existing PRs if requested (full re-plan)
|
|
73
|
+
if (replaceExisting && hasExistingPRs) {
|
|
74
|
+
if (verbose) {
|
|
75
|
+
logInfo(`Clearing ${context.existing_pull_requests.length} existing PRs for full re-plan...`);
|
|
76
|
+
}
|
|
77
|
+
await clearPullRequests({ featureId, verbose }, true);
|
|
78
|
+
}
|
|
79
|
+
// Format context for prompt
|
|
80
|
+
const contextInfo = formatContextForPrompt(context.feature, context.product, context.existing_branches);
|
|
81
|
+
const existingPRsInfo = formatExistingPRsForPrompt(context.existing_pull_requests);
|
|
82
|
+
// Create prompts based on mode
|
|
83
|
+
const systemPrompt = createPRSplittingSystemPrompt(config, featureId);
|
|
84
|
+
let userPrompt;
|
|
85
|
+
if (isIncrementalUpdate && feedbacksContext) {
|
|
86
|
+
if (verbose) {
|
|
87
|
+
logInfo('\nšÆ Feedbacks that will be addressed:');
|
|
88
|
+
feedbacksContext.feedbacks.forEach((fb, idx) => {
|
|
89
|
+
logInfo(` ${idx + 1}. [${fb.feedback_type}] ${fb.title}`);
|
|
90
|
+
logInfo(` ${fb.content.substring(0, 100)}${fb.content.length > 100 ? '...' : ''}`);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
const feedbacksInfo = await formatFeedbacksForContext(feedbacksContext);
|
|
94
|
+
const currentPlanInfo = existingPRsInfo || 'No existing PR plan documented.';
|
|
95
|
+
userPrompt = createImprovementPrompt(feedbacksInfo, currentPlanInfo);
|
|
96
|
+
userPrompt = `${userPrompt}\n\n## Feature Context\n\n${contextInfo}`;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
userPrompt = createPRSplittingPromptWithContext(featureId, contextInfo, existingPRsInfo);
|
|
100
|
+
}
|
|
101
|
+
if (verbose) {
|
|
102
|
+
logInfo('Starting Claude Code query for PR splitting...');
|
|
103
|
+
}
|
|
104
|
+
// Execute query
|
|
105
|
+
const result = await executePRSplittingQuery(userPrompt, systemPrompt, config, verbose);
|
|
106
|
+
if (!result ||
|
|
107
|
+
!result.pullRequests ||
|
|
108
|
+
result.pullRequests.length === 0) {
|
|
109
|
+
const errorMsg = 'No pull requests were generated';
|
|
110
|
+
logError(errorMsg);
|
|
111
|
+
return buildErrorResult(featureId, errorMsg);
|
|
112
|
+
}
|
|
113
|
+
// Validate PRs
|
|
114
|
+
const validation = validatePlannedPRs(result.pullRequests);
|
|
115
|
+
if (!validation.valid) {
|
|
116
|
+
const errorMsg = `Invalid PR plan: ${validation.errors.join(', ')}`;
|
|
117
|
+
logError(errorMsg);
|
|
118
|
+
return buildErrorResult(featureId, errorMsg);
|
|
119
|
+
}
|
|
120
|
+
// Sort PRs by dependency
|
|
121
|
+
const sortedPRs = sortPRsByDependency(result.pullRequests);
|
|
122
|
+
// For incremental update mode, clear existing PRs before creating new ones
|
|
123
|
+
if (isIncrementalUpdate && hasExistingPRs) {
|
|
124
|
+
if (verbose) {
|
|
125
|
+
logInfo(`š Clearing ${context.existing_pull_requests.length} existing PRs for incremental update...`);
|
|
126
|
+
}
|
|
127
|
+
await clearPullRequests({ featureId, verbose: false }, true);
|
|
128
|
+
}
|
|
129
|
+
// Create PRs in database with proper dependencies
|
|
130
|
+
const prBranchNameToId = new Map();
|
|
131
|
+
const prInputs = [];
|
|
132
|
+
for (const pr of sortedPRs) {
|
|
133
|
+
let basePrId;
|
|
134
|
+
if (pr.depends_on_branch_name) {
|
|
135
|
+
basePrId = prBranchNameToId.get(pr.depends_on_branch_name);
|
|
136
|
+
}
|
|
137
|
+
prInputs.push({
|
|
138
|
+
sequence: pr.sequence,
|
|
139
|
+
name: pr.name,
|
|
140
|
+
description: pr.description,
|
|
141
|
+
branch_name: pr.branch_name,
|
|
142
|
+
base_pr_id: basePrId,
|
|
143
|
+
files: pr.files,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
const created = await createPullRequests({ featureId, verbose: false }, prInputs);
|
|
147
|
+
// Build the branch name to ID mapping from created PRs
|
|
148
|
+
created.forEach((createdPR) => {
|
|
149
|
+
if (createdPR.branch_name) {
|
|
150
|
+
prBranchNameToId.set(createdPR.branch_name, createdPR.id);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
if (verbose) {
|
|
154
|
+
const modeLabel = isIncrementalUpdate ? 'š Updated' : 'ā
Created';
|
|
155
|
+
logInfo(`${modeLabel} ${sortedPRs.length} pull requests`);
|
|
156
|
+
sortedPRs.forEach((pr) => {
|
|
157
|
+
logInfo(` ${pr.sequence}. ${pr.name}`);
|
|
158
|
+
if (pr.files) {
|
|
159
|
+
logInfo(` Files: ${pr.files.map((f) => f.path).join(', ')}`);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
// Log phase completion
|
|
164
|
+
await logFeaturePhaseEvent({
|
|
165
|
+
featureId,
|
|
166
|
+
eventType: 'phase_completed',
|
|
167
|
+
phase: 'pr_splitting',
|
|
168
|
+
result: 'success',
|
|
169
|
+
metadata: {
|
|
170
|
+
prs_created: sortedPRs.length,
|
|
171
|
+
pr_names: sortedPRs.map((pr) => pr.name),
|
|
172
|
+
mode: isIncrementalUpdate ? 'incremental_update' : 'new_planning',
|
|
173
|
+
feedbacks_addressed: feedbacksContext?.feedbacks.length || 0,
|
|
174
|
+
timestamp: new Date().toISOString(),
|
|
175
|
+
},
|
|
176
|
+
}, verbose);
|
|
177
|
+
const summaryPrefix = isIncrementalUpdate
|
|
178
|
+
? `Updated PR plan based on ${feedbacksContext?.feedbacks.length || 0} feedbacks.`
|
|
179
|
+
: '';
|
|
180
|
+
return buildSuccessResult(featureId, sortedPRs, result.summary ||
|
|
181
|
+
`${summaryPrefix} ${sortedPRs.length} pull requests for incremental review`.trim(), result.rationale);
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
185
|
+
logError(`PR splitting failed: ${errorMessage}`);
|
|
186
|
+
// Log phase failure
|
|
187
|
+
await logFeaturePhaseEvent({
|
|
188
|
+
featureId,
|
|
189
|
+
eventType: 'phase_failed',
|
|
190
|
+
phase: 'pr_splitting',
|
|
191
|
+
result: 'error',
|
|
192
|
+
metadata: {
|
|
193
|
+
error: errorMessage,
|
|
194
|
+
timestamp: new Date().toISOString(),
|
|
195
|
+
},
|
|
196
|
+
}, verbose);
|
|
197
|
+
return buildErrorResult(featureId, errorMessage);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Execute the PR splitting query and parse the result
|
|
202
|
+
*/
|
|
203
|
+
async function executePRSplittingQuery(userPrompt, systemPrompt, config, verbose) {
|
|
204
|
+
let lastAssistantResponse = '';
|
|
205
|
+
let structuredResult = null;
|
|
206
|
+
if (verbose) {
|
|
207
|
+
logInfo('š¤ Starting PR splitting agent query...');
|
|
208
|
+
}
|
|
209
|
+
for await (const message of query({
|
|
210
|
+
prompt: prompt(userPrompt),
|
|
211
|
+
options: {
|
|
212
|
+
systemPrompt: {
|
|
213
|
+
type: 'preset',
|
|
214
|
+
preset: 'claude_code',
|
|
215
|
+
append: systemPrompt,
|
|
216
|
+
},
|
|
217
|
+
model: DEFAULT_MODEL,
|
|
218
|
+
maxTurns: 500,
|
|
219
|
+
permissionMode: 'bypassPermissions',
|
|
220
|
+
},
|
|
221
|
+
})) {
|
|
222
|
+
if (verbose) {
|
|
223
|
+
logInfo(` Received message type: ${message.type}`);
|
|
224
|
+
}
|
|
225
|
+
if (message.type === 'assistant' && message.message?.content) {
|
|
226
|
+
for (const content of message.message.content) {
|
|
227
|
+
if (content.type === 'text') {
|
|
228
|
+
lastAssistantResponse += content.text + '\n';
|
|
229
|
+
if (verbose) {
|
|
230
|
+
console.log(`\nš¤ ${content.text}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
else if (content.type === 'tool_use') {
|
|
234
|
+
if (verbose) {
|
|
235
|
+
console.log(`\nš§ ${content.name}: ${content.input.description || 'Running...'}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (message.type === 'result') {
|
|
241
|
+
if (message.subtype === 'success') {
|
|
242
|
+
logInfo('\nš PR splitting completed, parsing results...');
|
|
243
|
+
try {
|
|
244
|
+
const responseText = message.result || lastAssistantResponse;
|
|
245
|
+
structuredResult = parseJsonResponse(responseText, verbose);
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
logError(`Failed to parse PR splitting result: ${error}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
logError(`\nā ļø PR splitting incomplete: ${message.subtype}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (!structuredResult) {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
pullRequests: structuredResult.pullRequests || [],
|
|
261
|
+
summary: structuredResult.summary,
|
|
262
|
+
rationale: structuredResult.rationale,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Parse JSON response from Claude
|
|
267
|
+
*/
|
|
268
|
+
function parseJsonResponse(responseText, verbose) {
|
|
269
|
+
// Try to extract JSON from markdown code block
|
|
270
|
+
const jsonBlockMatch = responseText.match(/```json\s*\n([\s\S]*?)\n\s*```/);
|
|
271
|
+
if (jsonBlockMatch) {
|
|
272
|
+
try {
|
|
273
|
+
const parsed = JSON.parse(jsonBlockMatch[1]);
|
|
274
|
+
if (parsed.pr_splitting_result) {
|
|
275
|
+
return {
|
|
276
|
+
pullRequests: parsed.pr_splitting_result.pull_requests || [],
|
|
277
|
+
summary: parsed.pr_splitting_result.summary,
|
|
278
|
+
rationale: parsed.pr_splitting_result.rationale,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
if (parsed.pull_requests) {
|
|
282
|
+
return {
|
|
283
|
+
pullRequests: parsed.pull_requests,
|
|
284
|
+
summary: parsed.summary,
|
|
285
|
+
rationale: parsed.rationale,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
return parsed;
|
|
289
|
+
}
|
|
290
|
+
catch (e) {
|
|
291
|
+
if (verbose) {
|
|
292
|
+
logError(`Failed to parse JSON from code block: ${e}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// Try to find JSON object containing "pr_splitting_result"
|
|
297
|
+
const resultMatch = responseText.match(/\{[\s\S]*"pr_splitting_result"[\s\S]*\}/);
|
|
298
|
+
if (resultMatch) {
|
|
299
|
+
try {
|
|
300
|
+
let braceCount = 0;
|
|
301
|
+
let startIndex = -1;
|
|
302
|
+
let endIndex = -1;
|
|
303
|
+
for (let i = 0; i < responseText.length; i++) {
|
|
304
|
+
if (responseText[i] === '{') {
|
|
305
|
+
if (startIndex === -1)
|
|
306
|
+
startIndex = i;
|
|
307
|
+
braceCount++;
|
|
308
|
+
}
|
|
309
|
+
else if (responseText[i] === '}') {
|
|
310
|
+
braceCount--;
|
|
311
|
+
if (braceCount === 0 && startIndex !== -1) {
|
|
312
|
+
endIndex = i;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (startIndex !== -1 && endIndex !== -1) {
|
|
318
|
+
const jsonStr = responseText.substring(startIndex, endIndex + 1);
|
|
319
|
+
const parsed = JSON.parse(jsonStr);
|
|
320
|
+
if (parsed.pr_splitting_result) {
|
|
321
|
+
return {
|
|
322
|
+
pullRequests: parsed.pr_splitting_result.pull_requests || [],
|
|
323
|
+
summary: parsed.pr_splitting_result.summary,
|
|
324
|
+
rationale: parsed.pr_splitting_result.rationale,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
catch (e) {
|
|
330
|
+
if (verbose) {
|
|
331
|
+
logError(`Failed to extract JSON from response: ${e}`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// Try to parse the entire response as JSON
|
|
336
|
+
try {
|
|
337
|
+
const parsed = JSON.parse(responseText);
|
|
338
|
+
if (parsed.pr_splitting_result) {
|
|
339
|
+
return {
|
|
340
|
+
pullRequests: parsed.pr_splitting_result.pull_requests || [],
|
|
341
|
+
summary: parsed.pr_splitting_result.summary,
|
|
342
|
+
rationale: parsed.pr_splitting_result.rationale,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
if (parsed.pull_requests) {
|
|
346
|
+
return {
|
|
347
|
+
pullRequests: parsed.pull_requests,
|
|
348
|
+
summary: parsed.summary,
|
|
349
|
+
rationale: parsed.rationale,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
catch (e) {
|
|
354
|
+
if (verbose) {
|
|
355
|
+
logError(`Failed to parse response as JSON: ${e}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
// Re-export types and functions for external use
|
|
361
|
+
export { fetchPRSplittingContext } from './context.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { PRSplittingResult, PlannedPullRequest } from './index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build a successful PR splitting result
|
|
4
|
+
*/
|
|
5
|
+
export declare function buildSuccessResult(featureId: string, pullRequests: PlannedPullRequest[], summary: string, rationale?: string): PRSplittingResult;
|
|
6
|
+
/**
|
|
7
|
+
* Build an error result
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildErrorResult(featureId: string, errorMessage: string): PRSplittingResult;
|
|
10
|
+
/**
|
|
11
|
+
* Build a result when no changes are needed
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildNoChangeResult(featureId: string, existingPRCount: number): PRSplittingResult;
|
|
14
|
+
/**
|
|
15
|
+
* Validate planned pull requests
|
|
16
|
+
*/
|
|
17
|
+
export declare function validatePlannedPRs(prs: PlannedPullRequest[]): {
|
|
18
|
+
valid: boolean;
|
|
19
|
+
errors: string[];
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Sort pull requests by dependency order (topological sort based on depends_on_branch_name)
|
|
23
|
+
*/
|
|
24
|
+
export declare function sortPRsByDependency(prs: PlannedPullRequest[]): PlannedPullRequest[];
|