wordpress-sage-theme — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited wordpress-sage-theme (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Sage is a WordPress theme framework by Roots that provides modern development practices including Blade templates, dependency management with Composer, and build tools with Vite/Bud.
composer create-project roots/sagenpm install && composer install, then configure bud.config.js for asset entries and Tailwindresources/views/, using layouts in layouts/, components in components/page.blade.php for page templates)get_field() for basic fields, have_rows() loops for repeaters and flexible contentnpm run build, verify public/manifest.json exists, check browser console for asset errorsnpm run build) runs during deployment; raw source files cannot be served directlyCreate a new Sage theme:
composer create-project roots/sage my-theme
cd my-theme
npm install && composer install
npm run devBlade page template:
@extends('layouts.app')
@section('content')
<main class="content">
<h1>{{ the_title() }}</h1>
<div class="entry-content">
{{ the_content() }}
</div>
</main>
@endsectionACF flexible content in Blade:
@if (have_rows('flexible_content'))
@while (have_rows('flexible_content'))
@php the_row() @endphp
@switch(get_row_layout())
@case('hero_section')
@include('components.hero')
@break
@endswitch
@endwhile
@endifPrerequisites: PHP 8.0+, Node.js 18+, Composer
# Create new Sage theme
wp scaffold theme-theme my-theme --theme_name="My Theme" --author="Your Name" --activate
# Or install Sage directly via Composer
composer create-project roots/sage my-theme
cd my-theme
# Install dependencies
npm install
composer install
# Build for development
npm run dev
# Build for production
npm run buildresources/
├── views/ # Blade templates
│ ├── layouts/ # Base layouts (app.blade.php)
│ ├── components/ # Reusable components
│ └── partials/ # Template partials
├── styles/ # CSS/SASS files
│ └── main.scss # Main stylesheet
└── scripts/ # JavaScript files
└── main.js # Main JavaScriptBase Layout (resources/views/layouts/app.blade.php):
<!DOCTYPE html>
<html {{ site_html_language_attributes() }}>
<head>
{{ wp_head() }}
</head>
<body {{ body_class() }}>
@yield('content')
{{ wp_footer() }}
</body>
</html>| WordPress Template | Sage Blade File |
|---|---|
| front-page.php | views/front-page.blade.php |
| single.php | views/single.blade.php |
| page.php | views/page.blade.php |
| archive.php | views/archive.blade.php |
| index.php | views/index.blade.php |
Example Page Template (resources/views/page.blade.php):
@extends('layouts.app')
@section('content')
<main class="content">
<h1>{{ the_title() }}</h1>
<div class="entry-content">
{{ the_content() }}
</div>
</main>
@endsectionReusable Button Component (resources/views/components/button.blade.php):
@props(['url' => '#', 'text' => 'Click', 'variant' => 'primary'])
<a href="{{ $url }}" class="btn btn-{{ $variant }}">
{{ $text }}
</a>Usage:
<x-button url="/contact" text="Contact Us" variant="secondary" />Basic Field:
@while(the_post())
<h1>{{ get_field('hero_title') ?? the_title() }}</h1>
<p>{{ get_field('hero_description') }}</p>
@endwhileFlexible Content:
@if (have_rows('flexible_content'))
@while (have_rows('flexible_content'))
@php the_row() @endphp
@switch(get_row_layout())
@case('hero_section')
@include('components.hero')
@break
@case('features_grid')
@include('components.features-grid')
@break
@endswitch
@endwhile
@endifRepeater Field:
@if (have_rows('testimonials'))
<div class="testimonials">
@while (have_rows('testimonials'))
@php the_row() @endphp
<blockquote>
<p>{{ get_sub_field('testimonial_text') }}</p>
<cite>{{ get_sub_field('author_name') }}</cite>
</blockquote>
@endwhile
</div>
@endifInstall Tailwind:
npm install -D tailwindcss
npx tailwindcss init -pConfigure (bud.config.js):
export default async (app) => {
app
.entry({
app: ['@/scripts/main.js', '@styles/main.css'],
editor: ['@scripts/editor.js', '@styles/editor.css'],
})
.assets('images', 'fonts')
.tailwind()
.runtime()
};Tailwind CSS (resources/styles/main.css):
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.btn {
@apply px-4 py-2 rounded font-semibold transition;
}
.btn-primary {
@apply bg-blue-600 text-white hover:bg-blue-700;
}
}@if (is_front_page())
@include('components.hero')
@elseif (is_singular('post'))
@include('components.post-meta')
@endif@php
$args = [
'post_type' => 'service',
'posts_per_page' => 6,
];
$query = new WP_Query($args);
@endphp
@if ($query->have_posts())
@while ($query->have_posts())
@php $query->the_post() @endphp
<article>
<h2>{{ the_title() }}</h2>
</article>
@endwhile
@php wp_reset_postdata() @endphp
@endiffunctions.php additions:
// Custom image sizes
add_image_size('hero-lg', 1920, 1080, true);
// Custom post types
add_action('init', function() {
register_post_type('service', [
'label' => 'Services',
'public' => true,
'has_archive' => true,
'supports' => ['title', 'editor', 'thumbnail'],
]);
});Build workflow validation (always run in order):
npm run build completes without errorspublic/manifest.json exists and contains asset entriesBuild issues:
# Clear cache
npm run clean
# Rebuild
rm -rf node_modules public
npm install
npm run build
# Validate build output
ls -la public/
cat public/manifest.json | head -20Blade not compiling: Check public/manifest.json exists after build
AC fields not showing: Verify field names match exactly (case-sensitive)
resources/views/components/ for buttons, cards, and other repeated elements@include, @each, and @component for better code organization@php blocks sparingly; move complex logic to Composers or service classesfield_12345678) for reliabilityapp/View/Composers/){{ }} (Blade auto-escapes) or WordPress functions like esc_html(), esc_url()current_user_can() before privileged operationsnpm run build before deploymentpublic/manifest.json; missing this file breaks asset loadingnpm run build; raw source files cannot be servedpublic/ directory is writable during builds; incorrect permissions cause build failures~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.