@@ -86,7 +86,29 @@ export async function getCommits(from: string, to: string): Promise<Commit[]> {
8686 } )
8787 }
8888
89- return commits
89+ return filterRevertedCommits ( commits )
90+ }
91+
92+ function filterRevertedCommits ( commits : Commit [ ] ) : Commit [ ] {
93+ const revertPattern = / ^ R e v e r t " ( .+ ) " $ /
94+ const seen = new Map < string , Commit > ( )
95+
96+ for ( const commit of commits ) {
97+ const match = commit . message . match ( revertPattern )
98+ if ( match ) {
99+ // It's a revert - remove the original if we've seen it
100+ const original = match [ 1 ] !
101+ if ( seen . has ( original ) ) seen . delete ( original )
102+ else seen . set ( commit . message , commit ) // Keep revert if original not in range
103+ } else {
104+ // Regular commit - remove if its revert exists, otherwise add
105+ const revertMsg = `Revert "${ commit . message } "`
106+ if ( seen . has ( revertMsg ) ) seen . delete ( revertMsg )
107+ else seen . set ( commit . message , commit )
108+ }
109+ }
110+
111+ return [ ...seen . values ( ) ]
90112}
91113
92114const sections = {
@@ -110,15 +132,12 @@ function getSection(areas: Set<string>): string {
110132 return "Core"
111133}
112134
113- async function summarizeCommit (
114- opencode : Awaited < ReturnType < typeof createOpencode > > ,
115- sessionId : string ,
116- message : string ,
117- ) : Promise < string > {
135+ async function summarizeCommit ( opencode : Awaited < ReturnType < typeof createOpencode > > , message : string ) : Promise < string > {
118136 console . log ( "summarizing commit:" , message )
137+ const session = await opencode . client . session . create ( )
119138 const result = await opencode . client . session
120139 . prompt ( {
121- path : { id : sessionId } ,
140+ path : { id : session . data ! . id } ,
122141 body : {
123142 model : { providerID : "opencode" , modelID : "claude-sonnet-4-5" } ,
124143 tools : {
@@ -140,15 +159,21 @@ Commit: ${message}`,
140159}
141160
142161export async function generateChangelog ( commits : Commit [ ] , opencode : Awaited < ReturnType < typeof createOpencode > > ) {
143- const session = await opencode . client . session . create ( )
162+ // Summarize commits in parallel with max 10 concurrent requests
163+ const BATCH_SIZE = 10
164+ const summaries : string [ ] = [ ]
165+ for ( let i = 0 ; i < commits . length ; i += BATCH_SIZE ) {
166+ const batch = commits . slice ( i , i + BATCH_SIZE )
167+ const results = await Promise . all ( batch . map ( ( c ) => summarizeCommit ( opencode , c . message ) ) )
168+ summaries . push ( ...results )
169+ }
144170
145171 const grouped = new Map < string , string [ ] > ( )
146-
147- for ( const commit of commits ) {
172+ for ( let i = 0 ; i < commits . length ; i ++ ) {
173+ const commit = commits [ i ] !
148174 const section = getSection ( commit . areas )
149- const summary = await summarizeCommit ( opencode , session . data ! . id , commit . message )
150175 const attribution = commit . author && ! team . includes ( commit . author ) ? ` (@${ commit . author } )` : ""
151- const entry = `- ${ summary } ${ attribution } `
176+ const entry = `- ${ summaries [ i ] } ${ attribution } `
152177
153178 if ( ! grouped . has ( section ) ) grouped . set ( section , [ ] )
154179 grouped . get ( section ) ! . push ( entry )
0 commit comments