Token导航 LogoToken导航

Agent Skill · 效率

statamic-dev(静态发展)

statamic-dev 用于整理文档、README、Markdown 和说明材料,适合在 OpenClaw 中需要把零散信息整理成结构清晰的文档时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

statamic-dev 是效率类 Agent Skill,可用宿主为 OpenClaw,通过ClawHub获取,权限为需要联网;许可证为 MIT-0,第三方扫描已通过,按安装量在效率类的 21,003 个 Skill 里排第 19,321。

总安装 2,076周安装 84GitHub Stars 1下载 652核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT-0
版本
0.0.1

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:statamic-dev(静态发展)
来源仓库:https://github.com/michael-stokoe/statamic-dev
安装命令:
openclaw skills install statamic-dev
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 OpenClaw 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

ClawHubOpenClaw
openclaw skills install statamic-dev

这个 Skill 是做什么的

效率类 Skill 把重复的整理、汇总、转换工作交给助手完成,减少手工复制粘贴。

拥有管理 Statamic CMS 内容文件、集合、蓝图、字段集以及以 YAML 格式编写 Bard 字段内容的专业知识。

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 OpenClaw;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 openclaw skills install statamic-dev;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

statamic-dev 怎么安装?

在终端执行 openclaw skills install statamic-dev。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

statamic-dev 支持哪些 AI 工具?

公开资料标注支持 OpenClaw。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

statamic-dev 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

statamic-dev 是免费的吗?

来源标注的许可证是 MIT-0。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

statamic-dev 用的人多吗?

公开数据是累计安装 2,076 次,近一周安装 84 次,下载 652 次,GitHub 1 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

statamic-dev 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 专注 OpenClaw 一种宿主,触发方式和使用习惯不用来回切换。
  • 许可证明确为 MIT-0,商用和分发边界好判断。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 OpenClaw,想直接扩展它能力的人。
  • 需要处理效率类任务、又不想每次都手写提示词的人。
  • 需要许可证清楚、并且有第三方扫描结果的团队项目。

不太适合

  • 需要厂商级技术支持和 SLA 的生产系统。

效率类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
github

GitHub 代码协作 · OpenClaw

7,632,918545需要联网MIT-0通过
nano-pdf

Nano PDF 文档 · OpenClaw

4,249,476231需要联网MIT-0通过
auto-updater

自动更新程序 · OpenClaw

2,964,063386操作浏览器MIT-0通过
video-frames

视频帧 · OpenClaw

2,621,304112需要联网MIT-0通过
gemini

Gemini 助手 · OpenClaw

2,547,39648需要联网MIT-0有提醒
statamic-dev

静态发展 · 本页

2,0761需要联网MIT-0通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Statamic CMS Development

This skill covers how to work with the Statamic CMS layer: content structure, blueprints, fieldsets, and writing content files.

Content File Format

All content lives in content/ as Markdown files with YAML front matter. The front matter holds all field data; the body after --- is typically empty (Bard fields are stored in the front matter, not the Markdown body).

---
id: unique-id
blueprint: page
title: My Page
some_field: value
---

Every entry must have an id (unique across the site) and a blueprint that determines which fields are available.

Collections

Collections live in content/collections/. Each collection has a config YAML at content/collections/{handle}.yaml and entries as .md files in content/collections/{handle}/.

Collection Config

# content/collections/{handle}.yaml
title: Articles
template: articles/show     # Default Antlers template
layout: layout              # Layout wrapper
route: '/articles/{slug}'   # URL pattern
sort_dir: desc
revisions: true
structure:                  # Only for structured collections
  root: true                # true = can have entries at root level
taxonomies:
  - categories              # Attach taxonomy terms
preview_targets:
  -
    label: Entry
    url: '{permalink}'
    refresh: true

The pages collection is a default Statamic structured collection using {parent_uri}/{slug} routing and a tree structure.

Blueprints

Blueprints define the field schema for entries. They live in resources/blueprints/collections/{collection}/{blueprint}.yaml.

Blueprint Structure

