@jay-framework/jay-stack-cli 0.20.0 → 0.22.0

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.
@@ -71,6 +71,20 @@ Use `<jay:contract-name>` tags with props:
71
71
  </jay:product-widget>
72
72
  ```
73
73
 
74
+ **With bindings from page data** (props from keyed components or page ViewState):
75
+
76
+ ```html
77
+ <!-- p is a keyed headless component providing product data -->
78
+ <jay:category-products categorySlug="{p.categorySlug}" limit="4">
79
+ <div class="product-card">
80
+ <h3>{name}</h3>
81
+ <span>{price}</span>
82
+ </div>
83
+ </jay:category-products>
84
+ ```
85
+
86
+ Use `{path}` syntax to bind props to values from the page's ViewState. The binding is resolved at render time — works with both slow and fast phase data.
87
+
74
88
  **With forEach** (dynamic props from parent data):
75
89
 
76
90
  ```html
@@ -85,6 +99,34 @@ Use `<jay:contract-name>` tags with props:
85
99
 
86
100
  Inside `<jay:...>`, bindings resolve to **that instance's** contract tags (not the parent).
87
101
 
102
+ ### Prop binding summary
103
+
104
+ | Syntax | Resolves to | Example |
105
+ | --------------------------------- | -------------------- | ------------------------- |
106
+ | `prop="literal"` | Literal string value | `productId="prod-1"` |
107
+ | `prop="{field}"` | Page ViewState field | `slug="{p.categorySlug}"` |
108
+ | `prop="{field}"` (inside forEach) | ForEach item field | `productId="{_id}"` |
109
+
110
+ ### Prop phase constraints
111
+
112
+ Contract props can declare a `phase` (defaults to `slow`). The binding source must be available at that phase:
113
+
114
+ - A **slow** prop (default) must bind to a literal, a route param, or a slow-phase tag
115
+ - A **fast** prop can also bind to fast-phase tags
116
+
117
+ If a slow prop binds to a fast-phase field, `jay-stack validate` flags an error — the component's slow render would receive an empty value.
118
+
119
+ ```yaml
120
+ # In the component's contract:
121
+ props:
122
+ - name: categorySlug
123
+ type: string
124
+ phase: slow # Must be available at build time
125
+ - name: filter
126
+ type: string
127
+ phase: fast # Only needs to be available at request time
128
+ ```
129
+
88
130
  ## Headfull Components
89
131
 
90
132
  In Jay Stack, headfull components are full-stack. They must have a `.jay-contract` file and are created using `makeJayStackComponent` in their `.ts` file. They support server rendering (slow/fast/interactive phases) and must include a `contract` attribute in the import.
@@ -51,23 +51,22 @@ src/pages/products/
51
51
 
52
52
  The static `ceramic-flower-vase/` route takes priority over `[slug]/` for that URL, but all other product URLs still use the dynamic route.
53
53
 
54
- ### Static Override Params (`jay-params`)
54
+ ### Static Override Params and Headless Component Props
55
55
 
56
- Static override routes often use the same contract as the dynamic route they override. Since the static route has no dynamic directory segment, the params must be declared explicitly using `<script type="application/jay-params">`:
56
+ Static override routes use the same headless component as the dynamic route they override. Since the static route has no dynamic directory segment, the params must be declared in the headless component's YAML body:
57
57
 
58
58
  ```html
59
59
  <!-- src/pages/products/ceramic-flower-vase/page.jay-html -->
60
60
  <html>
61
61
  <head>
62
- <script type="application/jay-params">
63
- slug: ceramic-flower-vase
64
- </script>
65
62
  <script
66
63
  type="application/jay-headless"
67
64
  plugin="wix-stores"
68
65
  contract="product-page"
69
66
  key="product"
70
- ></script>
67
+ >
68
+ slug: ceramic-flower-vase
69
+ </script>
71
70
  </head>
72
71
  <body>
73
72
  <h1>{product.productName}</h1>
@@ -75,7 +74,20 @@ Static override routes often use the same contract as the dynamic route they ove
75
74
  </html>
76
75
  ```
