strapi-configuration
npx skills add https://github.com/paulbratslavsky/strapi-configuration-skill --skill strapi-configuration
Agent 安装分布
Skill 文档
Strapi Configuration Skill
Create a fully configured Strapi project with preconfigured content types, seed data, public API access, and route-based middleware for default population.
Usage
/strapi-configuration /path/to/my-project
/strapi-configuration
The skill will always present a selection menu showing available presets and a custom description option. If no output path is provided, it will ask for one.
Available Presets
| Preset | Description |
|---|---|
blog |
Articles, Authors, Categories, Tags, Homepage, About, Global |
ecommerce |
Products, Categories, Reviews, Homepage, Global |
portfolio |
Projects, Skills, Testimonials, Homepage, About, Contact |
restaurant |
Menu Items, Categories, Reservations, Locations, Homepage |
Template definitions are located in: .claude/skills/strapi-configuration/templates/<preset>.json
Execution Steps
When the user invokes this skill, follow these steps in order:
Step 1: Parse Arguments and Select Template
Extract <output-path> from the user’s input. Any path-like argument should be treated as the output path.
IMPORTANT: ALWAYS present a selection menu to the user. Never auto-detect or assume a preset from the arguments. Even if the argument text contains a word like “blog” or “restaurant”, treat it as an output path â do NOT silently select a matching preset.
-
If no output path was provided, ask the user for the desired project directory.
-
List all available template JSON files by scanning
.claude/skills/strapi-configuration/templates/*.json. Read each template’sname,displayName, anddescriptionfields. -
Present a numbered selection menu to the user showing all available presets, with the last option always being the custom description option:
Which type of project would you like to create? 1. Blog â Articles, Authors, Categories, Tags, Homepage, About, Global 2. E-commerce â Products, Categories, Reviews, Homepage, Global 3. Portfolio â Projects, Skills, Testimonials, Homepage, About, Contact 4. Restaurant â Menu Items, Categories, Reservations, Locations, Homepage 5. Describe what you want to build (custom)Use the
AskUserQuestiontool to present this selection. -
Based on the user’s selection:
- If they chose a preset (options 1-4 or any template name), proceed to Step 2 (Read the Template).
- If they chose the last option (custom), proceed to Step 2b (Custom Project Generation).
Step 2: Read the Template
Read the template JSON file at .claude/skills/strapi-configuration/templates/<preset>.json.
The template contains:
contentTypes– Collection types and single types (schemas)components– Reusable component definitionsmiddlewares– Route-based populate middleware configs per content typeseedData– Sample data entries for each content typepublicPermissions– Which API endpoints to make publicly accessible
Step 2b: Custom Project Generation
When the user provides a description instead of a preset name, generate the template data dynamically.
1. Gather requirements:
If the user provided a short description (e.g., “online learning platform”), ask 1-2 clarifying questions to understand:
- What are the main entities/content types? (e.g., courses, lessons, instructors, students)
- What pages should exist? (e.g., homepage, about)
- Any special features? (e.g., reviews, categories, ratings)
If the user provided a detailed description, skip clarification and proceed.
2. Design the content model:
Based on the description, design content types following these patterns from existing templates:
-
Collection Types: The main entities (e.g.,
course,instructor,lesson). Always include:- A
nameortitlefield (string, required) - A
slugfield (uid, targetField pointing to the name/title) - A
descriptionfield (text or richtext) - Relevant relations between types (use manyToOne/oneToMany with inversedBy/mappedBy)
- A
seocomponent (shared.seo) on public-facing types
- A
-
Single Types: Always include
homepageandglobal. Add others as needed (about, contact, etc.)global: always hassiteName(string),siteDescription(text),favicon(media),defaultSeo(shared.seo)homepage: always has a hero section andseo(shared.seo)
-
Components: Always include the shared set (
seo,media,rich-text). Add custom components for the domain (e.g.,learning.lesson-resource,learning.curriculum-item).
3. Generate all template data in memory:
Construct the same data structure as the preset JSON templates:
contentTypes, components, middlewares, seedData, publicPermissions
Follow the exact same Strapi schema format as the existing templates. Reference .claude/skills/strapi-configuration/templates/blog.json for the correct structure if needed.
4. Generate seed data:
Create 2-5 realistic sample entries per collection type with:
- Realistic names, descriptions, and content
- Component data inline (e.g., SEO metadata, technology tags)
- No media fields (skip image/file uploads in seed data)
- No relation fields in seed data (these require a two-pass approach)
5. Design middleware populate configs:
For each content type that has relations, media, or components, create a populate config that auto-fetches:
- Media fields:
{ fields: ["name", "alternativeText", "url", "formats"] } - Relations:
{ fields: ["name", "slug"] }(or relevant identifying fields) - Components with media: nested populate to reach media fields
- SEO components:
{ populate: { shareImage: { fields: [...] } } }
6. Set public permissions:
- Collection types:
["find", "findOne"] - Single types:
["find"] - If the type accepts user submissions (like reservations or contact forms): add
"create"
After constructing all data in memory, proceed to Step 3 using the generated data exactly as if it came from a template file.
7. Save as reusable template (optional):
After generating the project, offer to save the generated template as a new JSON file:
Would you like me to save this as a reusable template at
.claude/skills/strapi-configuration/templates/<name>.json?
If the user agrees, write the template file so it can be reused with a preset name next time.
Step 3: Create the Strapi Project
Run the CLI to create a vanilla Strapi project:
node packages/cli/create-strapi-app/bin/index.js <output-path> --non-interactive
Wait for this to complete before proceeding.
Step 4: Generate Content Type Schemas
For each content type in the template, create the full API structure:
<output-path>/src/api/<name>/
âââ content-types/
â âââ <name>/
â âââ schema.json
âââ controllers/
â âââ <name>.ts
âââ routes/
â âââ <name>.ts
âââ middlewares/
â âââ default-populate.ts (if middleware config exists for this type)
âââ services/
âââ <name>.ts
Schema file (schema.json): Use the schema directly from the template’s contentTypes.<name> object.
Controller (<name>.ts):
import { factories } from '@strapi/strapi';
export default factories.createCoreController('api::<name>.<name>');
Service (<name>.ts):
import { factories } from '@strapi/strapi';
export default factories.createCoreService('api::<name>.<name>');
Route (<name>.ts) – if NO middleware config exists for this type:
import { factories } from '@strapi/strapi';
export default factories.createCoreRouter('api::<name>.<name>');
Route (<name>.ts) – if middleware config EXISTS for this type:
import { factories } from '@strapi/strapi';
export default factories.createCoreRouter('api::<name>.<name>', {
config: {
find: {
middlewares: ['api::<name>.default-populate'],
},
findOne: {
middlewares: ['api::<name>.default-populate'],
},
},
});
Step 5: Generate Middleware Files
For each content type that has a middleware populate config in the template, create:
<output-path>/src/api/<name>/middlewares/default-populate.ts:
export default (config, { strapi }) => {
return async (ctx, next) => {
// Set default populate if not already specified in the request
if (!ctx.query.populate) {
ctx.query.populate = <POPULATE_CONFIG_FROM_TEMPLATE>;
}
await next();
};
};
Replace <POPULATE_CONFIG_FROM_TEMPLATE> with the actual populate object from the template’s middlewares.<name>.populate value, serialized as a JavaScript object literal (not JSON.stringify).
Step 6: Generate Component Files
For each component in the template, create:
<output-path>/src/components/<namespace>/<component-name>.json
Use the component definition directly from the template’s components.<namespace>.<component-name> object.
Step 7: Generate Seed Script
Create <output-path>/scripts/seed.js with the following structure:
const { createStrapi, compileStrapi } = require('@strapi/strapi');
let strapi;
async function seedExampleApp() {
const shouldImportSeedData = await isFirstRun();
if (shouldImportSeedData) {
try {
console.log('Setting up the template...');
await importSeedData();
console.log('Ready to go');
} catch (error) {
console.log('Could not import seed data');
console.error(error);
}
}
}
async function isFirstRun() {
const pluginStore = strapi.store({
environment: strapi.config.environment,
type: 'type',
name: 'setup',
});
const initHasRun = await pluginStore.get({ key: 'initHasRun' });
await pluginStore.set({ key: 'initHasRun', value: true });
return !initHasRun;
}
async function setPublicPermissions(newPermissions) {
const publicRole = await strapi.query('plugin::users-permissions.role').findOne({
where: { type: 'public' },
});
const allPermissionsToCreate = [];
Object.keys(newPermissions).map((controller) => {
const actions = newPermissions[controller];
const permissionsToCreate = actions.map((action) => {
return strapi.query('plugin::users-permissions.permission').create({
data: {
action: `api::${controller}.${controller}.${action}`,
role: publicRole.id,
},
});
});
allPermissionsToCreate.push(...permissionsToCreate);
});
await Promise.all(allPermissionsToCreate);
}
async function createEntry({ model, entry }) {
try {
await strapi.documents(`api::${model}.${model}`).create({
data: entry,
status: 'published',
});
} catch (error) {
console.error({ model, entry, error });
}
}
async function importSeedData() {
// Set public permissions
await setPublicPermissions(<PUBLIC_PERMISSIONS_FROM_TEMPLATE>);
// Import seed data for each content type
<SEED_IMPORT_CALLS>
}
async function main() {
const appContext = await compileStrapi();
const app = await createStrapi(appContext).load();
strapi = app;
app.log.level = 'error';
await seedExampleApp();
await app.destroy();
process.exit(0);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
Replace <PUBLIC_PERMISSIONS_FROM_TEMPLATE> with the publicPermissions object from the template.
Replace <SEED_IMPORT_CALLS> with calls to createEntry() for each item in seedData, iterating collection types and creating entries for single types.
Step 8: Add Seed Script to package.json
Read <output-path>/package.json and add a seed script:
{
"scripts": {
"seed": "node scripts/seed.js"
}
}
Step 9: Summary
After all files are generated, print a summary:
Project created at: <output-path>
Preset: <preset>
Content Types:
- <list each content type with kind>
Components:
- <list each component>
Middleware (default populate):
- <list each content type with middleware>
Public API Endpoints:
- GET /api/<pluralName> (find)
- GET /api/<pluralName>/:id (findOne)
...
Next steps:
1. cd <output-path>
2. npm run develop # Start the Strapi server
3. npm run seed # Seed the database with sample data
4. Visit http://localhost:1337/admin to create an admin user
5. Try the public API: curl http://localhost:1337/api/<first-plural-name>
Adding New Templates
To add a new preset, create a new JSON file in .claude/skills/strapi-configuration/templates/ following this structure:
{
"name": "my-preset",
"displayName": "My Preset",
"description": "Description of what this preset provides",
"contentTypes": {
"<singular-name>": {
"kind": "collectionType | singleType",
"collectionName": "<plural_name_with_underscores>",
"info": {
"singularName": "<singular-name>",
"pluralName": "<plural-name>",
"displayName": "<Display Name>",
"description": "<description>"
},
"options": {
"draftAndPublish": true
},
"pluginOptions": {},
"attributes": {
"<fieldName>": {
"type": "<type>",
...constraints
}
}
}
},
"components": {
"<namespace>": {
"<component-name>": {
"collectionName": "components_<namespace>_<component_name>",
"info": {
"displayName": "<Display Name>",
"icon": "<icon-name>",
"description": ""
},
"options": {},
"attributes": { ... }
}
}
},
"middlewares": {
"<content-type-singular-name>": {
"populate": {
"<relation-field>": {
"fields": ["field1", "field2"],
"populate": { ... }
}
}
}
},
"seedData": {
"<content-type-singular-name>": [
{ "field1": "value1", ... },
...
]
},
"publicPermissions": {
"<content-type-singular-name>": ["find", "findOne"]
}
}
Attribute Types Reference
| Type | Example |
|---|---|
string |
{ "type": "string" } |
text |
{ "type": "text", "maxLength": 500 } |
richtext |
{ "type": "richtext" } |
email |
{ "type": "email" } |
integer |
{ "type": "integer" } |
float |
{ "type": "float" } |
decimal |
{ "type": "decimal" } |
boolean |
{ "type": "boolean", "default": false } |
date |
{ "type": "date" } |
datetime |
{ "type": "datetime" } |
uid |
{ "type": "uid", "targetField": "title" } |
enumeration |
{ "type": "enumeration", "enum": ["a", "b", "c"] } |
media |
{ "type": "media", "multiple": false, "allowedTypes": ["images"] } |
relation |
{ "type": "relation", "relation": "manyToOne", "target": "api::x.x" } |
component |
{ "type": "component", "repeatable": false, "component": "shared.seo" } |
dynamiczone |
{ "type": "dynamiczone", "components": ["shared.media", "shared.rich-text"] } |
json |
{ "type": "json" } |