@@ -750,12 +750,81 @@ function sectionEmoji(heading: string): string {
750750 * doesn't have enough structure to justify splitting.
751751 */
752752function parseOutputSections ( output : string ) : TrelloSection [ ] {
753- const headingRegex = / ^ ( # { 1 , 2 } ) \s + ( .+ ) $ / gm;
754- const matches = [ ...output . matchAll ( headingRegex ) ] ;
753+ // Strategy 1: Markdown headings (h1–h4)
754+ const headingRegex = / ^ ( # { 1 , 4 } ) \s + ( .+ ) $ / gm;
755+ let matches = [ ...output . matchAll ( headingRegex ) ] ;
755756
756- // Need at least 2 headings to justify splitting into multiple cards
757- if ( matches . length < 2 ) return [ ] ;
757+ if ( matches . length >= 2 ) {
758+ console . log ( `[Trello] parseOutputSections: found ${ matches . length } markdown headings` ) ;
759+ return buildSectionsFromMatches ( output , matches ) ;
760+ }
761+
762+ // Strategy 2: Bold-line section headers (e.g., **Section Name** on its own line)
763+ const boldRegex = / ^ \* \* ( [ ^ * ] { 3 , 80 } ) \* \* \s * $ / gm;
764+ matches = [ ...output . matchAll ( boldRegex ) ] ;
765+
766+ if ( matches . length >= 2 ) {
767+ console . log ( `[Trello] parseOutputSections: found ${ matches . length } bold-line sections` ) ;
768+ return buildSectionsFromMatches ( output , matches ) ;
769+ }
770+
771+ // Strategy 3: Horizontal rule separators (---)
772+ const parts = output . split ( / \n - - - + \n / ) ;
773+ if ( parts . length >= 2 ) {
774+ console . log ( `[Trello] parseOutputSections: found ${ parts . length } separator-delimited sections` ) ;
775+ const sections : TrelloSection [ ] = [ ] ;
776+ for ( let i = 0 ; i < parts . length ; i ++ ) {
777+ const part = parts [ i ] . trim ( ) ;
778+ if ( ! part ) continue ;
779+
780+ // Try to extract a title from the first line
781+ const firstLine = part . split ( '\n' ) [ 0 ] . trim ( )
782+ . replace ( / ^ # + \s * / , '' ) // strip leading #
783+ . replace ( / ^ \* \* | \* \* $ / g, '' ) // strip bold markers
784+ . substring ( 0 , 80 ) ;
785+ const rest = part . substring ( part . indexOf ( '\n' ) + 1 ) . trim ( ) ;
786+
787+ sections . push ( {
788+ title : `${ sectionEmoji ( firstLine ) } ${ firstLine } ` ,
789+ content : rest || part ,
790+ } ) ;
791+ }
792+ return sections . length >= 2 ? sections : [ ] ;
793+ }
794+
795+ // Strategy 4: Large content with no clear structure — split by size
796+ if ( output . length > 2000 ) {
797+ console . log ( `[Trello] parseOutputSections: no structure found, splitting by paragraphs` ) ;
798+ const paragraphs = output . split ( / \n \n + / ) ;
799+ const sections : TrelloSection [ ] = [ ] ;
800+ let currentContent = '' ;
801+ let sectionIndex = 1 ;
802+
803+ for ( const para of paragraphs ) {
804+ if ( currentContent . length + para . length > 4000 && currentContent . length > 500 ) {
805+ sections . push ( {
806+ title : `📄 Part ${ sectionIndex } ` ,
807+ content : currentContent . trim ( ) ,
808+ } ) ;
809+ currentContent = para ;
810+ sectionIndex ++ ;
811+ } else {
812+ currentContent += ( currentContent ? '\n\n' : '' ) + para ;
813+ }
814+ }
815+ if ( currentContent . trim ( ) ) {
816+ sections . push ( {
817+ title : `📄 Part ${ sectionIndex } ` ,
818+ content : currentContent . trim ( ) ,
819+ } ) ;
820+ }
821+ return sections . length >= 2 ? sections : [ ] ;
822+ }
823+
824+ return [ ] ;
825+ }
758826
827+ function buildSectionsFromMatches ( output : string , matches : RegExpMatchArray [ ] ) : TrelloSection [ ] {
759828 const sections : TrelloSection [ ] = [ ] ;
760829
761830 // Preamble — any text before the first heading
@@ -766,7 +835,8 @@ function parseOutputSections(output: string): TrelloSection[] {
766835
767836 // Each heading becomes a card
768837 for ( let i = 0 ; i < matches . length ; i ++ ) {
769- const heading = matches [ i ] [ 2 ] . trim ( ) ;
838+ // Use capture group 2 if present (markdown headings), otherwise group 1 (bold)
839+ const heading = ( matches [ i ] [ 2 ] ?? matches [ i ] [ 1 ] ) . trim ( ) ;
770840 const contentStart = matches [ i ] . index ! + matches [ i ] [ 0 ] . length ;
771841 const contentEnd = i + 1 < matches . length ? matches [ i + 1 ] . index ! : output . length ;
772842 const content = output . substring ( contentStart , contentEnd ) . trim ( ) ;
0 commit comments