77
76
 
78
- The script body is YAML. The declared params are passed to the component as if extracted from a dynamic URL segment. Without this, the component would receive no param values.
77
+ The script body is YAML. Values are passed to the component as props alongside route params. This same mechanism is used for any per-component configuration:
78
+
79
+ ```html
80
+ <script
81
+ type="application/jay-headless"
82
+ plugin="@jay-framework/markdown"
83
+ contract="markdown-pages"
84
+ key="post"
85
+ >
86
+ contentDir: ./content
87
+ </script>
88
+ ```
89
+
90
+ > **Note:** `<script type="application/jay-params">` is deprecated. Move param values into the headless component's script tag body.
79
91
 
80
92
  ## Page Files
81
93
 
@@ -51,23 +51,22 @@ src/pages/products/
51
51
 
52
52
  The static `ceramic-flower-vase/` route takes priority over `[slug]/` for that URL, but all other product URLs still use the dynamic route.
53
53
 
54
- ### Static Override Params (`jay-params`)
54
+ ### Static Override Params and Headless Component Props
55
55
 
56
- Static override routes often use the same contract as the dynamic route they override. Since the static route has no dynamic directory segment, the params must be declared explicitly using `<script type="application/jay-params">`:
56
+ Static override routes use the same headless component as the dynamic route they override. Since the static route has no dynamic directory segment, the params must be declared in the headless component's YAML body:
57
57
 
58
58
  ```html
59
59
  <!-- src/pages/products/ceramic-flower-vase/page.jay-html -->
60
60
  <html>
61
61
  <head>
62
- <script type="application/jay-params">
63
- slug: ceramic-flower-vase
64
- </script>
65
62
  <script
66
63
  type="application/jay-headless"
67
64
  plugin="wix-stores"
68
65
  contract="product-page"
69
66
  key="product"
70
- ></script>
67
+ >
68
+ slug: ceramic-flower-vase
69
+ </script>
71
70
  </head>
72
71
  <body>
73
72
  <h1>{product.productName}</h1>
@@ -75,7 +74,20 @@ Static override routes often use the same contract as the dynamic route they ove
75
74
  </html>
76
75
  ```
77
76
 
78
- The script body is YAML. The declared params are passed to the component as if extracted from a dynamic URL segment. Without this, the component would receive no param values.
77
+ The script body is YAML. Values are passed to the component as props alongside route params. This same mechanism is used for any per-component configuration:
78
+
79
+ ```html
80
+ <script
81
+ type="application/jay-headless"
82
+ plugin="@jay-framework/markdown"
83
+ contract="markdown-pages"
84
+ key="post"
85
+ >
86
+ contentDir: ./content
87
+ </script>
88
+ ```
89
+
90
+ > **Note:** `<script type="application/jay-params">` is deprecated. Move param values into the headless component's script tag body.
79
91
 
80
92
  ## Page Files
81
93
 
@@ -12,9 +12,25 @@ A plugin provides headless components (data + interactions, no UI) that project
12
12
  2. **Implement components** matching the contracts
13
13
  3. **Define actions** with `.jay-action` metadata
14
14
  4. **Optionally add routes** — pages for admin tools and dashboards