title: Page
tabs:
  main:
    display: Main
    sections:
      -
        display: Content
        fields:
          -
            handle: title
            field:
              type: text
              required: true
              validate:
                - required
          -
            import: my_fieldset        # Import a fieldset
  seo:
    display: SEO
    sections:
      -
        fields:
          -
            import: common/seo         # Import from subfolder
  sidebar:
    display: Sidebar
    sections:
      -
        fields:
          -
            handle: slug
            field:
              type: slug
              localizable: true
              validate: 'max:200'

Tabs organise the CP editing UI. The sidebar tab renders in the right sidebar. Use import: to pull in reusable fieldsets.

Fieldsets

Reusable field groups live in resources/fieldsets/. They are imported into blueprints and other fieldsets with import: {path}.

Fieldsets in subfolders are referenced with dot or slash notation (e.g. import: common/seo or import: common.seo).

Fieldset Structure

title: My Fieldset
fields:
  -
    handle: heading
    field:
      type: text
      display: Heading
  -
    handle: content
    field:
      type: bard
      display: Content
      buttons:
        - h2
        - h3
        - bold
        - italic
        - unorderedlist
        - orderedlist
        - quote
        - link

Writing Bard Content

Bard fields store content as a ProseMirror document structure in YAML. This is an array of nodes, not raw HTML or Markdown. Understanding this structure is essential when writing or editing content files by hand.

Basic Structure

A Bard field value is an array of block-level nodes. Each node has a type and usually content (an array of inline nodes):

my_bard_field:
  -
    type: paragraph
    content:
      -
        type: text
        text: 'Plain paragraph text.'

Paragraphs

The most common node. Contains inline text nodes:

-
  type: paragraph
  content:
    -
      type: text
      text: 'This is a paragraph.'

A paragraph with text alignment uses attrs:

-
  type: paragraph
  attrs:
    textAlign: center
  content:
    -
      type: text
      text: 'Centered paragraph text.'

Headings

Headings use the heading type with a level attribute:

-
  type: heading
  attrs:
    level: 2
  content:
    -
      type: text
      text: 'This is an H2'

Valid levels: 1 through 6 (corresponding to h1–h6). Only use levels that are enabled in the Bard field's buttons config.

Inline Formatting (Marks)

Bold, italic, and other inline styles are applied via marks on text nodes. Multiple marks can be combined on a single text node:

Bold:

-
  type: text
  marks:
    -
      type: bold
  text: 'Bold text'

Italic:

-
  type: text
  marks:
    -
      type: italic
  text: 'Italic text'

Bold + Italic:

-
  type: text
  marks:
    -
      type: bold
    -
      type: italic
  text: 'Bold and italic'

Underline:

-
  type: text
  marks:
    -
      type: underline
  text: 'Underlined text'

Strikethrough:

-
  type: text
  marks:
    -
      type: strike
  text: 'Struck through'

Inline code:

-
  type: text
  marks:
    -
      type: code
  text: 'some_code()'

Superscript / Subscript:

-
  type: text
  marks:
    -
      type: superscript
  text: '2'

Small text:

-
  type: text
  marks:
    -
      type: small
  text: 'Fine print'

Links

Links are a mark with attrs containing the href:

-
  type: text
  marks:
    -
      type: link
      attrs:
        href: 'https://example.com'
  text: 'Click here'

Link with target and rel attributes:

-
  type: text
  marks:
    -
      type: link
      attrs:
        href: 'https://example.com'
        target: _blank
        rel: 'noopener noreferrer'
  text: 'External link'

Links to Statamic entries use the entry:: prefix:

-
  type: text
  marks:
    -
      type: link
      attrs:
        href: 'entry::some-entry-id'
  text: 'Internal link'

Mixed Inline Content

A paragraph often contains a mix of plain and formatted text. Each run of text with different formatting is a separate node in the content array:

-
  type: paragraph
  content:
    -
      type: text
      text: 'This is '
    -
      type: text
      marks:
        -
          type: bold
      text: 'important'
    -
      type: text
      text: ' information.'

Lists

