Skip to content

Commit 3cce5dc

Browse files
kgowruKapil Gowru
andauthored
feat: updated scribe to have better turbopuffer mapping (#241)
Co-authored-by: Kapil Gowru <[email protected]>
1 parent 8658a33 commit 3cce5dc

File tree

3 files changed

+96
-40
lines changed

3 files changed

+96
-40
lines changed

.github/ISSUE_TEMPLATE/fern_scribe_request.yml

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,31 @@ body:
1414
id: request_description
1515
attributes:
1616
label: "📝 What do you want Fern Scribe (AI Technical Writer) to do?"
17-
description: "Describe the documentation changes you need"
18-
placeholder: "Example: Update the authentication guide to include new OAuth 2.0 flow, add examples for the new webhook endpoints"
17+
description: "Example: Update the authentication guide to include new OAuth 2.0 flow, add examples for the new webhook endpoints"
18+
placeholder: "Describe docs changes here..."
1919
validations:
2020
required: true
2121

2222
- type: input
2323
id: slack_thread
2424
attributes:
2525
label: "🔗 Link to existing Slack thread"
26-
description: "Optional: Link to relevant Slack discussion"
27-
placeholder: "https://your-workspace.slack.com/archives/..."
26+
description: "Ex. https://your-workspace.slack.com/archives/..."
27+
placeholder: "Paste slack link here..."
2828

2929
- type: textarea
3030
id: existing_instructions
3131
attributes:
32-
label: "📋 Are there existing instructions in that docs for this issue?"
33-
description: "Reference any existing documentation or guidelines"
34-
placeholder: "See docs/guides/authentication.md for current approach"
32+
label: "📋 Do you know of any documentation that needs to be updated?"
33+
description: "Ex. See buildwithfern.com/learn/docs/guides/authentication for current approach"
34+
placeholder: "Paste docs links here..."
3535

3636
- type: textarea
3737
id: why_not_work
3838
attributes:
39-
label: "❌ If yes, describe why they didn't work"
40-
description: "Explain what's missing or incorrect in current docs"
41-
placeholder: "Current docs don't cover the new OAuth scopes introduced in v2.1"
39+
label: "❌ If yes, describe why that documentation is incorrect"
40+
description: "Ex. Current docs don't cover the new OAuth scopes introduced in v2.1"
41+
placeholder: "Describe here..."
4242

4343
- type: checkboxes
4444
id: changelog_required
@@ -49,21 +49,9 @@ body:
4949
- label: "Yes, include changelog entry"
5050
- label: "No changelog needed"
5151

52-
- type: dropdown
53-
id: priority
54-
attributes:
55-
label: "🚨 Priority Level"
56-
options:
57-
- "Low - Documentation enhancement"
58-
- "Medium - Important update needed"
59-
- "High - Critical documentation gap"
60-
- "Urgent - Blocks user adoption"
61-
validations:
62-
required: true
63-
6452
- type: textarea
6553
id: additional_context
6654
attributes:
6755
label: "📎 Additional Context"
6856
description: "Any other relevant information, links, or requirements"
69-
placeholder: "Related API changes, user feedback, etc."
57+
placeholder: "Add info here..."

.github/scripts/fern-scribe.js

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,20 @@ Changelog entry:`;
573573
resolvedPath = `fern/${resolvedPath}`;
574574
}
575575

576+
// Determine the effective slug for URL generation
577+
let effectiveSlug = null;
578+
if (product['skip-slug'] === true) {
579+
// Skip slug entirely - pages will be at root level
580+
effectiveSlug = '';
581+
} else if (product.slug) {
582+
// Use explicit slug
583+
effectiveSlug = product.slug;
584+
} else {
585+
// Derive slug from path or display-name
586+
const pathBasename = product.path.split('/').pop().replace(/\.yml$/, '');
587+
effectiveSlug = pathBasename;
588+
}
589+
576590
// Load product config
577591
const productContent = await this.fetchFileContent(resolvedPath);
578592
if (!productContent) continue;
@@ -581,7 +595,7 @@ Changelog entry:`;
581595
if (!productConfig.navigation) continue;
582596

583597
// Process navigation structure
584-
await this.processNavigation(productConfig, product.slug, resolvedPath);
598+
await this.processNavigation(productConfig, effectiveSlug, resolvedPath);
585599
}
586600

