edsger 0.39.1 → 0.39.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.
- package/dist/phases/pr-execution/index.js +13 -2
- package/dist/phases/pr-execution/pr-executor.d.ts +2 -1
- package/dist/phases/pr-execution/pr-executor.js +3 -2
- package/dist/phases/pr-execution/prompts.js +27 -10
- package/dist/services/phase-hooks/plugin-loader.js +2 -2
- package/dist/skills/phase/incremental-sync/SKILL.md +22 -11
- package/dist/skills/phase/pr-execution/SKILL.md +25 -10
- package/dist/skills/phase/pr-splitting/SKILL.md +9 -1
- package/package.json +1 -1
|
@@ -128,6 +128,13 @@ export const executeFeaturePRs = async (options, config) => {
|
|
|
128
128
|
skippedBranches: [],
|
|
129
129
|
failedBranches: [],
|
|
130
130
|
};
|
|
131
|
+
// Build PR id → branch name map for stacked PR base resolution
|
|
132
|
+
const prIdToBranch = new Map();
|
|
133
|
+
for (const pr of context.pullRequests) {
|
|
134
|
+
if (pr.branch_name) {
|
|
135
|
+
prIdToBranch.set(pr.id, pr.branch_name);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
131
138
|
for (const pr of context.pullRequests) {
|
|
132
139
|
if (!pr.branch_name) {
|
|
133
140
|
if (verbose) {
|
|
@@ -156,8 +163,12 @@ export const executeFeaturePRs = async (options, config) => {
|
|
|
156
163
|
else {
|
|
157
164
|
executionSummary.branchesCreated++;
|
|
158
165
|
}
|
|
159
|
-
//
|
|
160
|
-
const
|
|
166
|
+
// Resolve base branch for stacked PRs
|
|
167
|
+
const baseBranch = pr.base_pr_id
|
|
168
|
+
? prIdToBranch.get(pr.base_pr_id) || 'main'
|
|
169
|
+
: 'main';
|
|
170
|
+
// Push branch and build compare URL (using stacked base)
|
|
171
|
+
const result = pushBranchAndBuildUrl(executionConfig, pr.branch_name, baseBranch);
|
|
161
172
|
if (result.success) {
|
|
162
173
|
// Update database record with compare URL and sync commit
|
|
163
174
|
await updatePRDatabaseRecord(pr.id, result.compareUrl, context.devBranchHeadSha, verbose).catch((error) => {
|
|
@@ -33,8 +33,9 @@ export declare function getLocalBranchSha(branchName: string): string;
|
|
|
33
33
|
export declare function buildCompareUrl(forkInfo: RepoForkInfo, owner: string, repo: string, branchName: string, base?: string): string;
|
|
34
34
|
/**
|
|
35
35
|
* Push a branch and build the compare URL
|
|
36
|
+
* For stacked PRs, baseBranch is the dependency PR's branch instead of 'main'
|
|
36
37
|
*/
|
|
37
|
-
export declare function pushBranchAndBuildUrl(config: PRExecutionConfig, branchName: string): PRBranchResult;
|
|
38
|
+
export declare function pushBranchAndBuildUrl(config: PRExecutionConfig, branchName: string, baseBranch?: string): PRBranchResult;
|
|
38
39
|
/**
|
|
39
40
|
* Update a PR record in the database after branch push
|
|
40
41
|
*/
|
|
@@ -37,13 +37,14 @@ export function buildCompareUrl(forkInfo, owner, repo, branchName, base = 'main'
|
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* Push a branch and build the compare URL
|
|
40
|
+
* For stacked PRs, baseBranch is the dependency PR's branch instead of 'main'
|
|
40
41
|
*/
|
|
41
|
-
export function pushBranchAndBuildUrl(config, branchName) {
|
|
42
|
+
export function pushBranchAndBuildUrl(config, branchName, baseBranch = 'main') {
|
|
42
43
|
const { owner, repo, forkInfo, verbose, token } = config;
|
|
43
44
|
const headSha = getLocalBranchSha(branchName);
|
|
44
45
|
try {
|
|
45
46
|
pushBranch(branchName, verbose, token);
|
|
46
|
-
const compareUrl = buildCompareUrl(forkInfo, owner, repo, branchName);
|
|
47
|
+
const compareUrl = buildCompareUrl(forkInfo, owner, repo, branchName, baseBranch);
|
|
47
48
|
if (verbose) {
|
|
48
49
|
logInfo(`🔗 Compare URL: ${compareUrl}`);
|
|
49
50
|
}
|
|
@@ -34,6 +34,16 @@ export async function createIncrementalSyncSystemPrompt(featureId, devBranchName
|
|
|
34
34
|
});
|
|
35
35
|
return `${prompt}\n\n${OUTPUT_CONTRACTS['incremental-sync']}`;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Resolve the base branch for a PR based on its dependency chain
|
|
39
|
+
*/
|
|
40
|
+
function resolveBaseBranch(pr, allPRs) {
|
|
41
|
+
if (!pr.base_pr_id) {
|
|
42
|
+
return 'main';
|
|
43
|
+
}
|
|
44
|
+
const basePR = allPRs.find((p) => p.id === pr.base_pr_id);
|
|
45
|
+
return basePR?.branch_name || 'main';
|
|
46
|
+
}
|
|
37
47
|
/**
|
|
38
48
|
* Create the user prompt for first-time branch creation
|
|
39
49
|
*/
|
|
@@ -43,27 +53,29 @@ export function createPRExecutionPrompt(featureId, devBranchName, pullRequests)
|
|
|
43
53
|
const files = pr.files
|
|
44
54
|
? pr.files.map((f) => ` - ${f.path} (${f.change_type})`).join('\n')
|
|
45
55
|
: ' (no files specified)';
|
|
56
|
+
const baseBranch = resolveBaseBranch(pr, pullRequests);
|
|
46
57
|
return `### PR ${pr.sequence}: ${pr.name}
|
|
47
58
|
- Branch: \`${pr.branch_name}\`
|
|
59
|
+
- Base: \`${baseBranch}\`
|
|
48
60
|
- Description: ${pr.description}
|
|
49
61
|
- Files:
|
|
50
62
|
${files}`;
|
|
51
63
|
})
|
|
52
64
|
.join('\n\n');
|
|
53
|
-
return `# Create PR Branches
|
|
65
|
+
return `# Create PR Branches (Stacked)
|
|
54
66
|
|
|
55
|
-
Create the following PR branches
|
|
67
|
+
Create the following PR branches as a stacked chain, applying the relevant changes from \`${devBranchName}\`:
|
|
56
68
|
|
|
57
69
|
${prList}
|
|
58
70
|
|
|
59
71
|
## Instructions
|
|
60
72
|
|
|
61
73
|
For each PR above (in sequence order):
|
|
62
|
-
1.
|
|
74
|
+
1. Switch to the **base branch** specified in "Base" above (either \`main\` or a previous PR's branch)
|
|
63
75
|
2. Create the branch: \`git checkout -b <branch_name>\`
|
|
64
76
|
3. Apply only the listed files from \`${devBranchName}\`
|
|
65
77
|
4. Commit with a descriptive message
|
|
66
|
-
5. Verify with \`git diff --stat
|
|
78
|
+
5. Verify with \`git diff --stat <base>...<branch_name>\`
|
|
67
79
|
|
|
68
80
|
After all branches are created, switch back to \`main\` and provide the execution result JSON.`;
|
|
69
81
|
}
|
|
@@ -74,13 +86,15 @@ export function createIncrementalSyncPrompt(opts) {
|
|
|
74
86
|
const files = pr.files
|
|
75
87
|
? pr.files.map((f) => ` - ${f.path} (${f.change_type})`).join('\n')
|
|
76
88
|
: ' (no files specified)';
|
|
89
|
+
const baseBranch = resolveBaseBranch(pr, pullRequests);
|
|
77
90
|
return `### PR ${pr.sequence}: ${pr.name}
|
|
78
91
|
- Branch: \`${pr.branch_name}\`
|
|
92
|
+
- Base: \`${baseBranch}\`
|
|
79
93
|
- Files:
|
|
80
94
|
${files}`;
|
|
81
95
|
})
|
|
82
96
|
.join('\n\n');
|
|
83
|
-
return `# Sync PR Branches with Latest Changes
|
|
97
|
+
return `# Sync PR Branches with Latest Changes (Stacked)
|
|
84
98
|
|
|
85
99
|
New changes have been made on \`${devBranchName}\` since the last sync at commit \`${lastSyncedCommit}\`.
|
|
86
100
|
|
|
@@ -94,19 +108,22 @@ ${diffStat || 'No changes detected'}
|
|
|
94
108
|
### Changed Files (${changedFiles.length} files)
|
|
95
109
|
${changedFiles.map((f) => `- ${f}`).join('\n') || 'No files changed'}
|
|
96
110
|
|
|
97
|
-
## Existing PR Branches
|
|
111
|
+
## Existing PR Branches (Stacked)
|
|
98
112
|
|
|
99
113
|
${prList}
|
|
100
114
|
|
|
101
115
|
## Instructions
|
|
102
116
|
|
|
117
|
+
These branches are stacked — each PR branch is based on its dependency. Update them **in sequence order** (base PRs first):
|
|
118
|
+
|
|
103
119
|
For each PR branch that has files affected by the new changes:
|
|
104
120
|
1. Switch to the PR branch
|
|
105
|
-
2.
|
|
106
|
-
3.
|
|
107
|
-
4.
|
|
121
|
+
2. If this PR has a base PR that was just updated, first merge the base branch into this branch: \`git merge <base-branch> --no-edit\`
|
|
122
|
+
3. Apply the relevant new changes from \`${devBranchName}\` (checkout the updated files)
|
|
123
|
+
4. Commit with a sync message
|
|
124
|
+
5. Verify the branch state
|
|
108
125
|
|
|
109
|
-
If a PR branch has no files affected by the new changes,
|
|
126
|
+
If a PR branch has no files affected by the new changes but its base PR was updated, still merge the base branch to keep the stack consistent.
|
|
110
127
|
|
|
111
128
|
After all branches are updated, switch back to \`main\` and provide the execution result JSON.`;
|
|
112
129
|
}
|
|
@@ -176,8 +176,8 @@ export async function loadSkillFile(pluginName, skillName, verbose, cacheDir) {
|
|
|
176
176
|
return result;
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
|
-
|
|
180
|
-
`in cache dir "${resolvedCacheDir}"
|
|
179
|
+
logDebug(`Skill file not found for plugin "${pluginName}", skill "${skillName}" ` +
|
|
180
|
+
`in cache dir "${resolvedCacheDir}"`, true);
|
|
181
181
|
return null;
|
|
182
182
|
}
|
|
183
183
|
/**
|
|
@@ -7,20 +7,29 @@ variables:
|
|
|
7
7
|
- DEV_BRANCH
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
You are a git operations expert. Your task is to update existing PR branches with new changes from the dev branch.
|
|
10
|
+
You are a git operations expert. Your task is to update existing stacked PR branches with new changes from the dev branch.
|
|
11
11
|
|
|
12
12
|
## Task
|
|
13
13
|
|
|
14
|
-
The PR branches already exist from a previous run. New changes have been made on `$DEV_BRANCH` since the last sync. You need to update each PR branch with the relevant new changes.
|
|
14
|
+
The PR branches already exist from a previous run as a stacked chain (each dependent PR was branched from its base PR). New changes have been made on `$DEV_BRANCH` since the last sync. You need to update each PR branch with the relevant new changes, maintaining the stack.
|
|
15
15
|
|
|
16
|
-
## Strategy for Updating Branches
|
|
16
|
+
## Strategy for Updating Stacked Branches
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
**Process branches in sequence order** (base PRs first, then dependent PRs):
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
### For a base PR (no dependency, based on main):
|
|
21
|
+
|
|
22
|
+
1. Switch to the PR branch: `git checkout pr/$FEATURE_ID/1-description`
|
|
21
23
|
2. Apply the relevant NEW changes from `$DEV_BRANCH`
|
|
22
24
|
3. Commit the changes
|
|
23
25
|
|
|
26
|
+
### For a dependent PR (stacked on another PR):
|
|
27
|
+
|
|
28
|
+
1. Switch to the PR branch: `git checkout pr/$FEATURE_ID/2-description`
|
|
29
|
+
2. **First merge the updated base branch**: `git merge pr/$FEATURE_ID/1-description --no-edit`
|
|
30
|
+
3. Apply the relevant NEW changes from `$DEV_BRANCH`
|
|
31
|
+
4. Commit the changes
|
|
32
|
+
|
|
24
33
|
### Option A: Selective file checkout (for files entirely owned by this PR)
|
|
25
34
|
|
|
26
35
|
```bash
|
|
@@ -40,9 +49,11 @@ git commit -m "sync: update with latest changes from dev"
|
|
|
40
49
|
|
|
41
50
|
## Rules
|
|
42
51
|
|
|
43
|
-
1. **
|
|
44
|
-
2. **
|
|
45
|
-
3. **
|
|
46
|
-
4. **
|
|
47
|
-
5. **
|
|
48
|
-
6. **
|
|
52
|
+
1. **Process in sequence order** — base PRs must be updated before their dependents
|
|
53
|
+
2. **Merge base branch into dependent PRs** — even if the dependent PR has no new changes, merge the updated base to keep the stack consistent
|
|
54
|
+
3. **Only update/checkout files that belong to each PR** according to the file assignments
|
|
55
|
+
4. **Do NOT push branches** — just update them locally. Pushing is handled separately.
|
|
56
|
+
5. **If a branch doesn't exist locally**, check remote and create a tracking branch
|
|
57
|
+
6. **Verify after each update**: Check that the branch has the expected changes
|
|
58
|
+
7. **Commit all changes** before switching branches
|
|
59
|
+
8. **After all branches are updated**, switch back to `main`
|
|
@@ -11,13 +11,21 @@ You are a git operations expert. Your task is to create PR branches and move the
|
|
|
11
11
|
|
|
12
12
|
## Task
|
|
13
13
|
|
|
14
|
-
You will receive a PR plan with file assignments. For each PR in sequence order, you must:
|
|
14
|
+
You will receive a PR plan with file assignments. Each PR specifies a **base branch** — either `main` or a previous PR's branch. For each PR in sequence order, you must:
|
|
15
15
|
|
|
16
|
-
1. Switch to
|
|
17
|
-
2. Create a new branch `pr/$FEATURE_ID/N-description` from
|
|
16
|
+
1. Switch to the PR's **base branch** (specified in the plan)
|
|
17
|
+
2. Create a new branch `pr/$FEATURE_ID/N-description` from that base
|
|
18
18
|
3. Apply ONLY the relevant file changes from the `$DEV_BRANCH` branch to this new branch
|
|
19
19
|
4. Commit the changes with a meaningful commit message
|
|
20
20
|
|
|
21
|
+
## Stacked Branches
|
|
22
|
+
|
|
23
|
+
PRs form a dependency chain (stacked PRs):
|
|
24
|
+
- **First PR** (no dependency): branches from `main`
|
|
25
|
+
- **Dependent PRs**: branch from the PR they depend on
|
|
26
|
+
|
|
27
|
+
This ensures each branch compiles correctly — a dependent PR inherits all changes from its base PR.
|
|
28
|
+
|
|
21
29
|
## Strategy for Moving Code
|
|
22
30
|
|
|
23
31
|
Choose the best approach for each PR:
|
|
@@ -25,28 +33,35 @@ Choose the best approach for each PR:
|
|
|
25
33
|
### Option A: Selective file checkout (recommended for most cases)
|
|
26
34
|
|
|
27
35
|
```bash
|
|
36
|
+
# First PR: base is main
|
|
28
37
|
git checkout main
|
|
29
|
-
git checkout -b pr/$FEATURE_ID/1-
|
|
38
|
+
git checkout -b pr/$FEATURE_ID/1-foundation
|
|
30
39
|
git checkout $DEV_BRANCH -- path/to/file1 path/to/file2
|
|
31
40
|
git commit -m "feat: descriptive message"
|
|
41
|
+
|
|
42
|
+
# Second PR: base is the first PR's branch (stacked)
|
|
43
|
+
git checkout pr/$FEATURE_ID/1-foundation
|
|
44
|
+
git checkout -b pr/$FEATURE_ID/2-feature
|
|
45
|
+
git checkout $DEV_BRANCH -- path/to/file3 path/to/file4
|
|
46
|
+
git commit -m "feat: descriptive message"
|
|
32
47
|
```
|
|
33
48
|
|
|
34
49
|
### Option B: Using git diff + apply (for partial file changes)
|
|
35
50
|
|
|
36
51
|
```bash
|
|
37
|
-
git checkout
|
|
38
|
-
git checkout -b pr/$FEATURE_ID/
|
|
39
|
-
git diff
|
|
52
|
+
git checkout <base-branch>
|
|
53
|
+
git checkout -b pr/$FEATURE_ID/N-description
|
|
54
|
+
git diff <base-branch>...$DEV_BRANCH -- path/to/file | git apply
|
|
40
55
|
git add -A
|
|
41
56
|
git commit -m "feat: descriptive message"
|
|
42
57
|
```
|
|
43
58
|
|
|
44
59
|
## Rules
|
|
45
60
|
|
|
46
|
-
1. **Always start each PR branch from `main
|
|
47
|
-
2. **Each branch must
|
|
61
|
+
1. **Always start each PR branch from its specified base** — `main` for the first PR, or the dependency PR's branch for stacked PRs
|
|
62
|
+
2. **Each branch must compile/build** when merged to its base branch
|
|
48
63
|
3. **Do NOT push branches** — just create them locally. Pushing is handled separately.
|
|
49
|
-
4. **Verify after each branch**: Run `git diff --stat
|
|
64
|
+
4. **Verify after each branch**: Run `git diff --stat <base>...pr/$FEATURE_ID/N-description` to confirm only the expected files changed
|
|
50
65
|
5. **Commit all changes** before switching branches
|
|
51
66
|
6. **Handle new files**: For newly added files, use `git checkout $DEV_BRANCH -- path/to/new/file`
|
|
52
67
|
7. **Handle deleted files**: For deleted files, use `git rm path/to/deleted/file`
|
|
@@ -37,13 +37,21 @@ Branch names MUST follow this pattern:
|
|
|
37
37
|
- `pr/$FEATURE_ID/2-{short-description}`
|
|
38
38
|
- etc.
|
|
39
39
|
|
|
40
|
-
## PR Dependencies
|
|
40
|
+
## PR Dependencies (Stacked PRs)
|
|
41
|
+
|
|
42
|
+
PRs are created as **stacked branches** — each dependent PR's branch is created from its base PR's branch, so it inherits all the base PR's changes. This means:
|
|
43
|
+
|
|
44
|
+
- A dependent PR can safely import/use code introduced by its base PR
|
|
45
|
+
- On GitHub, a dependent PR targets its base PR's branch (not main)
|
|
46
|
+
- When the base PR is merged, GitHub automatically retargets the dependent PR to main
|
|
41
47
|
|
|
42
48
|
Use `depends_on_branch_name` to specify which PR must be merged before this one:
|
|
43
49
|
|
|
44
50
|
- First PR: `"depends_on_branch_name": null` (merges directly to main)
|
|
45
51
|
- Subsequent PRs: `"depends_on_branch_name": "pr/$FEATURE_ID/1-database"` (depends on previous PR)
|
|
46
52
|
|
|
53
|
+
**Important**: If file A imports from file B, and both are changed files, file B must be in the same PR as file A or in an earlier PR in the dependency chain. This ensures each PR compiles correctly.
|
|
54
|
+
|
|
47
55
|
## File Assignment Guidelines
|
|
48
56
|
|
|
49
57
|
- **Database migrations and schema**: First PR
|