@jay-framework/jay-stack-cli 0.15.5 → 0.16.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.
Files changed (39) hide show
  1. package/agent-kit-template/{INSTRUCTIONS.md → designer/INSTRUCTIONS.md} +11 -8
  2. package/agent-kit-template/{jay-html-syntax.md → designer/jay-html-components.md} +89 -158
  3. package/agent-kit-template/designer/jay-html-styling.md +97 -0
  4. package/agent-kit-template/designer/jay-html-syntax.md +44 -0
  5. package/agent-kit-template/designer/jay-html-template-syntax.md +203 -0
  6. package/agent-kit-template/developer/INSTRUCTIONS.md +34 -0
  7. package/agent-kit-template/developer/cli-commands.md +228 -0
  8. package/agent-kit-template/developer/component-data.md +109 -0
  9. package/agent-kit-template/developer/component-refs.md +117 -0
  10. package/agent-kit-template/developer/component-state.md +140 -0
  11. package/agent-kit-template/developer/configuration.md +76 -0
  12. package/agent-kit-template/developer/dev-server-service.md +126 -0
  13. package/agent-kit-template/developer/page-components.md +103 -0
  14. package/agent-kit-template/developer/page-contracts.md +114 -0
  15. package/agent-kit-template/developer/project-structure.md +242 -0
  16. package/agent-kit-template/developer/render-results.md +112 -0
  17. package/agent-kit-template/developer/routing.md +175 -0
  18. package/agent-kit-template/developer/seo-guide.md +93 -0
  19. package/agent-kit-template/plugin/INSTRUCTIONS.md +43 -0
  20. package/agent-kit-template/plugin/actions-guide.md +184 -0
  21. package/agent-kit-template/plugin/component-context.md +103 -0
  22. package/agent-kit-template/plugin/component-data.md +109 -0
  23. package/agent-kit-template/plugin/component-refs.md +117 -0
  24. package/agent-kit-template/plugin/component-state.md +140 -0
  25. package/agent-kit-template/plugin/component-structure.md +174 -0
  26. package/agent-kit-template/plugin/contracts-guide.md +193 -0
  27. package/agent-kit-template/plugin/dev-server-service.md +137 -0
  28. package/agent-kit-template/plugin/plugin-routes.md +146 -0
  29. package/agent-kit-template/plugin/plugin-structure.md +210 -0
  30. package/agent-kit-template/plugin/render-results.md +112 -0
  31. package/agent-kit-template/plugin/seo-guide.md +93 -0
  32. package/agent-kit-template/plugin/services-guide.md +116 -0
  33. package/agent-kit-template/plugin/validation.md +101 -0
  34. package/dist/index.js +791 -60
  35. package/package.json +10 -10
  36. /package/agent-kit-template/{cli-commands.md → designer/cli-commands.md} +0 -0
  37. /package/agent-kit-template/{contracts-and-plugins.md → designer/contracts-and-plugins.md} +0 -0
  38. /package/agent-kit-template/{project-structure.md → designer/project-structure.md} +0 -0
  39. /package/agent-kit-template/{routing.md → designer/routing.md} +0 -0
