edsger 0.41.2 → 0.41.3

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.
@@ -130,9 +130,7 @@ async function validateAndLogChecklists(options, name, checklistContext, feature
130
130
  }
131
131
  }
132
132
  // Higher-order function for phase execution
133
- export const createPhaseRunner = (phaseConfig) => async (options, config
134
- // eslint-disable-next-line complexity -- orchestration function with sequential phase setup, execution, and result processing
135
- ) => {
133
+ export const createPhaseRunner = (phaseConfig) => async (options, config) => {
136
134
  const { featureId, verbose } = options;
137
135
  const { name, execute } = phaseConfig;
138
136
  // Track phase duration for logging
@@ -36,9 +36,7 @@ const logAndMarkPhaseCompleted = async (result, verbose) => {
36
36
  * Orchestrate phase execution based on execution mode
37
37
  * Routes to appropriate phase sequence based on mode (only_*, from_*, full_pipeline)
38
38
  */
39
- export const runPipelineByMode = async (options, config, executionMode
40
- // eslint-disable-next-line complexity -- orchestration function routing many execution modes to phase sequences
41
- ) => {
39
+ export const runPipelineByMode = async (options, config, executionMode) => {
42
40
  const { featureId, verbose } = options;
43
41
  if (verbose) {
44
42
  logInfo(`🚀 Starting pipeline with mode: ${executionMode} for feature: ${featureId}`);
package/dist/index.js CHANGED
@@ -16,10 +16,10 @@ import { runConfigGet, runConfigList, runConfigSet, runConfigUnset, } from './co
16
16
  import { runGrowthAnalysis } from './commands/growth-analysis/index.js';
17
17
  import { runInit } from './commands/init/index.js';
18
18
  import { runIntelligence } from './commands/intelligence/index.js';
19
+ import { runPRResolve } from './commands/pr-resolve/index.js';
20
+ import { runPRReview } from './commands/pr-review/index.js';
19
21
  import { runRefactor } from './commands/refactor/refactor.js';
20
22
  import { runTaskWorker } from './commands/task-worker/index.js';
21
- import { runPRReview } from './commands/pr-review/index.js';
22
- import { runPRResolve } from './commands/pr-resolve/index.js';
23
23
  import { runWorkflow } from './commands/workflow/index.js';
24
24
  import { logError, logInfo } from './utils/logger.js';
25
25
  // Get package.json version dynamically
@@ -13,9 +13,7 @@ function truncateKeywords(keywords) {
13
13
  }
14
14
  return keywords.slice(0, 100).replace(/,[^,]*$/, '');
15
15
  }
16
- export const generateAppStoreAssets = async (options, config
17
- // eslint-disable-next-line complexity -- orchestration function with sequential asset generation steps
18
- ) => {
16
+ export const generateAppStoreAssets = async (options, config) => {
19
17
  const { productId, targetStore, screenshotsOnly, listingsOnly, verbose } = options;
20
18
  if (verbose) {
21
19
  logInfo(`Starting app store generation for product: ${productId}`);
@@ -116,9 +116,7 @@ async function persistBranches(sortedBranches, featureId, verbose) {
116
116
  }
117
117
  }
118
118
  }
119
- export const planFeatureBranches = async (options, config
120
- // eslint-disable-next-line complexity -- orchestration function with context fetching, agent execution, and result processing
121
- ) => {
119
+ export const planFeatureBranches = async (options, config) => {
122
120
  const { featureId, verbose, replaceExisting } = options;
123
121
  if (verbose) {
124
122
  logInfo(`Starting branch planning for feature ID: ${featureId}`);
@@ -32,9 +32,7 @@ async function* prompt(bugFixPrompt) {
32
32
  setTimeout(res, 10000);
33
33
  });
34
34
  }
35
- export const fixTestFailures = async (options, config
36
- // eslint-disable-next-line complexity -- orchestration function with agent setup, message processing, and result extraction
37
- ) => {
35
+ export const fixTestFailures = async (options, config) => {
38
36
  const { featureId, testErrors, attemptNumber = 1, verbose } = options;
39
37
  if (verbose) {
40
38
  logInfo(`Starting bug fixing for feature ID: ${featureId} (Attempt ${attemptNumber})`);
@@ -170,9 +170,7 @@ message, lastAssistantResponse, featureId, verbose
170
170
  }
171
171
  return null;
172
172
  }
173
- export const implementFeatureCode = async (options, config, checklistContext
174
- // eslint-disable-next-line complexity -- orchestration function coordinating full code implementation lifecycle
175
- ) => {
173
+ export const implementFeatureCode = async (options, config, checklistContext) => {
176
174
  const { featureId, verbose, baseBranch = 'main' } = options;
177
175
  if (verbose) {
178
176
  logInfo(`Starting code implementation for feature ID: ${featureId}`);
@@ -41,9 +41,7 @@ export const MAX_REFINE_ITERATIONS = 10;
41
41
  * Similar to technical-design, this includes an iterative improvement cycle:
42
42
  * refine → verification → improve → re-refine (if needed)
43
43
  */
44
- export const refineCodeFromPRFeedback = async (options, config, checklistContext
45
- // eslint-disable-next-line complexity -- orchestration function coordinating PR feedback refinement lifecycle
46
- ) => {
44
+ export const refineCodeFromPRFeedback = async (options, config, checklistContext) => {
47
45
  const { featureId, githubToken, verbose } = options;
48
46
  if (verbose) {
49
47
  logInfo(`Starting code refine for feature ID: ${featureId}`);
@@ -67,9 +67,7 @@ baseBranchInfo, baseBranchForRebase, originalBaseBranchForRebase, verbose) {
67
67
  /**
68
68
  * Main code review function
69
69
  */
70
- export const reviewPullRequest = async (options, config, checklistContext
71
- // eslint-disable-next-line complexity -- orchestration function coordinating full code review lifecycle
72
- ) => {
70
+ export const reviewPullRequest = async (options, config, checklistContext) => {
73
71
  const { featureId, githubToken, verbose } = options;
74
72
  if (verbose) {
75
73
  logInfo(`Starting code review for feature ID: ${featureId}`);
@@ -31,9 +31,7 @@ async function* prompt(testingPrompt) {
31
31
  setTimeout(res, 10000);
32
32
  });
33
33
  }
34
- export const writeCodeTests = async (options, config
35
- // eslint-disable-next-line complexity -- orchestration function with context fetching, agent execution, and test result processing
36
- ) => {
34
+ export const writeCodeTests = async (options, config) => {
37
35
  const { featureId, verbose } = options;
38
36
  if (verbose) {
39
37
  logInfo(`Starting code testing phase for feature ID: ${featureId}`);
@@ -6,9 +6,7 @@ import { executeAnalysisQuery, parseAnalysisResult } from './agent.js';
6
6
  import { prepareAnalysisContext } from './context.js';
7
7
  import { buildAnalysisResult, deleteArtifacts, deleteSpecificArtifacts, getAllDraftArtifactIds, resetReadyArtifactsToDraft, saveAnalysisArtifactsAsDraft, updateArtifactsToReady, } from './outcome.js';
8
8
  import { createFeatureAnalysisSystemPrompt } from './prompts.js';
9
- export const analyseFeature = async (options, config, checklistContext
10
- // eslint-disable-next-line complexity -- orchestration function with context assembly, agent execution, and result processing
11
- ) => {
9
+ export const analyseFeature = async (options, config, checklistContext) => {
12
10
  const { featureId, verbose } = options;
13
11
  if (verbose) {
14
12
  logInfo(`Starting feature analysis for feature ID: ${featureId}`);
@@ -326,9 +326,7 @@ async function saveTestResults(featureId, testStatus, structuredTestResult, last
326
326
  }
327
327
  return testReportResult;
328
328
  }
329
- export const runFunctionalTesting = async (options, config, checklistContext
330
- // eslint-disable-next-line complexity -- orchestration function coordinating functional test execution and result processing
331
- ) => {
329
+ export const runFunctionalTesting = async (options, config, checklistContext) => {
332
330
  const { featureId, verbose } = options;
333
331
  if (verbose) {
334
332
  logInfo(`Starting functional testing for feature ID: ${featureId}`);
@@ -49,9 +49,7 @@ contentSuggestions) {
49
49
  }
50
50
  return plans;
51
51
  }
52
- export const analyseGrowth = async (options, config
53
- // eslint-disable-next-line complexity -- orchestration function with context assembly, agent execution, and result processing
54
- ) => {
52
+ export const analyseGrowth = async (options, config) => {
55
53
  const { productId, verbose, guidance, analysisId } = options;
56
54
  if (verbose) {
57
55
  logInfo(`Starting growth analysis for product ID: ${productId}`);
@@ -2,7 +2,7 @@ import { query } from '@anthropic-ai/claude-agent-sdk';
2
2
  import { execSync } from 'child_process';
3
3
  import { DEFAULT_MODEL } from '../../constants.js';
4
4
  import { logFeaturePhaseEvent } from '../../services/audit-logs.js';
5
- import { getPullRequests } from '../../services/pull-requests.js';
5
+ import { getPullRequests, } from '../../services/pull-requests.js';
6
6
  import { getCurrentBranch, returnToMainBranch, } from '../../utils/git-branch-manager.js';
7
7
  import { logDebug, logError, logInfo } from '../../utils/logger.js';
8
8
  import { fetchPRExecutionContext } from './context.js';
@@ -98,6 +98,18 @@ export const executeFeaturePRs = async (options, config) => {
98
98
  }
99
99
  }
100
100
  // ======================================
101
+ // Filter to only the next eligible PR
102
+ // ======================================
103
+ // Only process the first pending PR whose predecessor is merged/closed.
104
+ // Already-executed PRs (branch_created, pr_opened, etc.) are included for sync.
105
+ activePullRequests = filterToEligiblePRs(activePullRequests, verbose);
106
+ if (activePullRequests.length === 0) {
107
+ if (verbose) {
108
+ logInfo('No eligible PRs to process — waiting for current PR to be merged or closed.');
109
+ }
110
+ return buildNoChangeResult(featureId, context.pullRequests.length);
111
+ }
112
+ // ======================================
101
113
  // Agent: Create/Update branches
102
114
  // ======================================
103
115
  if (verbose) {
@@ -285,6 +297,52 @@ export const executeFeaturePRs = async (options, config) => {
285
297
  return buildExecutionErrorResult(featureId, errorMessage);
286
298
  }
287
299
  };
300
+ /**
301
+ * Filter PRs to only include the next eligible pending PR.
302
+ *
303
+ * - Already-executed PRs (branch_created, pr_opened, in_review, approved) are
304
+ * kept for incremental sync.
305
+ * - Merged/closed PRs are excluded (no branch work needed).
306
+ * - Only the first pending PR is included, and only if the PR immediately
307
+ * before it (by sequence) is merged or closed (or it's the first PR).
308
+ * - All other pending PRs are blocked until the current one completes.
309
+ */
310
+ function filterToEligiblePRs(pullRequests, verbose) {
311
+ const sorted = [...pullRequests].sort((a, b) => a.sequence - b.sequence);
312
+ const eligible = [];
313
+ let foundPendingToProcess = false;
314
+ let blockedCount = 0;
315
+ for (let i = 0; i < sorted.length; i++) {
316
+ const pr = sorted[i];
317
+ if (pr.status === 'merged' || pr.status === 'closed') {
318
+ // Done — no branch work needed
319
+ continue;
320
+ }
321
+ if (pr.status !== 'pending') {
322
+ // Already executed but not done — include for incremental sync
323
+ eligible.push(pr);
324
+ continue;
325
+ }
326
+ // PR is pending
327
+ if (foundPendingToProcess) {
328
+ blockedCount++;
329
+ continue;
330
+ }
331
+ // Check if the previous PR (by sequence) is merged or closed
332
+ const prevPR = i > 0 ? sorted[i - 1] : null;
333
+ if (!prevPR || prevPR.status === 'merged' || prevPR.status === 'closed') {
334
+ eligible.push(pr);
335
+ }
336
+ else {
337
+ blockedCount++;
338
+ }
339
+ foundPendingToProcess = true;
340
+ }
341
+ if (blockedCount > 0 && verbose) {
342
+ logInfo(`⏳ ${blockedCount} pending PR(s) waiting for previous PR to be merged or closed`);
343
+ }
344
+ return eligible;
345
+ }
288
346
  /**
289
347
  * Execute an agent query for branch creation/update
290
348
  */
@@ -73,8 +73,9 @@ export function createResolveUserPrompt(unresolvedThreads) {
73
73
  let commentIndex = 0;
74
74
  for (const thread of unresolvedThreads) {
75
75
  const firstComment = thread.comments.nodes[0];
76
- if (!firstComment)
76
+ if (!firstComment) {
77
77
  continue;
78
+ }
78
79
  commentIndex++;
79
80
  const commentId = `comment_${commentIndex}`;
80
81
  commentIdToThreadId.set(commentId, thread.id);
@@ -18,7 +18,7 @@ export declare function prepareWorkspace(owner: string, repo: string, headRef: s
18
18
  /**
19
19
  * Push changes from workspace back to remote.
20
20
  */
21
- export declare function pushChanges(repoPath: string, headRef: string, token: string, verbose?: boolean): boolean;
21
+ export declare function pushChanges(repoPath: string, headRef: string, token: string, _verbose?: boolean): boolean;
22
22
  /**
23
23
  * Check if there are uncommitted changes in the workspace.
24
24
  */
@@ -98,7 +98,7 @@ export function prepareWorkspace(owner, repo, headRef, prNumber, token, verbose)
98
98
  /**
99
99
  * Push changes from workspace back to remote.
100
100
  */
101
- export function pushChanges(repoPath, headRef, token, verbose) {
101
+ export function pushChanges(repoPath, headRef, token, _verbose) {
102
102
  const gitCredentialArgs = buildCredentialArgs(token);
103
103
  try {
104
104
  execFileSync('git', [...gitCredentialArgs, 'push', 'origin', headRef], {
@@ -17,11 +17,13 @@ function mapCommentsToReviewPayload(agentComments, files) {
17
17
  const result = [];
18
18
  for (const comment of agentComments) {
19
19
  const lineToPosition = fileLineToPosition.get(comment.file);
20
- if (!lineToPosition)
20
+ if (!lineToPosition) {
21
21
  continue;
22
+ }
22
23
  const positionResult = findClosestPosition(comment.line, lineToPosition);
23
- if (!positionResult)
24
+ if (!positionResult) {
24
25
  continue;
26
+ }
25
27
  let body = comment.comment;
26
28
  if (positionResult.actualLine !== comment.line) {
27
29
  body = `**Note**: Comment originally for line ${comment.line}, adjusted to line ${positionResult.actualLine} (nearest line in diff).\n\n${body}`;
@@ -14,9 +14,7 @@ import { createStandaloneReviewSystemPrompt, createStandaloneReviewUserPrompt, }
14
14
  /**
15
15
  * Review a standalone PR and post comments to GitHub.
16
16
  */
17
- export async function reviewStandalonePR(options
18
- // eslint-disable-next-line complexity
19
- ) {
17
+ export async function reviewStandalonePR(options) {
20
18
  const { pullRequestUrl, githubToken, verbose, prId } = options;
21
19
  logInfo(`Starting standalone PR review: ${pullRequestUrl}`);
22
20
  try {
@@ -24,7 +24,6 @@ export async function* createPromptGenerator(prompt) {
24
24
  /**
25
25
  * Extract text content from assistant message content array.
26
26
  */
27
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
27
  export function extractTextFromContent(
29
28
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
29
  content, verbose) {
@@ -3,8 +3,8 @@
3
3
  * Reuses GitHub API utilities from code-review/context.ts without feature dependencies.
4
4
  */
5
5
  import { type PRCommit, type PRData, type PRFile } from '../code-review/context.js';
6
- export { parsePullRequestUrl } from '../code-review/context.js';
7
6
  export type { PRCommit, PRData, PRFile } from '../code-review/context.js';
7
+ export { parsePullRequestUrl } from '../code-review/context.js';
8
8
  export interface StandalonePRContext {
9
9
  pullRequestUrl: string;
10
10
  pullRequestNumber: number;
@@ -27,9 +27,7 @@ async function* prompt(analysisPrompt) {
27
27
  * then uses AI to produce a PR split plan saved to the database.
28
28
  * Human review is expected before running the pr-execution phase.
29
29
  */
30
- export const splitFeatureIntoPRs = async (options, config
31
- // eslint-disable-next-line complexity -- orchestration function with context fetching, agent execution, and result processing
32
- ) => {
30
+ export const splitFeatureIntoPRs = async (options, config) => {
33
31
  const { featureId, verbose, replaceExisting } = options;
34
32
  if (verbose) {
35
33
  logInfo(`Starting PR splitting for feature ID: ${featureId}`);
@@ -24,9 +24,7 @@ async function* prompt(analysisPrompt) {
24
24
  setTimeout(res, 10000);
25
25
  });
26
26
  }
27
- export const generateTechnicalDesign = async (options, config, checklistContext
28
- // eslint-disable-next-line complexity -- orchestration function with context assembly, design generation, and verification
29
- ) => {
27
+ export const generateTechnicalDesign = async (options, config, checklistContext) => {
30
28
  const { featureId, verbose } = options;
31
29
  if (verbose) {
32
30
  logInfo(`Starting technical design generation for feature ID: ${featureId}`);
@@ -6,9 +6,7 @@ import { executeTestCasesAnalysisQuery, parseAnalysisResult } from './agent.js';
6
6
  import { prepareTestCasesAnalysisContext } from './context.js';
7
7
  import { buildTestCasesAnalysisResult, deleteSpecificTestCases, deleteTestCaseArtifacts, getAllDraftTestCaseIds, resetReadyTestCasesToDraft, saveTestCasesAsDraft, updateTestCasesToReady, } from './outcome.js';
8
8
  import { createTestCasesAnalysisSystemPrompt } from './prompts.js';
9
- export const analyseTestCases = async (options, config, checklistContext
10
- // eslint-disable-next-line complexity -- orchestration function with context assembly, agent execution, and result processing
11
- ) => {
9
+ export const analyseTestCases = async (options, config, checklistContext) => {
12
10
  const { featureId, verbose } = options;
13
11
  if (verbose) {
14
12
  logInfo(`Starting test cases analysis for feature ID: ${featureId}`);
@@ -6,9 +6,7 @@ import { executeUserStoriesAnalysisQuery, parseAnalysisResult, } from './agent.j
6
6
  import { prepareUserStoriesAnalysisContext } from './context.js';
7
7
  import { buildUserStoriesAnalysisResult, deleteSpecificUserStories, deleteUserStoryArtifacts, getAllDraftUserStoryIds, resetReadyUserStoriesToDraft, saveUserStoriesAsDraft, updateUserStoriesToReady, } from './outcome.js';
8
8
  import { createUserStoriesAnalysisSystemPrompt } from './prompts.js';
9
- export const analyseUserStories = async (options, config, checklistContext
10
- // eslint-disable-next-line complexity -- orchestration function with context assembly, agent execution, and result processing
11
- ) => {
9
+ export const analyseUserStories = async (options, config, checklistContext) => {
12
10
  const { featureId, verbose } = options;
13
11
  if (verbose) {
14
12
  logInfo(`Starting user stories analysis for feature ID: ${featureId}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edsger",
3
- "version": "0.41.2",
3
+ "version": "0.41.3",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "edsger": "dist/index.js"