@nextsparkjs/ai-workflow 0.1.0-beta.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/claude/_docs/workflows-optimizations.md +359 -0
- package/claude/agents/api-tester.md +634 -0
- package/claude/agents/architecture-supervisor.md +1351 -0
- package/claude/agents/backend-developer.md +997 -0
- package/claude/agents/backend-validator.md +417 -0
- package/claude/agents/bdd-docs-writer.md +737 -0
- package/claude/agents/block-developer.md +677 -0
- package/claude/agents/code-reviewer.md +1432 -0
- package/claude/agents/db-developer.md +721 -0
- package/claude/agents/db-validator.md +407 -0
- package/claude/agents/demo-video-generator.md +493 -0
- package/claude/agents/documentation-writer.md +1268 -0
- package/claude/agents/frontend-developer.md +1234 -0
- package/claude/agents/frontend-validator.md +777 -0
- package/claude/agents/functional-validator.md +630 -0
- package/claude/agents/mock-analyst.md +387 -0
- package/claude/agents/product-manager.md +963 -0
- package/claude/agents/qa-automation.md +1762 -0
- package/claude/agents/release-manager.md +634 -0
- package/claude/agents/selectors-translator.md +262 -0
- package/claude/agents/unit-test-writer.md +785 -0
- package/claude/agents/visual-comparator.md +329 -0
- package/claude/agents/workflow-maintainer.md +352 -0
- package/claude/commands/do/README.md +88 -0
- package/claude/commands/do/create-api.md +64 -0
- package/claude/commands/do/create-entity.md +66 -0
- package/claude/commands/do/create-migration.md +64 -0
- package/claude/commands/do/create-plugin.md +56 -0
- package/claude/commands/do/create-theme.md +70 -0
- package/claude/commands/do/mock-data.md +67 -0
- package/claude/commands/do/reset-db.md +71 -0
- package/claude/commands/do/setup-scheduled-action.md +75 -0
- package/claude/commands/do/sync-code-review.md +117 -0
- package/claude/commands/do/update-selectors.md +112 -0
- package/claude/commands/do/use-skills.md +90 -0
- package/claude/commands/do/validate-blocks.md +69 -0
- package/claude/commands/how-to/README.md +261 -0
- package/claude/commands/how-to/add-metadata.md +692 -0
- package/claude/commands/how-to/add-taxonomies.md +806 -0
- package/claude/commands/how-to/add-translations.md +571 -0
- package/claude/commands/how-to/create-api.md +577 -0
- package/claude/commands/how-to/create-block.md +575 -0
- package/claude/commands/how-to/create-child-entities.md +771 -0
- package/claude/commands/how-to/create-entity.md +597 -0
- package/claude/commands/how-to/create-migrations.md +605 -0
- package/claude/commands/how-to/create-plugin.md +654 -0
- package/claude/commands/how-to/customize-app.md +481 -0
- package/claude/commands/how-to/customize-dashboard.md +553 -0
- package/claude/commands/how-to/customize-theme.md +438 -0
- package/claude/commands/how-to/define-features-flows.md +632 -0
- package/claude/commands/how-to/deploy.md +507 -0
- package/claude/commands/how-to/handle-file-uploads.md +746 -0
- package/claude/commands/how-to/implement-search.md +1001 -0
- package/claude/commands/how-to/install-plugins.md +352 -0
- package/claude/commands/how-to/manage-test-coverage.md +984 -0
- package/claude/commands/how-to/run-tests.md +400 -0
- package/claude/commands/how-to/set-app-languages.md +601 -0
- package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
- package/claude/commands/how-to/set-scheduled-actions.md +527 -0
- package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
- package/claude/commands/how-to/setup-authentication.md +388 -0
- package/claude/commands/how-to/setup-claude-code.md +440 -0
- package/claude/commands/how-to/setup-database.md +274 -0
- package/claude/commands/how-to/setup-email-providers.md +598 -0
- package/claude/commands/how-to/setup-mobile-dev.md +627 -0
- package/claude/commands/how-to/start.md +500 -0
- package/claude/commands/how-to/use-devtools.md +639 -0
- package/claude/commands/how-to/use-superadmin.md +622 -0
- package/claude/commands/session/README.md +193 -0
- package/claude/commands/session/block-create.md +190 -0
- package/claude/commands/session/block-list.md +203 -0
- package/claude/commands/session/block-update.md +192 -0
- package/claude/commands/session/block-validate.md +218 -0
- package/claude/commands/session/changelog.md +115 -0
- package/claude/commands/session/close.md +225 -0
- package/claude/commands/session/commit.md +174 -0
- package/claude/commands/session/db-entity.md +206 -0
- package/claude/commands/session/db-fix.md +212 -0
- package/claude/commands/session/db-sample.md +206 -0
- package/claude/commands/session/demo.md +178 -0
- package/claude/commands/session/doc-bdd.md +207 -0
- package/claude/commands/session/doc-feature.md +218 -0
- package/claude/commands/session/doc-read.md +225 -0
- package/claude/commands/session/execute.md +204 -0
- package/claude/commands/session/explain.md +202 -0
- package/claude/commands/session/fix-bug.md +210 -0
- package/claude/commands/session/fix-build.md +182 -0
- package/claude/commands/session/fix-test.md +189 -0
- package/claude/commands/session/pending.md +232 -0
- package/claude/commands/session/refine.md +188 -0
- package/claude/commands/session/resume.md +192 -0
- package/claude/commands/session/review.md +192 -0
- package/claude/commands/session/scope-change.md +181 -0
- package/claude/commands/session/start-blocks.md +347 -0
- package/claude/commands/session/start.md +604 -0
- package/claude/commands/session/status.md +169 -0
- package/claude/commands/session/test-fix.md +221 -0
- package/claude/commands/session/test-run.md +203 -0
- package/claude/commands/session/test-write.md +242 -0
- package/claude/commands/session/validate.md +162 -0
- package/claude/config/context.json +40 -0
- package/claude/config/github.json +69 -0
- package/claude/config/github.schema.json +106 -0
- package/claude/config/team.json +46 -0
- package/claude/config/team.schema.json +106 -0
- package/claude/config/workspace.json +43 -0
- package/claude/config/workspace.schema.json +75 -0
- package/claude/skills/README.md +228 -0
- package/claude/skills/accessibility/SKILL.md +573 -0
- package/claude/skills/api-bypass-layers/SKILL.md +550 -0
- package/claude/skills/asana-integration/SKILL.md +499 -0
- package/claude/skills/better-auth/SKILL.md +666 -0
- package/claude/skills/billing-subscriptions/SKILL.md +660 -0
- package/claude/skills/block-decision-matrix/SKILL.md +359 -0
- package/claude/skills/clickup-integration/SKILL.md +434 -0
- package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
- package/claude/skills/create-plugin/SKILL.md +425 -0
- package/claude/skills/create-theme/SKILL.md +331 -0
- package/claude/skills/cypress-api/SKILL.md +511 -0
- package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
- package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
- package/claude/skills/cypress-e2e/SKILL.md +526 -0
- package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
- package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
- package/claude/skills/cypress-selectors/SKILL.md +309 -0
- package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
- package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
- package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
- package/claude/skills/database-migrations/SKILL.md +335 -0
- package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
- package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
- package/claude/skills/design-system/SKILL.md +682 -0
- package/claude/skills/documentation/SKILL.md +540 -0
- package/claude/skills/entity-api/SKILL.md +482 -0
- package/claude/skills/entity-system/SKILL.md +635 -0
- package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
- package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
- package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
- package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
- package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
- package/claude/skills/github/SKILL.md +467 -0
- package/claude/skills/i18n-nextintl/SKILL.md +302 -0
- package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
- package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
- package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
- package/claude/skills/impact-analysis/SKILL.md +203 -0
- package/claude/skills/jest-unit/SKILL.md +306 -0
- package/claude/skills/jest-unit/references/component-testing.md +371 -0
- package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
- package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
- package/claude/skills/jira-integration/SKILL.md +539 -0
- package/claude/skills/media-library/SKILL.md +743 -0
- package/claude/skills/mock-analysis/SKILL.md +276 -0
- package/claude/skills/monorepo-architecture/SKILL.md +162 -0
- package/claude/skills/nextjs-api-development/SKILL.md +364 -0
- package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
- package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
- package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
- package/claude/skills/notion-integration/SKILL.md +641 -0
- package/claude/skills/npm-development-workflow/SKILL.md +480 -0
- package/claude/skills/page-builder-blocks/SKILL.md +530 -0
- package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
- package/claude/skills/permissions-system/SKILL.md +619 -0
- package/claude/skills/plugins/SKILL.md +340 -0
- package/claude/skills/plugins/references/plugin-templates.md +414 -0
- package/claude/skills/plugins/references/plugin-testing.md +353 -0
- package/claude/skills/plugins/references/plugin-types.md +198 -0
- package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
- package/claude/skills/pom-patterns/SKILL.md +452 -0
- package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
- package/claude/skills/rate-limiting/SKILL.md +342 -0
- package/claude/skills/react-best-practices/AGENTS.md +2410 -0
- package/claude/skills/react-best-practices/README.md +123 -0
- package/claude/skills/react-best-practices/SKILL.md +125 -0
- package/claude/skills/react-best-practices/metadata.json +15 -0
- package/claude/skills/react-best-practices/rules/_sections.md +46 -0
- package/claude/skills/react-best-practices/rules/_template.md +28 -0
- package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
- package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
- package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/claude/skills/react-patterns/SKILL.md +688 -0
- package/claude/skills/registry-system/SKILL.md +331 -0
- package/claude/skills/scheduled-actions/SKILL.md +671 -0
- package/claude/skills/scope-enforcement/SKILL.md +542 -0
- package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
- package/claude/skills/server-actions/SKILL.md +493 -0
- package/claude/skills/service-layer/SKILL.md +587 -0
- package/claude/skills/session-management/SKILL.md +266 -0
- package/claude/skills/session-management/scripts/create-session.py +166 -0
- package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
- package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
- package/claude/skills/session-management/scripts/session-archive.sh +87 -0
- package/claude/skills/session-management/scripts/session-close.sh +133 -0
- package/claude/skills/session-management/scripts/session-init.sh +225 -0
- package/claude/skills/session-management/scripts/session-list.sh +163 -0
- package/claude/skills/session-management/scripts/split-plan.sh +116 -0
- package/claude/skills/shadcn-components/SKILL.md +586 -0
- package/claude/skills/shadcn-theming/SKILL.md +446 -0
- package/claude/skills/suspense-loading/SKILL.md +280 -0
- package/claude/skills/tailwind-theming/SKILL.md +507 -0
- package/claude/skills/tanstack-query/SKILL.md +608 -0
- package/claude/skills/test-coverage/SKILL.md +239 -0
- package/claude/skills/web-design-guidelines/SKILL.md +39 -0
- package/claude/skills/zod-validation/SKILL.md +537 -0
- package/claude/templates/blocks/progress.md +86 -0
- package/claude/templates/iteration/changes.md +61 -0
- package/claude/templates/iteration/progress.md +55 -0
- package/claude/templates/log.md +31 -0
- package/claude/templates/story/context.md +77 -0
- package/claude/templates/story/pendings.md +37 -0
- package/claude/templates/story/plan.md +299 -0
- package/claude/templates/story/requirements.md +109 -0
- package/claude/templates/story/scope.json +10 -0
- package/claude/templates/story/tests.md +91 -0
- package/claude/templates/task/progress.md +58 -0
- package/claude/templates/task/requirements.md +54 -0
- package/claude/workflows/README.md +154 -0
- package/claude/workflows/blocks.md +614 -0
- package/claude/workflows/story.md +1207 -0
- package/claude/workflows/task.md +927 -0
- package/claude/workflows/tweak.md +527 -0
- package/cursor/.gitkeep +0 -0
- package/package.json +35 -0
- package/scripts/postinstall.mjs +198 -0
- package/scripts/setup.mjs +282 -0
- package/scripts/sync.mjs +209 -0
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
# /how-to:create-plugin
|
|
2
|
+
|
|
3
|
+
Interactive guide to create plugins for NextSpark.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Required Skills
|
|
8
|
+
|
|
9
|
+
Before executing, these skills provide deeper context:
|
|
10
|
+
- `.claude/skills/plugins/SKILL.md` - Plugin development patterns
|
|
11
|
+
- `.claude/skills/create-plugin/SKILL.md` - Plugin scaffolding guide
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Syntax
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
/how-to:create-plugin
|
|
19
|
+
/how-to:create-plugin [plugin-name]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Behavior
|
|
25
|
+
|
|
26
|
+
Guides the user through creating a plugin from scratch with proper structure, configuration, and NPM distribution rules.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Tutorial Structure
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
STEPS OVERVIEW (6 steps)
|
|
34
|
+
|
|
35
|
+
Step 1: Understanding Plugins
|
|
36
|
+
└── Plugin types and architecture
|
|
37
|
+
|
|
38
|
+
Step 2: Scaffold the Plugin
|
|
39
|
+
└── Create directory structure
|
|
40
|
+
|
|
41
|
+
Step 3: Configure plugin.config.ts
|
|
42
|
+
└── Define metadata and hooks
|
|
43
|
+
|
|
44
|
+
Step 4: Add Components and Hooks
|
|
45
|
+
└── React components and hooks
|
|
46
|
+
|
|
47
|
+
Step 5: Manage Dependencies (CRITICAL)
|
|
48
|
+
└── NPM distribution rules
|
|
49
|
+
|
|
50
|
+
Step 6: Test and Register
|
|
51
|
+
└── Verify plugin works
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Step 1: Understanding Plugins
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
60
|
+
📚 HOW TO: CREATE A PLUGIN
|
|
61
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
62
|
+
|
|
63
|
+
STEP 1 OF 6: Understanding Plugins
|
|
64
|
+
|
|
65
|
+
Plugins extend NextSpark with reusable functionality:
|
|
66
|
+
|
|
67
|
+
┌─────────────────────────────────────────────┐
|
|
68
|
+
│ PLUGIN TYPES │
|
|
69
|
+
│ ───────────────────────────────────────── │
|
|
70
|
+
│ │
|
|
71
|
+
│ utility Simple utilities and helpers │
|
|
72
|
+
│ service Full-stack with API, UI │
|
|
73
|
+
│ configuration Settings management │
|
|
74
|
+
│ │
|
|
75
|
+
└─────────────────────────────────────────────┘
|
|
76
|
+
|
|
77
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
78
|
+
|
|
79
|
+
📂 Plugin Structure:
|
|
80
|
+
|
|
81
|
+
plugins/{plugin-name}/
|
|
82
|
+
├── package.json # NPM package config
|
|
83
|
+
├── plugin.config.ts # Plugin configuration
|
|
84
|
+
├── README.md # Documentation
|
|
85
|
+
├── .env.example # Environment template
|
|
86
|
+
├── types/
|
|
87
|
+
│ └── {plugin}.types.ts # TypeScript types
|
|
88
|
+
├── lib/
|
|
89
|
+
│ ├── core.ts # Core logic
|
|
90
|
+
│ └── utils.ts # Utilities
|
|
91
|
+
├── hooks/
|
|
92
|
+
│ └── use{Plugin}.ts # React hooks
|
|
93
|
+
├── components/
|
|
94
|
+
│ └── {Component}.tsx # React components
|
|
95
|
+
├── providers/
|
|
96
|
+
│ └── {Plugin}Provider.tsx
|
|
97
|
+
├── api/ # API routes (optional)
|
|
98
|
+
│ └── {endpoint}/route.ts
|
|
99
|
+
└── docs/ # Documentation
|
|
100
|
+
└── 01-getting-started/
|
|
101
|
+
|
|
102
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
103
|
+
|
|
104
|
+
📋 Example Plugins:
|
|
105
|
+
|
|
106
|
+
• plugin-ai - AI assistant integration
|
|
107
|
+
• plugin-analytics - Usage analytics
|
|
108
|
+
• plugin-email - Email sending
|
|
109
|
+
• plugin-storage - File storage
|
|
110
|
+
• plugin-payments - Payment processing
|
|
111
|
+
|
|
112
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
113
|
+
|
|
114
|
+
What would you like to do?
|
|
115
|
+
|
|
116
|
+
[1] Continue to Step 2 (Scaffold)
|
|
117
|
+
[2] Show me plugin type examples
|
|
118
|
+
[3] Where do plugins live?
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Step 2: Scaffold the Plugin
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
127
|
+
STEP 2 OF 6: Scaffold the Plugin
|
|
128
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
129
|
+
|
|
130
|
+
Create the plugin directory structure:
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**📋 Create Directory Structure:**
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Create plugin directory
|
|
137
|
+
mkdir -p plugins/my-plugin/{types,lib,hooks,components,providers,docs}
|
|
138
|
+
|
|
139
|
+
# Create initial files
|
|
140
|
+
touch plugins/my-plugin/{package.json,plugin.config.ts,README.md,.env.example}
|
|
141
|
+
touch plugins/my-plugin/types/my-plugin.types.ts
|
|
142
|
+
touch plugins/my-plugin/lib/{core.ts,utils.ts,plugin-env.ts}
|
|
143
|
+
touch plugins/my-plugin/hooks/useMyPlugin.ts
|
|
144
|
+
touch plugins/my-plugin/components/MyPluginWidget.tsx
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**📋 package.json Template:**
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{
|
|
151
|
+
"name": "@nextsparkjs/plugin-my-plugin",
|
|
152
|
+
"version": "1.0.0",
|
|
153
|
+
"private": false,
|
|
154
|
+
"main": "./plugin.config.ts",
|
|
155
|
+
"types": "./types/index.ts",
|
|
156
|
+
"dependencies": {},
|
|
157
|
+
"peerDependencies": {
|
|
158
|
+
"@nextsparkjs/core": "workspace:*",
|
|
159
|
+
"next": "^15.0.0",
|
|
160
|
+
"react": "^19.0.0",
|
|
161
|
+
"react-dom": "^19.0.0",
|
|
162
|
+
"zod": "^4.0.0"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**📋 .env.example Template:**
|
|
168
|
+
|
|
169
|
+
```env
|
|
170
|
+
# ============================================
|
|
171
|
+
# MY PLUGIN ENVIRONMENT VARIABLES
|
|
172
|
+
# ============================================
|
|
173
|
+
#
|
|
174
|
+
# ONLY put MY_PLUGIN_* namespaced variables here
|
|
175
|
+
# Global variables belong in root .env ONLY
|
|
176
|
+
#
|
|
177
|
+
# ============================================
|
|
178
|
+
|
|
179
|
+
MY_PLUGIN_ENABLED=true
|
|
180
|
+
MY_PLUGIN_API_KEY=your-api-key-here
|
|
181
|
+
MY_PLUGIN_DEBUG=false
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
186
|
+
|
|
187
|
+
What would you like to do?
|
|
188
|
+
|
|
189
|
+
[1] Continue to Step 3 (Configuration)
|
|
190
|
+
[2] Use the scaffold script instead
|
|
191
|
+
[3] What's the naming convention?
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Step 3: Configure plugin.config.ts
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
200
|
+
STEP 3 OF 6: Configure plugin.config.ts
|
|
201
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
202
|
+
|
|
203
|
+
Define your plugin's configuration:
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**📋 plugin.config.ts Example:**
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
// plugins/my-plugin/plugin.config.ts
|
|
210
|
+
import type { PluginConfig } from '@/core/types/plugin'
|
|
211
|
+
import { z } from 'zod'
|
|
212
|
+
|
|
213
|
+
// Configuration schema
|
|
214
|
+
const MyPluginConfigSchema = z.object({
|
|
215
|
+
apiKey: z.string().min(1).describe('API Key'),
|
|
216
|
+
debug: z.boolean().default(false),
|
|
217
|
+
timeout: z.number().min(1000).max(30000).default(5000),
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
export const myPluginConfig: PluginConfig = {
|
|
221
|
+
// Required: Identity
|
|
222
|
+
name: 'my-plugin',
|
|
223
|
+
version: '1.0.0',
|
|
224
|
+
displayName: 'My Custom Plugin',
|
|
225
|
+
description: 'Add amazing functionality to your app',
|
|
226
|
+
enabled: true,
|
|
227
|
+
|
|
228
|
+
// Optional: Dependencies on other plugins
|
|
229
|
+
dependencies: [], // e.g., ['plugin-storage']
|
|
230
|
+
|
|
231
|
+
// Optional: Components exposed to the app
|
|
232
|
+
components: {
|
|
233
|
+
MyPluginWidget: undefined, // Lazy loaded
|
|
234
|
+
MyPluginSettings: undefined,
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
// Optional: Hooks and services
|
|
238
|
+
services: {
|
|
239
|
+
useMyPlugin: undefined,
|
|
240
|
+
useMyPluginMutation: undefined,
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
// Lifecycle hooks
|
|
244
|
+
hooks: {
|
|
245
|
+
// Called when plugin is first loaded
|
|
246
|
+
async onLoad() {
|
|
247
|
+
console.log('[My Plugin] Loading...')
|
|
248
|
+
|
|
249
|
+
// Validate environment variables
|
|
250
|
+
const apiKey = process.env.MY_PLUGIN_API_KEY
|
|
251
|
+
if (!apiKey) {
|
|
252
|
+
throw new Error('MY_PLUGIN_API_KEY is required')
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
// Called when plugin is activated
|
|
257
|
+
async onActivate() {
|
|
258
|
+
console.log('[My Plugin] Activated')
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
// Called when plugin is deactivated
|
|
262
|
+
async onDeactivate() {
|
|
263
|
+
console.log('[My Plugin] Deactivated')
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
// Called when plugin is unloaded
|
|
267
|
+
async onUnload() {
|
|
268
|
+
console.log('[My Plugin] Cleanup...')
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
// Configuration schema for validation
|
|
273
|
+
configSchema: MyPluginConfigSchema,
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export default myPluginConfig
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**📋 Lifecycle Hooks:**
|
|
280
|
+
|
|
281
|
+
- `onLoad` - Initialize resources, validate config
|
|
282
|
+
- `onActivate` - Start background tasks
|
|
283
|
+
- `onDeactivate` - Pause/stop tasks
|
|
284
|
+
- `onUnload` - Cleanup resources
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
288
|
+
|
|
289
|
+
What would you like to do?
|
|
290
|
+
|
|
291
|
+
[1] Continue to Step 4 (Components)
|
|
292
|
+
[2] What config options are available?
|
|
293
|
+
[3] How do I validate environment vars?
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Step 4: Add Components and Hooks
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
302
|
+
STEP 4 OF 6: Add Components and Hooks
|
|
303
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**📋 Create a React Hook:**
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
// plugins/my-plugin/hooks/useMyPlugin.ts
|
|
310
|
+
'use client'
|
|
311
|
+
|
|
312
|
+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
|
313
|
+
import type { MyPluginData, MyPluginInput } from '../types/my-plugin.types'
|
|
314
|
+
|
|
315
|
+
const QUERY_KEY = ['my-plugin'] as const
|
|
316
|
+
|
|
317
|
+
export function useMyPlugin() {
|
|
318
|
+
return useQuery({
|
|
319
|
+
queryKey: QUERY_KEY,
|
|
320
|
+
queryFn: async (): Promise<MyPluginData> => {
|
|
321
|
+
const response = await fetch('/api/plugin/my-plugin/data')
|
|
322
|
+
if (!response.ok) {
|
|
323
|
+
throw new Error('Failed to fetch plugin data')
|
|
324
|
+
}
|
|
325
|
+
return response.json()
|
|
326
|
+
},
|
|
327
|
+
})
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export function useMyPluginMutation() {
|
|
331
|
+
const queryClient = useQueryClient()
|
|
332
|
+
|
|
333
|
+
return useMutation({
|
|
334
|
+
mutationFn: async (input: MyPluginInput) => {
|
|
335
|
+
const response = await fetch('/api/plugin/my-plugin/process', {
|
|
336
|
+
method: 'POST',
|
|
337
|
+
headers: { 'Content-Type': 'application/json' },
|
|
338
|
+
body: JSON.stringify(input),
|
|
339
|
+
})
|
|
340
|
+
if (!response.ok) {
|
|
341
|
+
throw new Error('Failed to process')
|
|
342
|
+
}
|
|
343
|
+
return response.json()
|
|
344
|
+
},
|
|
345
|
+
onSuccess: () => {
|
|
346
|
+
queryClient.invalidateQueries({ queryKey: QUERY_KEY })
|
|
347
|
+
},
|
|
348
|
+
})
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**📋 Create a React Component:**
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
// plugins/my-plugin/components/MyPluginWidget.tsx
|
|
356
|
+
'use client'
|
|
357
|
+
|
|
358
|
+
import { useMyPlugin, useMyPluginMutation } from '../hooks/useMyPlugin'
|
|
359
|
+
import { Card, CardHeader, CardContent } from '@/core/components/ui/card'
|
|
360
|
+
import { Button } from '@/core/components/ui/button'
|
|
361
|
+
import { Loader2 } from 'lucide-react'
|
|
362
|
+
|
|
363
|
+
interface MyPluginWidgetProps {
|
|
364
|
+
readonly title?: string
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function MyPluginWidget({ title = 'My Plugin' }: MyPluginWidgetProps) {
|
|
368
|
+
const { data, isLoading, error } = useMyPlugin()
|
|
369
|
+
const mutation = useMyPluginMutation()
|
|
370
|
+
|
|
371
|
+
const handleAction = () => {
|
|
372
|
+
mutation.mutate({ action: 'process' })
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return (
|
|
376
|
+
<Card data-cy="my-plugin-widget">
|
|
377
|
+
<CardHeader>
|
|
378
|
+
<h3 className="text-lg font-semibold">{title}</h3>
|
|
379
|
+
</CardHeader>
|
|
380
|
+
<CardContent>
|
|
381
|
+
{isLoading && (
|
|
382
|
+
<div data-cy="my-plugin-loading" className="flex items-center gap-2">
|
|
383
|
+
<Loader2 className="animate-spin" />
|
|
384
|
+
<span>Loading...</span>
|
|
385
|
+
</div>
|
|
386
|
+
)}
|
|
387
|
+
|
|
388
|
+
{error && (
|
|
389
|
+
<div data-cy="my-plugin-error" className="text-destructive">
|
|
390
|
+
{error.message}
|
|
391
|
+
</div>
|
|
392
|
+
)}
|
|
393
|
+
|
|
394
|
+
{data && (
|
|
395
|
+
<div data-cy="my-plugin-content">
|
|
396
|
+
<p>{data.message}</p>
|
|
397
|
+
<Button
|
|
398
|
+
data-cy="my-plugin-action-btn"
|
|
399
|
+
onClick={handleAction}
|
|
400
|
+
disabled={mutation.isPending}
|
|
401
|
+
>
|
|
402
|
+
{mutation.isPending ? 'Processing...' : 'Run Action'}
|
|
403
|
+
</Button>
|
|
404
|
+
</div>
|
|
405
|
+
)}
|
|
406
|
+
</CardContent>
|
|
407
|
+
</Card>
|
|
408
|
+
)
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export default MyPluginWidget
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**📋 Types Definition:**
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
// plugins/my-plugin/types/my-plugin.types.ts
|
|
418
|
+
export interface MyPluginData {
|
|
419
|
+
readonly id: string
|
|
420
|
+
readonly message: string
|
|
421
|
+
readonly status: 'active' | 'inactive'
|
|
422
|
+
readonly lastUpdated: string
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export interface MyPluginInput {
|
|
426
|
+
readonly action: 'process' | 'reset'
|
|
427
|
+
readonly options?: MyPluginOptions
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export interface MyPluginOptions {
|
|
431
|
+
readonly timeout?: number
|
|
432
|
+
readonly force?: boolean
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
```
|
|
437
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
438
|
+
|
|
439
|
+
What would you like to do?
|
|
440
|
+
|
|
441
|
+
[1] Continue to Step 5 (Dependencies - CRITICAL)
|
|
442
|
+
[2] How do I add API endpoints?
|
|
443
|
+
[3] Show me provider pattern
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## Step 5: Manage Dependencies (CRITICAL)
|
|
449
|
+
|
|
450
|
+
```
|
|
451
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
452
|
+
STEP 5 OF 6: Manage Dependencies (CRITICAL)
|
|
453
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
454
|
+
|
|
455
|
+
⚠️ THIS IS MANDATORY FOR NPM DISTRIBUTION
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
**📋 The Golden Rule:**
|
|
459
|
+
|
|
460
|
+
If @nextsparkjs/core has a dependency,
|
|
461
|
+
plugins MUST declare it as peerDependency,
|
|
462
|
+
NEVER as dependency.
|
|
463
|
+
|
|
464
|
+
**❌ WRONG - Duplicated Dependencies:**
|
|
465
|
+
|
|
466
|
+
```
|
|
467
|
+
node_modules/
|
|
468
|
+
├── @nextsparkjs/core/
|
|
469
|
+
│ └── node_modules/zod@4.1.5 ← Instance 1
|
|
470
|
+
├── @nextsparkjs/plugin-ai/
|
|
471
|
+
│ └── node_modules/zod@4.1.5 ← Instance 2
|
|
472
|
+
└── @nextsparkjs/plugin-email/
|
|
473
|
+
└── node_modules/zod@3.23.0 ← Instance 3!
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Result: Type conflicts, instanceof failures, bloated bundle
|
|
477
|
+
|
|
478
|
+
**✅ CORRECT - Single Instance:**
|
|
479
|
+
|
|
480
|
+
```
|
|
481
|
+
node_modules/
|
|
482
|
+
├── zod@4.1.5 ← ONE instance (hoisted)
|
|
483
|
+
├── @nextsparkjs/core/ (provides zod)
|
|
484
|
+
├── @nextsparkjs/plugin-ai/ (uses host's zod)
|
|
485
|
+
└── @nextsparkjs/plugin-email/ (uses host's zod)
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
Result: No conflicts, optimized bundle
|
|
489
|
+
|
|
490
|
+
**📋 Dependencies Core Already Provides (NEVER duplicate):**
|
|
491
|
+
|
|
492
|
+
```json
|
|
493
|
+
{
|
|
494
|
+
"zod": "^4.1.5",
|
|
495
|
+
"@tanstack/react-query": "^5.85.0",
|
|
496
|
+
"lucide-react": "^0.539.0",
|
|
497
|
+
"class-variance-authority": "^0.7.1",
|
|
498
|
+
"clsx": "^2.1.1",
|
|
499
|
+
"tailwind-merge": "^3.3.1",
|
|
500
|
+
"date-fns": "^4.1.0",
|
|
501
|
+
"react-hook-form": "^7.62.0",
|
|
502
|
+
"sonner": "^2.0.7",
|
|
503
|
+
"next-intl": "^4.3.4",
|
|
504
|
+
"next-themes": "^0.4.6",
|
|
505
|
+
"uuid": "^13.0.0",
|
|
506
|
+
"better-auth": "^1.3.5"
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
**📋 Correct package.json:**
|
|
511
|
+
|
|
512
|
+
```json
|
|
513
|
+
{
|
|
514
|
+
"name": "@nextsparkjs/plugin-my-plugin",
|
|
515
|
+
"dependencies": {
|
|
516
|
+
"@some-external-lib/sdk": "^1.0.0"
|
|
517
|
+
},
|
|
518
|
+
"peerDependencies": {
|
|
519
|
+
"@nextsparkjs/core": "workspace:*",
|
|
520
|
+
"next": "^15.0.0",
|
|
521
|
+
"react": "^19.0.0",
|
|
522
|
+
"react-dom": "^19.0.0",
|
|
523
|
+
"zod": "^4.0.0"
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
**📋 Verify No Duplicates:**
|
|
529
|
+
|
|
530
|
+
```bash
|
|
531
|
+
# Check for duplicate packages
|
|
532
|
+
pnpm ls zod
|
|
533
|
+
# Should show ONE version only
|
|
534
|
+
|
|
535
|
+
# Check workspace structure
|
|
536
|
+
pnpm ls --depth=0
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
```
|
|
540
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
541
|
+
|
|
542
|
+
What would you like to do?
|
|
543
|
+
|
|
544
|
+
[1] Continue to Step 6 (Test & Register)
|
|
545
|
+
[2] How do I check if core has a dependency?
|
|
546
|
+
[3] My plugin needs a new dependency
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Step 6: Test and Register
|
|
552
|
+
|
|
553
|
+
```
|
|
554
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
555
|
+
STEP 6 OF 6: Test and Register
|
|
556
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**1️⃣ Add Plugin to Workspace:**
|
|
560
|
+
|
|
561
|
+
```yaml
|
|
562
|
+
# pnpm-workspace.yaml
|
|
563
|
+
packages:
|
|
564
|
+
- 'core'
|
|
565
|
+
- 'plugins/*'
|
|
566
|
+
- 'contents/themes/*'
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
**2️⃣ Install Dependencies:**
|
|
570
|
+
|
|
571
|
+
```bash
|
|
572
|
+
pnpm install
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
**3️⃣ Rebuild Plugin Registry:**
|
|
576
|
+
|
|
577
|
+
```bash
|
|
578
|
+
node core/scripts/build/registry.mjs
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
**4️⃣ Test in Development:**
|
|
582
|
+
|
|
583
|
+
```bash
|
|
584
|
+
pnpm dev
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
Use your plugin components:
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
// In your theme or app
|
|
591
|
+
import { MyPluginWidget } from '@nextsparkjs/plugin-my-plugin'
|
|
592
|
+
|
|
593
|
+
export function Dashboard() {
|
|
594
|
+
return (
|
|
595
|
+
<div>
|
|
596
|
+
<MyPluginWidget title="My Plugin" />
|
|
597
|
+
</div>
|
|
598
|
+
)
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
**5️⃣ Write Tests:**
|
|
603
|
+
|
|
604
|
+
```typescript
|
|
605
|
+
// plugins/my-plugin/__tests__/my-plugin.test.ts
|
|
606
|
+
import { myPluginConfig } from '../plugin.config'
|
|
607
|
+
|
|
608
|
+
describe('My Plugin', () => {
|
|
609
|
+
it('should have valid configuration', () => {
|
|
610
|
+
expect(myPluginConfig.name).toBe('my-plugin')
|
|
611
|
+
expect(myPluginConfig.version).toBeDefined()
|
|
612
|
+
})
|
|
613
|
+
|
|
614
|
+
it('should validate environment on load', async () => {
|
|
615
|
+
process.env.MY_PLUGIN_API_KEY = 'test-key'
|
|
616
|
+
await expect(myPluginConfig.hooks?.onLoad?.()).resolves.not.toThrow()
|
|
617
|
+
})
|
|
618
|
+
})
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
**6️⃣ Build and Verify:**
|
|
622
|
+
|
|
623
|
+
```bash
|
|
624
|
+
pnpm build
|
|
625
|
+
# Should complete without errors
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
```
|
|
629
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
630
|
+
|
|
631
|
+
✅ TUTORIAL STORY!
|
|
632
|
+
|
|
633
|
+
You've created a plugin with:
|
|
634
|
+
• Proper directory structure
|
|
635
|
+
• Configuration with lifecycle hooks
|
|
636
|
+
• React components and hooks
|
|
637
|
+
• Correct NPM dependencies
|
|
638
|
+
• Registry integration
|
|
639
|
+
|
|
640
|
+
📚 Related tutorials:
|
|
641
|
+
• /how-to:install-plugins - Install existing plugins
|
|
642
|
+
• /how-to:create-api - Add API to your plugin
|
|
643
|
+
|
|
644
|
+
🔙 Back to menu: /how-to:start
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
---
|
|
648
|
+
|
|
649
|
+
## Related Commands
|
|
650
|
+
|
|
651
|
+
| Command | Action |
|
|
652
|
+
|---------|--------|
|
|
653
|
+
| `/how-to:install-plugins` | Install plugins |
|
|
654
|
+
| `/session:block:create` | Create blocks (similar pattern) |
|