@kb-labs/shared 1.1.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 (232) hide show
  1. package/.cursorrules +32 -0
  2. package/.github/workflows/ci.yml +13 -0
  3. package/.github/workflows/deploy.yml +28 -0
  4. package/.github/workflows/docker-build.yml +25 -0
  5. package/.github/workflows/drift-check.yml +10 -0
  6. package/.github/workflows/profiles-validate.yml +16 -0
  7. package/.github/workflows/release.yml +8 -0
  8. package/.kb/devkit/agents/devkit-maintainer/context.globs +15 -0
  9. package/.kb/devkit/agents/devkit-maintainer/permissions.yml +17 -0
  10. package/.kb/devkit/agents/devkit-maintainer/prompt.md +28 -0
  11. package/.kb/devkit/agents/devkit-maintainer/runbook.md +31 -0
  12. package/.kb/devkit/agents/docs-crafter/prompt.md +24 -0
  13. package/.kb/devkit/agents/docs-crafter/runbook.md +18 -0
  14. package/.kb/devkit/agents/release-manager/context.globs +7 -0
  15. package/.kb/devkit/agents/release-manager/prompt.md +27 -0
  16. package/.kb/devkit/agents/release-manager/runbook.md +17 -0
  17. package/.kb/devkit/agents/test-generator/context.globs +7 -0
  18. package/.kb/devkit/agents/test-generator/prompt.md +27 -0
  19. package/.kb/devkit/agents/test-generator/runbook.md +18 -0
  20. package/.vscode/settings.json +23 -0
  21. package/CHANGELOG.md +33 -0
  22. package/CONTRIBUTING.md +117 -0
  23. package/LICENSE +21 -0
  24. package/README.md +306 -0
  25. package/docs/DECLARATIVE-FLAGS-AND-ENV.md +622 -0
  26. package/docs/DOCUMENTATION.md +70 -0
  27. package/docs/adr/0000-template.md +52 -0
  28. package/docs/adr/0001-architecture-and-repository-layout.md +31 -0
  29. package/docs/adr/0002-plugins-and-extensibility.md +44 -0
  30. package/docs/adr/0003-package-and-module-boundaries.md +35 -0
  31. package/docs/adr/0004-versioning-and-release-policy.md +36 -0
  32. package/docs/adr/0005-reactive-loader-pattern.md +179 -0
  33. package/docs/adr/0006-declarative-flags-and-env-systems.md +376 -0
  34. package/eslint.config.js +27 -0
  35. package/kb-labs.config.json +5 -0
  36. package/package.json +88 -0
  37. package/package.json.bin +25 -0
  38. package/package.json.lib +30 -0
  39. package/packages/shared-cli-ui/CHANGELOG.md +20 -0
  40. package/packages/shared-cli-ui/README.md +342 -0
  41. package/packages/shared-cli-ui/docs/ARCHITECTURE.md +105 -0
  42. package/packages/shared-cli-ui/eslint.config.js +27 -0
  43. package/packages/shared-cli-ui/package.json +72 -0
  44. package/packages/shared-cli-ui/src/__tests__/artifacts-display.spec.ts +89 -0
  45. package/packages/shared-cli-ui/src/__tests__/format.spec.ts +44 -0
  46. package/packages/shared-cli-ui/src/__tests__/loader-json-mode.test.ts +119 -0
  47. package/packages/shared-cli-ui/src/artifacts-display.ts +266 -0
  48. package/packages/shared-cli-ui/src/cli-auto-discovery.ts +120 -0
  49. package/packages/shared-cli-ui/src/colors.ts +142 -0
  50. package/packages/shared-cli-ui/src/command-discovery.ts +72 -0
  51. package/packages/shared-cli-ui/src/command-output.ts +153 -0
  52. package/packages/shared-cli-ui/src/command-result.ts +267 -0
  53. package/packages/shared-cli-ui/src/command-runner.ts +310 -0
  54. package/packages/shared-cli-ui/src/command-suggestions.ts +204 -0
  55. package/packages/shared-cli-ui/src/debug/components/output.ts +141 -0
  56. package/packages/shared-cli-ui/src/debug/components/trace.ts +101 -0
  57. package/packages/shared-cli-ui/src/debug/components/tree.ts +88 -0
  58. package/packages/shared-cli-ui/src/debug/formatters/ai.ts +17 -0
  59. package/packages/shared-cli-ui/src/debug/formatters/human.ts +98 -0
  60. package/packages/shared-cli-ui/src/debug/formatters/timeline.ts +94 -0
  61. package/packages/shared-cli-ui/src/debug/index.ts +56 -0
  62. package/packages/shared-cli-ui/src/debug/types.ts +57 -0
  63. package/packages/shared-cli-ui/src/debug/utilities.ts +203 -0
  64. package/packages/shared-cli-ui/src/dynamic-command-discovery.ts +131 -0
  65. package/packages/shared-cli-ui/src/format.ts +412 -0
  66. package/packages/shared-cli-ui/src/index.ts +34 -0
  67. package/packages/shared-cli-ui/src/loader.ts +196 -0
  68. package/packages/shared-cli-ui/src/manifest-parser.ts +151 -0
  69. package/packages/shared-cli-ui/src/modern-format.ts +271 -0
  70. package/packages/shared-cli-ui/src/multi-cli-suggestions.ts +159 -0
  71. package/packages/shared-cli-ui/src/table.ts +134 -0
  72. package/packages/shared-cli-ui/src/timing-tracker.ts +68 -0
  73. package/packages/shared-cli-ui/src/utils/context.ts +12 -0
  74. package/packages/shared-cli-ui/src/utils/env.ts +164 -0
  75. package/packages/shared-cli-ui/src/utils/flags.ts +269 -0
  76. package/packages/shared-cli-ui/src/utils/path.ts +8 -0
  77. package/packages/shared-cli-ui/tsconfig.build.json +15 -0
  78. package/packages/shared-cli-ui/tsconfig.json +9 -0
  79. package/packages/shared-cli-ui/tsup.config.ts +11 -0
  80. package/packages/shared-cli-ui/vitest.config.ts +15 -0
  81. package/packages/shared-command-kit/CHANGELOG.md +20 -0
  82. package/packages/shared-command-kit/LICENSE +22 -0
  83. package/packages/shared-command-kit/README.md +1030 -0
  84. package/packages/shared-command-kit/docs/HIGH-LEVEL-API.md +89 -0
  85. package/packages/shared-command-kit/docs/LOW-LEVEL-API.md +105 -0
  86. package/packages/shared-command-kit/docs/MIGRATION-GUIDE.md +135 -0
  87. package/packages/shared-command-kit/eslint.config.js +27 -0
  88. package/packages/shared-command-kit/eslint.config.ts +14 -0
  89. package/packages/shared-command-kit/package.json +76 -0
  90. package/packages/shared-command-kit/prettierrc.json +5 -0
  91. package/packages/shared-command-kit/src/__tests__/define-command.spec.ts +294 -0
  92. package/packages/shared-command-kit/src/__tests__/define-route.test.ts +285 -0
  93. package/packages/shared-command-kit/src/__tests__/define-system-command.spec.ts +508 -0
  94. package/packages/shared-command-kit/src/__tests__/define-webhook.test.ts +156 -0
  95. package/packages/shared-command-kit/src/__tests__/define-websocket.test.ts +316 -0
  96. package/packages/shared-command-kit/src/__tests__/errors.spec.ts +45 -0
  97. package/packages/shared-command-kit/src/__tests__/flags.spec.ts +353 -0
  98. package/packages/shared-command-kit/src/__tests__/platform-api.test.ts +135 -0
  99. package/packages/shared-command-kit/src/__tests__/plugin-context-v3.snapshot.spec.ts +240 -0
  100. package/packages/shared-command-kit/src/__tests__/ws-types.test.ts +359 -0
  101. package/packages/shared-command-kit/src/analytics/index.ts +6 -0
  102. package/packages/shared-command-kit/src/analytics/with-analytics.ts +195 -0
  103. package/packages/shared-command-kit/src/define-action.ts +100 -0
  104. package/packages/shared-command-kit/src/define-command.ts +113 -0
  105. package/packages/shared-command-kit/src/define-route.ts +113 -0
  106. package/packages/shared-command-kit/src/define-system-command.ts +362 -0
  107. package/packages/shared-command-kit/src/define-webhook.ts +115 -0
  108. package/packages/shared-command-kit/src/define-websocket.ts +308 -0
  109. package/packages/shared-command-kit/src/errors/factory.ts +282 -0
  110. package/packages/shared-command-kit/src/errors/format-validation.ts +144 -0
  111. package/packages/shared-command-kit/src/errors/format.ts +92 -0
  112. package/packages/shared-command-kit/src/errors/index.ts +9 -0
  113. package/packages/shared-command-kit/src/errors/types.ts +32 -0
  114. package/packages/shared-command-kit/src/flags/define.ts +92 -0
  115. package/packages/shared-command-kit/src/flags/index.ts +9 -0
  116. package/packages/shared-command-kit/src/flags/types.ts +153 -0
  117. package/packages/shared-command-kit/src/flags/validate.ts +358 -0
  118. package/packages/shared-command-kit/src/helpers/context.ts +8 -0
  119. package/packages/shared-command-kit/src/helpers/flags.ts +84 -0
  120. package/packages/shared-command-kit/src/helpers/index.ts +42 -0
  121. package/packages/shared-command-kit/src/helpers/patterns.ts +464 -0
  122. package/packages/shared-command-kit/src/helpers/platform.ts +335 -0
  123. package/packages/shared-command-kit/src/helpers/use-analytics.ts +95 -0
  124. package/packages/shared-command-kit/src/helpers/use-cache.ts +97 -0
  125. package/packages/shared-command-kit/src/helpers/use-config.ts +99 -0
  126. package/packages/shared-command-kit/src/helpers/use-embeddings.ts +49 -0
  127. package/packages/shared-command-kit/src/helpers/use-llm.ts +316 -0
  128. package/packages/shared-command-kit/src/helpers/use-logger.ts +77 -0
  129. package/packages/shared-command-kit/src/helpers/use-platform.ts +111 -0
  130. package/packages/shared-command-kit/src/helpers/use-resource-broker.ts +106 -0
  131. package/packages/shared-command-kit/src/helpers/use-storage.ts +71 -0
  132. package/packages/shared-command-kit/src/helpers/use-vector-store.ts +49 -0
  133. package/packages/shared-command-kit/src/helpers/validation.ts +398 -0
  134. package/packages/shared-command-kit/src/index.ts +410 -0
  135. package/packages/shared-command-kit/src/jobs.ts +132 -0
  136. package/packages/shared-command-kit/src/lifecycle/define-handlers.ts +366 -0
  137. package/packages/shared-command-kit/src/lifecycle/index.ts +6 -0
  138. package/packages/shared-command-kit/src/manifest.ts +127 -0
  139. package/packages/shared-command-kit/src/rest/define-handler.ts +187 -0
  140. package/packages/shared-command-kit/src/rest/index.ts +11 -0
  141. package/packages/shared-command-kit/src/studio/index.ts +12 -0
  142. package/packages/shared-command-kit/src/validation/index.ts +6 -0
  143. package/packages/shared-command-kit/src/validation/schema-builders.ts +409 -0
  144. package/packages/shared-command-kit/src/ws-types.ts +106 -0
  145. package/packages/shared-command-kit/tsconfig.build.json +15 -0
  146. package/packages/shared-command-kit/tsconfig.json +9 -0
  147. package/packages/shared-command-kit/tsup.config.ts +30 -0
  148. package/packages/shared-command-kit/vitest.config.ts +4 -0
  149. package/packages/shared-http/package.json +67 -0
  150. package/packages/shared-http/src/__tests__/log-correlation.test.ts +81 -0
  151. package/packages/shared-http/src/__tests__/operation-metrics-tracker.test.ts +55 -0
  152. package/packages/shared-http/src/http-observability-collector.ts +363 -0
  153. package/packages/shared-http/src/index.ts +36 -0
  154. package/packages/shared-http/src/log-correlation.ts +89 -0
  155. package/packages/shared-http/src/operation-metrics-tracker.ts +107 -0
  156. package/packages/shared-http/src/register-openapi.ts +108 -0
  157. package/packages/shared-http/src/resolve-schema-ref.ts +75 -0
  158. package/packages/shared-http/src/schemas.ts +29 -0
  159. package/packages/shared-http/src/service-observability.ts +63 -0
  160. package/packages/shared-http/tsconfig.build.json +15 -0
  161. package/packages/shared-http/tsconfig.json +9 -0
  162. package/packages/shared-http/tsup.config.ts +23 -0
  163. package/packages/shared-http/vitest.config.ts +13 -0
  164. package/packages/shared-perm-presets/CHANGELOG.md +20 -0
  165. package/packages/shared-perm-presets/README.md +78 -0
  166. package/packages/shared-perm-presets/eslint.config.js +27 -0
  167. package/packages/shared-perm-presets/package.json +45 -0
  168. package/packages/shared-perm-presets/src/__tests__/combine.test.ts +403 -0
  169. package/packages/shared-perm-presets/src/__tests__/presets.test.ts +205 -0
  170. package/packages/shared-perm-presets/src/combine.ts +278 -0
  171. package/packages/shared-perm-presets/src/index.ts +18 -0
  172. package/packages/shared-perm-presets/src/presets/ci-environment.ts +34 -0
  173. package/packages/shared-perm-presets/src/presets/full-env.ts +16 -0
  174. package/packages/shared-perm-presets/src/presets/git-workflow.ts +40 -0
  175. package/packages/shared-perm-presets/src/presets/index.ts +8 -0
  176. package/packages/shared-perm-presets/src/presets/kb-platform.ts +30 -0
  177. package/packages/shared-perm-presets/src/presets/llm-access.ts +29 -0
  178. package/packages/shared-perm-presets/src/presets/minimal.ts +21 -0
  179. package/packages/shared-perm-presets/src/presets/npm-publish.ts +48 -0
  180. package/packages/shared-perm-presets/src/presets/vector-store.ts +40 -0
  181. package/packages/shared-perm-presets/src/types.ts +192 -0
  182. package/packages/shared-perm-presets/tsconfig.build.json +15 -0
  183. package/packages/shared-perm-presets/tsconfig.json +9 -0
  184. package/packages/shared-perm-presets/tsup.config.ts +8 -0
  185. package/packages/shared-perm-presets/vitest.config.ts +9 -0
  186. package/packages/shared-testing/CHANGELOG.md +20 -0
  187. package/packages/shared-testing/README.md +430 -0
  188. package/packages/shared-testing/package.json +51 -0
  189. package/packages/shared-testing/src/__tests__/create-test-context.test.ts +199 -0
  190. package/packages/shared-testing/src/__tests__/mock-cache.test.ts +174 -0
  191. package/packages/shared-testing/src/__tests__/mock-llm.test.ts +212 -0
  192. package/packages/shared-testing/src/__tests__/setup-platform.test.ts +90 -0
  193. package/packages/shared-testing/src/__tests__/test-command.test.ts +557 -0
  194. package/packages/shared-testing/src/create-test-context.ts +550 -0
  195. package/packages/shared-testing/src/index.ts +77 -0
  196. package/packages/shared-testing/src/mock-cache.ts +179 -0
  197. package/packages/shared-testing/src/mock-llm.ts +319 -0
  198. package/packages/shared-testing/src/mock-logger.ts +97 -0
  199. package/packages/shared-testing/src/mock-storage.ts +108 -0
  200. package/packages/shared-testing/src/setup-platform.ts +101 -0
  201. package/packages/shared-testing/src/test-command.ts +288 -0
  202. package/packages/shared-testing/tsconfig.build.json +15 -0
  203. package/packages/shared-testing/tsconfig.json +9 -0
  204. package/packages/shared-testing/tsup.config.ts +20 -0
  205. package/packages/shared-testing/vitest.config.ts +3 -0
  206. package/packages/shared-tool-kit/CHANGELOG.md +20 -0
  207. package/packages/shared-tool-kit/package.json +47 -0
  208. package/packages/shared-tool-kit/src/__tests__/factory.test.ts +103 -0
  209. package/packages/shared-tool-kit/src/__tests__/mock-tool.test.ts +95 -0
  210. package/packages/shared-tool-kit/src/factory.ts +126 -0
  211. package/packages/shared-tool-kit/src/index.ts +32 -0
  212. package/packages/shared-tool-kit/src/testing/index.ts +84 -0
  213. package/packages/shared-tool-kit/tsconfig.build.json +15 -0
  214. package/packages/shared-tool-kit/tsconfig.json +9 -0
  215. package/packages/shared-tool-kit/tsup.config.ts +21 -0
  216. package/pnpm-workspace.yaml +11070 -0
  217. package/prettierrc.json +1 -0
  218. package/scripts/devkit-sync.mjs +37 -0
  219. package/scripts/hooks/post-push +9 -0
  220. package/scripts/hooks/pre-commit +9 -0
  221. package/scripts/hooks/pre-push +9 -0
  222. package/tsconfig.base.json +9 -0
  223. package/tsconfig.build.json +15 -0
  224. package/tsconfig.json +9 -0
  225. package/tsconfig.paths.json +50 -0
  226. package/tsconfig.tools.json +18 -0
  227. package/tsup.config.bin.ts +34 -0
  228. package/tsup.config.cli.ts +41 -0
  229. package/tsup.config.dual.ts +46 -0
  230. package/tsup.config.ts +36 -0
  231. package/tsup.external.json +104 -0
  232. package/vitest.config.ts +48 -0