@@ -0,0 +1,193 @@
1
+ # Contract Authoring Guide
2
+
3
+ Contracts (`.jay-contract` files) are the source of truth for a component's data shape. Define the contract before implementing the component.
4
+
5
+ ## Basic Structure
6
+
7
+ ```yaml
8
+ name: ProductCard
9
+ description: Displays a single product with price and add-to-cart. Use for product grids and featured sections.
10
+ props:
11
+ - name: productId
12
+ type: string
13
+ required: true
14
+ description: The product to display
15
+ params:
16
+ slug: string
17
+ tags:
18
+ - tag: name
19
+ type: data
20
+ dataType: string
21
+ phase: slow
22
+ ```
23
+
24
+ ## Tag Types
25
+
26
+ ### `data` — Read-only values
27
+
28
+ ```yaml
29
+ - tag: productName
30
+ type: data
31
+ dataType: string # string (default), number, boolean, date
32
+ required: true # optional, defaults to false
33
+ phase: slow # slow, fast, or fast+interactive
34
+ description: Display name
35
+ ```
36
+
37
+ ### `variant` — Enum/boolean for conditionals
38
+
39
+ ```yaml
40
+ - tag: status
41
+ type: variant
42
+ dataType: enum (AVAILABLE | OUT_OF_STOCK | PREORDER)
43
+ phase: fast+interactive
44
+ ```
45
+
46
+ ### `interactive` — Element refs for user interaction
47
+
48
+ ```yaml
49
+ - tag: addToCart
50
+ type: interactive
51
+ elementType: HTMLButtonElement # HTMLAnchorElement, HTMLInputElement, HTMLSelectElement, etc.
52
+ ```
53
+
54
+ Interactive tags are always `fast+interactive` — do not specify a phase.
55
+
56
+ A tag can be both data and interactive:
57
+
58
+ ```yaml
59
+ - tag: quantityInput
60
+ type: [data, interactive]
61
+ dataType: number
62
+ elementType: HTMLInputElement
63
+ ```
64
+
65
+ ### `sub-contract` — Nested objects
66
+
67
+ Inline:
68
+
69
+ ```yaml
70
+ - tag: pricing
71
+ type: sub-contract
72
+ tags:
73
+ - tag: amount
74
+ type: data
75
+ dataType: number
76
+ - tag: currency
77
+ type: data
78
+ dataType: string
79
+ ```
80
+
81
+ Linked (reference another contract file):
82
+
83
+ ```yaml
84
+ - tag: author
85
+ type: sub-contract
86
+ link: ./author.jay-contract
87
+ ```
88
+
89
+ ### `sub-contract` with `repeated: true` — Arrays
90
+
91
+ ```yaml
92
+ - tag: items
93
+ type: sub-contract
94
+ repeated: true
95
+ trackBy: id # Required: identifies each item
96
+ phase: fast
97
+ tags:
98
+ - tag: id
99
+ type: data
100
+ dataType: string
101
+ - tag: name
102
+ type: data
103
+ dataType: string
104
+ ```
105
+
106
+ `trackBy` must reference a `data` tag with `string` or `number` type within the same sub-contract.
107
+
108
+ ## Async Data
109
+
110
+ Wrap any tag in `Promise<T>` with `async: true`:
111
+
112
+ ```yaml
113
+ - tag: reviews
114
+ type: data
115
+ async: true
116
+ dataType: string # Compiles to Promise<string>
117
+
118
+ - tag: relatedProducts
119
+ type: sub-contract
120
+ repeated: true
121
+ trackBy: id
122
+ async: true # Compiles to Promise<Array<...>>
123
+ tags:
124
+ - tag: id
125
+ type: data
126
+ dataType: string
127
+ ```
128
+
129
+ ## Rendering Phases
130
+
131
+ Each tag has a phase that determines when its data is available:
132
+
133
+ | Phase | When | Use For |
134
+ | ------------------ | ------------------ | --------------------------------------- |
135
+ | `slow` | Build time (SSG) | Static content, SEO data, product names |
136
+ | `fast` | Request time (SSR) | Per-request data, live pricing, stock |
137
+ | `fast+interactive` | Request + client | Data that also updates on the client |
138
+
139
+ **How to choose:**
140
+
141
+ - Can the data be known at build time? Use `slow`
142
+ - Does it change per request (user, time, session)? Use `fast`
143
+ - Does it also update on the client after interaction? Use `fast+interactive`
144
+ - Interactive tags (refs) are always `fast+interactive`
145
+
146
+ **Phase rules for arrays:** Child phases must be >= parent phase. If the array is `fast`, all children must be `fast` or later.
147
+
148
+ ## Props vs Params
149
+
150
+ ### Props — Component configuration
151
+
152
+ Props are passed by the parent component or jay-html template. Use for component inputs like IDs, configuration flags, display options.
153
+
154
+ ```yaml
155
+ props:
156
+ - name: productId
157
+ type: string
158
+ required: true
159
+ description: The product to display
160
+ - name: showPricing
161
+ type: boolean
162
+ default: 'true'
163
+ ```
164
+
165
+ ### Params — URL route segments
166
+
167
+ Params come from dynamic route segments (`[slug]`, `[[lang]]`, `[...path]`). Use for page-level routing data.
168
+
169
+ ```yaml
170
+ params:
171
+ slug: string # required — from [slug]
172
+ lang: string? # optional — from [[lang]]
173
+ path: string[] # catch-all — from [...path]
174
+ ```
175
+
176
+ ## Description Field
177
+
178
+ Always include a `description` at the contract level explaining when to use this contract:
179
+
180
+ ```yaml
181
+ name: product-search
182
+ description: Product listing with filters, sorting, and pagination. Use for search results and category pages.
183
+ ```
184
+
185
+ ## Validation Rules
186
+
187
+ - Tag names must be unique at each level
188
+ - `repeated: true` requires `trackBy`
189
+ - `trackBy` must reference a `data` tag with `string` or `number` type
190
+ - Interactive tags cannot have an explicit `phase`
191
+ - Sub-contracts must have either `tags` (inline) or `link` (external), not both
192
+ - Array children must have phase >= parent phase
193
+ - Prop names must be unique
@@ -0,0 +1,137 @@
1
+ # Dev Server Service API
2
+
3
+ The dev server exposes a `DevServerService` for plugins, design board applications, and CLI tools. It provides route listing, param discovery, and freeze management.
4
+
5
+ ## Service Marker
6
+
7
+ Registered as `DEV_SERVER_SERVICE` — inject in actions and components:
8
+
9
+ ```typescript
10
+ import { DEV_SERVER_SERVICE } from '@jay-framework/dev-server';
11
+
12
+ export const listAllRoutes = makeJayAction('admin.listRoutes')
13
+ .withServices(DEV_SERVER_SERVICE)
14
+ .withHandler(async (_input, devServer) => {
15
+ return devServer.listRoutes();
16
+ });
17
+ ```
18
+
19
+ Or in a component:
20
+
21
+ ```typescript
22
+ makeJayStackComponent()
23
+ .withServices(DEV_SERVER_SERVICE)
24
+ .withFastRender(async (_props, devServer) => {
25
+ const routes = devServer.listRoutes();
26
+ return phaseOutput({ routes, routeCount: routes.length }, {});
27
+ });
28
+ ```
29
+
30
+ ## Direct Access
31
+
32
+ Also returned from `mkDevServer()` for CLI usage:
33
+
34
+ ```typescript
35
+ const { service } = await mkDevServer(options);
36
+ ```
37
+
38
+ ## Routes
39
+
40
+ ### listRoutes()
41
+
42
+ Returns all page routes in the project (including plugin-provided routes):
43
+
44
+ ```typescript
45
+ const routes = service.listRoutes();
46
+ // [{ path: '/products/kitan/[[category]]', jayHtmlPath: '...', compPath: '...' }]
47
+ ```
48
+
49
+ ### loadRouteParams(route)
50
+
51
+ Async generator that yields param batches from the route's `loadParams`:
52
+
53
+ ```typescript
54
+ for await (const batch of service.loadRouteParams('/products/kitan/[[category]]')) {
55
+ console.log(batch); // [{ category: 'shirts' }, { category: 'pants' }]
56
+ }
57
+ ```
58
+
59
+ Returns empty if the route has no `page.ts` or no `loadParams`. Throws if the route doesn't exist.
60
+
61
+ ## Freeze Management
62
+
63
+ ### FreezeStore
64
+
65
+ Accessible via `service.freezeStore`:
66
+
67
+ ```typescript
68
+ const store = service.freezeStore;
69
+
70
+ // Save a ViewState snapshot
71
+ const entry = await store.save('/products/kitan', viewState);
72
+
73
+ // List freezes for a route
74
+ const freezes = await store.list('/products/kitan');
75
+
76
+ // Get a specific freeze
77
+ const freeze = await store.get('abc123');
78
+
79
+ // Rename
80
+ await store.rename('abc123', 'in-stock state');
81
+
82
+ // Delete
83
+ await store.delete('abc123');
84
+ ```
85
+
86
+ ## Editor Protocol
87
+
88
+ These APIs are also exposed via the editor protocol (Socket.IO) for design board applications:
89
+
90
+ | Protocol Message | Service Method |
91
+ | ----------------- | ----------------------------------------- |
92
+ | `listRoutes` | `service.listRoutes()` |
93
+ | `listFreezes` | `service.freezeStore.list(route)` |
94
+ | `renameFreeze` | `service.freezeStore.rename(id, name)` |
95
+ | `deleteFreeze` | `service.freezeStore.delete(id)` |
96
+ | `loadRouteParams` | `service.loadRouteParams(route, onBatch)` |
97
+
98
+ ### Streaming Events
99
+
100
+ `loadRouteParams` streams batches via `routeParamsBatch` socket events:
101
+
102
+ ```typescript
103
+ // Client sends: { type: 'loadRouteParams', route: '/products/[slug]' }
104
+ // Server responds: { type: 'loadRouteParams', success: true }
105
+ // Server emits: { type: 'routeParamsBatch', route: '...', params: [...], hasMore: true }
106
+ // Server emits: { type: 'routeParamsBatch', route: '...', params: [...], hasMore: true }
107
+ // Server emits: { type: 'routeParamsBatch', route: '...', params: [], hasMore: false }
108
+ ```
109
+
110
+ ### Freeze Changed Event
111
+
112
+ The `freezeChanged` socket event is emitted when jay-html or CSS files change. Design board applications should listen for this to refresh their frozen views:
113
+
114
+ ```typescript
115
+ socket.on('freezeChanged', () => {
116
+ // Re-fetch frozen page fragments
117
+ });
118
+ ```
119
+
120
+ ## Iframe / Embed Mode
121
+
122
+ When a page is loaded inside an iframe with `?_jay_embed=true` (e.g., by the AIditor), the Alt+S shortcut is disabled (parent owns it). Freeze is triggered via `postMessage`:
123
+
124
+ ```typescript
125
+ // Parent → iframe: request freeze
126
+ iframe.contentWindow.postMessage({ type: 'jay:requestFreeze' }, '*');
127
+
128
+ // Iframe → parent: freeze done
129
+ // { type: 'jay:freeze', id: string, route: string }
130
+ ```
131
+
132
+ The parent constructs the frozen page URL:
133
+
134
+ ```
135
+ route + '?_jay_freeze=' + id // full page
136
+ route + '?_jay_freeze=' + id + '&format=fragment' // shadow DOM fragment
137
+ ```
@@ -0,0 +1,146 @@
1
+ # Plugin Routes
2
+
3
+ Plugins can provide complete pages served by the dev server. This is designed for backoffice tools, admin dashboards, and editors — pages with a boxed design that doesn't need per-site visual customization.
4
+
5
+ ## When to Use Plugin Routes
6
+
7
+ - **Admin dashboards** — product management, analytics, settings
8
+ - **Editor tools** — visual page editors, contract browsers
9
+ - **Developer tools** — debugging panels, state inspectors
10
+
11
+ Plugin routes are NOT for end-user pages that need visual customization per site. For those, provide headless components and let the project create its own pages.
12
+
13
+ ## Creating a Plugin Route
14
+
15
+ A plugin route is a **headless component + jay-html template + route path**. It uses the same rendering pipeline as project pages.
16
+
17
+ ### 1. Create the jay-html template
18
+
19
+ ```html
20
+ <!-- pages/admin/page.jay-html -->
21
+ <html>
22
+ <head>
23
+ <script type="application/jay-data">
24
+ data:
25
+ title: string
26
+ items:
27
+ - name: string
28
+ count: number
29
+ </script>
30
+ <style>
31
+ .admin {
32
+ max-width: 800px;
33
+ margin: 0 auto;
34
+ padding: 20px;
35
+ font-family: system-ui;
36
+ }
37
+ </style>
38
+ </head>
39
+ <body>
40
+ <div class="admin">
41
+ <h1>{title}</h1>
42
+ <div forEach="items" trackBy="name"><span>{name}</span>: <strong>{count}</strong></div>
43
+ </div>
44
+ </body>
45
+ </html>
46
+ ```
47
+
48
+ ### 2. Create the page component
49
+
50
+ ```typescript
51
+ // pages/admin/page.ts
52
+ import { makeJayStackComponent, phaseOutput } from '@jay-framework/fullstack-component';
53
+ import { MY_SERVICE, MyService } from '../../services';
54
+
55
+ export const page = makeJayStackComponent()
56
+ .withProps<{}>()
57
+ .withServices(MY_SERVICE)
58
+ .withFastRender(async (_props, myService: MyService) => {
59
+ const data = await myService.getDashboardData();
60
+ return phaseOutput({ title: 'Admin Dashboard', items: data.items }, {});
61
+ });
62
+ ```
63
+
64
+ The page component follows the same pattern as any `makeJayStackComponent` — supports `withSlowlyRender`, `withFastRender`, `withInteractive`, `withLoadParams`, and `withServices`.
65
+
66
+ ### 3. Declare the route in plugin.yaml
67
+
68
+ ```yaml
69
+ name: my-plugin
70
+ routes:
71
+ - path: /admin/dashboard
72
+ jayHtml: ./pages/admin/page.jay-html
73
+ component: ./pages/admin/page.ts
74
+ description: Admin dashboard showing key metrics
75
+ ```
76
+
77
+ For local plugins, `jayHtml` and `component` are relative paths. For NPM packages, use `package.json` export subpaths.
78
+
79
+ ### 4. For NPM packages — export the page files
80
+
81
+ ```json
82
+ {
83
+ "exports": {
84
+ "./admin-dashboard.jay-html": "./dist/pages/admin/page.jay-html",
85
+ "./admin-dashboard.css": "./dist/pages/admin/page.css"
86
+ }
87
+ }
88
+ ```
89
+
90
+ Then reference the export subpath in plugin.yaml:
91
+
92
+ ```yaml
93
+ routes:
94
+ - path: /admin/dashboard
95
+ jayHtml: admin-dashboard.jay-html
96
+ css: admin-dashboard.css
97
+ component: adminDashboard
98
+ ```
99
+
100
+ ## Route Parameters
101
+
102
+ Plugin routes support the same parameter patterns as project routes:
103
+
104
+ ```yaml
105
+ routes:
106
+ - path: /admin/products/[id]
107
+ jayHtml: ./pages/product-detail/page.jay-html
108
+ component: ./pages/product-detail/page.ts
109
+ ```
110
+
111
+ The page component can use `withLoadParams` for SSG parameter discovery:
112
+
113
+ ```typescript
114
+ export const page = makeJayStackComponent()
115
+ .withProps<{}>()
116
+ .withServices(PRODUCTS_SERVICE)
117
+ .withLoadParams<{ id: string }>(async function* (productsService) {
118
+ const products = await productsService.listAll();
119
+ yield products.map((p) => ({ id: p.id }));
120
+ })
121
+ .withSlowlyRender(async (props: { id: string }, productsService) => {
122
+ const product = await productsService.getById(props.id);
123
+ return phaseOutput({ name: product.name, price: product.price }, {});
124
+ });
125
+ ```
126
+
127
+ ## Route Priority
128
+
129
+ Project routes always take precedence. If the project creates a page at the same path, the plugin's route is skipped:
130
+
131
+ ```
132
+ src/pages/admin/dashboard/page.jay-html ← project wins
133
+ plugin provides /admin/dashboard ← skipped
134
+ ```
135
+
136
+ This lets projects override any plugin page without modifying the plugin.
137
+
138
+ ## Prefix Convention
139
+
140
+ Each plugin should choose a recognizable route prefix to avoid collisions:
141
+
142
+ - `/admin/...` — admin tools
143
+ - `/aiditor/...` — AIditor editor
144
+ - `/cms/...` — content management
145
+
146
+ There is no enforced convention — just pick a prefix that's unique and descriptive.
@@ -0,0 +1,210 @@
1
+ # Plugin Structure
2
+
3
+ A plugin provides headless components, contracts, and actions. It can be a standalone npm package or inline within a project.
4
+
5
+ ## plugin.yaml
6
+
7
+ The plugin manifest declares all contracts, actions, services, contexts, and configuration:
8
+
9
+ ```yaml
10
+ name: my-plugin
11
+ contracts:
12
+ - name: product-page
13
+ contract: product-page.jay-contract
14
+ component: productPage
15
+ description: Complete product detail page with SSR
16
+
17
+ - name: product-search
18
+ contract: product-search.jay-contract
19
+ component: productSearch
20
+ description: Product listing with filters and pagination
21
+
22
+ actions:
23
+ - name: searchProducts
24
+ action: search-products.jay-action
25
+ - name: addToCart
26
+ action: add-to-cart.jay-action
27
+
28
+ services:
29
+ - name: my-store
30
+ marker: MY_STORE_SERVICE_MARKER
31
+ description: Provides product catalog API (query, filter, sort)
32
+
33
+ contexts:
34
+ - name: my-cart
35
+ marker: MY_CART_CONTEXT
36
+ description: Client-side cart state (add/remove items, totals)
37
+
38
+ routes:
39
+ - path: /admin/dashboard
40
+ jayHtml: ./pages/admin/page.jay-html
41
+ component: ./pages/admin/page.ts
42
+ description: Admin dashboard with product stats
43
+
44
+ setup:
45
+ handler: setup-handler
46
+ references: references-handler
47
+ configTemplate:
48
+ - source: templates/config.yaml
49
+ target: my-plugin.yaml
50
+ ```
51
+
52
+ ### Contract Entry Fields
53
+
54
+ - `name` — Contract name (used in `contract="..."` in jay-html)
55
+ - `contract` — Path to `.jay-contract` file (relative to plugin root)
56
+ - `component` — Export name of the component (e.g., `productPage`)
57
+ - `description` — What this component does and when to use it
58
+
59
+ ### Action Entry Fields
60
+
61
+ - `name` — Action name (used with `jay-stack action <plugin>/<action>`)
62
+ - `action` — Path to `.jay-action` metadata file
63
+
64
+ ### Service Entry Fields
65
+
66
+ - `name` — Service name (for identification in plugins-index)
67
+ - `marker` — Exported service marker constant (e.g., `MY_STORE_SERVICE_MARKER`)
68
+ - `description` — What APIs this service provides
69
+ - `doc` — (optional) Path to a markdown file documenting the service API
70
+
71
+ Services are server-side APIs created with `createJayService`. Other plugins and page components consume them via `.withServices(MARKER)`.
72
+
73
+ ### Context Entry Fields
74
+
75
+ - `name` — Context name (for identification in plugins-index)
76
+ - `marker` — Exported context marker constant (e.g., `MY_CART_CONTEXT`)
77
+ - `description` — What reactive state this context provides
78
+ - `doc` — (optional) Path to a markdown file documenting the context API
79
+
80
+ Contexts are client-side reactive state. Other plugins and page components consume them via `.withContexts(MARKER)`.
81
+
82
+ ### Documentation Files
83
+
84
+ When `doc` is specified, the markdown file must exist and (for NPM packages) be exported in `package.json`:
85
+
86
+ ```yaml
87
+ services:
88
+ - name: my-store
89
+ marker: MY_STORE_SERVICE_MARKER
90
+ description: Product catalog API
91
+ doc: ./docs/my-store-service.md
92
+ ```
93
+
94
+ ```json
95
+ {
96
+ "exports": {
97
+ "./docs/my-store-service.md": "./docs/my-store-service.md"
98
+ }
99
+ }
100
+ ```
101
+
102
+ ### Route Entry Fields
103
+
104
+ - `path` — Route path (e.g., `/admin/products`, `/dashboard/[section]`)
105
+ - `jayHtml` — Path to the page's jay-html template (relative to plugin root, or export subpath for NPM)
106
+ - `css` — (optional) Path to the page's CSS file
107
+ - `component` — Path to the page component (relative to plugin root, or exported member name for NPM)
108
+ - `description` — What this page does
109
+
110
+ Plugin routes are served by the dev server alongside project routes. If a project defines the same route path, the project's page takes precedence.
111
+
112
+ ### Setup Fields
113
+
114
+ - `handler` — Setup handler for `jay-stack setup` (handles config, credentials)
115
+ - `references` — Reference generator for `jay-stack agent-kit` (generates discovery data)
116
+ - `configTemplate` — Config file templates to copy during setup
117
+
118
+ ## Package Layout
119
+
120
+ ### Standalone NPM Package
121
+
122
+ ```
123
+ my-plugin/
124
+ ├── plugin.yaml
125
+ ├── package.json
126
+ ├── lib/
127
+ │ ├── contracts/
128
+ │ │ ├── product-page.jay-contract
129
+ │ │ └── product-search.jay-contract
130
+ │ ├── actions/
131
+ │ │ ├── search-products.jay-action
132
+ │ │ └── add-to-cart.jay-action
133
+ │ ├── components/
134
+ │ │ ├── product-page.ts
135
+ │ │ └── product-search.ts
136
+ │ ├── services/
137
+ │ │ └── products-db.ts
138
+ │ └── init.ts
139
+ ├── agent-kit/ # Optional: plugin-contributed guides
140
+ │ ├── designer/
141
+ │ │ └── my-plugin-usage.md
142
+ │ └── developer/
143
+ │ └── my-plugin-config.md
144
+ └── dist/
145
+ ```
146
+
147
+ ### Inline Plugin (within a project)
148
+
149
+ ```
150
+ my-project/
151
+ ├── src/
152
+ │ ├── pages/
153
+ │ │ └── ...
154
+ │ └── plugins/
155
+ │ └── my-plugin/
156
+ │ ├── plugin.yaml
157
+ │ ├── product-page.jay-contract
158
+ │ ├── product-page.ts
159
+ │ └── init.ts
160
+ ```
161
+
162
+ See `examples/jay-stack/fake-shop` for a working example.
163
+
164
+ ## package.json Exports
165
+
166
+ For NPM packages, declare exports so the framework can resolve the plugin:
167
+
168
+ ```json
169
+ {
170
+ "name": "@my-org/my-plugin",
171
+ "type": "module",
172
+ "exports": {
173
+ ".": "./dist/index.js",
174
+ "./plugin.yaml": "./plugin.yaml"
175
+ },
176
+ "files": ["dist", "plugin.yaml", "lib/contracts", "lib/actions"]
177
+ }
178
+ ```
179
+
180
+ ## Plugin-Contributed Agent-Kit Guides
181
+
182
+ A plugin can include guides that are merged into the project's agent-kit during `jay-stack agent-kit`. Create an `agent-kit/` folder with subfolders for each role:
183
+
184
+ ```
185
+ my-plugin/
186
+ └── agent-kit/
187
+ ├── designer/
188
+ │ └── my-plugin-usage.md # How to use contracts in jay-html
189
+ ├── developer/
190
+ │ └── my-plugin-config.md # How to configure the plugin
191
+ └── plugin/
192
+ └── my-plugin-extending.md # How to extend the plugin
193
+ ```
194
+
195
+ ## Reference Declarations
196
+
197
+ Plugins can declare reference data generated by `jay-stack agent-kit`:
198
+
199
+ ```yaml
200
+ # In plugin.yaml
201
+ references:
202
+ - name: product-catalog
203
+ description: All products with IDs, slugs, names, and prices
204
+ file: product-catalog.json
205
+ - name: collection-schemas
206
+ description: Collection field schemas for filtering
207
+ file: collection-schemas.json
208
+ ```
209
+
210
+ The `agent-kit` command generates `references/<plugin>/INDEX.md` from these declarations.