edsger 0.40.0 → 0.40.1

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.
@@ -38,6 +38,18 @@ export declare function removeDeletedFilesFromPRs(changedFiles: ChangedFileInfo[
38
38
  change_type: string;
39
39
  }[];
40
40
  }, verbose?: boolean) => Promise<unknown>, verbose?: boolean): Promise<number>;
41
+ /**
42
+ * Remove files from PR file lists that are no longer in the current diff.
43
+ * Used after rebase when the full diff against main may differ from what
44
+ * was previously recorded in PR file lists.
45
+ * Returns the number of PRs updated.
46
+ */
47
+ export declare function removeStaleFilesFromPRs(changedFiles: ChangedFileInfo[], pullRequests: PullRequest[], updater?: (prId: string, updates: {
48
+ files: {
49
+ path: string;
50
+ change_type: string;
51
+ }[];
52
+ }, verbose?: boolean) => Promise<unknown>, verbose?: boolean): Promise<number>;
41
53
  /**
42
54
  * Detect unassigned files and use LLM to assign them to existing PRs.
43
55
  * Returns the number of files assigned, or 0 if none needed assignment.
@@ -194,6 +194,40 @@ export async function removeDeletedFilesFromPRs(changedFiles, pullRequests, upda
194
194
  }
195
195
  return updatedCount;
196
196
  }
197
+ /**
198
+ * Remove files from PR file lists that are no longer in the current diff.
199
+ * Used after rebase when the full diff against main may differ from what
200
+ * was previously recorded in PR file lists.
201
+ * Returns the number of PRs updated.
202
+ */
203
+ export async function removeStaleFilesFromPRs(changedFiles, pullRequests, updater = updatePullRequest, verbose) {
204
+ const currentPaths = new Set(changedFiles.map((f) => f.path));
205
+ let updatedCount = 0;
206
+ for (const pr of pullRequests) {
207
+ if (!pr.files || pr.files.length === 0) {
208
+ continue;
209
+ }
210
+ const filtered = pr.files.filter((f) => currentPaths.has(f.path));
211
+ if (filtered.length === pr.files.length) {
212
+ continue;
213
+ }
214
+ const removed = pr.files.length - filtered.length;
215
+ try {
216
+ await updater(pr.id, { files: filtered }, verbose);
217
+ updatedCount++;
218
+ if (verbose) {
219
+ logInfo(` Cleaned PR "${pr.name}": removed ${removed} stale file(s)`);
220
+ }
221
+ }
222
+ catch (error) {
223
+ logError(`Failed to clean PR ${pr.id}: ${error instanceof Error ? error.message : String(error)}`);
224
+ }
225
+ }
226
+ if (verbose && updatedCount > 0) {
227
+ logInfo(`🧹 Removed stale files from ${updatedCount} PR(s)`);
228
+ }
229
+ return updatedCount;
230
+ }
197
231
  /**
198
232
  * Detect unassigned files and use LLM to assign them to existing PRs.
199
233
  * Returns the number of files assigned, or 0 if none needed assignment.
@@ -6,7 +6,7 @@ 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';
9
- import { assignNewFilesToPRs, removeDeletedFilesFromPRs, } from './file-assigner.js';
9
+ import { assignNewFilesToPRs, removeDeletedFilesFromPRs, removeStaleFilesFromPRs, } from './file-assigner.js';
10
10
  import { buildExecutionErrorResult, buildExecutionSuccessResult, buildNoChangeResult, } from './outcome.js';
11
11
  import { pushBranchAndBuildUrl, updatePRDatabaseRecord, } from './pr-executor.js';
12
12
  import { createIncrementalSyncPrompt, createIncrementalSyncSystemPrompt, createPRExecutionPrompt, createPRExecutionSystemPrompt, } from './prompts.js';
@@ -67,16 +67,31 @@ export const executeFeaturePRs = async (options, config) => {
67
67
  return buildNoChangeResult(featureId, context.pullRequests.length);
68
68
  }
69
69
  // ======================================
70
- // Assign unassigned files to PRs (incremental sync only)
70
+ // Reconcile file assignments
71
71
  // ======================================
72
72
  let activePullRequests = context.pullRequests;
73
- if (context.isIncrementalSync) {
74
- // Remove deleted files from PR file lists
75
- const deletedCount = await removeDeletedFilesFromPRs(context.changedFiles, activePullRequests, undefined, verbose);
76
- // Assign new/modified files not covered by any PR
73
+ // Reconcile file lists against the current diff.
74
+ if (context.changedFiles.length > 0) {
75
+ let needsRefetch = false;
76
+ if (context.isIncrementalSync) {
77
+ // Incremental: only remove explicitly deleted files
78
+ const deletedCount = await removeDeletedFilesFromPRs(context.changedFiles, activePullRequests, undefined, verbose);
79
+ if (deletedCount > 0)
80
+ needsRefetch = true;
81
+ }
82
+ else {
83
+ // Full re-execution (e.g. after rebase): remove any PR files that
84
+ // are no longer in the current diff against main
85
+ const staleCount = await removeStaleFilesFromPRs(context.changedFiles, activePullRequests, undefined, verbose);
86
+ if (staleCount > 0)
87
+ needsRefetch = true;
88
+ }
89
+ // Assign changed files not covered by any PR
90
+ if (needsRefetch) {
91
+ activePullRequests = await getPullRequests({ featureId, verbose });
92
+ }
77
93
  const assignedCount = await assignNewFilesToPRs(context.changedFiles, activePullRequests, verbose);
78
- if (assignedCount > 0 || deletedCount > 0) {
79
- // Re-fetch PR records so prompts use the updated file lists
94
+ if (assignedCount > 0) {
80
95
  activePullRequests = await getPullRequests({ featureId, verbose });
81
96
  }
82
97
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edsger",
3
- "version": "0.40.0",
3
+ "version": "0.40.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "edsger": "dist/index.js"