Unordered list:

-
  type: bulletList
  content:
    -
      type: listItem
      content:
        -
          type: paragraph
          content:
            -
              type: text
              text: 'First item'
    -
      type: listItem
      content:
        -
          type: paragraph
          content:
            -
              type: text
              text: 'Second item'

Ordered list:

-
  type: orderedList
  attrs:
    start: 1
  content:
    -
      type: listItem
      content:
        -
          type: paragraph
          content:
            -
              type: text
              text: 'Step one'
    -
      type: listItem
      content:
        -
          type: paragraph
          content:
            -
              type: text
              text: 'Step two'

Blockquotes

-
  type: blockquote
  content:
    -
      type: paragraph
      content:
        -
          type: text
          text: 'This is a quoted passage.'

Horizontal Rule

-
  type: horizontalRule

Code Blocks

-
  type: codeBlock
  attrs:
    language: php
  content:
    -
      type: text
      text: 'echo "Hello World";'

Images

Images in Bard reference assets by their path:

-
  type: image
  attrs:
    src: 'assets::images/photo.jpg'
    alt: 'Description of the image'

Tables

-
  type: table
  content:
    -
      type: tableRow
      content:
        -
          type: tableHeader
          content:
            -
              type: paragraph
              content:
                -
                  type: text
                  text: 'Column 1'
        -
          type: tableHeader
          content:
            -
              type: paragraph
              content:
                -
                  type: text
                  text: 'Column 2'
    -
      type: tableRow
      content:
        -
          type: tableCell
          content:
            -
              type: paragraph
              content:
                -
                  type: text
                  text: 'Cell value'
        -
          type: tableCell
          content:
            -
              type: paragraph
              content:
                -
                  type: text
                  text: 'Another value'

Empty Bard Field

An empty Bard field can be represented as an empty array or omitted entirely:

story: []

Globals

Global sets store site-wide data. Configs live at content/globals/{handle}.yaml, values at content/globals/default/{handle}.yaml, and blueprints at resources/blueprints/globals/{handle}.yaml.

Accessing Globals in Templates

{{ settings:website_name }}
{{ settings:primary_contact_tel }}
{{ settings:socials }}
    {{ name }} — {{ link }}
{{ /settings:socials }}

Taxonomies

Taxonomies live in content/taxonomies/. Each taxonomy has a config YAML and term files in a subfolder.

Taxonomy Terms

Terms are YAML files in content/taxonomies/{handle}/:

# content/taxonomies/categories/my-term.yaml
title: My Term

Attaching Taxonomies to Collections

In the collection config:

taxonomies:
  - categories

In the blueprint, add a terms field:

-
  handle: category
  field:
    type: terms
    taxonomies:
      - categories
    display: Category
    mode: select
    max_items: 1

In content files, reference terms by slug:

category:
  - my_term

Navigation

Navigation menus live in content/navigation/. Each has a config YAML and a tree YAML in content/trees/navigation/.

Navigation Config

# content/navigation/{handle}.yaml
title: Primary
collections:
  - pages
max_depth: 1

Navigation Blueprints

Navigation items can have custom fields via blueprints at resources/blueprints/navigation/{handle}.yaml.

Rendering Navigation

{{ nav:primary }}
    <a href="{{ url }}">{{ title }}</a>
{{ /nav:primary }}

Statamic Forms

Statamic's built-in form system handles user submissions. Form configs live at resources/forms/{handle}.yaml and blueprints at resources/blueprints/forms/{handle}.yaml.

Form Config

title: 'Contact Form'
honeypot: honeypot
store: true
email:
  -
    to: '{{ site:contact_email }}'
    from: '{{ email }}'
    reply_to: '{{ email }}'
    subject: 'New Contact Submission'
    html: emails/contact

Rendering Forms in Templates

{{ form:contact }}
    {{ if errors }}
        {{ errors }}
            <p>{{ value }}</p>
        {{ /errors }}
    {{ /if }}
    {{ if success }}
        <p>Thank you!</p>
    {{ else }}
        <input type="text" name="name" value="{{ old:name }}" />
        <!-- more fields -->
        <button type="submit">Submit</button>
    {{ /if }}
{{ /form:contact }}