@@ -0,0 +1,410 @@
1
+ /**
2
+ * @module @kb-labs/shared-command-kit
3
+ * Command Kit - High-level API and utilities for building CLI commands
4
+ */
5
+
6
+ import type { PluginContextV3 } from '@kb-labs/plugin-contracts';
7
+ import type { FlagSchemaDefinition, InferFlags } from './flags/index';
8
+
9
+ // Re-exports
10
+ export * from './flags/index';
11
+ export * from './analytics/index';
12
+ export * from './errors/index';
13
+ export * from './helpers/index';
14
+ export * from './define-system-command';
15
+ // Removed: output-helpers (success, error, warning, info, result helpers) - no longer used
16
+ export * from './manifest';
17
+ // TODO: V3 migration - permissions helpers need to be rewritten for V3 PermissionSpec structure
18
+ // export * from './permissions';
19
+ export * from './validation/index';
20
+ export * from './rest/index';
21
+ export * from './lifecycle/index';
22
+ // TODO: Remove studio - it's a stub that throws error, not implemented
23
+ // export * from './studio/index';
24
+ export * from './jobs';
25
+ export type { CommandOutput } from '@kb-labs/shared-cli-ui';
26
+
27
+ // Plugin handler definitions (CLI, REST, Webhooks, WebSockets, Workflows)
28
+ export * from './define-command';
29
+ export * from './define-route';
30
+ export * from './define-webhook';
31
+ export * from './define-websocket';
32
+ export * from './define-action';
33
+ export * from './ws-types';
34
+
35
+ /**
36
+ * Command execution status
37
+ */
38
+ // Note: 'warning' and 'info' are legacy statuses from cli-ui, they may be used for display purposes
39
+ // but semantically a command is either successful or not
40
+ export type CommandStatus = 'success' | 'failed' | 'error' | 'cancelled' | 'skipped' | 'warning' | 'info';
41
+
42
+ /**
43
+ * Helper types for command results (optional - use when convenient)
44
+ *
45
+ * These types make it easier to define command results without repeating `CommandResult &`.
46
+ * They are completely optional - you can still use `CommandResult & { ... }` directly.
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * // Using helper types (optional)
51
+ * type MyResult = SuccessResult<{ items: Item[]; total: number }>;
52
+ *
53
+ * // Direct usage (also works)
54
+ * type MyResult = CommandResult & { ok: true; items: Item[]; total: number };
55
+ * ```
56
+ */
57
+ export type SuccessResult<T extends Record<string, unknown> = Record<string, never>> = CommandResult & { ok: true } & T;
58
+ export type ErrorResult<T extends Record<string, unknown> = Record<string, never>> = CommandResult & { ok: false; error: string } & T;
59
+ export type ResultWith<T extends Record<string, unknown>> = CommandResult & T;
60
+
61
+ /**
62
+ * Base command result contract - all command results must extend this
63
+ *
64
+ * This defines the minimal contract that every command must fulfill.
65
+ * Every command MUST explicitly declare its result type via generic TResult parameter.
66
+ *
67
+ * Required fields:
68
+ * - `ok: boolean` - execution success status
69
+ *
70
+ * Recommended fields:
71
+ * - `error?: string` - error message when ok === false
72
+ * - `status?: CommandStatus` - execution status (auto-inferred if not provided)
73
+ *
74
+ * Additional fields should be added via generic TResult type parameter.
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * // Minimal result (not recommended - use explicit type)
79
+ * type MyResult = CommandResult; // { ok: boolean; error?: string; status?: CommandStatus }
80
+ *
81
+ * // Extended result with custom fields (RECOMMENDED)
82
+ * type WorkflowRunResult = CommandResult & {
83
+ * run: WorkflowRun;
84
+ * timingMs: number;
85
+ * };
86
+ *
87
+ * type ListResult = CommandResult & {
88
+ * items: Item[];
89
+ * total: number;
90
+ * };
91
+ * ```
92
+ */
93
+ export type CommandResult = {
94
+ /** Whether the command executed successfully - REQUIRED */
95
+ ok: boolean;
96
+ /** Error message if ok === false - RECOMMENDED for error cases */
97
+ error?: string;
98
+ /** Execution status - automatically inferred from ok if not provided */
99
+ status?: CommandStatus;
100
+ };
101
+
102
+ /**
103
+ * Command handler function signature
104
+ *
105
+ * TConfig is the product configuration type (auto-loaded from kb.config.json)
106
+ * TEnv is the environment variables type (validated at runtime)
107
+ * TResult must extend CommandResult ({ ok: boolean }) and represents the contract
108
+ * for what the command returns. This ensures type safety and enables future contract
109
+ * validation.
110
+ *
111
+ * TArgv is optional - by default it's `string[]`, but you can provide a tuple type
112
+ * for strict argument typing if needed.
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * // Default: argv is string[]
117
+ * handler: (ctx, argv, flags) => { ... }
118
+ *
119
+ * // With typed config
120
+ * handler: (ctx, argv, flags) => {
121
+ * ctx.config?.llmProvider // TypeScript knows the config type
122
+ * }
123
+ *
124
+ * // With typed env
125
+ * handler: (ctx, argv, flags) => {
126
+ * ctx.env.OPENAI_API_KEY // TypeScript knows it's a string
127
+ * }
128
+ *
129
+ * // Strict typing: argv is tuple ['workflow-id', ...string[]]
130
+ * handler: (ctx, argv: ['workflow-id', ...string[]], flags) => {
131
+ * const workflowId = argv[0]; // TypeScript knows it's 'workflow-id'
132
+ * }
133
+ * ```
134
+ */
135
+ export type CommandHandler<
136
+ _TConfig = any,
137
+ TFlags extends Record<string, unknown> = Record<string, unknown>,
138
+ TResult extends CommandResult = CommandResult,
139
+ TArgv extends readonly string[] = string[],
140
+ _TEnv = Record<string, string | undefined>
141
+ > = (
142
+ ctx: PluginContextV3,
143
+ argv: TArgv,
144
+ flags: TFlags
145
+ ) => Promise<number | TResult> | number | TResult;
146
+
147
+ /**
148
+ * Command formatter function
149
+ *
150
+ * All type parameters are optional with defaults - same as CommandHandler.
151
+ * Use explicit types when you want type safety, skip them for simplicity.
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * // Default: argv is string[]
156
+ * formatter: (result, ctx, flags) => { ... }
157
+ *
158
+ * // With typed config
159
+ * formatter: (result, ctx, flags) => {
160
+ * ctx.config?.llmProvider // TypeScript knows the config type
161
+ * }
162
+ *
163
+ * // With typed env
164
+ * formatter: (result, ctx, flags) => {
165
+ * ctx.env.OPENAI_API_KEY // TypeScript knows it's a string
166
+ * }
167
+ *
168
+ * // Strict typing: argv is tuple ['workflow-id', ...string[]]
169
+ * formatter: (result, ctx, flags, argv: ['workflow-id', ...string[]]) => {
170
+ * const workflowId = argv[0]; // TypeScript knows it's 'workflow-id'
171
+ * }
172
+ * ```
173
+ */
174
+ export type CommandFormatter<
175
+ _TConfig = any,
176
+ TFlags extends Record<string, unknown> = Record<string, unknown>,
177
+ TResult extends CommandResult = CommandResult,
178
+ TArgv extends readonly string[] = string[],
179
+ _TEnv = Record<string, string | undefined>
180
+ > = (
181
+ result: TResult,
182
+ ctx: PluginContextV3,
183
+ flags: TFlags,
184
+ argv?: TArgv
185
+ ) => void;
186
+
187
+ /**
188
+ * Command configuration
189
+ *
190
+ * All type parameters are optional with sensible defaults:
191
+ * - TConfig defaults to any (product config type)
192
+ * - TFlags defaults to FlagSchemaDefinition (flags are Record<string, unknown>)
193
+ * - TResult defaults to CommandResult (basic { ok: boolean })
194
+ * - TArgv defaults to string[] (arguments are string[])
195
+ * - TEnv defaults to Record<string, string | undefined> (process.env)
196
+ *
197
+ * Use explicit types when you want better type safety, but everything works without them.
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * // Default: argv is string[]
202
+ * const cmd = defineCommand({
203
+ * handler: (ctx, argv, flags) => { ... }
204
+ * });
205
+ *
206
+ * // Simple usage with flags and result only (RECOMMENDED)
207
+ * const cmd = defineCommand<MyFlags, MyResult>({
208
+ * handler: (ctx, argv, flags) => {
209
+ * // flags are typed as InferFlags<MyFlags>
210
+ * return { ok: true, ...result };
211
+ * }
212
+ * });
213
+ *
214
+ * // With typed config (third parameter)
215
+ * type MindConfig = { llmProvider: string; maxTokens: number };
216
+ * const cmd = defineCommand<Flags, Result, MindConfig>({
217
+ * handler: (ctx, argv, flags) => {
218
+ * ctx.config?.llmProvider // TypeScript knows it's a string
219
+ * }
220
+ * });
221
+ *
222
+ * // With typed env (fifth parameter)
223
+ * type Env = { OPENAI_API_KEY: string; DEBUG?: string };
224
+ * const cmd = defineCommand<Flags, Result, Config, Argv, Env>({
225
+ * handler: (ctx, argv, flags) => {
226
+ * ctx.env.OPENAI_API_KEY // string (validated!)
227
+ * }
228
+ * });
229
+ *
230
+ * // Strict typing: argv is tuple ['workflow-id', ...string[]]
231
+ * const cmd = defineCommand<Flags, Result, Config, ['workflow-id', ...string[]]>({
232
+ * handler: (ctx, argv, flags) => {
233
+ * const workflowId = argv[0]; // TypeScript knows it's 'workflow-id'
234
+ * }
235
+ * });
236
+ * ```
237
+ */
238
+ export interface CommandConfig<
239
+ TFlags extends FlagSchemaDefinition = FlagSchemaDefinition,
240
+ TResult extends CommandResult = CommandResult,
241
+ TConfig = any,
242
+ TArgv extends readonly string[] = string[],
243
+ TEnv = Record<string, string | undefined>
244
+ > {
245
+ /** Command name (for logging) */
246
+ name?: string;
247
+ /** Flag schema definition */
248
+ flags: TFlags;
249
+ /**
250
+ * Analytics configuration (legacy field, kept for backward compatibility)
251
+ * Use ctx.platform.analytics.track() or withAnalytics() helper instead
252
+ */
253
+ analytics?: {
254
+ command?: string;
255
+ startEvent?: string;
256
+ finishEvent?: string;
257
+ actor?: string;
258
+ context?: Record<string, unknown>;
259
+ includeFlags?: boolean;
260
+ };
261
+ /**
262
+ * Optional environment variable schema for validation.
263
+ * If provided, required env vars will be validated at runtime.
264
+ *
265
+ * @example
266
+ * ```typescript
267
+ * env: {
268
+ * OPENAI_API_KEY: { required: true },
269
+ * DEBUG: { required: false },
270
+ * }
271
+ * ```
272
+ */
273
+ env?: Record<string, { required?: boolean }>;
274
+ /** Command handler - must return TResult */
275
+ handler: CommandHandler<TConfig, InferFlags<TFlags>, TResult, TArgv, TEnv>;
276
+ /** Optional formatter for output - receives TResult */
277
+ formatter?: CommandFormatter<TConfig, InferFlags<TFlags>, TResult, TArgv, TEnv>;
278
+ }
279
+
280
+ /**
281
+ * Define a command with automatic validation, logging, analytics, and error handling
282
+ *
283
+ * Returns a function with signature `(ctx, argv, flags) => Promise<number>`
284
+ * that can be used as a command handler in manifest.v2.ts
285
+ *
286
+ * All type parameters are optional - use them when you want type safety, skip them for simplicity.
287
+ *
288
+ * @template TFlags - Flag schema definition type (FIRST - most commonly used)
289
+ * @template TResult - Command result type (must extend CommandResult)
290
+ * @template TConfig - Product configuration type (auto-loaded from kb.config.json)
291
+ * @template TArgv - Argument tuple type (defaults to string[])
292
+ * @template TEnv - Environment variables type (defaults to process.env)
293
+ *
294
+ * @example
295
+ * ```typescript
296
+ * // Simple command with flags and result (RECOMMENDED)
297
+ * export const releaseRunHandler = defineCommand<
298
+ * { scope: { type: 'string'; required: true } }, // flags
299
+ * CommandResult & { published: number } // result
300
+ * >({
301
+ * name: 'release:run',
302
+ * flags: {
303
+ * scope: { type: 'string', required: true },
304
+ * 'dry-run': { type: 'boolean', default: false },
305
+ * },
306
+ * analytics: {
307
+ * startEvent: 'RELEASE_RUN_STARTED',
308
+ * finishEvent: 'RELEASE_RUN_FINISHED',
309
+ * },
310
+ * async handler(ctx, argv, flags) {
311
+ * ctx.platform?.logger?.info('Release started', { scope: flags.scope });
312
+ * ctx.tracker.checkpoint('planning');
313
+ * // ... business logic
314
+ * return { ok: true, published: 5 };
315
+ * },
316
+ * });
317
+ *
318
+ * // Command with typed config (third parameter)
319
+ * type MindConfig = { llmProvider: string; maxTokens: number };
320
+ * type RagQueryFlags = {
321
+ * text: { type: 'string'; required: true };
322
+ * mode: { type: 'string' };
323
+ * };
324
+ * type RagQueryResult = CommandResult & { answer: string; confidence: number };
325
+ *
326
+ * export const ragQueryHandler = defineCommand<
327
+ * RagQueryFlags,
328
+ * RagQueryResult,
329
+ * MindConfig
330
+ * >({
331
+ * flags: {
332
+ * text: { type: 'string', required: true },
333
+ * mode: { type: 'string', default: 'instant' },
334
+ * },
335
+ * async handler(ctx, argv, flags) {
336
+ * // ctx.config is typed as MindConfig | undefined
337
+ * const provider = ctx.config?.llmProvider ?? 'openai';
338
+ * const maxTokens = ctx.config?.maxTokens ?? 4000;
339
+ *
340
+ * // flags.text is typed as string (required)
341
+ * // flags.mode is typed as string | undefined
342
+ *
343
+ * const answer = await query(flags.text, { provider, maxTokens });
344
+ * return { ok: true, answer, confidence: 0.85 };
345
+ * },
346
+ * });
347
+ *
348
+ * // Command with typed env variables (fifth parameter)
349
+ * type Env = {
350
+ * OPENAI_API_KEY: string;
351
+ * DEBUG?: string;
352
+ * };
353
+ *
354
+ * export const envAwareHandler = defineCommand<Flags, Result, Config, Argv, Env>({
355
+ * env: {
356
+ * OPENAI_API_KEY: { required: true },
357
+ * DEBUG: { required: false },
358
+ * },
359
+ * flags: { ... },
360
+ * async handler(ctx, argv, flags) {
361
+ * // ctx.env.OPENAI_API_KEY is typed as string (validated!)
362
+ * const apiKey = ctx.env.OPENAI_API_KEY;
363
+ * // ctx.env.DEBUG is typed as string | undefined
364
+ * const debug = ctx.env.DEBUG;
365
+ * return { ok: true };
366
+ * },
367
+ * });
368
+ *
369
+ * // Command with UI output (new convenience methods)
370
+ * export const processDataHandler = defineCommand<
371
+ * { json: { type: 'boolean' } },
372
+ * CommandResult & { processed: number; items: string[] }
373
+ * >({
374
+ * name: 'process:data',
375
+ * flags: {
376
+ * json: { type: 'boolean', default: false },
377
+ * },
378
+ * async handler(ctx, argv, flags) {
379
+ * // Show progress (CLI only, no-op in REST/Workflow)
380
+ * ctx.ui?.startProgress('loading', 'Fetching data...');
381
+ * const data = await fetchData();
382
+ * ctx.ui?.completeProgress('loading', 'Data loaded!');
383
+ *
384
+ * // Process data
385
+ * const result = processItems(data);
386
+ *
387
+ * // UI output (CLI only)
388
+ * if (!flags.json) {
389
+ * if (ctx.ui?.success) {
390
+ * ctx.ui.success('Processing Complete', [
391
+ * { header: 'Summary', items: [`Processed: ${result.processed}`, `Total: ${result.items.length}`] },
392
+ * { items: result.items.map(i => `✓ ${i}`) },
393
+ * ]);
394
+ * }
395
+ * } else {
396
+ * ctx.ui?.json(result);
397
+ * }
398
+ *
399
+ * // Return value (for invoke/REST/workflow)
400
+ * return { ok: true, ...result };
401
+ * },
402
+ * });
403
+ *
404
+ * // Note: Use ctx.ui.showError() for error display (error() is from PresenterFacade)
405
+ * // Available methods: success(), showError(), warning(), info()
406
+ * // Progress helpers: startProgress(), updateProgress(), completeProgress(), failProgress()
407
+ * // Low-level UI: table(), keyValue(), list(), box(), sideBox()
408
+ * ```
409
+ */
410
+
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Job definition helpers
3
+ * @module @kb-labs/shared-command-kit/jobs
4
+ */
5
+
6
+ import type { JobDecl, PermissionSpec, PluginContextV3 } from '@kb-labs/plugin-contracts';
7
+
8
+ /**
9
+ * Job input passed to handler at runtime
10
+ */
11
+ export interface JobInput {
12
+ /** Job identifier */
13
+ jobId: string;
14
+ /** When the job was scheduled to execute */
15
+ executedAt: Date;
16
+ /** How many times this job has run */
17
+ runCount: number;
18
+ }
19
+
20
+ /**
21
+ * Job handler function signature
22
+ *
23
+ * @template TInput - Custom input type (extends JobInput)
24
+ * @template TOutput - Handler return type
25
+ */
26
+ export type JobHandler<
27
+ TInput extends JobInput = JobInput,
28
+ TOutput = { ok: boolean; [key: string]: unknown }
29
+ > = (input: TInput, ctx: PluginContextV3) => Promise<TOutput>;
30
+
31
+ /**
32
+ * Job definition configuration
33
+ *
34
+ * Combines JobDecl manifest fields with the actual handler function
35
+ * for type-safe job creation.
36
+ */
37
+ export interface JobDefinition<
38
+ TInput extends JobInput = JobInput,
39
+ TOutput = { ok: boolean; [key: string]: unknown }
40
+ > {
41
+ /** Unique job identifier within plugin (e.g., 'auto-index') */
42
+ id: string;
43
+
44
+ /**
45
+ * Cron schedule expression
46
+ * Supports shortcuts (@hourly, @daily, @weekly, @monthly, @yearly) and standard cron format
47
+ */
48
+ schedule: string;
49
+
50
+ /** Human-readable description */
51
+ describe?: string;
52
+
53
+ /** Whether job is enabled (default: true) */
54
+ enabled?: boolean;
55
+
56
+ /** Job priority (1-10, default: 5, higher = more important) */
57
+ priority?: number;
58
+
59
+ /** Execution timeout in milliseconds (default: 60000 = 1min) */
60
+ timeout?: number;
61
+
62
+ /** Number of retry attempts on failure (default: 2) */
63
+ retries?: number;
64
+
65
+ /** Tags for filtering and organization */
66
+ tags?: string[];
67
+
68
+ /** Permissions for job execution (filesystem, network, quotas) */
69
+ permissions?: PermissionSpec;
70
+
71
+ /** Job handler function */
72
+ handler: JobHandler<TInput, TOutput>;
73
+ }
74
+
75
+ /**
76
+ * Defined job object with manifest conversion
77
+ */
78
+ export interface DefinedJob<
79
+ TInput extends JobInput = JobInput,
80
+ TOutput = { ok: boolean; [key: string]: unknown }
81
+ > {
82
+ /** Job configuration */
83
+ readonly config: Omit<JobDefinition<TInput, TOutput>, 'handler'> & { handler: string };
84
+
85
+ /** Job handler function */
86
+ readonly handler: JobHandler<TInput, TOutput>;
87
+
88
+ /** Convert to manifest JobDecl */
89
+ toManifest(handlerPath: string): JobDecl;
90
+ }
91
+
92
+ /**
93
+ * Define a type-safe job with handler
94
+ *
95
+ * This helper provides compile-time type safety for job definitions
96
+ * and allows sharing the same handler function between manifest and runtime.
97
+ *
98
+ * The handler function can be exported from a separate file and the manifest
99
+ * references it via the handler path.
100
+ *
101
+ * See plugin-template/src/jobs/hello.ts for a complete example.
102
+ *
103
+ * @param definition - Job configuration with handler
104
+ * @returns DefinedJob object with handler and manifest conversion
105
+ */
106
+ export function defineJob<
107
+ TInput extends JobInput = JobInput,
108
+ TOutput = { ok: boolean; [key: string]: unknown }
109
+ >(
110
+ definition: JobDefinition<TInput, TOutput>
111
+ ): DefinedJob<TInput, TOutput> {
112
+ const { handler, ...config } = definition;
113
+
114
+ return {
115
+ config: { ...config, handler: '' }, // handler path will be set in toManifest()
116
+ handler,
117
+ toManifest(handlerPath: string): JobDecl {
118
+ // JobDecl now extends CronDecl, which has job.type instead of handler
119
+ return {
120
+ id: config.id,
121
+ schedule: config.schedule,
122
+ job: {
123
+ type: config.id, // Use job id as type
124
+ payload: {}, // Empty payload by default
125
+ },
126
+ describe: config.describe,
127
+ enabled: config.enabled,
128
+ permissions: definition.permissions,
129
+ };
130
+ },
131
+ };
132
+ }