Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/prevent-ai-generated-label.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Prevent "auto-generated by AI" label on PRs

on:
pull_request:
types: [opened, edited, synchronize, labeled]

permissions:
contents: read
pull-requests: write

jobs:
strip-label:
runs-on: ubuntu-latest
steps:
- name: Remove forbidden AI label if present
uses: actions/github-script@v7
with:
script: |
const forbiddenLabels = [
'auto-generated by AI',
'AI-generated',
'ai generated',
'auto generated by ai',
'generated by ai',
].map(l => l.toLowerCase());

const pr = context.payload.pull_request;
if (!pr) {
core.info('No pull_request context; skipping');
return;
}

const prNumber = pr.number;
const existing = (pr.labels || []).map(l => l.name.toLowerCase());
const toRemove = existing.filter(l => forbiddenLabels.includes(l));

if (toRemove.length === 0) {
core.info('No forbidden labels to remove.');
return;
}

for (const label of toRemove) {
core.info(`Removing label '${label}' from PR #${prNumber}`);
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
name: label,
}).catch(() => {});
}

core.setOutput('removed', toRemove.join(','));

100 changes: 100 additions & 0 deletions .github/workflows/remove-ai-labels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Bulk remove AI-generated labels from PRs

on:
workflow_dispatch:
inputs:
state:
description: "PR state to process (open|closed|all)"
default: "all"
required: true
days:
description: "Lookback window in days for closed PRs (ignored for open)"
default: "120"
required: true
labels:
description: "Comma-separated labels to remove (case-insensitive)"
default: "auto-generated by AI,AI-generated,ai generated,auto generated by ai,generated by ai"
required: true
dry_run:
description: "If true, only logs matches without removing"
default: "false"
required: true

permissions:
contents: read
pull-requests: write

jobs:
scrub:
runs-on: ubuntu-latest
steps:
- name: Remove labels from PRs
uses: actions/github-script@v7
with:
script: |
const state = core.getInput('state');
const days = parseInt(core.getInput('days'), 10) || 120;
const dryRun = /^true$/i.test(core.getInput('dry_run'));
const labels = core.getInput('labels')
.split(',')
.map(s => s.trim().toLowerCase())
.filter(Boolean);

core.info(`Processing state='${state}', days='${days}', dry_run='${dryRun}', labels=[${labels.join(', ')}]`);

const per_page = 100;

async function* iteratePRs(targetState) {
let page = 1;
while (true) {
const { data } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: targetState === 'all' ? 'all' : targetState,
per_page,
page,
});
if (!data || data.length === 0) break;
for (const pr of data) yield pr;
if (data.length < per_page) break;
page += 1;
}
}

const cutoff = Date.now() - days * 24 * 3600 * 1000;

let totalReviewed = 0;
let totalEdited = 0;
for await (const pr of iteratePRs(state)) {
totalReviewed += 1;
if (state !== 'open') {
const updatedAt = Date.parse(pr.updated_at || pr.closed_at || pr.merged_at || pr.created_at);
if (isFinite(updatedAt) && updatedAt < cutoff) continue;
}

const labelNames = (pr.labels || []).map(l => (typeof l === 'string' ? l : l.name)).filter(Boolean);
const present = labelNames.map(n => n.toLowerCase()).filter(n => labels.includes(n));
if (present.length === 0) continue;

core.info(`PR #${pr.number} matches [${present.join(', ')}]`);
if (dryRun) continue;

for (const name of present) {
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
name,
});
totalEdited += 1;
} catch (e) {
core.warning(`Failed to remove '${name}' from #${pr.number}: ${e.message}`);
}
}
}

core.info(`Reviewed PRs: ${totalReviewed} | Labels removed ops: ${totalEdited}`);
core.setOutput('reviewed', totalReviewed);
core.setOutput('edited', totalEdited);

60 changes: 60 additions & 0 deletions docusaurus/docs/ai-tools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
title: AI tools in Strapi docs
description: One place to discover and use all AI-related helpers available on the Strapi documentation site.
tags:
- ai
- llms
- productivity
toc_max_heading_level: 3
---

<Tldr>
Use this page to quickly find and use AI helpers across the Strapi docs: copy a page’s Markdown, open a chat prefilled with context, or retrieve purpose‑built text files for LLMs.
</Tldr>

# AI tools in Strapi docs

Strapi documentation includes a few AI‑oriented helpers designed to make it easier to ask questions about the docs, learn from code examples, and share focused context with an assistant.