15
- 5. **Set up `plugin.yaml`**list contracts, actions, services, contexts, routes
16
- 6. **Configure build** — dual entry points (server + client), vite.config.ts, package.json exports
17
- 7. **Validate** with `jay-stack validate-plugin`
15
+ 5. **Optionally add validators**custom jay-html validation rules
16
+ 6. **Optionally add setup/agentkit handlers** — config templating, add-menu generation
17
+ 7. **Set up `plugin.yaml`** — list contracts, actions, services, contexts, routes, validators, setup, agentkit
18
+ 8. **Configure build** — dual entry points (server + client), vite.config.ts, package.json exports
19
+ 9. **Validate** with `jay-stack validate-plugin`
20
+
21
+ ## Plugin Lifecycle — CLI Commands
22
+
23
+ The plugin participates in four CLI commands, each running different hooks:
24
+
25
+ | Command | When | What runs from your plugin |
26
+ | --------------------------- | ------------------ | ---------------------------------------------------------------------------------- |
27
+ | `jay-stack validate-plugin` | Plugin development | Checks plugin.yaml structure, contracts, exports, handler references |
28
+ | `jay-stack setup <plugin>` | Project setup | `setup` — creates config files, validates credentials |
29
+ | `jay-stack agent-kit` | Before development | `agentkit` — generates add-menu items, reference data, skills, thumbnails |
30
+ | `jay-stack validate` | During development | `validators[].handler` — runs your validation rules against project jay-html files |
31
+
32
+ **`validate-plugin`** validates YOUR plugin's structure. Run it during plugin development.
33
+ **`validate`** runs your plugin's validators against a PROJECT that uses your plugin. Run it from the project.
18
34
 
19
35
  ## Guides
20
36
 
@@ -36,6 +52,7 @@ A plugin provides headless components (data + interactions, no UI) that project
36
52
  | [seo-guide.md](seo-guide.md) | SEO head tags: title, meta, OG, canonical via phaseOutput |
37
53
  | [commands-guide.md](commands-guide.md) | makeCliCommand, .jay-command files, CONSOLE_CONTEXT, jay-stack run |
38
54
  | [validation.md](validation.md) | jay-stack validate-plugin, writing custom jay-html validators |
55
+ | [setup-guide.md](setup-guide.md) | Setup handlers, references handlers, add-menu generation |
39
56
  | [dev-server-service.md](dev-server-service.md) | Dev server service API: routes, params, freeze management |
40
57
  | `../references/<plugin>/` | Plugin reference data |
41
58
 
@@ -63,15 +63,12 @@ commands:
63
63
 
64
64
  validators:
65
65
  - name: media-optimization
66
- handler: ./validators/media-validator
66
+ handler: validateMediaOptimization
67
67
  description: Ensures media URLs use resize parameters
68
68
 