Assets

Asset containers are configured at content/assets/{handle}.yaml. The main container is assets. Assets are referenced by their path relative to the container:

featured_image: images/photo.jpg
gallery:
  - images/photo1.jpg
  - images/photo2.jpg

In Bard fields, assets use the assets:: prefix:

-
  type: image
  attrs:
    src: 'assets::images/photo.jpg'
    alt: 'A photo'

Replicator Fields

Replicators allow content editors to build flexible content by stacking predefined sets of fields. They appear in blueprints, fieldsets, and globals.

Replicator Content Format

items:
  -
    id: item-1
    type: my_set
    value: 'Some value'
    label: 'Some label'
    enabled: true
  -
    id: item-2
    type: my_set
    value: 'Another value'
    label: 'Another label'
    enabled: true

Each item needs id, type (matching the set handle), and enabled. The remaining fields come from the set's field definitions.

Link Fields

Statamic's link fieldtype can store various link types:

# Entry link
cta_link: 'entry::some-entry-id'

# URL
button_url: 'https://example.com'

# Relative path
cta_link: /some-page

Entries Field

The entries fieldtype stores references to other entries by their ID:

# Single entry
related: b031fb38-4392-42b8-9b72-4554d1fed7c5

# Multiple entries
team_members:
  - person-entry-id-1
  - person-entry-id-2

Addons

Statamic addons are Laravel packages that extend Statamic's functionality. They live in addons/{vendor}/{name}/ during local development and are registered as path repositories in the root composer.json.

Addon Directory Structure

A Statamic addon follows this layout:

addons/{vendor}/{name}/
├── composer.json              # Package definition + Statamic/Laravel metadata
├── package.json               # Node dependencies (if addon has CP JS)
├── vite.config.js             # Vite config for building CP assets
├── phpunit.xml                # Test configuration
├── config/
│   └── {name}.php             # Publishable config file
├── resources/
│   ├── js/
│   │   ├── cp.js              # CP JavaScript entry point
│   │   └── components/        # Vue components for the CP
│   ├── views/                 # Blade views (for CP pages)
│   └── dist/                  # Built assets (committed for distribution)
├── routes/
│   ├── api.php                # Public/API routes
│   └── cp.php                 # Control Panel routes
├── src/
│   ├── ServiceProvider.php    # Main addon service provider
│   ├── Http/
│   │   ├── Controllers/       # Route controllers
│   │   └── Middleware/         # Custom middleware
│   ├── Support/               # Helper/utility classes
│   ├── Policies/              # Authorization policies
│   ├── Exceptions/            # Custom exception classes
│   └── Tools/                 # Domain-specific classes
│       └── Contracts/         # Interfaces
└── tests/
    ├── TestCase.php           # Base test case
    ├── Unit/                  # Unit tests
    ├── Integration/           # Integration tests
    └── Property/              # Property-based tests (optional)

Addon composer.json

The addon's composer.json defines the package, its autoloading, and Statamic/Laravel metadata:

{
    "name": "{vendor}/{name}",
    "autoload": {
        "psr-4": {
            "{Vendor}\\{Name}\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "{Vendor}\\{Name}\\Tests\\": "tests"
        }
    },
    "require": {
        "statamic/cms": "^6.0"
    },
    "require-dev": {
        "orchestra/testbench": "^10.8"
    },
    "config": {
        "allow-plugins": {
            "pixelfear/composer-dist-plugin": true
        }
    },
    "extra": {
        "statamic": {
            "name": "My Addon",
            "description": "What the addon does"
        },
        "laravel": {
            "providers": [
                "{Vendor}\\{Name}\\ServiceProvider"
            ]
        }
    },
    "minimum-stability": "dev",
    "prefer-stable": true
}

Key points:

  • extra.statamic provides the addon name and description for the Statamic marketplace/CP.
  • extra.laravel.providers registers the service provider for auto-discovery.
  • pixelfear/composer-dist-plugin must be allowed for Statamic's asset distribution.

