Skip to content

Commit 61d5fc0

Browse files
committed
Change LLM contract to minimal verdict with targets; drop suggestedChanges
1 parent 2a93a61 commit 61d5fc0

File tree

1 file changed

+47
-62
lines changed
  • docusaurus/scripts/strapi-release-analyzer

1 file changed

+47
-62
lines changed

docusaurus/scripts/strapi-release-analyzer/index.js

Lines changed: 47 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -406,22 +406,15 @@ Analyze the changes and determine:
406406
2. What parts of the Strapi documentation need updates?
407407
3. 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
462455
6. **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
467460
If 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

Comments
 (0)