69
- setup:
70
- handler: setup-handler
71
- references: references-handler
72
- configTemplate:
73
- - source: templates/config.yaml
74
- target: my-plugin.yaml
69
+ setup: setup-handler
70
+ agentkit: agentkit-handler
71
+ description: Configure My Plugin
75
72
  ```
76
73
 
77
74
  ### Contract Entry Fields
@@ -193,16 +190,26 @@ Commands are CLI operations run via `jay-stack run`. Use `makeCliCommand()` to c
193
190
  ### Validator Entry Fields
194
191
 
195
192
  - `name` — Validator name (shown in validation output as `plugin-name/validator-name`)
196
- - `handler` — Relative path to the validator module (must export a `validate` function)
193
+ - `handler` — Export name (NPM plugins) or relative path (local plugins) to the validator function
197
194
  - `description` — (optional) What this validator checks
198
195
 
199
- Validators run during `jay-stack validate` against every parsed jay-html file. The handler module exports a `validate` function that receives a `JayHtmlValidationContext` and returns an array of findings. See [validation.md](validation.md) for implementation details.
196
+ **NPM plugins:** `handler` is the export name from the package entry point (e.g., `validateMediaOptimization`). The function must be exported from `lib/index.ts`.
197
+ **Local plugins:** `handler` is a relative path to the module (e.g., `./validators/media-validator`). The module must export a `validate` function.
200
198
 
201
- ### Setup Fields
199
+ Validators run during `jay-stack validate` against every parsed jay-html file in the project. See [validation.md](validation.md) for implementation details.
202
200
 
203
- - `handler` — Setup handler for `jay-stack setup` (handles config, credentials)
204
- - `references` — Reference generator for `jay-stack agent-kit` (generates discovery data)
205
- - `configTemplate` — Config file templates to copy during setup
201
+ ### Setup and agent-kit fields
202
+
203
+ - `setup` — Export name (NPM) or relative path (local) for `jay-stack setup <plugin>`. Creates config files, validates credentials and services.
204
+ - `agentkit` — Export name (NPM) or relative path (local) for `jay-stack agent-kit`. Generates discovery data: add-menu catalogs, reference files, skills, thumbnails.
205
+ - `description` — (optional, top-level) Human-readable description of what setup validates
206
+
207
+ **NPM plugins:** `setup` and `agentkit` are export names from the package entry point.
208
+ **Local plugins:** relative paths to the handler modules.
209
+
210
+ `jay-stack validate-plugin` checks that declared handlers exist and are correctly exported.
211
+
212
+ See [setup-guide.md](setup-guide.md) for implementation details.
206
213
 
207
214
  ## Package Layout
208
215
 
@@ -0,0 +1,180 @@
1
+ # Plugin Setup & Agent-Kit
2
+
3
+ Plugins can provide two hooks for project configuration and AI agent discovery:
4
+
5
+ - **Setup handler** (`setup` in `plugin.yaml`) — runs during `jay-stack setup <plugin>`. Creates config files, validates credentials.
6
+ - **Agent-kit handler** (`agentkit` in `plugin.yaml`) — runs during `jay-stack agent-kit`. Generates discovery data (add-menu catalogs, reference files, skills, thumbnails) using live services when needed.
7
+
8
+ ## When Each Runs
9
+
10
+ ```
11
+ jay-stack setup <plugin> → setup handler (config + credentials)
12
+ jay-stack agent-kit → agentkit handler (after contract materialization)
13
+ ```
14
+
15
+ Setup runs when a project configures the plugin. Agent-kit runs whenever the developer regenerates the agent kit — it can use live services to produce fresh data.
16
+
17
+ ## Declaring in plugin.yaml
18
+
19
+ ```yaml
20
+ name: my-plugin
21
+ setup: setupMyPlugin # export name (NPM) or ./path (local) — optional
22
+ agentkit: generateMyAgentKit # export name (NPM) or ./path (local) — optional
23
+ description: Validate credentials and install config # optional, top-level
24
+ ```
25
+
26
+ **NPM plugins:** `setup` and `agentkit` are export names from the package entry point (`lib/index.ts`).
27
+ **Local plugins:** relative paths to handler modules (e.g. `agentkit: ./agentkit` — export `agentkit` or `default` from that module).
28
+
29
+ `jay-stack validate-plugin` checks that declared handlers exist and are correctly exported.
30
+
31
+ ## Writing a Setup Handler
32
+
33
+ The setup handler creates config files and validates services. It receives a `PluginSetupContext` and returns a `PluginSetupResult`.
34
+
35
+ **Do not** write add-menu catalogs in setup — use the agent-kit handler.
36
+
37
+ ```typescript
38
+ import type { PluginSetupContext, PluginSetupResult } from '@jay-framework/stack-server-runtime';
39
+ import fs from 'node:fs';
40
+ import path from 'node:path';
41
+
42
+ export async function setupMyPlugin(ctx: PluginSetupContext): Promise<PluginSetupResult> {
43
+ if (ctx.initError) {
44
+ return { status: 'error', message: `Init failed: ${ctx.initError.message}` };
45
+ }
46
+
47
+ const configCreated: string[] = [];
48
+ const configPath = path.join(ctx.configDir, '.my-plugin.yaml');
49
+
50
+ if (!fs.existsSync(configPath) || ctx.force) {
51
+ fs.mkdirSync(ctx.configDir, { recursive: true });
52
+ fs.writeFileSync(configPath, '# My Plugin config\n', 'utf-8');
53
+ configCreated.push('config/.my-plugin.yaml');
54
+ }
55
+
56
+ return {
57
+ status: 'configured',
58
+ configCreated,
59
+ message:
60
+ configCreated.length > 0
61
+ ? 'My Plugin config installed.'
62
+ : 'My Plugin config already present (use --force to rewrite).',
63
+ };
64
+ }
65
+ ```
66
+
67
+ ### PluginSetupContext
68
+
69
+ | Field | Type | Description |
70
+ | ------------- | --------- | ----------------------------------------------------------------- |
71
+ | `pluginName` | `string` | Plugin name from plugin.yaml |
72
+ | `projectRoot` | `string` | Absolute project root path |
73
+ | `configDir` | `string` | Config directory (from `.jay` configBase, defaults to `./config`) |
74
+ | `services` | `Map` | Registered services (may be empty if init failed) |
75
+ | `initError` | `Error?` | Present if plugin init failed — check this before using services |
76
+ | `force` | `boolean` | Whether `--force` flag was passed |
77
+
78
+ ### PluginSetupResult
79
+
80
+ | Field | Type | Description |
81
+ | --------------- | ------------------------------------------- | ----------------------------------------------- |
82
+ | `status` | `'configured' \| 'needs-config' \| 'error'` | Overall result |
83
+ | `configCreated` | `string[]?` | Config files created (relative to project root) |
84
+ | `message` | `string?` | Human-readable status message |
85
+
86
+ ## Writing an Agent-Kit Handler
87
+
88
+ The agent-kit handler generates discovery data at agent-kit time: add-menu catalogs, `agent-kit/references/<plugin>/` files, skills, thumbnails. It can use live services (database queries, API calls) to produce dynamic content.
89
+
90
+ ```typescript
91
+ import type {
92
+ PluginAgentKitContext,
93
+ PluginAgentKitResult,
94
+ } from '@jay-framework/stack-server-runtime';
95
+ import fs from 'node:fs';
96
+ import path from 'node:path';
97
+ import yaml from 'yaml';
98
+
99
+ export async function generateMyAgentKit(
100
+ ctx: PluginAgentKitContext,
101
+ ): Promise<PluginAgentKitResult> {
102
+ if (ctx.initError) {
103
+ return { agentKitCreated: [], message: `Skipped: ${ctx.initError.message}` };
104
+ }
105
+
106
+ const outputPath = path.join(ctx.projectRoot, 'agent-kit/aiditor/add-menu/my-plugin.yaml');
107
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
108
+
109
+ const items = [
110
+ { id: 'my-plugin:feature-1', title: 'Feature 1', category: 'My Plugin', prompt: '...' },
111
+ ];
112
+ fs.writeFileSync(outputPath, yaml.stringify({ items }), 'utf-8');
113
+
114
+ return {
115
+ agentKitCreated: ['agent-kit/aiditor/add-menu/my-plugin.yaml'],
116
+ message: `Generated ${items.length} add-menu items`,
117
+ };
118
+ }
119
+ ```
120
+
121
+ ### PluginAgentKitContext
122
+
123
+ | Field | Type | Description |
124
+ | --------------- | --------- | --------------------------------------------------------------- |
125
+ | `pluginName` | `string` | Plugin name from plugin.yaml |
126
+ | `projectRoot` | `string` | Absolute project root path |
127
+ | `referencesDir` | `string` | Directory for reference data (`agent-kit/references/<plugin>/`) |
128
+ | `services` | `Map` | Registered services |
129
+ | `initError` | `Error?` | Present if plugin init failed |
130
+ | `force` | `boolean` | Whether `--force` flag was passed |
131
+
132
+ ### PluginAgentKitResult
133
+
134
+ | Field | Type | Description |
135
+ | ----------------- | ---------- | ---------------------------------------- |
136
+ | `agentKitCreated` | `string[]` | Files created (relative to project root) |
137
+ | `message` | `string?` | Human-readable status message |
138
+
139
+ ## Setup vs Agent-Kit — When to Use Which
140
+
141
+ | Use case | Hook | Why |
142
+ | -------------------------------------------------------------------- | ---------- | ----------------------------------------------------------- |
143
+ | Copy static add-menu template, skills, thumbnails | `agentkit` | Discovery data — regenerated on `jay-stack agent-kit` |
144
+ | Generate data from live services (product catalogs, CMS schemas) | `agentkit` | Needs services initialized; refreshed on each agent-kit run |
145
+ | Validate credentials / API keys | `setup` | Part of initial project configuration |
146
+ | Write AIditor add-menu from project-specific data (DESIGN.md tokens) | `agentkit` | Data comes from project files at agent-kit time |
147
+
148
+ ## AIditor Add-Menu Items
149
+
150
+ The agent-kit handler writes to `agent-kit/aiditor/add-menu/<plugin-name>.yaml`. The AIditor discovers and loads all YAML files in this directory.
151
+
152
+ Each item:
153
+
154
+ ```yaml
155
+ items:
156
+ - id: my-plugin:feature-name # unique ID
157
+ title: Feature Name # shown in the add menu
158
+ category: My Plugin # grouping
159
+ subCategory: Components # sub-grouping
160
+ pluginName: my-plugin # optional: plugin attribution
161
+ packageName: '@my-org/my-plugin' # optional: npm package name
162
+ prompt: | # instructions for the AI agent
163
+ Use headless component @my-org/my-plugin / contract feature-name.
164
+ Read agent-kit/designer/feature-name.md for usage guide.
165
+ ```
166
+
167
+ See `agent-kit/plugin/aiditor-add-menu.md` (installed by `jay-stack setup aiditor`) for the full contributor guide.
168
+
169
+ ## Exporting Handlers
170
+
171
+ For NPM plugins, export handlers from the package entry point:
172
+
173
+ ```typescript
174
+ // lib/index.ts
175
+ export { setupMyPlugin } from './setup.js';
176
+ export { generateMyAgentKit } from './agentkit.js';
177
+ // ... other exports (components, actions, services)
178
+ ```
179
+
180
+ For local plugins, use relative paths in `plugin.yaml` and export `agentkit` or `default` from the handler module.
@@ -1,5 +1,12 @@
1
1
  # Plugin Validation
2
2
 
3
+ Two separate validation commands:
4
+
5
+ - **`jay-stack validate-plugin`** — validates your plugin's own structure (run during plugin development)
6
+ - **`jay-stack validate`** — validates a project's jay-html files, including running your plugin's custom validators (run from a project that uses your plugin)
7
+
8
+ ## validate-plugin
9
+
3
10
  Run `jay-stack validate-plugin` to check your plugin for errors.
4
11
 
5
12
  ## Usage
@@ -119,20 +126,27 @@ Values: `title`, `meta:<name>` (e.g., `meta:description`), `link:<rel>` (e.g., `
119
126
 
