@majkapp/plugin-kit 3.2.1 → 3.3.1
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/bin/promptable-cli.js +91 -0
- package/docs/API.md +394 -0
- package/docs/CONFIG.md +428 -0
- package/docs/CONTEXT.md +500 -0
- package/docs/FULL.md +848 -0
- package/docs/FUNCTIONS.md +623 -0
- package/docs/HOOKS.md +532 -0
- package/docs/INDEX.md +605 -0
- package/docs/LIFECYCLE.md +490 -0
- package/docs/SCREENS.md +547 -0
- package/docs/SERVICES.md +350 -0
- package/docs/TESTING.md +593 -0
- package/docs/mcp-execution-api.md +490 -0
- package/package.json +18 -3
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Promptable CLI for @majkapp/plugin-kit
|
|
5
|
+
* Makes the plugin kit documentation available in LLM-friendly formats
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createPromptable } from '@juleswhite/promptable';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
|
|
15
|
+
const cli = createPromptable({
|
|
16
|
+
title: '@majkapp/plugin-kit',
|
|
17
|
+
description: 'Build MAJK plugins with functions, UI screens, and services',
|
|
18
|
+
docsDir: join(__dirname, '../docs'),
|
|
19
|
+
packageCommand: 'npx @majkapp/plugin-kit'
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Main entry point - quick start
|
|
23
|
+
cli.addCommand('--llm',
|
|
24
|
+
cli.createFullDocCommand('INDEX.md'),
|
|
25
|
+
{
|
|
26
|
+
description: 'Quick start: Build your first plugin',
|
|
27
|
+
aliases: ['--start', '--quick']
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// Function patterns
|
|
32
|
+
cli.addCommand('--functions',
|
|
33
|
+
cli.createFullDocCommand('FUNCTIONS.md'),
|
|
34
|
+
{ description: 'Function patterns with examples' }
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// Screen configuration
|
|
38
|
+
cli.addCommand('--screens',
|
|
39
|
+
cli.createFullDocCommand('SCREENS.md'),
|
|
40
|
+
{ description: 'Configure UI screens' }
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// Generated React hooks
|
|
44
|
+
cli.addCommand('--hooks',
|
|
45
|
+
cli.createFullDocCommand('HOOKS.md'),
|
|
46
|
+
{ description: 'Auto-generated React hooks' }
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Context API
|
|
50
|
+
cli.addCommand('--context',
|
|
51
|
+
cli.createFullDocCommand('CONTEXT.md'),
|
|
52
|
+
{ description: 'Context API reference' }
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Service grouping
|
|
56
|
+
cli.addCommand('--services',
|
|
57
|
+
cli.createFullDocCommand('SERVICES.md'),
|
|
58
|
+
{ description: 'Organize functions by service' }
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Lifecycle hooks
|
|
62
|
+
cli.addCommand('--lifecycle',
|
|
63
|
+
cli.createFullDocCommand('LIFECYCLE.md'),
|
|
64
|
+
{ description: 'Plugin lifecycle hooks' }
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Testing guide
|
|
68
|
+
cli.addCommand('--testing',
|
|
69
|
+
cli.createFullDocCommand('TESTING.md'),
|
|
70
|
+
{ description: 'Testing guide' }
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Configuration
|
|
74
|
+
cli.addCommand('--config',
|
|
75
|
+
cli.createFullDocCommand('CONFIG.md'),
|
|
76
|
+
{ description: 'Project configuration' }
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
// API reference
|
|
80
|
+
cli.addCommand('--api',
|
|
81
|
+
cli.createFullDocCommand('API.md'),
|
|
82
|
+
{ description: 'Complete API reference' }
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Full documentation
|
|
86
|
+
cli.addCommand('--full',
|
|
87
|
+
cli.createFullDocCommand('FULL.md'),
|
|
88
|
+
{ description: 'Complete documentation' }
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
cli.run();
|
package/docs/API.md
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
Quick reference for @majkapp/plugin-kit API.
|
|
4
|
+
|
|
5
|
+
## definePlugin()
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { definePlugin } from '@majkapp/plugin-kit';
|
|
9
|
+
|
|
10
|
+
const plugin = definePlugin(id, name, version)
|
|
11
|
+
.pluginRoot(__dirname) // REQUIRED FIRST
|
|
12
|
+
.function(/* ... */)
|
|
13
|
+
.screenReact(/* ... */)
|
|
14
|
+
.service(/* ... */)
|
|
15
|
+
.onReady(/* ... */)
|
|
16
|
+
.build();
|
|
17
|
+
|
|
18
|
+
export = plugin;
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Parameters
|
|
22
|
+
- `id` - Unique plugin ID (lowercase, hyphens)
|
|
23
|
+
- `name` - Display name
|
|
24
|
+
- `version` - Semantic version (e.g., '1.0.0')
|
|
25
|
+
|
|
26
|
+
## .pluginRoot()
|
|
27
|
+
|
|
28
|
+
**REQUIRED.** Must be called first.
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
.pluginRoot(__dirname) // Enables resource loading
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## .function()
|
|
35
|
+
|
|
36
|
+
Define a callable function.
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
.function(name, {
|
|
40
|
+
description: string, // REQUIRED: 2-3 sentences
|
|
41
|
+
input: JsonSchema, // REQUIRED: Input validation schema
|
|
42
|
+
output: JsonSchema, // REQUIRED: Output validation schema
|
|
43
|
+
handler: async (input, ctx) => { /* ... */ }, // REQUIRED
|
|
44
|
+
tags: string[], // Optional: ['monitoring', 'health']
|
|
45
|
+
deprecated: boolean // Optional: Mark as deprecated
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Handler Context (ctx)
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
handler: async (input, ctx) => {
|
|
53
|
+
// Logging
|
|
54
|
+
ctx.logger.debug(message, data);
|
|
55
|
+
ctx.logger.info(message, data);
|
|
56
|
+
ctx.logger.warn(message, data);
|
|
57
|
+
ctx.logger.error(message, data);
|
|
58
|
+
|
|
59
|
+
// Storage
|
|
60
|
+
await ctx.storage.get(key);
|
|
61
|
+
await ctx.storage.set(key, value);
|
|
62
|
+
await ctx.storage.has(key);
|
|
63
|
+
await ctx.storage.delete(key);
|
|
64
|
+
await ctx.storage.clear();
|
|
65
|
+
|
|
66
|
+
// MAJK APIs
|
|
67
|
+
await ctx.majk.conversations.list();
|
|
68
|
+
await ctx.majk.conversations.get(id);
|
|
69
|
+
await ctx.majk.todos.list();
|
|
70
|
+
await ctx.majk.todos.get(id);
|
|
71
|
+
await ctx.majk.projects.list();
|
|
72
|
+
await ctx.majk.projects.get(id);
|
|
73
|
+
await ctx.majk.teammates.list();
|
|
74
|
+
await ctx.majk.teammates.get(id);
|
|
75
|
+
await ctx.majk.mcpServers.list();
|
|
76
|
+
await ctx.majk.mcpServers.get(id);
|
|
77
|
+
await ctx.majk.eventBus.subscribeAll(handler); // Use in .onReady()
|
|
78
|
+
|
|
79
|
+
return { /* output matching schema */ };
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## .screenReact()
|
|
84
|
+
|
|
85
|
+
Define a React screen.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
.screenReact({
|
|
89
|
+
id: string, // Unique ID
|
|
90
|
+
name: string, // Display name
|
|
91
|
+
description: string, // 2-3 sentences
|
|
92
|
+
route: `/plugin-screens/${id}/path`, // MUST start with /plugin-screens/{id}/
|
|
93
|
+
pluginPath: string, // Path to built React app (e.g., '/index.html')
|
|
94
|
+
pluginPathHash: string // Optional: Hash route (e.g., '#/dashboard')
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Multiple Screens
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
.screenReact({
|
|
102
|
+
id: 'my-plugin-dashboard',
|
|
103
|
+
name: 'Dashboard',
|
|
104
|
+
description: 'Main dashboard',
|
|
105
|
+
route: '/plugin-screens/my-plugin/dashboard',
|
|
106
|
+
pluginPath: '/index.html',
|
|
107
|
+
pluginPathHash: '#/'
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
.screenReact({
|
|
111
|
+
id: 'my-plugin-settings',
|
|
112
|
+
name: 'Settings',
|
|
113
|
+
description: 'Plugin settings',
|
|
114
|
+
route: '/plugin-screens/my-plugin/settings',
|
|
115
|
+
pluginPath: '/index.html',
|
|
116
|
+
pluginPathHash: '#/settings'
|
|
117
|
+
})
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## .service()
|
|
121
|
+
|
|
122
|
+
Group functions into a service.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
.service(serviceName, {
|
|
126
|
+
type: string, // Service type identifier
|
|
127
|
+
metadata: {
|
|
128
|
+
name: string, // Display name
|
|
129
|
+
description: string, // Service description
|
|
130
|
+
version: string, // Service version
|
|
131
|
+
author?: string,
|
|
132
|
+
homepage?: string,
|
|
133
|
+
tags?: string[]
|
|
134
|
+
},
|
|
135
|
+
discoverable: boolean // Allow discovery by other plugins
|
|
136
|
+
})
|
|
137
|
+
.withFunction(functionName, {
|
|
138
|
+
examples?: string[], // Usage examples
|
|
139
|
+
tags?: string[], // Additional tags
|
|
140
|
+
metadata?: Record<string, any> // Custom metadata
|
|
141
|
+
})
|
|
142
|
+
.withFunction(/* ... */)
|
|
143
|
+
.endService()
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Service Naming
|
|
147
|
+
|
|
148
|
+
Format: `plugin:{plugin-id}:{service-name}`
|
|
149
|
+
|
|
150
|
+
Example: `plugin:my-plugin:monitoring`
|
|
151
|
+
|
|
152
|
+
## .onReady()
|
|
153
|
+
|
|
154
|
+
Lifecycle hook called when plugin loads.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
.onReady(async (ctx, cleanup) => {
|
|
158
|
+
// Initialization code
|
|
159
|
+
const subscription = await ctx.majk.eventBus.subscribeAll(handler);
|
|
160
|
+
|
|
161
|
+
// Register cleanup (called on unload)
|
|
162
|
+
cleanup(() => {
|
|
163
|
+
subscription.unsubscribe();
|
|
164
|
+
});
|
|
165
|
+
})
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Generated React Hooks
|
|
169
|
+
|
|
170
|
+
Auto-generated from functions in `ui/src/generated/hooks.ts`.
|
|
171
|
+
|
|
172
|
+
### Query Hooks (Auto-Fetch)
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
const { data, error, loading, refetch } = useHealth(
|
|
176
|
+
input?, // Optional input parameters
|
|
177
|
+
options?: {
|
|
178
|
+
enabled?: boolean, // Only fetch if true (default: true)
|
|
179
|
+
refetchInterval?: number // Auto-refetch interval in ms
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Mutation Hooks (Manual)
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
const { mutate, data, error, loading } = useClearEvents();
|
|
188
|
+
|
|
189
|
+
// Call mutate manually
|
|
190
|
+
await mutate(input);
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Testing
|
|
194
|
+
|
|
195
|
+
### Function Testing
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import { test, invoke, mock } from '@majkapp/plugin-test';
|
|
199
|
+
import assert from 'assert';
|
|
200
|
+
|
|
201
|
+
test('test name', async () => {
|
|
202
|
+
const context = mock()
|
|
203
|
+
.storage({ key: 'value' })
|
|
204
|
+
.withMajkData({
|
|
205
|
+
conversations: [{ id: 'c1', title: 'Test' }],
|
|
206
|
+
todos: [{ id: 't1', status: 'pending' }],
|
|
207
|
+
projects: [{ id: 'p1', name: 'Project' }]
|
|
208
|
+
})
|
|
209
|
+
.build();
|
|
210
|
+
|
|
211
|
+
const result = await invoke('functionName', { input }, { context });
|
|
212
|
+
|
|
213
|
+
assert.strictEqual(result.expected, 'value');
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### UI Testing
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
import { test, bot, mock } from '@majkapp/plugin-test';
|
|
221
|
+
import assert from 'assert';
|
|
222
|
+
|
|
223
|
+
test('test name', async () => {
|
|
224
|
+
const context = mock()
|
|
225
|
+
.withMockHandler('health', async () => ({
|
|
226
|
+
status: 'ok',
|
|
227
|
+
timestamp: new Date().toISOString()
|
|
228
|
+
}))
|
|
229
|
+
.build();
|
|
230
|
+
|
|
231
|
+
const b = await bot(context);
|
|
232
|
+
await b.goto('/plugin-screens/my-plugin/main');
|
|
233
|
+
|
|
234
|
+
await b.waitForSelector('[data-majk-id="element"][data-majk-state="populated"]');
|
|
235
|
+
|
|
236
|
+
const text = await b.getText('[data-majk-id="element"]');
|
|
237
|
+
assert.ok(text.includes('expected'));
|
|
238
|
+
|
|
239
|
+
await b.screenshot('test.png');
|
|
240
|
+
await b.close();
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## data-majk-* Attributes
|
|
245
|
+
|
|
246
|
+
### data-majk-id
|
|
247
|
+
|
|
248
|
+
Identifies elements for testing.
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
<button data-majk-id="saveButton">Save</button>
|
|
252
|
+
<div data-majk-id="totalCount">{count}</div>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### data-majk-state
|
|
256
|
+
|
|
257
|
+
Tracks UI state for testing.
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
<div
|
|
261
|
+
data-majk-id="container"
|
|
262
|
+
data-majk-state={loading ? 'loading' : data ? 'populated' : 'empty'}
|
|
263
|
+
>
|
|
264
|
+
{/* content */}
|
|
265
|
+
</div>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Common states: `loading`, `empty`, `populated`, `error`, `idle`, `processing`
|
|
269
|
+
|
|
270
|
+
## JSON Schema Types
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
// String
|
|
274
|
+
{ type: 'string' }
|
|
275
|
+
{ type: 'string', enum: ['value1', 'value2'] }
|
|
276
|
+
{ type: 'string', format: 'date-time' }
|
|
277
|
+
|
|
278
|
+
// Number
|
|
279
|
+
{ type: 'number' }
|
|
280
|
+
{ type: 'number', minimum: 0, maximum: 100 }
|
|
281
|
+
|
|
282
|
+
// Boolean
|
|
283
|
+
{ type: 'boolean' }
|
|
284
|
+
|
|
285
|
+
// Object
|
|
286
|
+
{
|
|
287
|
+
type: 'object',
|
|
288
|
+
properties: {
|
|
289
|
+
field1: { type: 'string' },
|
|
290
|
+
field2: { type: 'number' }
|
|
291
|
+
},
|
|
292
|
+
required: ['field1'],
|
|
293
|
+
additionalProperties: false
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Array
|
|
297
|
+
{
|
|
298
|
+
type: 'array',
|
|
299
|
+
items: { type: 'string' }
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Array of objects
|
|
303
|
+
{
|
|
304
|
+
type: 'array',
|
|
305
|
+
items: {
|
|
306
|
+
type: 'object',
|
|
307
|
+
properties: { /* ... */ }
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Common Patterns
|
|
313
|
+
|
|
314
|
+
### uiLog Function
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
.function('uiLog', {
|
|
318
|
+
description: 'Log messages from UI for debugging',
|
|
319
|
+
input: {
|
|
320
|
+
type: 'object',
|
|
321
|
+
properties: {
|
|
322
|
+
level: { type: 'string', enum: ['debug', 'info', 'warn', 'error'] },
|
|
323
|
+
component: { type: 'string' },
|
|
324
|
+
message: { type: 'string' },
|
|
325
|
+
data: { type: 'object' }
|
|
326
|
+
},
|
|
327
|
+
required: ['level', 'component', 'message']
|
|
328
|
+
},
|
|
329
|
+
output: {
|
|
330
|
+
type: 'object',
|
|
331
|
+
properties: { success: { type: 'boolean' } }
|
|
332
|
+
},
|
|
333
|
+
handler: async (input, ctx) => {
|
|
334
|
+
ctx.logger[input.level](`[UI:${input.component}] ${input.message}`, input.data);
|
|
335
|
+
return { success: true };
|
|
336
|
+
}
|
|
337
|
+
})
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Health Check Function
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
.function('health', {
|
|
344
|
+
description: 'Check plugin health status',
|
|
345
|
+
input: {
|
|
346
|
+
type: 'object',
|
|
347
|
+
properties: {},
|
|
348
|
+
additionalProperties: false
|
|
349
|
+
},
|
|
350
|
+
output: {
|
|
351
|
+
type: 'object',
|
|
352
|
+
properties: {
|
|
353
|
+
status: { type: 'string', enum: ['ok', 'error'] },
|
|
354
|
+
timestamp: { type: 'string', format: 'date-time' }
|
|
355
|
+
},
|
|
356
|
+
required: ['status', 'timestamp']
|
|
357
|
+
},
|
|
358
|
+
handler: async (_, ctx) => ({
|
|
359
|
+
status: 'ok',
|
|
360
|
+
timestamp: new Date().toISOString()
|
|
361
|
+
})
|
|
362
|
+
})
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Event Bus Subscription
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
.onReady(async (ctx, cleanup) => {
|
|
369
|
+
const subscription = await ctx.majk.eventBus.subscribeAll((event) => {
|
|
370
|
+
ctx.logger.info('Event', {
|
|
371
|
+
entityType: event.entityType,
|
|
372
|
+
eventType: event.type,
|
|
373
|
+
entityId: event.entity?.id
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
cleanup(() => {
|
|
378
|
+
subscription.unsubscribe();
|
|
379
|
+
});
|
|
380
|
+
})
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Next Steps
|
|
384
|
+
|
|
385
|
+
Run `npx @majkapp/plugin-kit` - Quick start guide
|
|
386
|
+
Run `npx @majkapp/plugin-kit --functions` - Function patterns
|
|
387
|
+
Run `npx @majkapp/plugin-kit --screens` - Screen configuration
|
|
388
|
+
Run `npx @majkapp/plugin-kit --hooks` - Generated hooks
|
|
389
|
+
Run `npx @majkapp/plugin-kit --context` - Context API
|
|
390
|
+
Run `npx @majkapp/plugin-kit --services` - Service grouping
|
|
391
|
+
Run `npx @majkapp/plugin-kit --lifecycle` - Lifecycle hooks
|
|
392
|
+
Run `npx @majkapp/plugin-kit --testing` - Testing guide
|
|
393
|
+
Run `npx @majkapp/plugin-kit --config` - Project configuration
|
|
394
|
+
Run `npx @majkapp/plugin-kit --full` - Complete reference
|