Registering the Addon in the Root Project

In the root composer.json, add a path repository and require the addon:

{
    "repositories": [
        {
            "type": "path",
            "url": "addons/{vendor}/{name}"
        }
    ],
    "require": {
        "{vendor}/{name}": "*@dev"
    }
}

Then run composer update to symlink the addon into vendor/.

The Service Provider

Every addon has a service provider that extends Statamic\Providers\AddonServiceProvider. This is the central registration point for everything the addon provides.

<?php

namespace {Vendor}\{Name};

use Statamic\Facades\CP\Nav;
use Statamic\Providers\AddonServiceProvider;

class ServiceProvider extends AddonServiceProvider
{
    // Namespace for Blade views: used as '{namespace}::view.name'
    protected $viewNamespace = '{name}';

    // Vite configuration for CP JavaScript/CSS assets
    protected $vite = [
        'input' => [
            'resources/js/cp.js',
        ],
        'publicDirectory' => 'resources/dist',
        'hotFile' => __DIR__.'/../resources/dist/hot',
    ];

    public function register(): void
    {
        parent::register();

        // Merge default config so config('{name}.key') works immediately
        $this->mergeConfigFrom(__DIR__.'/../config/{name}.php', '{name}');

        // Register singletons that need to be available early
        $this->app->singleton(SomeRepository::class);
    }

    public function bootAddon(): void
    {
        // Publish config file: php artisan vendor:publish --tag={name}-config
        $this->publishes([
            __DIR__.'/../config/{name}.php' => config_path('{name}.php'),
        ], '{name}-config');

        // Register CP navigation items
        $this->bootCp();

        // Conditionally load routes, bindings, etc.
        if (! config('{name}.enabled')) {
            return; // Addon is invisible when disabled
        }

        $this->loadRoutesFrom(__DIR__.'/../routes/api.php');

        // Register additional singletons
        $this->app->singleton(SomeService::class, function ($app) {
            return new SomeService($app);
        });
    }

    protected function bootCp(): void
    {
        Nav::extend(function ($nav) {
            $nav->tools('My Addon')
                ->route('{name}.settings.index')
                ->icon('some-icon');
        });
    }
}

Key patterns:

  • register() runs first — merge config and bind early singletons here.
  • bootAddon() is the Statamic-specific boot method (not boot()). Load routes, publish assets, and register CP nav here.
  • The $vite property tells Statamic where to find the addon's built JS/CSS assets.
  • The $viewNamespace property sets the Blade view namespace.
  • Use a kill switch pattern (config('{name}.enabled')) to make the addon completely invisible when disabled — no routes, no bindings.
  • CP nav items should always be registered (even when the addon is disabled) so admins can access settings to enable it.

AddonServiceProvider Built-in Properties

AddonServiceProvider provides several properties you can set to register things declaratively:

// Register route files
protected $routes = [
    'cp' => __DIR__.'/../routes/cp.php',
    'web' => __DIR__.'/../routes/web.php',
    'actions' => __DIR__.'/../routes/actions.php',
];

// Register event listeners
protected $listen = [
    SomeEvent::class => [SomeListener::class],
];

// Register custom fieldtypes
protected $fieldtypes = [MyFieldtype::class];

// Register custom tags
protected $tags = [MyTag::class];

// Register custom modifiers
protected $modifiers = [MyModifier::class];

// Register custom widgets (CP dashboard)
protected $widgets = [MyWidget::class];

// Register custom actions (bulk actions in listings)
protected $actions = [MyAction::class];

// Register custom scopes (query scopes for listings)
protected $scopes = [MyScope::class];

// Register custom policies
protected $policies = [
    SomeModel::class => SomePolicy::class,
];

// Register publishable assets
protected $publishables = [
    __DIR__.'/../public' => 'vendor/{name}',
];

// Register scheduled commands
protected $commands = [MyCommand::class];

// Register middleware
protected $middlewareGroups = [
    'statamic.cp.authenticated' => [MyMiddleware::class],
];