120
127
  Validators access this via `ctx.headlessImports[].providedHeadTags`.
121
128
 
122
- ## Plugin Validators
129
+ ## Plugin Validators (jay-stack validate)
123
130
 
124
- Plugins can provide custom jay-html validation rules that run during `jay-stack validate`. Declare validators in `plugin.yaml`:
131
+ Plugins can provide custom jay-html validation rules that run during `jay-stack validate` in projects that use your plugin. Declare validators in `plugin.yaml`:
125
132
 
126
133
  ```yaml
127
134
  validators:
128
135
  - name: media-optimization
129
- handler: ./validators/media-validator
136
+ handler: validateMediaOptimization # export name from package entry point
130
137
  description: Ensures media URLs use resize parameters
131
138
  ```
132
139
 
140
+ **Handler format:**
141
+
142
+ - **NPM plugins** — `handler` is an export name from the package entry point (e.g., `validateMediaOptimization`). The function must be exported from `lib/index.ts`.
143
+ - **Local plugins** (`src/plugins/`) — `handler` is a relative path to the module (e.g., `./validators/media-validator`). The module must export a `validate` function.
144
+
145
+ `jay-stack validate-plugin` checks that the handler exists and is correctly exported.
146
+
133
147
  ### Writing a Validator
134
148
 
135
- The handler module exports a `validate` function:
149
+ Export the validator function from the package entry point (for NPM) or from the handler module (for local):
136
150
 
137
151
  ```typescript
138
152
  import type { JayHtmlValidatorFn, JayHtmlValidationFinding } from '@jay-framework/compiler-shared';