22
33import { $ } from "bun"
44
5+ const model = "opencode/gpt-5.3-codex"
6+
57interface PR {
68 number : number
79 title : string
@@ -50,20 +52,35 @@ async function cleanup() {
5052 } catch { }
5153}
5254
55+ function lines ( prs : PR [ ] ) {
56+ return prs . map ( ( x ) => `- #${ x . number } : ${ x . title } ` ) . join ( "\n" ) || "(none)"
57+ }
58+
59+ async function typecheck ( ) {
60+ try {
61+ await $ `bun typecheck` . cwd ( "packages/opencode" )
62+ return true
63+ } catch ( err ) {
64+ console . log ( `Typecheck failed: ${ err } ` )
65+ return false
66+ }
67+ }
68+
69+ async function build ( ) {
70+ try {
71+ await $ `./script/build.ts --single` . cwd ( "packages/opencode" )
72+ return true
73+ } catch ( err ) {
74+ console . log ( `Build failed: ${ err } ` )
75+ return false
76+ }
77+ }
78+
5379async function fix ( pr : PR , files : string [ ] , prs : PR [ ] , applied : number [ ] , idx : number ) {
5480 console . log ( ` Trying to auto-resolve ${ files . length } conflict(s) with opencode...` )
5581
56- const done =
57- prs
58- . filter ( ( x ) => applied . includes ( x . number ) )
59- . map ( ( x ) => `- #${ x . number } : ${ x . title } ` )
60- . join ( "\n" ) || "(none yet)"
61-
62- const next =
63- prs
64- . slice ( idx + 1 )
65- . map ( ( x ) => `- #${ x . number } : ${ x . title } ` )
66- . join ( "\n" ) || "(none)"
82+ const done = lines ( prs . filter ( ( x ) => applied . includes ( x . number ) ) )
83+ const next = lines ( prs . slice ( idx + 1 ) )
6784
6885 const prompt = [
6986 `Resolve the current git merge conflicts while merging PR #${ pr . number } into the beta branch.` ,
@@ -76,12 +93,14 @@ async function fix(pr: PR, files: string[], prs: PR[], applied: number[], idx: n
7693 "Prefer already-merged PRs over the base branch when resolving stacked conflicts." ,
7794 "If a PR already deleted a file/directory, do not re-add it, instead apply changes in the new semantic location." ,
7895 "If a PR already changed an import, keep that change." ,
96+ "After resolving the conflicts, run `bun typecheck` in `packages/opencode`." ,
97+ "Fix any merge-caused typecheck errors before finishing." ,
7998 "Keep the merge in progress, do not abort the merge, and do not create a commit." ,
80- "When done, leave the working tree with no unmerged files." ,
99+ "When done, leave the working tree with no unmerged files and a passing typecheck ." ,
81100 ] . join ( "\n" )
82101
83102 try {
84- await $ `opencode run -m opencode/gpt-5.3-codex ${ prompt } `
103+ await $ `opencode run -m ${ model } ${ prompt } `
85104 } catch ( err ) {
86105 console . log ( ` opencode failed: ${ err } ` )
87106 return false
@@ -93,10 +112,66 @@ async function fix(pr: PR, files: string[], prs: PR[], applied: number[], idx: n
93112 return false
94113 }
95114
115+ if ( ! ( await typecheck ( ) ) ) return false
116+
96117 console . log ( " Conflicts resolved with opencode" )
97118 return true
98119}
99120
121+ async function smoke ( prs : PR [ ] , applied : number [ ] ) {
122+ console . log ( "\nRunning final smoke check with opencode..." )
123+
124+ const done = lines ( prs . filter ( ( x ) => applied . includes ( x . number ) ) )
125+ const prompt = [
126+ "The beta merge batch is complete." ,
127+ `Merged PRs on HEAD:\n${ done } ` ,
128+ "Run `bun typecheck` in `packages/opencode`." ,
129+ "Run `./script/build.ts --single` in `packages/opencode`." ,
130+ "Fix any merge-caused issues until both commands pass." ,
131+ "Do not create a commit." ,
132+ ] . join ( "\n" )
133+
134+ try {
135+ await $ `opencode run -m ${ model } ${ prompt } `
136+ } catch ( err ) {
137+ console . log ( `Smoke fix failed: ${ err } ` )
138+ return false
139+ }
140+
141+ if ( ! ( await typecheck ( ) ) ) {
142+ return false
143+ }
144+
145+ if ( ! ( await build ( ) ) ) {
146+ return false
147+ }
148+
149+ const out = await $ `git status --porcelain` . text ( )
150+ if ( ! out . trim ( ) ) {
151+ console . log ( "Smoke check passed" )
152+ return true
153+ }
154+
155+ try {
156+ await $ `git add -A`
157+ await $ `git commit -m "Fix beta integration"`
158+ } catch ( err ) {
159+ console . log ( `Failed to commit smoke fixes: ${ err } ` )
160+ return false
161+ }
162+
163+ if ( ! ( await typecheck ( ) ) ) {
164+ return false
165+ }
166+
167+ if ( ! ( await build ( ) ) ) {
168+ return false
169+ }
170+
171+ console . log ( "Smoke check passed" )
172+ return true
173+ }
174+
100175async function main ( ) {
101176 console . log ( "Fetching open PRs with beta label..." )
102177
@@ -195,6 +270,13 @@ async function main() {
195270 throw new Error ( `${ failed . length } PR(s) failed to merge` )
196271 }
197272
273+ if ( applied . length > 0 ) {
274+ const ok = await smoke ( prs , applied )
275+ if ( ! ok ) {
276+ throw new Error ( "Final smoke check failed" )
277+ }
278+ }
279+
198280 console . log ( "\nChecking if beta branch has changes..." )
199281 await $ `git fetch origin beta`
200282
0 commit comments