// Register update scripts
protected $updateScripts = [UpdateSomething::class];

Config Files

Addon config files live in config/ within the addon and are publishable to the app's config/ directory.

// config/{name}.php
<?php

return [
    'enabled' => env('MY_ADDON_ENABLED', false),
    'some_setting' => env('MY_ADDON_SETTING', 'default'),
    'nested' => [
        'option' => env('MY_ADDON_NESTED_OPTION', true),
    ],
];

In the service provider, merge it in register():

$this->mergeConfigFrom(__DIR__.'/../config/{name}.php', '{name}');

And publish it in bootAddon():

$this->publishes([
    __DIR__.'/../config/{name}.php' => config_path('{name}.php'),
], '{name}-config');

Users publish with: php artisan vendor:publish --tag={name}-config

Routes

Addon routes are standard Laravel route files. API routes and CP routes are typically separate.

API routes (routes/api.php):

<?php

use Illuminate\Support\Facades\Route;
use {Vendor}\{Name}\Http\Controllers\MyController;
use {Vendor}\{Name}\Http\Middleware\MyMiddleware;

Route::prefix('{name}')
    ->middleware([MyMiddleware::class])
    ->group(function () {
        Route::post('/action', [MyController::class, 'action']);
        Route::get('/status', [MyController::class, 'status']);
    });

CP routes (routes/cp.php):

<?php

use Illuminate\Support\Facades\Route;
use {Vendor}\{Name}\Http\Controllers\SettingsController;

Route::prefix('{name}')->group(function () {
    Route::get('/settings', [SettingsController::class, 'index'])->name('{name}.settings.index');
    Route::post('/settings', [SettingsController::class, 'update'])->name('{name}.settings.update');
});

CP routes are automatically wrapped in Statamic's CP middleware (authentication, etc.). Load API routes manually in bootAddon() with $this->loadRoutesFrom(). CP routes can be declared via the $routes property or loaded manually.

CP Views and Vue Components

Addon CP pages use Blade views that extend Statamic's layout and mount Vue components.

Blade view (resources/views/settings/index.blade.php):

@extends('statamic::layout')

@section('title', 'My Addon Settings')

@section('content')
    <my-settings-component
        :settings='@json($settings)'
        update-url="{{ cp_route('{name}.settings.update') }}"
        csrf-token="{{ csrf_token() }}"
    ></my-settings-component>
@endsection

JS entry point (resources/js/cp.js):

import MySettings from './components/MySettings.vue';

Statamic.booting(() => {
    Statamic.$components.register('my-settings-component', MySettings);
});

Vue components are registered via Statamic.$components.register() during the Statamic.booting() lifecycle hook. The component name in JS must match the tag name used in the Blade view.

CP Controllers

CP controllers extend Statamic\Http\Controllers\CP\CpController and can use Statamic's user/permission system:

<?php

namespace {Vendor}\{Name}\Http\Controllers;

use Illuminate\Http\Request;
use Statamic\Facades\User;
use Statamic\Http\Controllers\CP\CpController;

class SettingsController extends CpController
{
    public function index()
    {
        abort_unless(User::current()?->isSuper(), 403);

        $settings = config('{name}');

        return view('{name}::settings.index', compact('settings'));
    }

    public function update(Request $request)
    {
        abort_unless(User::current()?->isSuper(), 403);

        $validated = $request->validate([
            'enabled' => 'boolean',
            'some_setting' => 'nullable|string',
        ]);

        // Persist settings (to YAML, database, etc.)

        if ($request->wantsJson()) {
            return response()->json(['message' => 'Settings saved.']);
        }

        return redirect()->back()->with('success', 'Settings saved.');
    }
}

Vite Configuration for Addons

Addons use Vite with the @statamic/cms vite plugin and laravel-vite-plugin:

// vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import statamic from '@statamic/cms/vite-plugin';

export default defineConfig({
    plugins: [
        statamic(),
        laravel({
            input: ['resources/js/cp.js'],
            refresh: true,
            publicDirectory: 'resources/dist',
            hotFile: 'resources/dist/hot',
        }),
    ],
});

