@@ -406,22 +406,15 @@ Analyze the changes and determine:
4064062. What parts of the Strapi documentation need updates?
4074073. What specific content should be added, updated, or clarified?
408408
409- Respond with a JSON object following this exact structure :
409+ Respond with a JSON object in this minimal shape (JSON only, no markdown) :
410410
411411{
412- "priority": "high" | "medium" | "low",
413- "affectedAreas": ["area1", "area2"],
414- "documentationSection": "Specific section name (e.g., 'Features - Media Library', 'APIs - REST API', 'Configurations - Database')",
415- "suggestedChanges": [
416- {
417- "file": "path/to/doc/file.md",
418- "section": "Section title or heading",
419- "changeType": "add" | "update" | "clarify" | "add-note",
420- "description": "Clear explanation of what needs to be changed and why",
421- "suggestedContent": "Complete markdown content ready to be added/inserted. Be specific and actionable. Include code examples if relevant."
422- }
423- ],
424- "reasoning": "Brief explanation of why these documentation changes are needed (2-3 sentences)"
412+ "summary": "≤200 chars plain text what/why",
413+ "needsDocs": "yes" | "no" | "maybe",
414+ "rationale": "1–2 sentence justification",
415+ "targets": [
416+ { "path": "docs/cms/...md", "anchor": "optional-section-anchor" }
417+ ]
425418}
426419
427420## Guidelines
@@ -461,8 +454,8 @@ Respond with a JSON object following this exact structure:
461454
4624556. **Grounding and targeting**:
463456 - Prefer targets among the "Candidate Documentation Pages" when applicable.
464- - If you suggest specific locations within a page, include an anchor from the page's anchors list when relevant.
465- - Keep the response JSON-only (no markdown formatting, no prose outside the JSON).
457+ - Include an anchor from the page's anchors list when relevant.
458+ - Keep the response JSON-only (no markdown outside JSON).
466459
467460If no documentation changes are needed, return empty suggestedChanges array with reasoning explaining why.
468461
@@ -478,19 +471,35 @@ Respond ONLY with valid JSON, no markdown formatting, no additional text.`;
478471 } ]
479472 } ) ;
480473
481- const responseText = message . content [ 0 ] . text ;
474+ const responseText = message . content ?. [ 0 ] ? .text || '' ;
482475 const jsonMatch = responseText . match ( / \{ [ \s \S ] * \} / ) ;
483-
484476 if ( ! jsonMatch ) {
485477 console . log ( ` ⚠️ Could not parse Claude response as JSON` ) ;
486478 return null ;
487479 }
488480
489- const suggestions = JSON . parse ( jsonMatch [ 0 ] ) ;
490-
491- console . log ( ` ✅ Generated ${ suggestions . suggestedChanges ?. length || 0 } suggestions (priority: ${ suggestions . priority } )` ) ;
492-
493- return suggestions ;
481+ let obj ;
482+ try {
483+ obj = JSON . parse ( jsonMatch [ 0 ] ) ;
484+ } catch ( e ) {
485+ console . log ( ` ⚠️ JSON parse error: ${ e . message } ` ) ;
486+ return null ;
487+ }
488+
489+ // Lightweight normalization
490+ const needs = ( obj . needsDocs || '' ) . toLowerCase ( ) ;
491+ const needsDocs = [ 'yes' , 'no' , 'maybe' ] . includes ( needs ) ? needs : 'maybe' ;
492+ const summary = String ( obj . summary || prAnalysis . _summary || '' ) . trim ( ) . slice ( 0 , 200 ) ;
493+ const rationale = String ( obj . rationale || '' ) . trim ( ) . slice ( 0 , 300 ) ;
494+ const targets = Array . isArray ( obj . targets ) ? obj . targets : [ ] ;
495+ const normTargets = targets
496+ . map ( t => ( { path : String ( t . path || '' ) . trim ( ) , anchor : t . anchor ? String ( t . anchor ) . trim ( ) : undefined } ) )
497+ . filter ( t => t . path ) ;
498+ const cappedTargets = normTargets . slice ( 0 , 5 ) ;
499+
500+ const normalized = { summary, needsDocs, rationale, targets : cappedTargets } ;
501+ console . log ( ` ✅ LLM verdict: ${ needsDocs . toUpperCase ( ) } — ${ cappedTargets . length } target(s)` ) ;
502+ return normalized ;
494503 } catch ( error ) {
495504 console . error ( ` ❌ Error calling Claude API: ${ error . message } ` ) ;
496505 return null ;
@@ -619,8 +628,6 @@ function generateMarkdownReport(releaseInfo, analyses) {
619628 analyses . forEach ( a => {
620629 categoryCounts [ a . category ] = ( categoryCounts [ a . category ] || 0 ) + 1 ;
621630 if ( a . claudeSuggestions ) {
622- priorityCounts [ a . claudeSuggestions . priority ] ++ ;
623-
624631 const { mainCategory, section } = categorizePRByDocumentation ( a ) ;
625632 if ( ! docSectionCounts [ mainCategory ] [ section ] ) {
626633 docSectionCounts [ mainCategory ] [ section ] = 0 ;
@@ -677,11 +684,9 @@ function generateMarkdownReport(releaseInfo, analyses) {
677684 prs . forEach ( analysis => {
678685 const { number, title, url, claudeSuggestions, body, category } = analysis ;
679686
680- const priorityEmoji = claudeSuggestions . priority === 'high' ? '🔴' :
681- claudeSuggestions . priority === 'medium' ? '🟡' : '🟢' ;
682-
683- markdown += `### ${ priorityEmoji } PR #${ number } : ${ title } \n\n` ;
684- markdown += `**Type:** ${ PR_CATEGORIES [ category ] || category } | **Priority:** ${ claudeSuggestions . priority . toUpperCase ( ) } | **Link:** ${ url } \n\n` ;
687+ const verdictEmoji = claudeSuggestions . needsDocs === 'yes' ? '✅' : claudeSuggestions . needsDocs === 'no' ? '🟦' : '⚠️' ;
688+ markdown += `### ${ verdictEmoji } PR #${ number } : ${ title } \n\n` ;
689+ markdown += `**Type:** ${ PR_CATEGORIES [ category ] || category } | **Docs required:** ${ claudeSuggestions . needsDocs . toUpperCase ( ) } | **Link:** ${ url } \n\n` ;
685690
686691 if ( claudeSuggestions . affectedAreas && claudeSuggestions . affectedAreas . length > 0 ) {
687692 markdown += `**Affected Areas:**\n` ;
@@ -698,40 +703,20 @@ function generateMarkdownReport(releaseInfo, analyses) {
698703 }
699704 }
700705
701- if ( claudeSuggestions . reasoning ) {
702- markdown += `**Analysis :** ${ claudeSuggestions . reasoning } \n\n` ;
706+ if ( claudeSuggestions . rationale ) {
707+ markdown += `**Rationale :** ${ claudeSuggestions . rationale } \n\n` ;
703708 }
704-
705- if ( claudeSuggestions . suggestedChanges && claudeSuggestions . suggestedChanges . length > 0 ) {
706- markdown += `**📝 TODO - Documentation Updates:**\n\n` ;
707- claudeSuggestions . suggestedChanges . forEach ( ( change , idx ) => {
708- const changeTitle = change . section || path . basename ( change . file , '.md' ) ;
709- markdown += `- [ ] **${ idx + 1 } . ${ change . changeType . toUpperCase ( ) } : ${ changeTitle } **\n` ;
710- markdown += ` - [ ] Update file: \`${ change . file } \`\n` ;
711- markdown += ` - [ ] ${ change . description } \n` ;
712- if ( change . suggestedContent ) {
713- markdown += ` - [ ] Add/update content (see below)\n\n` ;
714- markdown += ` <details>\n <summary>💡 Suggested content</summary>\n\n` ;
715-
716- const hasCodeBlock = change . suggestedContent . includes ( '```' ) ;
717-
718- if ( hasCodeBlock ) {
719- markdown += ` ${ change . suggestedContent . split ( '\n' ) . join ( '\n ' ) } \n\n` ;
720- } else {
721- const lines = change . suggestedContent . split ( '\n' ) ;
722- markdown += ` > **Content to add/update:**\n >\n` ;
723- lines . forEach ( line => {
724- markdown += ` > ${ line } \n` ;
725- } ) ;
726- markdown += `\n` ;
727- }
728-
729- markdown += ` </details>\n\n` ;
730- } else {
731- markdown += `\n` ;
732- }
709+
710+ const targets = Array . isArray ( claudeSuggestions . targets ) ? claudeSuggestions . targets : [ ] ;
711+ if ( targets . length > 0 ) {
712+ markdown += `**Targets:**\n` ;
713+ targets . forEach ( ( t ) => {
714+ const anchor = t . anchor ? `#${ t . anchor } ` : '' ;
715+ markdown += `- ${ t . path } ${ anchor ? ` (${ anchor } )` : '' } \n` ;
733716 } ) ;
734- } else {
717+ markdown += `\n` ;
718+ }
719+ if ( claudeSuggestions . needsDocs === 'no' ) {
735720 markdown += `**Documentation Impact:** No changes required.\n\n` ;
736721 }
737722
0 commit comments