@kb-labs/shared-command-kit 1.0.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.
@@ -0,0 +1,1696 @@
1
+ import { PluginContextV3, ManifestV3, PermissionSpec, JobDecl, CommandResult as CommandResult$1, HostContext, WSMessage, WSSender, WSInput } from '@kb-labs/plugin-contracts';
2
+ export { PluginContextV3 } from '@kb-labs/plugin-contracts';
3
+ import { FlagSchemaDefinition as FlagSchemaDefinition$1, InferFlags } from './flags/index.js';
4
+ export { ArrayFlagSchema, BaseFlagSchema, BooleanFlagSchema, FlagSchema, FlagSchemaWithInfer, FlagType, FlagValidationError, NumberFlagSchema, SafeValidationResult, StringFlagSchema, ValidationResult, defineFlags, validateFlags, validateFlagsSafe } from './flags/index.js';
5
+ export { AnalyticsContext, AnalyticsEvents, createAnalyticsWrapper, trackEvent, withAnalytics } from './analytics/index.js';
6
+ export { ErrorDefinition, ErrorDefinitions, FormatErrorOptions, FormattedError, PluginError, commonErrors, defineError, formatError } from './errors/index.js';
7
+ export { getLLMTier, isCacheAvailable, isEmbeddingsAvailable, isLLMAvailable, isPlatformConfigured, isVectorStoreAvailable, trackAnalyticsEvent, useAnalytics, useCache, useConfig, useEmbeddings, useLLM, useLogger, useLoggerWithContext, usePlatform, useStorage, useVectorStore } from './helpers/index.js';
8
+ import { z } from 'zod';
9
+ export { CommandOutput } from '@kb-labs/shared-cli-ui';
10
+ export { LLMTier, UseLLMOptions } from '@kb-labs/core-platform';
11
+ import '@kb-labs/core-runtime';
12
+
13
+ /**
14
+ * @module @kb-labs/shared-command-kit
15
+ * System Command Definition - For KB Labs official commands with full privileges
16
+ */
17
+
18
+ /**
19
+ * Flag definition for Command interface
20
+ */
21
+ interface FlagDefinition {
22
+ name: string;
23
+ type: 'string' | 'boolean' | 'number' | 'array';
24
+ alias?: string;
25
+ description?: string;
26
+ default?: unknown;
27
+ required?: boolean;
28
+ choices?: string[];
29
+ }
30
+ /**
31
+ * Command definition for system commands
32
+ */
33
+ interface Command {
34
+ name: string;
35
+ describe: string;
36
+ longDescription?: string;
37
+ category?: string;
38
+ aliases?: string[];
39
+ flags?: FlagDefinition[];
40
+ examples?: string[];
41
+ run: (ctx: PluginContextV3, argv: string[], flags: Record<string, unknown>) => Promise<number>;
42
+ }
43
+ /**
44
+ * Command group definition
45
+ */
46
+ interface CommandGroup {
47
+ name: string;
48
+ describe: string;
49
+ commands: Command[];
50
+ }
51
+ /**
52
+ * Extended command config for system commands
53
+ *
54
+ * All type parameters are optional - use them when you want type safety, skip them for simplicity.
55
+ *
56
+ * TFlags can be inferred from the flags schema, but can also be explicitly provided
57
+ * for better type inference in complex cases.
58
+ *
59
+ * TResult must extend CommandResult (requires ok: boolean field).
60
+ */
61
+ interface SystemCommandConfig<TFlags extends FlagSchemaDefinition$1 = FlagSchemaDefinition$1, TResult extends CommandResult = CommandResult, TArgv extends readonly string[] = string[]> {
62
+ /** Command name (required for system commands) */
63
+ name: string;
64
+ /** Command description (required for system commands) */
65
+ description: string;
66
+ /** Long description for help */
67
+ longDescription?: string;
68
+ /** Category (defaults to 'system') */
69
+ category?: string;
70
+ /** Command aliases */
71
+ aliases?: string[];
72
+ /** Usage examples */
73
+ examples?: string[];
74
+ /** Flag schema definition - TypeScript will infer TFlags from this */
75
+ flags: TFlags;
76
+ /**
77
+ * Analytics configuration (legacy field, kept for backward compatibility)
78
+ * Use ctx.platform.analytics.track() or withAnalytics() helper instead
79
+ */
80
+ analytics?: {
81
+ command?: string;
82
+ startEvent?: string;
83
+ finishEvent?: string;
84
+ actor?: string;
85
+ context?: Record<string, unknown>;
86
+ includeFlags?: boolean;
87
+ };
88
+ /** Command handler - receives PluginContextV3 and must return TResult */
89
+ handler: (ctx: PluginContextV3, argv: TArgv, flags: InferFlags<TFlags>) => Promise<number | TResult> | number | TResult;
90
+ /** Optional formatter - receives TResult with inferred flags */
91
+ formatter?: (result: TResult, ctx: PluginContextV3, flags: InferFlags<TFlags>, argv?: TArgv) => void;
92
+ }
93
+ /**
94
+ * Define a system command with full privileges
95
+ *
96
+ * System commands are KB Labs official commands that run with full system access,
97
+ * bypassing sandbox restrictions. They are architecturally separated from plugin
98
+ * commands - plugins can NEVER gain system privileges.
99
+ *
100
+ * TResult is REQUIRED - every command must explicitly declare its result contract.
101
+ * This ensures type safety and enables future contract validation.
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * import { defineSystemCommand, type CommandResult, type FlagSchemaDefinition } from '@kb-labs/shared-command-kit';
106
+ *
107
+ * // Simple command with automatic flag type inference (RECOMMENDED)
108
+ * export const helloCommand = defineSystemCommand<CommandResult & { message: string }>({
109
+ * name: 'hello',
110
+ * description: 'Print a friendly greeting',
111
+ * category: 'system',
112
+ * flags: {
113
+ * name: { type: 'string', description: 'Name to greet' },
114
+ * json: { type: 'boolean', default: false },
115
+ * } satisfies FlagSchemaDefinition, // Preserves literal types for type inference
116
+ * async handler(ctx, argv, flags) {
117
+ * // ctx is EnhancedCliContext (extends PluginContextV3)
118
+ * // ctx.cwd - working directory (V2 promoted field)
119
+ * // ctx.ui - UI output API with new convenience methods
120
+ * // flags.name is inferred as string | undefined
121
+ * const message = `Hello, ${flags.name || 'World'}!`;
122
+ *
123
+ * // UI output (CLI only, no-op in REST/Workflow)
124
+ * if (!flags.json) {
125
+ * if (ctx.ui?.success) {
126
+ * ctx.ui.success('Greeting', [
127
+ * { items: [message] },
128
+ * ]);
129
+ * }
130
+ * } else {
131
+ * ctx.ui?.json({ message });
132
+ * }
133
+ *
134
+ * return { ok: true, message };
135
+ * },
136
+ * });
137
+ *
138
+ * // Command with explicit result type and automatic flag inference
139
+ * type WorkflowListResult = CommandResult & {
140
+ * workflows: Array<{ id: string }>;
141
+ * total: number;
142
+ * };
143
+ *
144
+ * export const wfList = defineSystemCommand<WorkflowListResult>({
145
+ * name: 'list',
146
+ * description: 'List workflows',
147
+ * flags: {
148
+ * source: { type: 'string', default: 'all' },
149
+ * tag: { type: 'string' },
150
+ * json: { type: 'boolean', default: false },
151
+ * } satisfies FlagSchemaDefinition, // TypeScript infers flag types automatically
152
+ * async handler(ctx, argv, flags) {
153
+ * // flags.source is inferred as string
154
+ * // flags.tag is inferred as string | undefined
155
+ * // flags.json is inferred as boolean
156
+ * const workflows = await listWorkflows(flags.source, flags.tag);
157
+ *
158
+ * // UI output (new convenience methods)
159
+ * if (!flags.json) {
160
+ * if (ctx.ui?.success) {
161
+ * ctx.ui.success('Workflows', [
162
+ * { header: 'Summary', items: [`Total: ${workflows.length}`, `Source: ${flags.source}`] },
163
+ * { items: workflows.map(w => `• ${w.id}`) },
164
+ * ]);
165
+ * }
166
+ * } else {
167
+ * ctx.ui?.json({ workflows, total: workflows.length });
168
+ * }
169
+ *
170
+ * return { ok: true, workflows, total: workflows.length };
171
+ * },
172
+ * });
173
+ *
174
+ * // Command with explicit flag types (for complex cases)
175
+ * export const wfRun = defineSystemCommand<
176
+ * { 'workflow-id': { type: 'string'; required: true } },
177
+ * CommandResult & { run: WorkflowRun }
178
+ * >({
179
+ * name: 'run',
180
+ * description: 'Execute a workflow',
181
+ * flags: {
182
+ * 'workflow-id': { type: 'string', required: true },
183
+ * },
184
+ * async handler(ctx, argv, flags) {
185
+ * // flags['workflow-id'] is inferred as string (required)
186
+ *
187
+ * // Progress tracking (CLI only, no-op in REST/Workflow)
188
+ * ctx.ui?.startProgress('running', 'Starting workflow...');
189
+ * const run = await executeWorkflow(flags['workflow-id']);
190
+ * ctx.ui?.completeProgress('running', 'Workflow started!');
191
+ *
192
+ * return { ok: true, run };
193
+ * },
194
+ * });
195
+ *
196
+ * // Note: System commands support all UI convenience methods:
197
+ * // - ctx.ui.success() / showError() / warning() / info() - Result display with sideBox
198
+ * // - ctx.ui.startProgress() / completeProgress() / failProgress() - Progress tracking
199
+ * // - ctx.ui.table() / keyValue() / list() - Low-level formatting
200
+ * // All UI methods are CLI-only and no-op in REST/Workflow contexts
201
+ * ```
202
+ */
203
+ declare function defineSystemCommand<TFlags extends FlagSchemaDefinition$1, TResult extends CommandResult = CommandResult, TArgv extends readonly string[] = string[]>(config: SystemCommandConfig<TFlags, TResult, TArgv>): Command;
204
+ /**
205
+ * Create a system command group
206
+ *
207
+ * Groups organize related system commands for better help organization
208
+ * and registration.
209
+ *
210
+ * @example
211
+ * ```typescript
212
+ * export const systemInfoGroup = defineSystemCommandGroup(
213
+ * 'system:info',
214
+ * 'System information commands',
215
+ * [helloCommand, versionCommand, healthCommand]
216
+ * );
217
+ * ```
218
+ */
219
+ declare function defineSystemCommandGroup(name: string, describe: string, commands: Command[]): CommandGroup;
220
+
221
+ /**
222
+ * Manifest definition helpers
223
+ * @module @kb-labs/shared-command-kit/manifest
224
+ */
225
+
226
+ /**
227
+ * Define a ManifestV3 with type safety
228
+ *
229
+ * This is a type-safe wrapper for creating ManifestV3 objects.
230
+ * It provides compile-time validation via TypeScript and optional
231
+ * runtime validation via Zod (if enabled).
232
+ *
233
+ * For built plugins (dist/), this function is compiled away and only
234
+ * the plain object remains, avoiding runtime dependencies.
235
+ *
236
+ * @example
237
+ * ```typescript
238
+ * // Basic usage
239
+ * export const manifest = defineManifest({
240
+ * schema: 'kb.plugin/2',
241
+ * id: '@kb-labs/my-plugin',
242
+ * version: '1.0.0',
243
+ * cli: {
244
+ * commands: [...]
245
+ * }
246
+ * });
247
+ *
248
+ * // With contracts typing (Level 2)
249
+ * import type { PluginContracts } from '@kb-labs/my-plugin-contracts';
250
+ *
251
+ * export const manifest = defineManifest<typeof pluginContractsManifest>({
252
+ * schema: 'kb.plugin/2',
253
+ * id: '@kb-labs/my-plugin',
254
+ * artifacts: [
255
+ * { id: 'my.artifact.id' } // ✅ Type-checked against contracts
256
+ * ],
257
+ * cli: {
258
+ * commands: [{
259
+ * id: 'my:command', // ✅ Type-checked against contracts
260
+ * // ...
261
+ * }]
262
+ * }
263
+ * });
264
+ * ```
265
+ *
266
+ * @param manifest - Manifest configuration
267
+ * @returns ManifestV3 object
268
+ */
269
+ declare function defineManifest<TContracts = unknown>(manifest: ManifestV3): ManifestV3;
270
+ /**
271
+ * Flag schema definition (compatible with defineFlags)
272
+ */
273
+ type FlagSchemaDefinition = Record<string, {
274
+ type: 'string' | 'boolean' | 'number' | 'array';
275
+ alias?: string;
276
+ default?: unknown;
277
+ description?: string;
278
+ choices?: string[];
279
+ required?: boolean;
280
+ }>;
281
+ /**
282
+ * Convert flag schema definition to CliFlagDecl[] for manifest
283
+ *
284
+ * This helper converts the flag schema format used in defineCommand
285
+ * to the array format expected in ManifestV3.
286
+ *
287
+ * @example
288
+ * ```typescript
289
+ * const helloFlags = {
290
+ * name: { type: 'string', description: 'Name to greet', alias: 'n' },
291
+ * json: { type: 'boolean', description: 'Emit JSON', default: false }
292
+ * };
293
+ *
294
+ * const manifestFlags = defineCommandFlags(helloFlags);
295
+ * // Use in manifest: flags: manifestFlags
296
+ *
297
+ * // In manifest.v2.ts:
298
+ * export const manifest = defineManifest({
299
+ * cli: {
300
+ * commands: [{
301
+ * id: 'hello',
302
+ * flags: defineCommandFlags(helloFlags),
303
+ * // ...
304
+ * }]
305
+ * }
306
+ * });
307
+ * ```
308
+ */
309
+ declare function defineCommandFlags<TFlags extends FlagSchemaDefinition>(flags: TFlags): Array<{
310
+ name: string;
311
+ type: 'string' | 'boolean' | 'number' | 'array';
312
+ alias?: string;
313
+ default?: unknown;
314
+ description?: string;
315
+ choices?: string[];
316
+ required?: boolean;
317
+ }>;
318
+
319
+ /**
320
+ * Schema Builders for KB Labs Plugins
321
+ *
322
+ * Optional helpers for common Zod validation patterns.
323
+ * You can always use plain Zod - these are just conveniences for repetitive patterns.
324
+ *
325
+ * @example
326
+ * ```typescript
327
+ * import { schema } from '@kb-labs/shared-command-kit';
328
+ *
329
+ * const RequestSchema = schema.object({
330
+ * cwd: schema.cwd(),
331
+ * scope: schema.scopeId(),
332
+ * text: schema.text({ min: 1, max: 10000 }),
333
+ * mode: schema.enum(['instant', 'auto', 'thinking'], { default: 'auto' }),
334
+ * limit: schema.positiveInt({ max: 100, default: 10 }),
335
+ * });
336
+ * ```
337
+ */
338
+
339
+ /**
340
+ * Current working directory path (optional string)
341
+ * Commonly used in CLI and REST handlers
342
+ *
343
+ * @example
344
+ * ```typescript
345
+ * const schema = z.object({
346
+ * cwd: schema.cwd(),
347
+ * });
348
+ * // Equivalent to: cwd: z.string().optional()
349
+ * ```
350
+ */
351
+ declare function cwd(): z.ZodOptional<z.ZodString>;
352
+ /**
353
+ * Scope identifier (required non-empty string)
354
+ * Used for Mind RAG scopes, workflow scopes, etc.
355
+ *
356
+ * @example
357
+ * ```typescript
358
+ * const schema = z.object({
359
+ * scope: schema.scopeId(),
360
+ * });
361
+ * // Equivalent to: scope: z.string().min(1)
362
+ * ```
363
+ */
364
+ declare function scopeId(): z.ZodString;
365
+ /**
366
+ * Text string with optional length constraints
367
+ *
368
+ * @example
369
+ * ```typescript
370
+ * schema.text({ min: 1, max: 10000 })
371
+ * // Equivalent to: z.string().min(1).max(10000)
372
+ * ```
373
+ */
374
+ declare function text(options?: {
375
+ min?: number;
376
+ max?: number;
377
+ default?: string;
378
+ }): z.ZodString | z.ZodDefault<z.ZodString>;
379
+ /**
380
+ * Positive integer with optional constraints
381
+ *
382
+ * @example
383
+ * ```typescript
384
+ * schema.positiveInt({ max: 100, default: 10 })
385
+ * // Equivalent to: z.number().int().positive().max(100).default(10)
386
+ * ```
387
+ */
388
+ declare function positiveInt(options?: {
389
+ min?: number;
390
+ max?: number;
391
+ default?: number;
392
+ }): z.ZodNumber | z.ZodDefault<z.ZodNumber>;
393
+ /**
394
+ * Non-negative integer (0 or positive)
395
+ *
396
+ * @example
397
+ * ```typescript
398
+ * schema.nonNegativeInt({ max: 100 })
399
+ * // Equivalent to: z.number().int().min(0).max(100)
400
+ * ```
401
+ */
402
+ declare function nonNegativeInt(options?: {
403
+ max?: number;
404
+ default?: number;
405
+ }): z.ZodNumber | z.ZodDefault<z.ZodNumber>;
406
+ /**
407
+ * Enum with optional default value
408
+ *
409
+ * @example
410
+ * ```typescript
411
+ * schema.enum(['instant', 'auto', 'thinking'], { default: 'auto' })
412
+ * // Equivalent to: z.enum(['instant', 'auto', 'thinking']).default('auto')
413
+ * ```
414
+ */
415
+ declare function enumSchema<T extends [string, ...string[]]>(values: T, options?: {
416
+ default?: T[number];
417
+ }): z.ZodDefault<z.ZodEnum<T>> | z.ZodEnum<T>;
418
+ /**
419
+ * Plugin ID (@kb-labs/package-name format)
420
+ *
421
+ * @example
422
+ * ```typescript
423
+ * schema.pluginId()
424
+ * // Matches: @kb-labs/mind, @kb-labs/workflow, etc.
425
+ * ```
426
+ */
427
+ declare function pluginId(): z.ZodString;
428
+ /**
429
+ * Command ID (plugin:command format)
430
+ *
431
+ * @example
432
+ * ```typescript
433
+ * schema.commandId()
434
+ * // Matches: mind:query, workflow:run, etc.
435
+ * ```
436
+ */
437
+ declare function commandId(): z.ZodString;
438
+ /**
439
+ * Artifact ID (plugin.artifact.id format)
440
+ *
441
+ * @example
442
+ * ```typescript
443
+ * schema.artifactId()
444
+ * // Matches: mind.index.vector, workflow.run.result, etc.
445
+ * ```
446
+ */
447
+ declare function artifactId(): z.ZodString;
448
+ /**
449
+ * File path (string)
450
+ * Optional with default to undefined
451
+ *
452
+ * @example
453
+ * ```typescript
454
+ * schema.filePath()
455
+ * // Equivalent to: z.string()
456
+ * ```
457
+ */
458
+ declare function filePath(options?: {
459
+ optional?: boolean;
460
+ }): z.ZodString | z.ZodOptional<z.ZodString>;
461
+ /**
462
+ * URL string
463
+ *
464
+ * @example
465
+ * ```typescript
466
+ * schema.url()
467
+ * // Equivalent to: z.string().url()
468
+ * ```
469
+ */
470
+ declare function url(options?: {
471
+ optional?: boolean;
472
+ }): z.ZodString | z.ZodOptional<z.ZodString>;
473
+ /**
474
+ * Email string
475
+ *
476
+ * @example
477
+ * ```typescript
478
+ * schema.email()
479
+ * // Equivalent to: z.string().email()
480
+ * ```
481
+ */
482
+ declare function email(options?: {
483
+ optional?: boolean;
484
+ }): z.ZodString | z.ZodOptional<z.ZodString>;
485
+ /**
486
+ * UUID string
487
+ *
488
+ * @example
489
+ * ```typescript
490
+ * schema.uuid()
491
+ * // Equivalent to: z.string().uuid()
492
+ * ```
493
+ */
494
+ declare function uuid(options?: {
495
+ optional?: boolean;
496
+ }): z.ZodString | z.ZodOptional<z.ZodString>;
497
+ /**
498
+ * ISO datetime string
499
+ *
500
+ * @example
501
+ * ```typescript
502
+ * schema.datetime()
503
+ * // Equivalent to: z.string().datetime()
504
+ * ```
505
+ */
506
+ declare function datetime(options?: {
507
+ optional?: boolean;
508
+ }): z.ZodString | z.ZodOptional<z.ZodString>;
509
+ /**
510
+ * JSON object (any valid JSON object)
511
+ *
512
+ * @example
513
+ * ```typescript
514
+ * schema.json()
515
+ * // Equivalent to: z.record(z.unknown())
516
+ * ```
517
+ */
518
+ declare function json(): z.ZodRecord<z.ZodString, z.ZodUnknown>;
519
+ /**
520
+ * Boolean with optional default
521
+ *
522
+ * @example
523
+ * ```typescript
524
+ * schema.boolean({ default: false })
525
+ * // Equivalent to: z.boolean().default(false)
526
+ * ```
527
+ */
528
+ declare function boolean(options?: {
529
+ default?: boolean;
530
+ }): z.ZodBoolean | z.ZodDefault<z.ZodBoolean>;
531
+ /**
532
+ * Array of items with optional length constraints
533
+ *
534
+ * @example
535
+ * ```typescript
536
+ * schema.array(z.string(), { min: 1, max: 10 })
537
+ * // Equivalent to: z.array(z.string()).min(1).max(10)
538
+ * ```
539
+ */
540
+ declare function array<T extends z.ZodTypeAny>(itemSchema: T, options?: {
541
+ min?: number;
542
+ max?: number;
543
+ }): z.ZodArray<T, "many">;
544
+ /**
545
+ * Object schema builder (alias for z.object)
546
+ *
547
+ * @example
548
+ * ```typescript
549
+ * schema.object({ name: z.string() })
550
+ * // Equivalent to: z.object({ name: z.string() })
551
+ * ```
552
+ */
553
+ declare function object<T extends z.ZodRawShape>(shape: T): z.ZodObject<T, "strip", z.ZodTypeAny, z.objectUtil.addQuestionMarks<z.baseObjectOutputType<T>, any> extends infer T_1 ? { [k in keyof T_1]: T_1[k]; } : never, z.baseObjectInputType<T> extends infer T_2 ? { [k_1 in keyof T_2]: T_2[k_1]; } : never>;
554
+ /**
555
+ * Export schema builders namespace
556
+ *
557
+ * @example
558
+ * ```typescript
559
+ * import { schema } from '@kb-labs/shared-command-kit';
560
+ *
561
+ * const MySchema = schema.object({
562
+ * cwd: schema.cwd(),
563
+ * scope: schema.scopeId(),
564
+ * text: schema.text({ min: 1, max: 10000 }),
565
+ * });
566
+ * ```
567
+ */
568
+ declare const schema: {
569
+ cwd: typeof cwd;
570
+ scopeId: typeof scopeId;
571
+ pluginId: typeof pluginId;
572
+ commandId: typeof commandId;
573
+ artifactId: typeof artifactId;
574
+ filePath: typeof filePath;
575
+ text: typeof text;
576
+ url: typeof url;
577
+ email: typeof email;
578
+ uuid: typeof uuid;
579
+ datetime: typeof datetime;
580
+ positiveInt: typeof positiveInt;
581
+ nonNegativeInt: typeof nonNegativeInt;
582
+ enum: typeof enumSchema;
583
+ boolean: typeof boolean;
584
+ json: typeof json;
585
+ array: typeof array;
586
+ object: typeof object;
587
+ };
588
+
589
+ /**
590
+ * REST Handler Definition (V3)
591
+ *
592
+ * Define REST handlers compatible with plugin-runtime's runInProcess().
593
+ * Returns { execute } object with (ctx, input) signature.
594
+ *
595
+ * @example
596
+ * ```typescript
597
+ * import { defineHandler } from '@kb-labs/shared-command-kit';
598
+ *
599
+ * export default defineHandler({
600
+ * async execute(ctx, input: { scope?: string }) {
601
+ * const plan = await loadPlan(ctx.cwd);
602
+ * return plan;
603
+ * }
604
+ * });
605
+ * ```
606
+ */
607
+
608
+ /**
609
+ * REST input structure from route-mounter.
610
+ * Query params, body, and route params are separated to avoid conflicts.
611
+ */
612
+ interface RestInput<TQuery = unknown, TBody = unknown, TParams = unknown> {
613
+ query?: TQuery;
614
+ body?: TBody;
615
+ params?: TParams;
616
+ }
617
+ /**
618
+ * Handler interface expected by runInProcess().
619
+ */
620
+ interface Handler<TConfig = unknown, TInput = unknown, TOutput = unknown> {
621
+ /**
622
+ * Execute the handler
623
+ */
624
+ execute(ctx: PluginContextV3<TConfig>, input: TInput): Promise<TOutput>;
625
+ }
626
+ /**
627
+ * Handler definition options.
628
+ * Extensible for future features (inputSchema, middleware, etc.)
629
+ */
630
+ interface HandlerDefinition<TConfig = unknown, TInput = unknown, TOutput = unknown> {
631
+ /**
632
+ * Execute the handler.
633
+ * Returns data directly - no need for exitCode/CommandResult wrapper.
634
+ *
635
+ * @param ctx - Plugin context with runtime APIs (fs, fetch, env, ui, etc.)
636
+ * @param input - Request input (body for POST, query for GET, etc.)
637
+ * @returns Response data (will be serialized as JSON)
638
+ * @throws Error on failure (will be converted to HTTP 500)
639
+ */
640
+ execute(ctx: PluginContextV3<TConfig>, input: TInput): Promise<TOutput>;
641
+ }
642
+ /**
643
+ * Define a REST handler
644
+ *
645
+ * Creates a handler object compatible with runInProcess() from plugin-runtime.
646
+ * The handler receives full PluginContextV3 with runtime APIs.
647
+ *
648
+ * @example
649
+ * ```typescript
650
+ * // Simple handler
651
+ * export default defineHandler({
652
+ * async execute(ctx, input: { scope?: string }) {
653
+ * const plan = await loadPlan(ctx.cwd);
654
+ * return plan;
655
+ * }
656
+ * });
657
+ *
658
+ * // With typed query parameters using RestInput
659
+ * import { defineHandler, type RestInput } from '@kb-labs/sdk';
660
+ *
661
+ * export default defineHandler({
662
+ * async execute(ctx, input: RestInput<{ workspace?: string }>) {
663
+ * const workspace = input.query?.workspace || 'root';
664
+ * return { workspace };
665
+ * }
666
+ * });
667
+ *
668
+ * // With typed query and body
669
+ * interface QueryParams {
670
+ * workspace?: string;
671
+ * }
672
+ *
673
+ * interface BodyParams {
674
+ * name: string;
675
+ * description?: string;
676
+ * }
677
+ *
678
+ * export default defineHandler({
679
+ * async execute(ctx, input: RestInput<QueryParams, BodyParams>) {
680
+ * const workspace = input.query?.workspace || 'root';
681
+ * const name = input.body?.name;
682
+ * return { workspace, name };
683
+ * }
684
+ * });
685
+ *
686
+ * // With typed generics
687
+ * interface GetPlanInput {
688
+ * scope?: string;
689
+ * includeHistory?: boolean;
690
+ * }
691
+ *
692
+ * interface ReleasePlan {
693
+ * version: string;
694
+ * packages: string[];
695
+ * }
696
+ *
697
+ * export default defineHandler<unknown, GetPlanInput, ReleasePlan>({
698
+ * async execute(ctx, input) {
699
+ * // ctx: PluginContextV3 with runtime.fs, ui, etc.
700
+ * // input: GetPlanInput (typed)
701
+ * // return: ReleasePlan (typed)
702
+ * return await loadPlan(ctx.cwd, input.scope);
703
+ * }
704
+ * });
705
+ *
706
+ * // Using runtime APIs
707
+ * export default defineHandler({
708
+ * async execute(ctx, input: { path: string }) {
709
+ * // File system access
710
+ * const content = await ctx.runtime.fs.readFile(input.path, 'utf-8');
711
+ *
712
+ * // Environment variables
713
+ * const apiKey = ctx.runtime.env('API_KEY');
714
+ *
715
+ * // Logging
716
+ * ctx.runtime.log('info', 'Processing file', { path: input.path });
717
+ *
718
+ * return { content, hasApiKey: !!apiKey };
719
+ * }
720
+ * });
721
+ * ```
722
+ */
723
+ declare function defineHandler<TConfig = unknown, TInput = unknown, TOutput = unknown>(definition: HandlerDefinition<TConfig, TInput, TOutput>): Handler<TConfig, TInput, TOutput>;
724
+
725
+ /**
726
+ * Lifecycle Hook Helpers
727
+ *
728
+ * Optional helpers for plugin lifecycle hooks (setup, destroy, upgrade).
729
+ * You can always use plain functions - this is just convenience.
730
+ *
731
+ * @example
732
+ * ```typescript
733
+ * import { defineSetupHandler } from '@kb-labs/shared-command-kit';
734
+ *
735
+ * export const setup = defineSetupHandler({
736
+ * name: 'mind:setup',
737
+ * workspace: {
738
+ * directories: ['.kb/mind/index', '.kb/mind/cache'],
739
+ * },
740
+ * config: {
741
+ * 'kb.config.json': {
742
+ * mind: { enabled: true, scopes: ['default'] },
743
+ * },
744
+ * },
745
+ * async handler(ctx) {
746
+ * ctx.log('Mind workspace initialized');
747
+ * return { ok: true };
748
+ * },
749
+ * });
750
+ * ```
751
+ */
752
+ /**
753
+ * Lifecycle handler context
754
+ */
755
+ interface LifecycleContext {
756
+ /** Output directory (plugin workspace root) */
757
+ outdir: string;
758
+ /** Plugin ID */
759
+ pluginId: string;
760
+ /** Request ID for tracing */
761
+ requestId?: string;
762
+ /** Logger */
763
+ log?: (level: 'debug' | 'info' | 'warn' | 'error', msg: string, meta?: Record<string, unknown>) => void;
764
+ /** Environment getter */
765
+ env?: (key: string) => string | undefined;
766
+ }
767
+ /**
768
+ * Workspace setup configuration
769
+ */
770
+ interface WorkspaceConfig {
771
+ /** Directories to create (relative to outdir or absolute) */
772
+ directories?: string[];
773
+ /** Files to create with content */
774
+ files?: Record<string, string>;
775
+ }
776
+ /**
777
+ * Config file updates
778
+ */
779
+ type ConfigUpdates = Record<string, Record<string, unknown>>;
780
+ /**
781
+ * Setup handler definition
782
+ */
783
+ interface SetupHandlerDefinition {
784
+ /** Handler name (for logging) */
785
+ name: string;
786
+ /** Workspace configuration (directories/files to create) */
787
+ workspace?: WorkspaceConfig;
788
+ /** Config file updates (will be merged with existing config) */
789
+ config?: ConfigUpdates;
790
+ /** Custom setup logic (optional) */
791
+ handler?: (ctx: LifecycleContext) => Promise<{
792
+ ok: boolean;
793
+ message?: string;
794
+ }>;
795
+ }
796
+ /**
797
+ * Destroy handler definition
798
+ */
799
+ interface DestroyHandlerDefinition {
800
+ /** Handler name (for logging) */
801
+ name: string;
802
+ /** Workspace cleanup (directories/files to remove) */
803
+ workspace?: {
804
+ /** Directories to remove */
805
+ directories?: string[];
806
+ /** Files to remove */
807
+ files?: string[];
808
+ };
809
+ /** Config cleanup (keys to remove from config files) */
810
+ config?: Record<string, string[]>;
811
+ /** Custom destroy logic (optional) */
812
+ handler?: (ctx: LifecycleContext) => Promise<{
813
+ ok: boolean;
814
+ message?: string;
815
+ }>;
816
+ }
817
+ /**
818
+ * Setup handler result
819
+ */
820
+ interface SetupResult {
821
+ ok: boolean;
822
+ message?: string;
823
+ }
824
+ /**
825
+ * Define a setup handler with declarative workspace and config setup
826
+ *
827
+ * @example
828
+ * ```typescript
829
+ * export const setup = defineSetupHandler({
830
+ * name: 'mind:setup',
831
+ * workspace: {
832
+ * directories: [
833
+ * '.kb/mind/index',
834
+ * '.kb/mind/cache',
835
+ * '.kb/mind/pack',
836
+ * ],
837
+ * files: {
838
+ * '.kb/mind/.gitignore': 'cache/\npack/\n',
839
+ * },
840
+ * },
841
+ * config: {
842
+ * 'kb.config.json': {
843
+ * mind: {
844
+ * enabled: true,
845
+ * scopes: ['default'],
846
+ * },
847
+ * },
848
+ * },
849
+ * async handler(ctx) {
850
+ * ctx.log?.('info', 'Custom setup logic', {});
851
+ * return { ok: true };
852
+ * },
853
+ * });
854
+ * ```
855
+ */
856
+ declare function defineSetupHandler(definition: SetupHandlerDefinition): (ctx: LifecycleContext) => Promise<SetupResult>;
857
+ /**
858
+ * Define a destroy handler with declarative cleanup
859
+ *
860
+ * @example
861
+ * ```typescript
862
+ * export const destroy = defineDestroyHandler({
863
+ * name: 'mind:destroy',
864
+ * workspace: {
865
+ * directories: ['.kb/mind'],
866
+ * },
867
+ * config: {
868
+ * 'kb.config.json': ['mind'], // Remove 'mind' key
869
+ * },
870
+ * });
871
+ * ```
872
+ */
873
+ declare function defineDestroyHandler(definition: DestroyHandlerDefinition): (ctx: LifecycleContext) => Promise<SetupResult>;
874
+
875
+ /**
876
+ * Job definition helpers
877
+ * @module @kb-labs/shared-command-kit/jobs
878
+ */
879
+
880
+ /**
881
+ * Job input passed to handler at runtime
882
+ */
883
+ interface JobInput {
884
+ /** Job identifier */
885
+ jobId: string;
886
+ /** When the job was scheduled to execute */
887
+ executedAt: Date;
888
+ /** How many times this job has run */
889
+ runCount: number;
890
+ }
891
+ /**
892
+ * Job handler function signature
893
+ *
894
+ * @template TInput - Custom input type (extends JobInput)
895
+ * @template TOutput - Handler return type
896
+ */
897
+ type JobHandler<TInput extends JobInput = JobInput, TOutput = {
898
+ ok: boolean;
899
+ [key: string]: unknown;
900
+ }> = (input: TInput, ctx: PluginContextV3) => Promise<TOutput>;
901
+ /**
902
+ * Job definition configuration
903
+ *
904
+ * Combines JobDecl manifest fields with the actual handler function
905
+ * for type-safe job creation.
906
+ */
907
+ interface JobDefinition<TInput extends JobInput = JobInput, TOutput = {
908
+ ok: boolean;
909
+ [key: string]: unknown;
910
+ }> {
911
+ /** Unique job identifier within plugin (e.g., 'auto-index') */
912
+ id: string;
913
+ /**
914
+ * Cron schedule expression
915
+ * Supports shortcuts (@hourly, @daily, @weekly, @monthly, @yearly) and standard cron format
916
+ */
917
+ schedule: string;
918
+ /** Human-readable description */
919
+ describe?: string;
920
+ /** Whether job is enabled (default: true) */
921
+ enabled?: boolean;
922
+ /** Job priority (1-10, default: 5, higher = more important) */
923
+ priority?: number;
924
+ /** Execution timeout in milliseconds (default: 60000 = 1min) */
925
+ timeout?: number;
926
+ /** Number of retry attempts on failure (default: 2) */
927
+ retries?: number;
928
+ /** Tags for filtering and organization */
929
+ tags?: string[];
930
+ /** Permissions for job execution (filesystem, network, quotas) */
931
+ permissions?: PermissionSpec;
932
+ /** Job handler function */
933
+ handler: JobHandler<TInput, TOutput>;
934
+ }
935
+ /**
936
+ * Defined job object with manifest conversion
937
+ */
938
+ interface DefinedJob<TInput extends JobInput = JobInput, TOutput = {
939
+ ok: boolean;
940
+ [key: string]: unknown;
941
+ }> {
942
+ /** Job configuration */
943
+ readonly config: Omit<JobDefinition<TInput, TOutput>, 'handler'> & {
944
+ handler: string;
945
+ };
946
+ /** Job handler function */
947
+ readonly handler: JobHandler<TInput, TOutput>;
948
+ /** Convert to manifest JobDecl */
949
+ toManifest(handlerPath: string): JobDecl;
950
+ }
951
+ /**
952
+ * Define a type-safe job with handler
953
+ *
954
+ * This helper provides compile-time type safety for job definitions
955
+ * and allows sharing the same handler function between manifest and runtime.
956
+ *
957
+ * The handler function can be exported from a separate file and the manifest
958
+ * references it via the handler path.
959
+ *
960
+ * See plugin-template/src/jobs/hello.ts for a complete example.
961
+ *
962
+ * @param definition - Job configuration with handler
963
+ * @returns DefinedJob object with handler and manifest conversion
964
+ */
965
+ declare function defineJob<TInput extends JobInput = JobInput, TOutput = {
966
+ ok: boolean;
967
+ [key: string]: unknown;
968
+ }>(definition: JobDefinition<TInput, TOutput>): DefinedJob<TInput, TOutput>;
969
+
970
+ /**
971
+ * Define a CLI command handler
972
+ */
973
+
974
+ /**
975
+ * CLI input wrapper - V3 plugin system wraps flags in this structure
976
+ */
977
+ interface CLIInput<TFlags = unknown> {
978
+ flags: TFlags;
979
+ argv: string[];
980
+ }
981
+ interface CommandHandlerV3<TConfig = unknown, TInput = unknown, TResult = unknown> {
982
+ /**
983
+ * Execute the command
984
+ */
985
+ execute(context: PluginContextV3<TConfig>, input: TInput): Promise<CommandResult$1<TResult>> | CommandResult$1<TResult>;
986
+ /**
987
+ * Optional cleanup - called after execute completes
988
+ */
989
+ cleanup?(): Promise<void> | void;
990
+ }
991
+ interface CommandDefinition<TConfig = unknown, TInput = unknown, TResult = unknown> {
992
+ /**
993
+ * Command ID (e.g., "my-plugin:greet")
994
+ */
995
+ id: string;
996
+ /**
997
+ * Command description
998
+ */
999
+ description?: string;
1000
+ /**
1001
+ * Handler implementation
1002
+ */
1003
+ handler: CommandHandlerV3<TConfig, TInput, TResult>;
1004
+ /**
1005
+ * Optional input schema validation (future: use Zod/JSON Schema)
1006
+ */
1007
+ schema?: unknown;
1008
+ }
1009
+ /**
1010
+ * Define a CLI command
1011
+ *
1012
+ * For CLI commands, use CLIInput<TFlags> as TInput type to get proper typing
1013
+ * for the V3 plugin system's { flags, argv } structure.
1014
+ *
1015
+ * @example
1016
+ * ```typescript
1017
+ * interface GreetFlags {
1018
+ * name?: string;
1019
+ * }
1020
+ *
1021
+ * interface GreetResult {
1022
+ * message: string;
1023
+ * target: string;
1024
+ * }
1025
+ *
1026
+ * export default defineCommand<unknown, CLIInput<GreetFlags>, GreetResult>({
1027
+ * id: 'greet',
1028
+ * description: 'Greet a user',
1029
+ * handler: {
1030
+ * async execute(context, input) {
1031
+ * const target = input.flags.name || 'World';
1032
+ * context.ui.success(`Hello, ${target}!`);
1033
+ *
1034
+ * return {
1035
+ * exitCode: 0,
1036
+ * result: { message: `Hello, ${target}!`, target },
1037
+ * meta: { version: 'v3' }
1038
+ * };
1039
+ * }
1040
+ * }
1041
+ * });
1042
+ * ```
1043
+ */
1044
+ declare function defineCommand<TConfig = unknown, TInput = unknown, TResult = unknown>(definition: CommandDefinition<TConfig, TInput, TResult>): CommandHandlerV3<TConfig, TInput, TResult>;
1045
+ /**
1046
+ * Type guard to check if host context is CLI
1047
+ */
1048
+ declare function isCLIHost(hostContext: HostContext): hostContext is Extract<HostContext, {
1049
+ host: 'cli';
1050
+ }>;
1051
+
1052
+ /**
1053
+ * Define a REST API route handler
1054
+ */
1055
+
1056
+ interface RouteHandler<TConfig = unknown, TInput = unknown> {
1057
+ /**
1058
+ * Execute the route handler
1059
+ */
1060
+ execute(context: PluginContextV3<TConfig>, input: TInput): Promise<CommandResult$1 | void> | CommandResult$1 | void;
1061
+ /**
1062
+ * Optional cleanup - called after execute completes
1063
+ */
1064
+ cleanup?(): Promise<void> | void;
1065
+ }
1066
+ interface RouteDefinition<TConfig = unknown, TInput = unknown> {
1067
+ /**
1068
+ * Route path (e.g., "/api/greet")
1069
+ */
1070
+ path: string;
1071
+ /**
1072
+ * HTTP method (e.g., "GET", "POST")
1073
+ */
1074
+ method: string;
1075
+ /**
1076
+ * Route description
1077
+ */
1078
+ description?: string;
1079
+ /**
1080
+ * Handler implementation
1081
+ */
1082
+ handler: RouteHandler<TConfig, TInput>;
1083
+ /**
1084
+ * Optional input schema validation (future: use Zod/JSON Schema)
1085
+ */
1086
+ schema?: unknown;
1087
+ }
1088
+ /**
1089
+ * Define a REST API route
1090
+ *
1091
+ * @example
1092
+ * ```typescript
1093
+ * export default defineRoute({
1094
+ * path: '/greet',
1095
+ * method: 'POST',
1096
+ * description: 'Greet a user',
1097
+ * handler: {
1098
+ * async execute(context, input: { name: string }) {
1099
+ * return {
1100
+ * data: { message: `Hello, ${input.name}!` },
1101
+ * exitCode: 0,
1102
+ * };
1103
+ * }
1104
+ * }
1105
+ * });
1106
+ * ```
1107
+ */
1108
+ declare function defineRoute<TConfig = unknown, TInput = unknown>(definition: RouteDefinition<TConfig, TInput>): RouteHandler<TConfig, TInput>;
1109
+ /**
1110
+ * Type guard to check if host context is REST
1111
+ */
1112
+ declare function isRESTHost(hostContext: HostContext): hostContext is Extract<HostContext, {
1113
+ host: 'rest';
1114
+ }>;
1115
+
1116
+ /**
1117
+ * Define a webhook handler
1118
+ */
1119
+
1120
+ interface WebhookHandler<TConfig = unknown, TInput = unknown> {
1121
+ /**
1122
+ * Execute the webhook handler
1123
+ */
1124
+ execute(context: PluginContextV3<TConfig>, input: TInput): Promise<CommandResult$1 | void> | CommandResult$1 | void;
1125
+ /**
1126
+ * Optional cleanup - called after execute completes
1127
+ */
1128
+ cleanup?(): Promise<void> | void;
1129
+ }
1130
+ interface WebhookDefinition<TConfig = unknown, TInput = unknown> {
1131
+ /**
1132
+ * Event name (e.g., "github:push", "stripe:payment.success")
1133
+ */
1134
+ event: string;
1135
+ /**
1136
+ * Webhook description
1137
+ */
1138
+ description?: string;
1139
+ /**
1140
+ * Handler implementation
1141
+ */
1142
+ handler: WebhookHandler<TConfig, TInput>;
1143
+ /**
1144
+ * Optional input schema validation (future: use Zod/JSON Schema)
1145
+ */
1146
+ schema?: unknown;
1147
+ }
1148
+ /**
1149
+ * Define a webhook handler
1150
+ *
1151
+ * @example
1152
+ * ```typescript
1153
+ * export default defineWebhook({
1154
+ * event: 'github:push',
1155
+ * description: 'Handle GitHub push events',
1156
+ * handler: {
1157
+ * async execute(context, input: { ref: string; commits: any[] }) {
1158
+ * const { event, source, payload } = context.hostContext as WebhookHost;
1159
+ *
1160
+ * context.ui.info(`Received ${event} from ${source}`);
1161
+ *
1162
+ * // Process webhook...
1163
+ *
1164
+ * return {
1165
+ * data: { processed: true },
1166
+ * exitCode: 0,
1167
+ * };
1168
+ * }
1169
+ * }
1170
+ * });
1171
+ * ```
1172
+ */
1173
+ declare function defineWebhook<TConfig = unknown, TInput = unknown>(definition: WebhookDefinition<TConfig, TInput>): WebhookHandler<TConfig, TInput>;
1174
+ /**
1175
+ * Type guard to check if host context is webhook
1176
+ */
1177
+ declare function isWebhookHost(hostContext: HostContext): hostContext is Extract<HostContext, {
1178
+ host: 'webhook';
1179
+ }>;
1180
+
1181
+ /**
1182
+ * Define a WebSocket channel handler with enhanced DX
1183
+ *
1184
+ * Provides type-safe WebSocket handlers following the same pattern as defineCommand, defineRoute, etc.
1185
+ */
1186
+
1187
+ /**
1188
+ * Typed sender with message builders
1189
+ *
1190
+ * Wraps WSSender to provide generic type support for outgoing messages.
1191
+ */
1192
+ interface TypedSender<TOutgoing = WSMessage> extends Omit<WSSender, 'send' | 'broadcast' | 'sendTo'> {
1193
+ /** Send typed message to this client */
1194
+ send(message: TOutgoing): Promise<void>;
1195
+ /** Broadcast typed message to all clients in channel */
1196
+ broadcast(message: TOutgoing, excludeSelf?: boolean): Promise<void>;
1197
+ /** Send typed message to specific connections */
1198
+ sendTo(connectionIds: string[], message: TOutgoing): Promise<void>;
1199
+ /** Original WSSender methods for raw messages */
1200
+ raw: WSSender;
1201
+ }
1202
+ /**
1203
+ * WebSocket handler with typed messages
1204
+ */
1205
+ interface WebSocketHandler<TConfig = unknown, TIncoming = unknown, TOutgoing = WSMessage> {
1206
+ /**
1207
+ * Called when client connects
1208
+ */
1209
+ onConnect?(context: PluginContextV3<TConfig>, sender: TypedSender<TOutgoing>): Promise<void> | void;
1210
+ /**
1211
+ * Called when message received from client
1212
+ */
1213
+ onMessage?(context: PluginContextV3<TConfig>, message: TIncoming, sender: TypedSender<TOutgoing>): Promise<void> | void;
1214
+ /**
1215
+ * Called when client disconnects
1216
+ */
1217
+ onDisconnect?(context: PluginContextV3<TConfig>, code: number, reason: string): Promise<void> | void;
1218
+ /**
1219
+ * Called on error
1220
+ */
1221
+ onError?(context: PluginContextV3<TConfig>, error: Error, sender: TypedSender<TOutgoing>): Promise<void> | void;
1222
+ /**
1223
+ * Optional cleanup - called after any lifecycle event
1224
+ */
1225
+ cleanup?(): Promise<void> | void;
1226
+ }
1227
+ interface WebSocketDefinition<TConfig = unknown, TIncoming = unknown, TOutgoing = WSMessage> {
1228
+ /**
1229
+ * Channel path (e.g., "/live", "/chat")
1230
+ */
1231
+ path: string;
1232
+ /**
1233
+ * Channel description
1234
+ */
1235
+ description?: string;
1236
+ /**
1237
+ * Handler implementation
1238
+ */
1239
+ handler: WebSocketHandler<TConfig, TIncoming, TOutgoing>;
1240
+ /**
1241
+ * Optional message schema validation (future: use Zod/JSON Schema)
1242
+ */
1243
+ schema?: unknown;
1244
+ }
1245
+ /**
1246
+ * Define a WebSocket channel handler
1247
+ *
1248
+ * @example Basic usage
1249
+ * ```typescript
1250
+ * export default defineWebSocket({
1251
+ * path: '/chat',
1252
+ * handler: {
1253
+ * async onConnect(ctx, sender) {
1254
+ * await sender.send({ type: 'ready', payload: {}, timestamp: Date.now() });
1255
+ * },
1256
+ * async onMessage(ctx, message, sender) {
1257
+ * console.log('Received:', message);
1258
+ * },
1259
+ * }
1260
+ * });
1261
+ * ```
1262
+ *
1263
+ * @example With typed messages
1264
+ * ```typescript
1265
+ * interface IncomingMsg {
1266
+ * type: 'start' | 'stop';
1267
+ * payload: { scope?: string };
1268
+ * }
1269
+ *
1270
+ * interface OutgoingMsg {
1271
+ * type: 'progress' | 'complete';
1272
+ * payload: { progress: number; message: string };
1273
+ * }
1274
+ *
1275
+ * export default defineWebSocket<unknown, IncomingMsg, OutgoingMsg>({
1276
+ * path: '/live',
1277
+ * handler: {
1278
+ * async onMessage(ctx, message, sender) {
1279
+ * if (message.type === 'start') {
1280
+ * await sender.send({
1281
+ * type: 'progress',
1282
+ * payload: { progress: 50, message: 'Processing...' },
1283
+ * });
1284
+ * }
1285
+ * }
1286
+ * }
1287
+ * });
1288
+ * ```
1289
+ *
1290
+ * @example With message router (advanced pattern matching)
1291
+ * ```typescript
1292
+ * import { defineMessage, MessageRouter } from '@kb-labs/sdk';
1293
+ *
1294
+ * // Define message types
1295
+ * const StartMsg = defineMessage<{ scope?: string }>('start');
1296
+ * const StopMsg = defineMessage<{}>('stop');
1297
+ *
1298
+ * // Create outgoing message builders
1299
+ * const ProgressMsg = defineMessage<{ phase: string; progress: number }>('progress');
1300
+ * const CompleteMsg = defineMessage<{ commits: any[] }>('complete');
1301
+ *
1302
+ * export default defineWebSocket({
1303
+ * path: '/live',
1304
+ * handler: {
1305
+ * async onMessage(ctx, message, sender) {
1306
+ * const router = new MessageRouter()
1307
+ * .on(StartMsg, async (ctx, payload, sender) => {
1308
+ * await sender.send(ProgressMsg.create({
1309
+ * phase: 'analyzing',
1310
+ * progress: 0,
1311
+ * }));
1312
+ * })
1313
+ * .on(StopMsg, async (ctx, payload, sender) => {
1314
+ * sender.close(1000, 'Stopped by user');
1315
+ * });
1316
+ *
1317
+ * await router.handle(ctx, message, sender.raw);
1318
+ * }
1319
+ * }
1320
+ * });
1321
+ * ```
1322
+ */
1323
+ declare function defineWebSocket<TConfig = unknown, TIncoming = unknown, TOutgoing = WSMessage>(definition: WebSocketDefinition<TConfig, TIncoming, TOutgoing>): {
1324
+ execute(context: PluginContextV3<TConfig>, input: WSInput): Promise<CommandResult$1 | void>;
1325
+ };
1326
+ /**
1327
+ * Type guard to check if host context is WebSocket
1328
+ */
1329
+ declare function isWSHost(hostContext: HostContext): hostContext is Extract<HostContext, {
1330
+ host: 'ws';
1331
+ }>;
1332
+
1333
+ /**
1334
+ * Define a workflow action handler
1335
+ */
1336
+
1337
+ interface ActionHandler<TConfig = unknown, TInput = unknown> {
1338
+ /**
1339
+ * Execute the workflow action
1340
+ */
1341
+ execute(context: PluginContextV3<TConfig>, input: TInput): Promise<CommandResult$1 | void> | CommandResult$1 | void;
1342
+ /**
1343
+ * Optional cleanup - called after execute completes
1344
+ */
1345
+ cleanup?(): Promise<void> | void;
1346
+ }
1347
+ interface ActionDefinition<TConfig = unknown, TInput = unknown> {
1348
+ /**
1349
+ * Action ID (e.g., "send-email")
1350
+ */
1351
+ id: string;
1352
+ /**
1353
+ * Action description
1354
+ */
1355
+ description?: string;
1356
+ /**
1357
+ * Handler implementation
1358
+ */
1359
+ handler: ActionHandler<TConfig, TInput>;
1360
+ /**
1361
+ * Optional input schema validation (future: use Zod/JSON Schema)
1362
+ */
1363
+ schema?: unknown;
1364
+ }
1365
+ /**
1366
+ * Define a workflow action
1367
+ *
1368
+ * @example
1369
+ * ```typescript
1370
+ * export default defineAction({
1371
+ * id: 'send-email',
1372
+ * description: 'Send an email notification',
1373
+ * handler: {
1374
+ * async execute(context, input: { to: string; subject: string; body: string }) {
1375
+ * // Access workflow context
1376
+ * const { workflowId, runId, stepId } = context.hostContext as WorkflowHost;
1377
+ *
1378
+ * context.ui.info(`Sending email in workflow ${workflowId}`);
1379
+ *
1380
+ * // Send email...
1381
+ *
1382
+ * return {
1383
+ * data: { sent: true, messageId: '123' },
1384
+ * exitCode: 0,
1385
+ * };
1386
+ * }
1387
+ * }
1388
+ * });
1389
+ * ```
1390
+ */
1391
+ declare function defineAction<TConfig = unknown, TInput = unknown>(definition: ActionDefinition<TConfig, TInput>): ActionHandler<TConfig, TInput>;
1392
+ /**
1393
+ * Type guard to check if host context is workflow
1394
+ */
1395
+ declare function isWorkflowHost(hostContext: HostContext): hostContext is Extract<HostContext, {
1396
+ host: 'workflow';
1397
+ }>;
1398
+
1399
+ /**
1400
+ * Enhanced WebSocket types for better DX
1401
+ *
1402
+ * Provides type-safe message builders and pattern matching utilities.
1403
+ */
1404
+
1405
+ /**
1406
+ * Type-safe message builder
1407
+ *
1408
+ * @example
1409
+ * ```typescript
1410
+ * const Progress = defineMessage<{ phase: string; progress: number }>('progress');
1411
+ * const msg = Progress.create({ phase: 'analyzing', progress: 50 });
1412
+ * // msg: { type: 'progress', payload: { phase: 'analyzing', progress: 50 }, timestamp: number }
1413
+ * ```
1414
+ */
1415
+ declare class MessageBuilder<TPayload = unknown> {
1416
+ private type;
1417
+ constructor(type: string);
1418
+ /**
1419
+ * Create message with payload
1420
+ */
1421
+ create(payload: TPayload, messageId?: string): WSMessage;
1422
+ /**
1423
+ * Match against incoming message type (type guard)
1424
+ */
1425
+ is(message: WSMessage): message is WSMessage & {
1426
+ payload: TPayload;
1427
+ };
1428
+ }
1429
+ /**
1430
+ * Helper to define typed messages
1431
+ *
1432
+ * @example
1433
+ * ```typescript
1434
+ * const StartMsg = defineMessage<{ scope?: string }>('start');
1435
+ * const ProgressMsg = defineMessage<{ phase: string; progress: number }>('progress');
1436
+ * ```
1437
+ */
1438
+ declare function defineMessage<TPayload = unknown>(type: string): MessageBuilder<TPayload>;
1439
+ /**
1440
+ * Message router for pattern matching
1441
+ *
1442
+ * Provides elegant routing of incoming WebSocket messages to typed handlers.
1443
+ *
1444
+ * @example
1445
+ * ```typescript
1446
+ * const router = new MessageRouter()
1447
+ * .on(StartMsg, async (ctx, payload, sender) => {
1448
+ * // payload is typed as { scope?: string }
1449
+ * console.log('Starting with scope:', payload.scope);
1450
+ * })
1451
+ * .on(StopMsg, async (ctx, payload, sender) => {
1452
+ * // Different message type, different payload type
1453
+ * sender.close(1000, 'Stopped by user');
1454
+ * });
1455
+ *
1456
+ * await router.handle(ctx, message, sender);
1457
+ * ```
1458
+ */
1459
+ declare class MessageRouter<TConfig = unknown> {
1460
+ private handlers;
1461
+ /**
1462
+ * Register message handler
1463
+ */
1464
+ on<TPayload>(message: MessageBuilder<TPayload>, handler: (ctx: PluginContextV3<TConfig>, payload: TPayload, sender: WSSender) => Promise<void> | void): this;
1465
+ /**
1466
+ * Handle incoming message
1467
+ *
1468
+ * @returns true if message was handled, false otherwise
1469
+ */
1470
+ handle(ctx: PluginContextV3<TConfig>, message: WSMessage, sender: WSSender): Promise<boolean>;
1471
+ }
1472
+
1473
+ /**
1474
+ * @module @kb-labs/shared-command-kit
1475
+ * Command Kit - High-level API and utilities for building CLI commands
1476
+ */
1477
+
1478
+ /**
1479
+ * Command execution status
1480
+ */
1481
+ type CommandStatus = 'success' | 'failed' | 'error' | 'cancelled' | 'skipped' | 'warning' | 'info';
1482
+ /**
1483
+ * Helper types for command results (optional - use when convenient)
1484
+ *
1485
+ * These types make it easier to define command results without repeating `CommandResult &`.
1486
+ * They are completely optional - you can still use `CommandResult & { ... }` directly.
1487
+ *
1488
+ * @example
1489
+ * ```typescript
1490
+ * // Using helper types (optional)
1491
+ * type MyResult = SuccessResult<{ items: Item[]; total: number }>;
1492
+ *
1493
+ * // Direct usage (also works)
1494
+ * type MyResult = CommandResult & { ok: true; items: Item[]; total: number };
1495
+ * ```
1496
+ */
1497
+ type SuccessResult<T extends Record<string, unknown> = Record<string, never>> = CommandResult & {
1498
+ ok: true;
1499
+ } & T;
1500
+ type ErrorResult<T extends Record<string, unknown> = Record<string, never>> = CommandResult & {
1501
+ ok: false;
1502
+ error: string;
1503
+ } & T;
1504
+ type ResultWith<T extends Record<string, unknown>> = CommandResult & T;
1505
+ /**
1506
+ * Base command result contract - all command results must extend this
1507
+ *
1508
+ * This defines the minimal contract that every command must fulfill.
1509
+ * Every command MUST explicitly declare its result type via generic TResult parameter.
1510
+ *
1511
+ * Required fields:
1512
+ * - `ok: boolean` - execution success status
1513
+ *
1514
+ * Recommended fields:
1515
+ * - `error?: string` - error message when ok === false
1516
+ * - `status?: CommandStatus` - execution status (auto-inferred if not provided)
1517
+ *
1518
+ * Additional fields should be added via generic TResult type parameter.
1519
+ *
1520
+ * @example
1521
+ * ```typescript
1522
+ * // Minimal result (not recommended - use explicit type)
1523
+ * type MyResult = CommandResult; // { ok: boolean; error?: string; status?: CommandStatus }
1524
+ *
1525
+ * // Extended result with custom fields (RECOMMENDED)
1526
+ * type WorkflowRunResult = CommandResult & {
1527
+ * run: WorkflowRun;
1528
+ * timingMs: number;
1529
+ * };
1530
+ *
1531
+ * type ListResult = CommandResult & {
1532
+ * items: Item[];
1533
+ * total: number;
1534
+ * };
1535
+ * ```
1536
+ */
1537
+ type CommandResult = {
1538
+ /** Whether the command executed successfully - REQUIRED */
1539
+ ok: boolean;
1540
+ /** Error message if ok === false - RECOMMENDED for error cases */
1541
+ error?: string;
1542
+ /** Execution status - automatically inferred from ok if not provided */
1543
+ status?: CommandStatus;
1544
+ };
1545
+ /**
1546
+ * Command handler function signature
1547
+ *
1548
+ * TConfig is the product configuration type (auto-loaded from kb.config.json)
1549
+ * TEnv is the environment variables type (validated at runtime)
1550
+ * TResult must extend CommandResult ({ ok: boolean }) and represents the contract
1551
+ * for what the command returns. This ensures type safety and enables future contract
1552
+ * validation.
1553
+ *
1554
+ * TArgv is optional - by default it's `string[]`, but you can provide a tuple type
1555
+ * for strict argument typing if needed.
1556
+ *
1557
+ * @example
1558
+ * ```typescript
1559
+ * // Default: argv is string[]
1560
+ * handler: (ctx, argv, flags) => { ... }
1561
+ *
1562
+ * // With typed config
1563
+ * handler: (ctx, argv, flags) => {
1564
+ * ctx.config?.llmProvider // TypeScript knows the config type
1565
+ * }
1566
+ *
1567
+ * // With typed env
1568
+ * handler: (ctx, argv, flags) => {
1569
+ * ctx.env.OPENAI_API_KEY // TypeScript knows it's a string
1570
+ * }
1571
+ *
1572
+ * // Strict typing: argv is tuple ['workflow-id', ...string[]]
1573
+ * handler: (ctx, argv: ['workflow-id', ...string[]], flags) => {
1574
+ * const workflowId = argv[0]; // TypeScript knows it's 'workflow-id'
1575
+ * }
1576
+ * ```
1577
+ */
1578
+ type CommandHandler<_TConfig = any, TFlags extends Record<string, unknown> = Record<string, unknown>, TResult extends CommandResult = CommandResult, TArgv extends readonly string[] = string[], _TEnv = Record<string, string | undefined>> = (ctx: PluginContextV3, argv: TArgv, flags: TFlags) => Promise<number | TResult> | number | TResult;
1579
+ /**
1580
+ * Command formatter function
1581
+ *
1582
+ * All type parameters are optional with defaults - same as CommandHandler.
1583
+ * Use explicit types when you want type safety, skip them for simplicity.
1584
+ *
1585
+ * @example
1586
+ * ```typescript
1587
+ * // Default: argv is string[]
1588
+ * formatter: (result, ctx, flags) => { ... }
1589
+ *
1590
+ * // With typed config
1591
+ * formatter: (result, ctx, flags) => {
1592
+ * ctx.config?.llmProvider // TypeScript knows the config type
1593
+ * }
1594
+ *
1595
+ * // With typed env
1596
+ * formatter: (result, ctx, flags) => {
1597
+ * ctx.env.OPENAI_API_KEY // TypeScript knows it's a string
1598
+ * }
1599
+ *
1600
+ * // Strict typing: argv is tuple ['workflow-id', ...string[]]
1601
+ * formatter: (result, ctx, flags, argv: ['workflow-id', ...string[]]) => {
1602
+ * const workflowId = argv[0]; // TypeScript knows it's 'workflow-id'
1603
+ * }
1604
+ * ```
1605
+ */
1606
+ type CommandFormatter<_TConfig = any, TFlags extends Record<string, unknown> = Record<string, unknown>, TResult extends CommandResult = CommandResult, TArgv extends readonly string[] = string[], _TEnv = Record<string, string | undefined>> = (result: TResult, ctx: PluginContextV3, flags: TFlags, argv?: TArgv) => void;
1607
+ /**
1608
+ * Command configuration
1609
+ *
1610
+ * All type parameters are optional with sensible defaults:
1611
+ * - TConfig defaults to any (product config type)
1612
+ * - TFlags defaults to FlagSchemaDefinition (flags are Record<string, unknown>)
1613
+ * - TResult defaults to CommandResult (basic { ok: boolean })
1614
+ * - TArgv defaults to string[] (arguments are string[])
1615
+ * - TEnv defaults to Record<string, string | undefined> (process.env)
1616
+ *
1617
+ * Use explicit types when you want better type safety, but everything works without them.
1618
+ *
1619
+ * @example
1620
+ * ```typescript
1621
+ * // Default: argv is string[]
1622
+ * const cmd = defineCommand({
1623
+ * handler: (ctx, argv, flags) => { ... }
1624
+ * });
1625
+ *
1626
+ * // Simple usage with flags and result only (RECOMMENDED)
1627
+ * const cmd = defineCommand<MyFlags, MyResult>({
1628
+ * handler: (ctx, argv, flags) => {
1629
+ * // flags are typed as InferFlags<MyFlags>
1630
+ * return { ok: true, ...result };
1631
+ * }
1632
+ * });
1633
+ *
1634
+ * // With typed config (third parameter)
1635
+ * type MindConfig = { llmProvider: string; maxTokens: number };
1636
+ * const cmd = defineCommand<Flags, Result, MindConfig>({
1637
+ * handler: (ctx, argv, flags) => {
1638
+ * ctx.config?.llmProvider // TypeScript knows it's a string
1639
+ * }
1640
+ * });
1641
+ *
1642
+ * // With typed env (fifth parameter)
1643
+ * type Env = { OPENAI_API_KEY: string; DEBUG?: string };
1644
+ * const cmd = defineCommand<Flags, Result, Config, Argv, Env>({
1645
+ * handler: (ctx, argv, flags) => {
1646
+ * ctx.env.OPENAI_API_KEY // string (validated!)
1647
+ * }
1648
+ * });
1649
+ *
1650
+ * // Strict typing: argv is tuple ['workflow-id', ...string[]]
1651
+ * const cmd = defineCommand<Flags, Result, Config, ['workflow-id', ...string[]]>({
1652
+ * handler: (ctx, argv, flags) => {
1653
+ * const workflowId = argv[0]; // TypeScript knows it's 'workflow-id'
1654
+ * }
1655
+ * });
1656
+ * ```
1657
+ */
1658
+ interface CommandConfig<TFlags extends FlagSchemaDefinition$1 = FlagSchemaDefinition$1, TResult extends CommandResult = CommandResult, TConfig = any, TArgv extends readonly string[] = string[], TEnv = Record<string, string | undefined>> {
1659
+ /** Command name (for logging) */
1660
+ name?: string;
1661
+ /** Flag schema definition */
1662
+ flags: TFlags;
1663
+ /**
1664
+ * Analytics configuration (legacy field, kept for backward compatibility)
1665
+ * Use ctx.platform.analytics.track() or withAnalytics() helper instead
1666
+ */
1667
+ analytics?: {
1668
+ command?: string;
1669
+ startEvent?: string;
1670
+ finishEvent?: string;
1671
+ actor?: string;
1672
+ context?: Record<string, unknown>;
1673
+ includeFlags?: boolean;
1674
+ };
1675
+ /**
1676
+ * Optional environment variable schema for validation.
1677
+ * If provided, required env vars will be validated at runtime.
1678
+ *
1679
+ * @example
1680
+ * ```typescript
1681
+ * env: {
1682
+ * OPENAI_API_KEY: { required: true },
1683
+ * DEBUG: { required: false },
1684
+ * }
1685
+ * ```
1686
+ */
1687
+ env?: Record<string, {
1688
+ required?: boolean;
1689
+ }>;
1690
+ /** Command handler - must return TResult */
1691
+ handler: CommandHandler<TConfig, InferFlags<TFlags>, TResult, TArgv, TEnv>;
1692
+ /** Optional formatter for output - receives TResult */
1693
+ formatter?: CommandFormatter<TConfig, InferFlags<TFlags>, TResult, TArgv, TEnv>;
1694
+ }
1695
+
1696
+ export { type ActionDefinition, type ActionHandler, type CLIInput, type Command, type CommandConfig, type CommandDefinition, type CommandFormatter, type CommandGroup, type CommandHandler, type CommandHandlerV3, type CommandResult, type CommandStatus, type ConfigUpdates, type DefinedJob, type DestroyHandlerDefinition, type ErrorResult, FlagSchemaDefinition$1 as FlagSchemaDefinition, type Handler, type HandlerDefinition, InferFlags, type JobDefinition, type JobHandler, type JobInput, type LifecycleContext, MessageBuilder, MessageRouter, type RestInput, type ResultWith, type RouteDefinition, type RouteHandler, type SetupHandlerDefinition, type SetupResult, type SuccessResult, type SystemCommandConfig, type TypedSender, type WebSocketDefinition, type WebSocketHandler, type WebhookDefinition, type WebhookHandler, type WorkspaceConfig, array, artifactId, boolean, commandId, cwd, datetime, defineAction, defineCommand, defineCommandFlags, defineDestroyHandler, defineHandler, defineJob, defineManifest, defineMessage, defineRoute, defineSetupHandler, defineSystemCommand, defineSystemCommandGroup, defineWebSocket, defineWebhook, email, enumSchema, filePath, isCLIHost, isRESTHost, isWSHost, isWebhookHost, isWorkflowHost, json, nonNegativeInt, object, pluginId, positiveInt, schema, scopeId, text, url, uuid };