package.json:

{
    "private": true,
    "scripts": {
        "dev": "rm -rf resources/dist/build && vite",
        "build": "vite build"
    },
    "devDependencies": {
        "@statamic/cms": "file:./vendor/statamic/cms/resources/dist-package",
        "laravel-vite-plugin": "^1.3.0",
        "vite": "^6.4.1"
    }
}

The @statamic/cms dependency points to the local Statamic dist package within the addon's vendor directory. Run pnpm install (or npm install) from within the addon directory, then pnpm run build to compile assets into resources/dist/build/.

Testing Addons

Addon tests use Orchestra Testbench with Statamic's AddonTestCase:

Base TestCase (tests/TestCase.php):

<?php

namespace {Vendor}\{Name}\Tests;

use {Vendor}\{Name}\ServiceProvider;
use Statamic\Testing\AddonTestCase;

abstract class TestCase extends AddonTestCase
{
    protected string $addonServiceProvider = ServiceProvider::class;
}

PHPUnit config (phpunit.xml):

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    backupGlobals="false"
    bootstrap="vendor/autoload.php"
    colors="true"
    processIsolation="false"
    stopOnFailure="false">
  <testsuites>
    <testsuite name="Test Suite">
      <directory suffix="Test.php">./tests</directory>
    </testsuite>
  </testsuites>
  <php>
    <env name="APP_ENV" value="testing"/>
    <env name="APP_KEY" value="base64:ybcI9MKuhLnESRSuWDfnJQuohOXMBaynfbTC5Y5i1FE="/>
    <env name="CACHE_DRIVER" value="array"/>
    <env name="SESSION_DRIVER" value="array"/>
    <env name="QUEUE_CONNECTION" value="sync"/>
  </php>
</phpunit>

Run tests from the addon directory: ./vendor/bin/phpunit

Tests have their own composer.json dependencies (e.g. orchestra/testbench) and their own vendor/ directory, separate from the root project.

Middleware

Custom middleware follows standard Laravel patterns. Register it in route groups or via the service provider:

<?php

namespace {Vendor}\{Name}\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class MyMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        // Pre-processing logic (auth, validation, etc.)

        if ($someConditionFails) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return $next($request);
    }
}

Creating a New Addon — Step by Step

  1. Create the directory structure:
   addons/{vendor}/{name}/
   ├── composer.json
   ├── src/
   │   └── ServiceProvider.php
   └── config/
       └── {name}.php
  1. Set up composer.json with the extra.statamic and extra.laravel.providers keys.
  1. Create the ServiceProvider extending AddonServiceProvider.
  1. Register the addon in the root composer.json as a path repository and require it.
  1. Run composer update from the project root.
  1. If the addon has CP assets:

- Add package.json and vite.config.js. - Create resources/js/cp.js as the entry point. - Run pnpm install and pnpm run build from the addon directory.

  1. If the addon has config:

- Create config/{name}.php. - Merge it in register() and publish it in bootAddon(). - Run php artisan vendor:publish --tag={name}-config.

  1. If the addon has routes:

- Create routes/cp.php and/or routes/api.php. - Load them in the service provider.

  1. If the addon has tests:

- Add orchestra/testbench to require-dev. - Create tests/TestCase.php extending AddonTestCase. - Add phpunit.xml. - Run composer install from the addon directory to set up its own vendor.

Updating an Existing Addon

When modifying an addon:

  • PHP changes in src/ take effect immediately (the addon is symlinked via Composer path repository).
  • Config changes require php artisan config:clear or republishing.
  • Vue/JS changes require rebuilding: run pnpm run build from the addon directory.
  • New routes or service provider changes may require php artisan cache:clear.
  • New test files are picked up automatically by PHPUnit.
  • If you add new Composer dependencies to the addon, run composer update from both the addon directory and the root project.

平台分布

OpenClaw

74.85%
按下载量换算488

安全审计

VirusTotal

通过

ClawScan

通过

Static analysis

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills