diff --git a/README.md b/README.md index d8f64a8..3de44fe 100644 --- a/README.md +++ b/README.md @@ -463,6 +463,7 @@ This repository includes [Agent Skills](https://agentskills.io) that provide dom - **🔐 mapbox-token-security**: Token management, scope control, URL restrictions, rotation strategies - **📐 mapbox-style-patterns**: Common style patterns and layer configurations for typical scenarios - **🔧 mapbox-integration-patterns**: Framework-specific integration patterns for React, Vue, Svelte, Angular, and vanilla JS +- **✅ mapbox-style-quality**: Expert guidance on validating, optimizing, and ensuring quality of Mapbox styles through validation, accessibility checks, and optimization Skills complement the MCP server by providing expertise (how to think about design) while tools provide capabilities (how to execute actions). @@ -673,6 +674,39 @@ Arguments: color_scheme: "sequential" ``` +### prepare-style-for-production + +Comprehensive quality validation workflow for Mapbox styles before production deployment. + +**Arguments:** + +- `style_id_or_json` (required): Either a Mapbox style ID or complete style JSON +- `skip_optimization` (optional): Set to "true" to skip style optimization (default: false) +- `wcag_level` (optional): WCAG compliance level: "AA" or "AAA" (default: "AA") + +**What it does:** + +1. Loads the style (retrieves from Mapbox or parses JSON) +2. Validates all expressions (filters, paint properties, layout properties) +3. Validates GeoJSON sources for coordinate and structure errors +4. Checks color contrast for text layers (WCAG compliance) +5. Optimizes the style (removes redundancies, simplifies expressions) +6. Generates a comprehensive quality report with deployment readiness assessment + +**Example usage:** + +``` +Use prompt: prepare-style-for-production +Arguments: + style_id_or_json: "username/my-style-id" + wcag_level: "AA" + skip_optimization: "false" +``` + +**Related:** + +See the [mapbox-style-quality skill](skills/mapbox-style-quality/SKILL.md) for detailed guidance on when to use validation tools, best practices, and optimization strategies. + ## Resources This server exposes static reference documentation as MCP Resources. While these are primarily accessed through the `get_reference_tool`, MCP clients that fully support the resources protocol can access them directly. diff --git a/skills/mapbox-style-quality/SKILL.md b/skills/mapbox-style-quality/SKILL.md new file mode 100644 index 0000000..c4846c0 --- /dev/null +++ b/skills/mapbox-style-quality/SKILL.md @@ -0,0 +1,420 @@ +--- +name: mapbox-style-quality +description: Expert guidance on validating, optimizing, and ensuring quality of Mapbox styles through validation, accessibility checks, and optimization. Use when preparing styles for production, debugging issues, or ensuring map quality standards. +--- + +# Mapbox Style Quality Skill + +This skill provides expert guidance on ensuring Mapbox style quality through validation, accessibility, and optimization tools. + +## When to Use Quality Tools + +### Pre-Production Checklist + +Before deploying any Mapbox style to production: + +1. **Validate all expressions** - Catch syntax errors before runtime +2. **Check color contrast** - Ensure text is readable (WCAG compliance) +3. **Validate GeoJSON sources** - Ensure data integrity +4. **Optimize style** - Reduce file size and improve performance +5. **Compare versions** - Understand what changed + +### During Development + +**When adding GeoJSON data:** + +- Always validate external GeoJSON with `validate_geojson_tool` before using as a source + +**When writing expressions:** + +- Validate expressions with `validate_expression_tool` as you write them +- Catch type mismatches early (e.g., using string operator on number) +- Verify operator availability in your Mapbox GL JS version +- Test expressions with expected data types + +**When styling text/labels:** + +- Check foreground/background contrast with `check_color_contrast_tool` +- Aim for WCAG AA minimum (4.5:1 for normal text, 3:1 for large text) +- Use AAA standard (7:1 for normal text) for better accessibility +- Consider different background scenarios (map tiles, overlays) + +### Before Committing Changes + +**Compare style versions:** + +- Use `compare_styles_tool` to generate a diff report +- Review all layer changes, source modifications, and expression updates +- Understand the impact of your changes +- Document significant changes in commit messages + +### Before Deployment + +**Optimize the style:** + +- Run `optimize_style_tool` to reduce file size +- Remove unused sources that reference deleted layers +- Eliminate duplicate layers with identical properties +- Simplify boolean expressions for better performance +- Remove empty layers that serve no purpose + +## Validation Best Practices + +### GeoJSON Validation + +**Always validate when:** + +- Loading GeoJSON from user uploads +- Fetching GeoJSON from external APIs +- Processing GeoJSON from third-party sources +- Converting between data formats + +**Common GeoJSON errors:** + +- Invalid coordinate ranges (longitude > 180 or < -180) +- Unclosed polygon rings (first and last coordinates must match) +- Wrong coordinate order (should be [longitude, latitude], not [latitude, longitude]) +- Missing required properties (type, coordinates, geometry) +- Invalid geometry types or nesting + +**Example workflow:** + +``` +1. Receive GeoJSON data +2. Validate with validate_geojson_tool +3. If valid: Add as source to style +4. If invalid: Fix errors, re-validate +``` + +### Expression Validation + +**Validate expressions for:** + +- Filter conditions (`filter` property on layers) +- Data-driven styling (`paint` and `layout` properties) +- Feature state expressions +- Dynamic property calculations + +**Common expression errors:** + +- Type mismatches (string operators on numbers) +- Invalid operator names or wrong syntax +- Wrong number of arguments for operators +- Nested expression errors +- Using unavailable operators for your GL JS version + +**Prevention strategies:** + +- Validate as you write expressions, not at runtime +- Test expressions with representative data +- Use type checking (expectedType parameter) +- Validate in context (layer, filter, paint, layout) + +### Accessibility Validation + +**WCAG Levels:** + +- **AA** (minimum): 4.5:1 for normal text, 3:1 for large text +- **AAA** (enhanced): 7:1 for normal text, 4.5:1 for large text + +**Text size categories:** + +- **Normal**: < 18pt or < 14pt bold +- **Large**: ≥ 18pt or ≥ 14pt bold + +**Common scenarios to check:** + +- Text labels on map tiles +- POI labels with background colors +- Custom markers with text +- UI overlays on maps +- Legend text and symbols +- Attribution text + +**Testing strategy:** + +- Test against both light and dark map tiles +- Consider overlay backgrounds (popups, modals) +- Test in different lighting conditions (mobile outdoor use) +- Verify contrast at different zoom levels + +## Optimization Best Practices + +### When to Optimize + +**Before production deployment:** + +- After all development changes are complete +- After merging multiple feature branches +- When style has grown significantly over time +- Before major releases or launches + +**Benefits of optimization:** + +- Faster initial load times +- Reduced bandwidth usage +- Better runtime performance +- Cleaner, more maintainable code + +### Optimization Types + +**Remove unused sources:** + +- Automatically identifies sources not referenced by any layer +- Safe to remove without affecting functionality +- Common after deleting layers or refactoring + +**Remove duplicate layers:** + +- Finds layers with identical properties (excluding ID) +- Can occur when copying/pasting layers +- Reduces style complexity and file size + +**Simplify expressions:** + +- Converts `["all", true]` → `true` +- Converts `["any", false]` → `false` +- Converts `["!", false]` → `true` +- Converts `["!", true]` → `false` +- Improves expression evaluation performance + +**Remove empty layers:** + +- Removes layers with no paint or layout properties +- Preserves background layers (valid even when empty) +- Cleans up incomplete or placeholder layers + +**Consolidate filters:** + +- Identifies groups of layers with identical filter expressions +- Highlights opportunities for layer consolidation +- Doesn't automatically consolidate (informational only) + +### Optimization Strategy + +**Recommended order:** + +1. Remove unused sources first (reduces noise for other checks) +2. Remove duplicate layers (eliminates redundancy) +3. Simplify expressions (improves readability and performance) +4. Remove empty layers (final cleanup) +5. Review consolidation opportunities (manual step) + +**Selective optimization:** + +``` +// All optimizations (recommended for production) +optimize_style_tool({ style }) + +// Specific optimizations only +optimize_style_tool({ + style, + optimizations: ['remove-unused-sources', 'simplify-expressions'] +}) +``` + +**Review before deploying:** + +- Check the optimization report +- Verify size savings (percentReduction) +- Review the list of changes (optimizations array) +- Test the optimized style before deployment + +## Style Comparison Workflow + +### When to Compare Styles + +**Before merging changes:** + +- Review what changed in your feature branch +- Ensure no unintended modifications +- Generate change summary for PR description + +**When investigating issues:** + +- Compare working version vs. broken version +- Identify what changed between versions +- Narrow down root cause of problems + +**During migrations:** + +- Compare old format vs. new format +- Verify data integrity after conversion +- Document transformation differences + +### Comparison Best Practices + +**Use ignoreMetadata flag:** + +``` +// Ignore metadata differences (id, owner, created, modified) +compare_styles_tool({ + styleA: oldStyle, + styleB: newStyle, + ignoreMetadata: true +}) +``` + +**Focus on meaningful changes:** + +- Layer additions/removals +- Source changes +- Expression modifications +- Paint/layout property updates + +**Document significant changes:** + +- Note breaking changes in documentation +- Update style version numbers +- Communicate changes to team/users + +## Quality Workflow Examples + +### Basic Quality Check + +``` +1. Validate expressions in style +2. Check color contrast for text layers +3. Optimize if needed +``` + +### Full Pre-Production Workflow + +``` +1. Validate all GeoJSON sources +2. Validate all expressions (filters, paint, layout) +3. Check color contrast for all text layers +4. Compare with previous production version +5. Optimize style +6. Test optimized style +7. Deploy +``` + +### Troubleshooting Workflow + +``` +1. Compare working vs. broken style +2. Identify differences +3. Validate suspicious expressions +4. Check GeoJSON data if source-related +5. Verify color contrast if visibility issue +``` + +### Refactoring Workflow + +``` +1. Create backup of current style +2. Make refactoring changes +3. Compare before vs. after +4. Validate all modified expressions +5. Optimize to clean up +6. Review size impact +``` + +## Common Issues and Solutions + +### Runtime Expression Errors + +**Problem:** Map throws expression errors at runtime +**Solution:** Validate expressions with `validate_expression_tool` during development +**Prevention:** Add expression validation to pre-commit hooks or CI/CD + +### Poor Text Readability + +**Problem:** Text labels are hard to read on map +**Solution:** Check contrast with `check_color_contrast_tool`, adjust colors to meet WCAG AA +**Prevention:** Test text on both light and dark backgrounds, check at different zoom levels + +### Large Style File Size + +**Problem:** Style takes long to load or transfer +**Solution:** Run `optimize_style_tool` to remove redundancies and simplify +**Prevention:** Regularly optimize during development, remove unused sources immediately + +### Invalid GeoJSON Source + +**Problem:** GeoJSON source fails to load or render +**Solution:** Validate with `validate_geojson_tool`, fix coordinate issues, verify structure +**Prevention:** Validate all external GeoJSON before adding to style + +### Unexpected Style Changes + +**Problem:** Style changed but unsure what modified +**Solution:** Use `compare_styles_tool` to generate diff report +**Prevention:** Compare before/after for all significant changes, document modifications + +## Integration with Development Workflow + +### Git Pre-Commit Hook + +```bash +# Validate expressions before commit +npm run validate-style + +# Optimize before commit (optional) +npm run optimize-style +``` + +### CI/CD Pipeline + +``` +1. Validate all expressions +2. Check accessibility compliance +3. Run optimization (warning if significant savings) +4. Compare with production version +5. Generate quality report +``` + +### Code Review Checklist + +- [ ] All expressions validated +- [ ] Text contrast meets WCAG AA +- [ ] GeoJSON sources validated +- [ ] Style optimized for production +- [ ] Changes documented in comparison report + +## Best Practices Summary + +**During Development:** + +- Validate expressions as you write them +- Check GeoJSON data when adding sources +- Test color contrast for new text layers + +**Before Committing:** + +- Compare with previous version +- Document significant changes +- Validate modified expressions + +**Before Production:** + +- Run full validation suite +- Check accessibility compliance +- Optimize style +- Test optimized version +- Generate quality report + +**Regular Maintenance:** + +- Periodically optimize to prevent bloat +- Review and consolidate similar layers +- Update expressions to use simpler forms +- Remove deprecated or unused code + +## Tool Quick Reference + +| Tool | Use When | Output | +| --------------------------- | ---------------------- | -------------------------- | +| `validate_geojson_tool` | Adding GeoJSON sources | Valid/invalid + error list | +| `validate_expression_tool` | Writing expressions | Valid/invalid + error list | +| `check_color_contrast_tool` | Styling text labels | Passes/fails + WCAG levels | +| `compare_styles_tool` | Reviewing changes | Diff report with paths | +| `optimize_style_tool` | Before deployment | Optimized style + savings | + +## Additional Resources + +- [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/) +- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/) +- [GeoJSON Specification (RFC 7946)](https://tools.ietf.org/html/rfc7946) +- [Mapbox Expression Reference](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/) diff --git a/src/prompts/BuildCustomMapPrompt.ts b/src/prompts/BuildCustomMapPrompt.ts index e9a8951..b418d39 100644 --- a/src/prompts/BuildCustomMapPrompt.ts +++ b/src/prompts/BuildCustomMapPrompt.ts @@ -89,12 +89,27 @@ Follow these steps to create and preview the styled map: instructionText += `\n - Set zoom level to: ${previewZoom} - The preview will use an existing public token automatically -5. **Present results** +5. **Validate the style** + - Automatically run validation using the prepare-style-for-production prompt + - Pass the style ID from step 3 as the style_id_or_json parameter + - This checks: + * Expression syntax (especially important for AI-generated styles) + * Color contrast for accessibility (WCAG AA) + * Style optimization opportunities + - Validation is fast (offline processing only) + +6. **Present complete results** - Show the user: * A summary of the theme and customizations applied * The style ID for future reference * The preview URL to view the map + * Validation results summary: + - ✅ Issues found or "Style is production-ready" + - Expression validation status (critical for generated styles) + - Accessibility compliance (WCAG AA) + - Optimization recommendations * Suggestions for further customization if desired + - Note: Validation warnings can be ignored for experimental maps **Theme interpretation tips:** - "Dark cyberpunk": Dark backgrounds, neon colors (cyan, magenta, purple), high contrast @@ -108,9 +123,11 @@ Follow these steps to create and preview the styled map: instructionText += `\n\n**Important notes:** - The style_builder_tool is powered by AI and may need refinement +- Validation runs automatically to catch any issues in generated expressions - You can iterate on the style by making additional calls to style_builder_tool - If the initial result doesn't match expectations, try refining the theme description -- Consider the map's use case when choosing zoom levels and preview locations`; +- Consider the map's use case when choosing zoom levels and preview locations +- For experimental maps, validation warnings can be addressed later`; return [ { diff --git a/src/prompts/CreateAndPreviewStylePrompt.ts b/src/prompts/CreateAndPreviewStylePrompt.ts index 1d9e55d..bb7d9ba 100644 --- a/src/prompts/CreateAndPreviewStylePrompt.ts +++ b/src/prompts/CreateAndPreviewStylePrompt.ts @@ -112,16 +112,33 @@ Follow these steps carefully: instructionText += `\n - Set zoom level to: ${previewZoom} - The tool will automatically use the public token you created/found earlier -5. **Present results** +5. **Validate the style** + - Automatically run validation using the prepare-style-for-production prompt + - Pass the style ID from step 3 as the style_id_or_json parameter + - This checks: + * Expression syntax and correctness + * Color contrast for accessibility (WCAG AA) + * Style optimization opportunities + - Validation is fast (offline processing only) + +6. **Present complete results** - Show the user: * The created style ID * The preview URL (they can click to open in browser) + * Validation results summary: + - ✅ Issues found or "Style is production-ready" + - Expression validation status + - Accessibility compliance (WCAG AA) + - Optimization recommendations * Instructions to share or embed the preview + - Note: Validation warnings can be ignored for quick prototypes **Important notes:** - The preview_style_tool will automatically fetch and use an available public token - Make sure the style is created successfully before generating the preview -- If any step fails, provide clear error messages and suggest fixes`; +- Validation runs automatically to catch issues early (offline, fast) +- If any step fails, provide clear error messages and suggest fixes +- For quick prototypes, validation warnings can be addressed later`; return [ { diff --git a/src/prompts/DesignDataDrivenStylePrompt.ts b/src/prompts/DesignDataDrivenStylePrompt.ts index 7109801..85ebfda 100644 --- a/src/prompts/DesignDataDrivenStylePrompt.ts +++ b/src/prompts/DesignDataDrivenStylePrompt.ts @@ -395,7 +395,34 @@ Show property values as labels: - Use red/green combinations (colorblind-unfriendly) - Forget to handle null/undefined property values -## Step 8: Documentation +## Step 8: Validate the Style + +After creating your data-driven style, automatically run validation: + +1. **Run validation:** + - Use the prepare-style-for-production prompt + - Pass the style ID as the style_id_or_json parameter + - Data-driven styles use complex expressions that benefit from validation + - This checks: + * Expression syntax and type correctness + * Color contrast for accessibility (WCAG AA) + * Overall style optimization + - Validation is fast (offline processing only) + +2. **Present validation results:** + - Include validation summary with the preview: + * ✅ Issues found or "Style is production-ready" + * Expression validation status (critical for data-driven styles) + * Accessibility compliance (WCAG AA) + * Optimization recommendations + - This ensures your expressions work correctly with real data + +3. **Note for users:** + - Validation runs automatically to catch issues early + - For quick prototypes, warnings can be addressed later + - Complex nested expressions especially benefit from validation + +## Step 9: Documentation For more information on expressions: - Use get_reference_tool with 'style-spec-reference' diff --git a/src/prompts/PrepareStyleForProductionPrompt.ts b/src/prompts/PrepareStyleForProductionPrompt.ts new file mode 100644 index 0000000..b2f26f5 --- /dev/null +++ b/src/prompts/PrepareStyleForProductionPrompt.ts @@ -0,0 +1,257 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + +import type { PromptMessage } from '@modelcontextprotocol/sdk/types.js'; +import { BasePrompt, type PromptArgument } from './BasePrompt.js'; + +/** + * Prompt for preparing a Mapbox style for production deployment + * + * This prompt orchestrates multiple quality validation tools to: + * 1. Validate all expressions in the style + * 2. Validate GeoJSON sources (if any) + * 3. Check color contrast for text layers + * 4. Optimize the style + * 5. Generate a comprehensive quality report + */ +export class PrepareStyleForProductionPrompt extends BasePrompt { + readonly name = 'prepare-style-for-production'; + readonly description = + 'Comprehensive quality validation workflow for Mapbox styles before production deployment. Validates expressions, checks accessibility compliance, optimizes performance, and generates a deployment-ready quality report.'; + + readonly arguments: ReadonlyArray = [ + { + name: 'style_id_or_json', + description: + 'Either a Mapbox style ID (e.g., "username/style-id") or a complete style JSON string', + required: true + }, + { + name: 'skip_optimization', + description: 'Set to "true" to skip style optimization (default: false)', + required: false + }, + { + name: 'wcag_level', + description: + 'WCAG compliance level to check: "AA" or "AAA" (default: AA)', + required: false + } + ]; + + getMessages(args: Record): PromptMessage[] { + const styleInput = args['style_id_or_json']; + const skipOptimization = args['skip_optimization'] === 'true'; + const wcagLevel = args['wcag_level'] || 'AA'; + + const instructionText = `Prepare a Mapbox style for production deployment by running comprehensive quality checks. + +**Style to validate:** ${styleInput} + +Follow this quality validation workflow carefully: + +## Step 1: Load the Style + +${ + styleInput.includes('{') + ? '- The style appears to be JSON, parse it directly' + : '- Use retrieve_style_tool to fetch the style from Mapbox\n- Save the complete style JSON for validation' +} + +## Step 2: Validate All Expressions + +Mapbox styles use expressions in multiple places. Check each one: + +1. **Validate filter expressions:** + - Iterate through all layers in the style + - For each layer with a "filter" property, use validate_expression_tool + - Parameters: { expression: , context: "filter" } + - Track any validation errors + +2. **Validate paint property expressions:** + - For each layer, check all paint properties + - Identify properties with expression values (arrays starting with an operator) + - Validate each expression: { expression: , context: "paint" } + - Track any validation errors + +3. **Validate layout property expressions:** + - For each layer, check all layout properties + - Identify properties with expression values + - Validate each expression: { expression: , context: "layout" } + - Track any validation errors + +**Report expression validation results:** +- Total expressions validated +- Number of valid expressions +- Number of invalid expressions with details +- Any type mismatches or operator errors + +## Step 3: Validate GeoJSON Sources (if any) + +Check for GeoJSON data sources: + +1. **Identify GeoJSON sources:** + - Look through style.sources + - Find any sources with type: "geojson" + +2. **Validate each GeoJSON source:** + - If source has inline "data" property, validate it + - Use validate_geojson_tool with the GeoJSON data + - If source uses "url", note that it should be validated when loaded + - Track validation results + +**Report GeoJSON validation results:** +- Number of GeoJSON sources found +- Validation status for each source +- Any coordinate or structure errors + +## Step 4: Check Color Contrast for Text Layers + +Ensure text is readable (WCAG ${wcagLevel} compliance): + +1. **Identify text layers:** + - Find all layers with type: "symbol" + - Check if they have text-field in layout properties + +2. **Extract colors for each text layer:** + - Foreground: Get "text-color" from paint properties (default: #000000) + - Background: Get "text-halo-color" if present, otherwise note background is map tiles + - Handle expression-based colors (evaluate or use typical values) + +3. **Check contrast for each text layer:** + - Use check_color_contrast_tool + - Parameters: { + foregroundColor: , + backgroundColor: , + level: "${wcagLevel}", + fontSize: "normal" or "large" based on text-size + } + - Note: If no halo color, check against both light (#f0f0f0) and dark (#333333) backgrounds + +**Report accessibility results:** +- Total text layers checked +- Layers that pass WCAG ${wcagLevel} +- Layers that fail with specific issues +- Recommendations for failing layers + +${ + !skipOptimization + ? `## Step 5: Optimize the Style + +Run optimization to improve performance and reduce file size: + +1. **Run optimize_style_tool:** + - Input the complete style JSON + - Let it run all default optimizations + - Save the optimized style + +2. **Review optimization results:** + - Note size reduction percentage + - List specific optimizations applied + - Verify no functionality was lost + +**Report optimization results:** +- Original size vs optimized size +- Percentage reduction +- Optimizations applied (unused sources, duplicate layers, etc.) +- Recommendation to use optimized version` + : '## Step 5: Style Optimization\n\nSkipped per user request.' +} + +## Final Step: Generate Quality Report + +Create a comprehensive deployment checklist: + +\`\`\`markdown +# Production Quality Report + +## Style Information +- Style: ${styleInput} +- Validation Date: +- WCAG Level: ${wcagLevel} + +## Expression Validation +✓/✗ All expressions valid +- Total expressions: +- Valid: +- Invalid: + + +## GeoJSON Validation +✓/✗ All GeoJSON sources valid +- Sources checked: +- Valid: +- Invalid: + + +## Accessibility (WCAG ${wcagLevel}) +✓/✗ All text layers meet contrast requirements +- Text layers: +- Passing: +- Failing: + + +${ + !skipOptimization + ? `## Optimization +✓ Style optimized +- Size reduction: +- Optimizations: +` + : '' +} + +## Deployment Readiness + + +## Action Items + +\`\`\` + +## Important Notes + +- **Expression validation** catches runtime errors before deployment +- **GeoJSON validation** ensures data integrity +- **Color contrast** ensures readability for all users +- **Optimization** improves load times and performance +- **Fix all validation errors** before deploying to production +- **Test the ${!skipOptimization ? 'optimized' : 'validated'} style** in a staging environment +- **Document any changes** made during this process + +## If Issues Are Found + +For each type of issue, provide specific guidance: + +**Expression errors:** +- Show the invalid expression and error message +- Suggest corrected version if possible +- Link to Mapbox expression documentation + +**GeoJSON errors:** +- Identify the specific coordinate or structure problem +- Suggest fix (e.g., close polygon rings, fix coordinate order) +- Offer to fix automatically if appropriate + +**Contrast failures:** +- Show current ratio vs required ratio +- Suggest color adjustments to meet WCAG ${wcagLevel} +- Offer to calculate suitable colors if requested + +**After all fixes:** +- Re-run validation to confirm issues resolved +- Generate updated quality report +- Provide final deployment-ready style + +Execute these steps systematically and provide clear, actionable feedback at each stage.`; + + return [ + { + role: 'user', + content: { + type: 'text', + text: instructionText + } + } + ]; + } +} diff --git a/src/prompts/SetupMapboxProjectPrompt.ts b/src/prompts/SetupMapboxProjectPrompt.ts index 71a9a25..b1a9945 100644 --- a/src/prompts/SetupMapboxProjectPrompt.ts +++ b/src/prompts/SetupMapboxProjectPrompt.ts @@ -254,7 +254,27 @@ Type: ${projectType} instructionText += ` -The setup is now complete! The user can start building their map application.`; +📋 **Validate the Style** + +Automatically validate the created style: +- Use the prepare-style-for-production prompt with the style ID created above +- This checks: + * Expression syntax and correctness + * Color contrast for accessibility (WCAG AA) + * Style optimization opportunities +- Validation is fast (offline processing only) +- Include validation results in the final summary + +🎉 **Setup Complete!** + +Present the complete setup summary: +- All tokens created with proper security restrictions +- Map style created and validated +- Preview URL for testing +- Validation results (any issues or "✅ Production-ready") +- Next steps for integration + +The user can now start building their map application with confidence that their setup follows best practices.`; return [ { diff --git a/src/prompts/promptRegistry.ts b/src/prompts/promptRegistry.ts index d2ec720..b724066 100644 --- a/src/prompts/promptRegistry.ts +++ b/src/prompts/promptRegistry.ts @@ -7,6 +7,7 @@ import { AnalyzeGeojsonPrompt } from './AnalyzeGeojsonPrompt.js'; import { SetupMapboxProjectPrompt } from './SetupMapboxProjectPrompt.js'; import { DebugMapboxIntegrationPrompt } from './DebugMapboxIntegrationPrompt.js'; import { DesignDataDrivenStylePrompt } from './DesignDataDrivenStylePrompt.js'; +import { PrepareStyleForProductionPrompt } from './PrepareStyleForProductionPrompt.js'; // Central registry of all prompts export const ALL_PROMPTS = [ @@ -15,7 +16,8 @@ export const ALL_PROMPTS = [ new AnalyzeGeojsonPrompt(), new SetupMapboxProjectPrompt(), new DebugMapboxIntegrationPrompt(), - new DesignDataDrivenStylePrompt() + new DesignDataDrivenStylePrompt(), + new PrepareStyleForProductionPrompt() ] as const; export type PromptInstance = (typeof ALL_PROMPTS)[number]; diff --git a/test/prompts/PrepareStyleForProductionPrompt.test.ts b/test/prompts/PrepareStyleForProductionPrompt.test.ts new file mode 100644 index 0000000..ff5c1d9 --- /dev/null +++ b/test/prompts/PrepareStyleForProductionPrompt.test.ts @@ -0,0 +1,177 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + +import { describe, it, expect } from 'vitest'; +import { PrepareStyleForProductionPrompt } from '../../src/prompts/PrepareStyleForProductionPrompt.js'; + +describe('PrepareStyleForProductionPrompt', () => { + const prompt = new PrepareStyleForProductionPrompt(); + + it('should have correct metadata', () => { + expect(prompt.name).toBe('prepare-style-for-production'); + expect(prompt.description).toContain('quality validation'); + expect(prompt.arguments).toHaveLength(3); + }); + + it('should require style_id_or_json argument', () => { + const requiredArg = prompt.arguments.find( + (arg) => arg.name === 'style_id_or_json' + ); + expect(requiredArg).toBeDefined(); + expect(requiredArg?.required).toBe(true); + }); + + it('should have optional arguments', () => { + const optionalArgs = ['skip_optimization', 'wcag_level']; + optionalArgs.forEach((argName) => { + const arg = prompt.arguments.find((a) => a.name === argName); + expect(arg).toBeDefined(); + expect(arg?.required).toBe(false); + }); + }); + + it('should generate messages with style ID', () => { + const result = prompt.execute({ + style_id_or_json: 'username/style-id' + }); + + expect(result.messages).toHaveLength(1); + expect(result.messages[0].role).toBe('user'); + expect(result.messages[0].content.type).toBe('text'); + + const text = result.messages[0].content.text; + expect(text).toContain('username/style-id'); + expect(text).toContain('retrieve_style_tool'); + expect(text).toContain('validate_expression_tool'); + expect(text).toContain('check_color_contrast_tool'); + expect(text).toContain('optimize_style_tool'); + }); + + it('should generate messages with style JSON', () => { + const styleJson = '{"version": 8, "name": "Test"}'; + const result = prompt.execute({ + style_id_or_json: styleJson + }); + + const text = result.messages[0].content.text; + expect(text).toContain(styleJson); + expect(text).toContain('parse it directly'); + expect(text).not.toContain('retrieve_style_tool'); + }); + + it('should include validation tools in workflow', () => { + const result = prompt.execute({ + style_id_or_json: 'test/style' + }); + + const text = result.messages[0].content.text; + expect(text).toContain('validate_expression_tool'); + expect(text).toContain('validate_geojson_tool'); + expect(text).toContain('check_color_contrast_tool'); + expect(text).toContain('filter'); + expect(text).toContain('paint'); + expect(text).toContain('layout'); + }); + + it('should include optimization by default', () => { + const result = prompt.execute({ + style_id_or_json: 'test/style' + }); + + const text = result.messages[0].content.text; + expect(text).toContain('optimize_style_tool'); + expect(text).toContain('Step 5: Optimize the Style'); + expect(text).not.toContain('Skipped per user request'); + }); + + it('should skip optimization when requested', () => { + const result = prompt.execute({ + style_id_or_json: 'test/style', + skip_optimization: 'true' + }); + + const text = result.messages[0].content.text; + expect(text).toContain('Step 5: Style Optimization'); + expect(text).toContain('Skipped per user request'); + expect(text).not.toContain('optimize_style_tool'); + }); + + it('should use default WCAG level AA', () => { + const result = prompt.execute({ + style_id_or_json: 'test/style' + }); + + const text = result.messages[0].content.text; + expect(text).toContain('WCAG AA'); + expect(text).toContain('level: "AA"'); + }); + + it('should use custom WCAG level when specified', () => { + const result = prompt.execute({ + style_id_or_json: 'test/style', + wcag_level: 'AAA' + }); + + const text = result.messages[0].content.text; + expect(text).toContain('WCAG AAA'); + expect(text).toContain('level: "AAA"'); + }); + + it('should include quality report template', () => { + const result = prompt.execute({ + style_id_or_json: 'test/style' + }); + + const text = result.messages[0].content.text; + expect(text).toContain('Production Quality Report'); + expect(text).toContain('Expression Validation'); + expect(text).toContain('GeoJSON Validation'); + expect(text).toContain('Accessibility'); + expect(text).toContain('Deployment Readiness'); + expect(text).toContain('Action Items'); + }); + + it('should include troubleshooting guidance', () => { + const result = prompt.execute({ + style_id_or_json: 'test/style' + }); + + const text = result.messages[0].content.text; + expect(text).toContain('If Issues Are Found'); + expect(text).toContain('Expression errors:'); + expect(text).toContain('GeoJSON errors:'); + expect(text).toContain('Contrast failures:'); + }); + + it('should throw error if required argument is missing', () => { + expect(() => { + prompt.execute({}); + }).toThrow('Missing required arguments: style_id_or_json'); + }); + + it('should return proper metadata', () => { + const metadata = prompt.getMetadata(); + expect(metadata.name).toBe(prompt.name); + expect(metadata.description).toBe(prompt.description); + expect(metadata.arguments).toEqual(prompt.arguments); + }); + + it('should include all validation steps in order', () => { + const result = prompt.execute({ + style_id_or_json: 'test/style' + }); + + const text = result.messages[0].content.text; + const step1Index = text.indexOf('Step 1: Load the Style'); + const step2Index = text.indexOf('Step 2: Validate All Expressions'); + const step3Index = text.indexOf('Step 3: Validate GeoJSON Sources'); + const step4Index = text.indexOf('Step 4: Check Color Contrast'); + const step5Index = text.indexOf('Step 5:'); + + expect(step1Index).toBeGreaterThan(-1); + expect(step2Index).toBeGreaterThan(step1Index); + expect(step3Index).toBeGreaterThan(step2Index); + expect(step4Index).toBeGreaterThan(step3Index); + expect(step5Index).toBeGreaterThan(step4Index); + }); +}); diff --git a/test/prompts/promptRegistry.test.ts b/test/prompts/promptRegistry.test.ts index 6ec4ce0..86ef680 100644 --- a/test/prompts/promptRegistry.test.ts +++ b/test/prompts/promptRegistry.test.ts @@ -11,7 +11,7 @@ describe('promptRegistry', () => { describe('getAllPrompts', () => { it('should return all registered prompts', () => { const prompts = getAllPrompts(); - expect(prompts).toHaveLength(6); + expect(prompts).toHaveLength(7); }); it('should include create-and-preview-style prompt', () => { @@ -56,6 +56,15 @@ describe('promptRegistry', () => { expect(prompt?.description).toContain('data-driven properties'); }); + it('should include prepare-style-for-production prompt', () => { + const prompts = getAllPrompts(); + const prompt = prompts.find( + (p) => p.name === 'prepare-style-for-production' + ); + expect(prompt).toBeDefined(); + expect(prompt?.description).toContain('quality validation'); + }); + it('should return readonly array', () => { const prompts = getAllPrompts(); expect(Object.isFrozen(prompts)).toBe(false); // ReadonlyArray is not frozen, just typed @@ -82,7 +91,8 @@ describe('promptRegistry', () => { 'analyze-geojson', 'setup-mapbox-project', 'debug-mapbox-integration', - 'design-data-driven-style' + 'design-data-driven-style', + 'prepare-style-for-production' ]; promptNames.forEach((name) => {