All AI options live in the AI toolbar at the top of each documentation page. From there, you can copy the current page as raw Markdown, open a new chat in your preferred assistant with the page prefilled as context, or open text files tailored for LLMs.

Use Copy Markdown when you want an assistant to reason over the exact source of the page you are reading. This copies the raw Markdown to your clipboard so you can paste it into a chat or IDE assistant.

When you need site‑wide context, prefer one of the LLMs files:
- LLMs.txt provides a concise, link‑rich overview of pages. Open `/llms.txt` to get a lightweight map of the docs that you can copy or share with an assistant as high‑level context.
- LLMs-full.txt contains the entire documentation in a single file. Open `/llms-full.txt` when full‑site context is required. The file is large.
- LLMs-code.txt extracts all code examples, grouped by page and section with language and file‑path hints. Open `/llms-code.txt` for code‑centric work (migrations, refactors, API discovery). Pair examples with their page URL for best results.

To start a conversation without copy‑pasting, use the Open with LLM buttons in the toolbar. “Open with ChatGPT” and “Open with Claude” open a new tab and prefill a brief prompt that references the current page (often in your browser language). This is the fastest way to ask, “Read this page so I can ask questions about it.”

## Tips for better results

- Include the page URL: Reference the exact page (and section anchor, if relevant) so the assistant grounds its answer.
- Be explicit about Strapi version: Mention Strapi v5 (or your version) and plugin versions to avoid outdated code.
- Pair examples with context: If you share a code snippet from `/llms-code.txt`, also link the page it comes from.
- Ask for “explain first, then propose”: Request an explanation of the docs’ guidance before asking for code.

## Guidance for IDE AI tools (Cursor, Copilot, etc.)

If your IDE assistant struggles to generate correct Strapi code (for example, custom backend code or plugin services):

- Start from docs: Provide the assistant with the page URL and a relevant code example from `/llms-code.txt`.
- Clarify the goal and constraints: Describe your use case, expected inputs/outputs, and that you are targeting Strapi v5.
- Prefer public extension points: Ask for solutions using documented APIs (controllers, services, middlewares, policies, and plugin extension points) instead of relying on private internals.
- Verify against the docs: Ask the assistant to cite the pages or sections it relied on.

Related request: see the discussion about improving AI outcomes with Strapi code generation in the issue tracker.

## Kapa chatbot

The Strapi docs include an integrated Kapa chatbot to help you explore topics and code examples without leaving the page.

- Sidebar entry point: Click Ask AI in the left sidebar (next to search) to open the Kapa panel and start a conversation about anything in the docs.
- Code‑block entry point: Hover any substantial code block and click Ask AI to ask for an explanation or adaptation of that snippet.
- Deep thinking mode: For more thorough answers, enable Kapa’s deep thinking mode (slower, but better for multi‑step reasoning or complex refactors).

For admin‑panel AI features and settings, see the Strapi AI section of the Admin panel configuration page: /cms/configurations/admin-panel#strapi-ai.

:::info Privacy note
When you copy or share documentation content with third‑party tools, ensure you handle data safely and follow your organization’s policies.
:::
6 changes: 6 additions & 0 deletions docusaurus/docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ const config = {
onBrokenAnchors: 'throw',
favicon: 'https://strapi.io/assets/favicon-32x32.png',

// Custom fields used across the site (kept minimal and documented)
customFields: {
// Centralized path for the AI tools page so we can reference it from multiple places
aiToolsPath: '/ai-tools',
},

// Even if you don't use internalization, you can use this field to set useful
// metadata like html lang. For example, if your site is Chinese, you may want
// to replace "en" with "zh-Hans".
Expand Down
14 changes: 14 additions & 0 deletions docusaurus/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,23 @@

// @ts-check

/**
* Custom HTML sidebar entries
* Using const for reuse and easier maintenance
*/
const AI_OPTIONS_HTML = {
type: 'html',
// Link into the existing Strapi AI section until a dedicated page exists
value:
'<i class="ph-fill ph-sparkle"></i> <a href="/cms/configurations/admin-panel#strapi-ai">AI options</a>',
defaultStyle: true,
};

/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
const sidebars = {
cmsSidebar: [
// Quick entry for AI options
AI_OPTIONS_HTML,
// {
// type: 'html',
// value: '<i class="ph-fill ph-github-logo"></i> <a href="https://strapi.notion.site/Documentation-Contribution-Program-1d08f359807480d480fdde68bb7a5a71?pvs=74">Docs Contribution Program</a>', // The HTML to be rendered
Expand Down