587601
this.isPathMappingLoaded = true;
@@ -591,24 +605,64 @@ Changelog entry:`;
591605
}
592606
}
593607

594-
async processNavigation(config, productSlug, configPath) {
608+
async processNavigation(config, productSlug, configPath, parentSections = []) {
595609
if (!config.navigation) return;
596610

597-
const basePath = configPath.replace(/\/[^\/]+\.yml$/, '').replace('fern/', '');
611+
// Extract the directory path from the config file path
612+
// e.g., "fern/products/fern-def.yml" -> "products/fern-def"
613+
const basePath = configPath.replace(/\.yml$/, '').replace('fern/', '');
598614

599615
for (const navItem of config.navigation) {
600616
if (navItem.page) {
601-
// It's a page
602-
const pageFilePath = `fern/${basePath}/pages/${navItem.page}`;
603-
const pageUrl = `/${productSlug}/${navItem.page}`;
617+
// It's a page - check if it has a custom path
618+
let pageFilePath;
619+
if (navItem.path) {
620+
// Use custom path
621+
pageFilePath = `fern/${basePath}/pages/${navItem.path}`;
622+
} else {
623+
// Default path based on page name
624+
pageFilePath = `fern/${basePath}/pages/${navItem.page}`;
625+
}
626+
627+
// Build URL with parent sections
628+
const urlParts = [productSlug, ...parentSections, navItem.page].filter(Boolean);
629+
const pageUrl = `/${urlParts.join('/')}`;
604630
this.dynamicPathMapping.set(pageUrl, pageFilePath);
605631
} else if (navItem.section) {
606-
// It's a section with pages
632+
// It's a section with pages - create section-based URLs
633+
const sectionSlug = navItem.section.toLowerCase().replace(/\s+/g, '-');
634+
const newParentSections = [...parentSections, sectionSlug];
635+
607636
for (const pageItem of navItem.contents || []) {
608637
if (pageItem.page) {
609-
const pageFilePath = `fern/${basePath}/pages/${pageItem.page}`;
610-
const pageUrl = `/${productSlug}/${pageItem.page}`;
611-
this.dynamicPathMapping.set(pageUrl, pageFilePath);
638+
let pageFilePath;
639+
if (pageItem.path) {
640+
// Use custom path
641+
pageFilePath = `fern/${basePath}/pages/${pageItem.path}`;
642+
} else {
643+
// Default path based on page name
644+
pageFilePath = `fern/${basePath}/pages/${pageItem.page}`;
645+
}
646+
647+
// Build URL with all parent sections
648+
const urlParts = [productSlug, ...newParentSections, pageItem.page].filter(Boolean);
649+
const sectionUrl = `/${urlParts.join('/')}`;
650+
this.dynamicPathMapping.set(sectionUrl, pageFilePath);
651+
652+
// Also create direct URL for backwards compatibility (without parent sections)
653+
const directUrlParts = [productSlug, pageItem.page].filter(Boolean);
654+
const directUrl = `/${directUrlParts.join('/')}`;
655+
if (!this.dynamicPathMapping.has(directUrl)) {
656+
this.dynamicPathMapping.set(directUrl, pageFilePath);
657+
}
658+
} else if (pageItem.section) {
659+
// Handle nested sections recursively
660+
const nestedSectionSlug = pageItem.section.toLowerCase().replace(/\s+/g, '-');
661+
const nestedParentSections = [...newParentSections, nestedSectionSlug];
662+
663+
// Create a temporary config object for recursive processing
664+
const nestedConfig = { navigation: pageItem.contents || [] };
665+
await this.processNavigation(nestedConfig, productSlug, configPath, nestedParentSections);
612666
}
613667
}
614668
}
@@ -620,7 +674,21 @@ Changelog entry:`;
620674
// Remove /learn prefix and clean up trailing slashes
621675
let urlPath = turbopufferUrl.replace('/learn', '').replace(/\/$/, '');
622676

623-
// Extract product and path
677+
// First try to use dynamic mapping
678+
if (this.dynamicPathMapping.has(urlPath)) {
679+
const mappedPath = this.dynamicPathMapping.get(urlPath);
680+
// Add .mdx extension if not present and not already a complete path
681+
if (!mappedPath.endsWith('.mdx') && !mappedPath.endsWith('/')) {
682+
// Check if it's a known directory case (like changelog)
683+
if (mappedPath.endsWith('/changelog') || (mappedPath.includes('/changelog') && !mappedPath.includes('.'))) {
684+
return mappedPath; // Return as directory
685+
}
686+
return `${mappedPath}.mdx`;
687+
}
688+
return mappedPath;
689+
}
690+
691+
// Fallback to hardcoded logic for backwards compatibility
624692
const pathParts = urlPath.split('/').filter(p => p);
625693
if (pathParts.length === 0) return null;
626694

@@ -662,7 +730,7 @@ Changelog entry:`;
662730
// Ensure dynamic mapping is loaded
663731
await this.loadDynamicPathMapping();
664732

665-
// Use the new transformation logic
733+
// Use the improved transformation logic that prioritizes dynamic mapping
666734
return this.transformTurbopufferUrlToPath(turbopufferPath) || turbopufferPath;
667735
}
668736

.github/scripts/system-prompt.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Follow these key principles from Fern's contribution guide:
2424
- Use clear, descriptive headings with appropriate markdown hierarchy
2525
- Include practical code examples with proper syntax highlighting
2626
- Provide step-by-step instructions for complex procedures
27-
- Use Fern's callouts and admonitions for important notes, warnings, and tips
27+
- Use Fern's callouts for important notes, warnings, and tips
2828
- Maintain consistent formatting and style throughout
2929
- Include relevant links to related documentation
3030
- Cross-reference related concepts appropriately
@@ -75,16 +75,16 @@ Based on Fern's documentation standards (influenced by Google's developer docume
7575
## Fern-Specific Components
7676

7777
When creating documentation, leverage Fern's documentation components:
78-
- Callouts for important information
78+
- Callouts for important information, warnings, and tips
7979
- Code blocks with proper language tags
8080
- Tabs for multiple examples
8181
- Cards for organizing related content
82-
- Admonitions for warnings and tips
8382

8483
## Quality Checklist
8584

8685
Before finalizing content, ensure:
87-
- ✅ All code examples are tested and functional
86+
- ✅ All code examples are tested and functional, try to only show one example per edit
87+
- ✅ Prefer making edits to one page and point to that page and section from other pages if needed.
8888
- ✅ Links are valid and point to correct destinations
8989
- ✅ Formatting is consistent with Fern standards
9090
- ✅ Content is logically organized and flows well
@@ -102,6 +102,6 @@ Remember that Fern documentation:
102102
- Requires Node.js 16+ and npm
103103
- Uses Fern's documentation component system
104104
- Follows a specific file structure and organization
105-
- Is currently live at fern-api.docs.buildwithfern.com/learn/home
105+
- Is currently live at buildwithfern.com/learn
106106

107107
Always maintain consistency with existing documentation patterns and structure.

0 commit comments

Comments
 (0)