@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,358 @@
1
+ /**
2
+ * @module @kb-labs/shared-command-kit/flags/validate
3
+ * Validate flags against schema
4
+ */
5
+
6
+ import {
7
+ FlagValidationError,
8
+ type FlagSchema,
9
+ type FlagSchemaDefinition,
10
+ type SafeValidationResult,
11
+ } from './types';
12
+ import type { InferFlags } from './define';
13
+
14
+ /**
15
+ * Parse and coerce flag value based on type
16
+ */
17
+ function coerceValue(value: unknown, type: FlagSchema['type']): unknown {
18
+ if (value === undefined || value === null) {
19
+ return value;
20
+ }
21
+
22
+ switch (type) {
23
+ case 'boolean':
24
+ if (typeof value === 'boolean') {
25
+ return value;
26
+ }
27
+ if (typeof value === 'string') {
28
+ const lower = value.toLowerCase().trim();
29
+ if (lower === 'true' || lower === '1' || lower === 'yes') {
30
+ return true;
31
+ }
32
+ if (lower === 'false' || lower === '0' || lower === 'no' || lower === '') {
33
+ return false;
34
+ }
35
+ }
36
+ return Boolean(value);
37
+
38
+ case 'number':
39
+ if (typeof value === 'number') {
40
+ return value;
41
+ }
42
+ if (typeof value === 'string') {
43
+ const parsed = Number(value);
44
+ if (!Number.isNaN(parsed) && Number.isFinite(parsed)) {
45
+ return parsed;
46
+ }
47
+ }
48
+ return Number(value);
49
+
50
+ case 'array':
51
+ if (Array.isArray(value)) {
52
+ return value;
53
+ }
54
+ if (typeof value === 'string') {
55
+ return value.split(',').map((item) => item.trim());
56
+ }
57
+ return [value];
58
+
59
+ case 'string':
60
+ return String(value);
61
+
62
+ default:
63
+ return value;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Validate a single flag value against its schema
69
+ */
70
+ async function validateFlag(
71
+ flagName: string,
72
+ value: unknown,
73
+ schema: FlagSchema
74
+ ): Promise<true | string> {
75
+ const { type, required } = schema;
76
+
77
+ // Type-specific properties (using type guards)
78
+ const choices = 'choices' in schema ? schema.choices : undefined;
79
+ const pattern = 'pattern' in schema ? schema.pattern : undefined;
80
+ const min = 'min' in schema ? schema.min : undefined;
81
+ const max = 'max' in schema ? schema.max : undefined;
82
+ const validate = 'validate' in schema ? schema.validate : undefined;
83
+ const minLength = 'minLength' in schema ? schema.minLength : undefined;
84
+ const maxLength = 'maxLength' in schema ? schema.maxLength : undefined;
85
+ const items = 'items' in schema ? schema.items : undefined;
86
+ const transform = 'transform' in schema ? schema.transform : undefined;
87
+
88
+ // Check required
89
+ if (required && (value === undefined || value === null || value === '')) {
90
+ return `Flag --${flagName} is required`;
91
+ }
92
+
93
+ // Skip validation if value is undefined and not required
94
+ if (value === undefined || value === null) {
95
+ return true;
96
+ }
97
+
98
+ // Coerce value to correct type
99
+ const coerced = coerceValue(value, type);
100
+
101
+ // Type-specific validation
102
+ switch (type) {
103
+ case 'boolean':
104
+ if (typeof coerced !== 'boolean') {
105
+ return `Flag --${flagName} must be a boolean`;
106
+ }
107
+ break;
108
+
109
+ case 'string':
110
+ if (typeof coerced !== 'string') {
111
+ return `Flag --${flagName} must be a string`;
112
+ }
113
+ if (minLength !== undefined && coerced.length < minLength) {
114
+ return `Flag --${flagName} must be at least ${minLength} characters long`;
115
+ }
116
+ if (maxLength !== undefined && coerced.length > maxLength) {
117
+ return `Flag --${flagName} must be at most ${maxLength} characters long`;
118
+ }
119
+ if (choices && !choices.includes(coerced)) {
120
+ return `Flag --${flagName} must be one of: ${choices.join(', ')} (got: ${coerced})`;
121
+ }
122
+ if (pattern && !pattern.test(coerced)) {
123
+ return `Flag --${flagName} must match pattern ${pattern.toString()}`;
124
+ }
125
+ if (validate) {
126
+ const stringValidate = validate as (value: string) => true | string | Promise<true | string>;
127
+ const result = await stringValidate(coerced as string);
128
+ if (result !== true) {
129
+ return result;
130
+ }
131
+ }
132
+ break;
133
+
134
+ case 'number':
135
+ if (typeof coerced !== 'number' || !Number.isFinite(coerced)) {
136
+ return `Flag --${flagName} must be a number`;
137
+ }
138
+ if (min !== undefined && coerced < min) {
139
+ return `Flag --${flagName} must be >= ${min} (got: ${coerced})`;
140
+ }
141
+ if (max !== undefined && coerced > max) {
142
+ return `Flag --${flagName} must be <= ${max} (got: ${coerced})`;
143
+ }
144
+ if (validate) {
145
+ const numberValidate = validate as (value: number) => true | string | Promise<true | string>;
146
+ const result = await numberValidate(coerced as number);
147
+ if (result !== true) {
148
+ return result;
149
+ }
150
+ }
151
+ break;
152
+
153
+ case 'array':
154
+ if (!Array.isArray(coerced)) {
155
+ return `Flag --${flagName} must be an array`;
156
+ }
157
+ if (minLength !== undefined && coerced.length < minLength) {
158
+ return `Flag --${flagName} must have at least ${minLength} items`;
159
+ }
160
+ if (maxLength !== undefined && coerced.length > maxLength) {
161
+ return `Flag --${flagName} must have at most ${maxLength} items`;
162
+ }
163
+ if (items) {
164
+ for (let i = 0; i < coerced.length; i++) {
165
+ const item = coerced[i];
166
+ if (items === 'string' && typeof item !== 'string') {
167
+ return `Flag --${flagName}[${i}] must be a string`;
168
+ }
169
+ if (items === 'number' && typeof item !== 'number') {
170
+ return `Flag --${flagName}[${i}] must be a number`;
171
+ }
172
+ if (items === 'boolean' && typeof item !== 'boolean') {
173
+ return `Flag --${flagName}[${i}] must be a boolean`;
174
+ }
175
+ }
176
+ }
177
+ break;
178
+ }
179
+
180
+ return true;
181
+ }
182
+
183
+ /**
184
+ * Validate flags against schema and return typed result
185
+ *
186
+ * @throws {FlagValidationError} If validation fails and safe mode is not used
187
+ */
188
+ export async function validateFlags<T extends FlagSchemaDefinition>(
189
+ rawFlags: Record<string, unknown>,
190
+ schemaDefinition: { schema: T } | T
191
+ ): Promise<InferFlags<T>> {
192
+ const schema = 'schema' in schemaDefinition ? schemaDefinition.schema : schemaDefinition;
193
+ const errors: Array<{ flag: string; message: string; value?: unknown }> = [];
194
+ const result: Record<string, unknown> = {};
195
+ const flagValues: Record<string, unknown> = { ...rawFlags };
196
+
197
+ // First pass: apply implies
198
+ for (const [flagName, flagSchema] of Object.entries(schema)) {
199
+ const value = flagValues[flagName];
200
+ if (value !== undefined && value !== null && value !== false && value !== '') {
201
+ const implies = flagSchema.implies;
202
+ if (implies) {
203
+ for (const implied of implies) {
204
+ if (Array.isArray(implied) && implied.length === 2) {
205
+ // [flagName, value] format
206
+ const [impliedFlag, impliedValue] = implied;
207
+ if (flagValues[impliedFlag] === undefined) {
208
+ flagValues[impliedFlag] = impliedValue;
209
+ }
210
+ } else if (typeof implied === 'string') {
211
+ // Just flag name - set to true
212
+ if (flagValues[implied] === undefined) {
213
+ flagValues[implied] = true;
214
+ }
215
+ }
216
+ }
217
+ }
218
+ }
219
+ }
220
+
221
+ // Second pass: validate each flag
222
+ for (const [flagName, flagSchema] of Object.entries(schema)) {
223
+ const value = flagValues[flagName];
224
+ const schemaWithName: FlagSchema = { ...flagSchema, name: flagName };
225
+
226
+ // Check conflicts
227
+ const conflicts = flagSchema.conflicts;
228
+ if (conflicts && value !== undefined && value !== null && value !== false && value !== '') {
229
+ for (const conflictingFlag of conflicts) {
230
+ const conflictingValue = flagValues[conflictingFlag];
231
+ if (conflictingValue !== undefined && conflictingValue !== null && conflictingValue !== false && conflictingValue !== '') {
232
+ errors.push({
233
+ flag: flagName,
234
+ message: `Flag --${flagName} conflicts with --${conflictingFlag}`,
235
+ value,
236
+ });
237
+ break;
238
+ }
239
+ }
240
+ if (errors.some(e => e.flag === flagName)) {
241
+ continue;
242
+ }
243
+ }
244
+
245
+ // Check dependsOn
246
+ const dependsOn = flagSchema.dependsOn;
247
+ if (dependsOn && value !== undefined && value !== null && value !== '') {
248
+ for (const dependency of dependsOn) {
249
+ const dependencyValue = flagValues[dependency];
250
+ if (dependencyValue === undefined || dependencyValue === null || dependencyValue === '') {
251
+ errors.push({
252
+ flag: flagName,
253
+ message: `Flag --${flagName} depends on --${dependency}`,
254
+ value,
255
+ });
256
+ break;
257
+ }
258
+ }
259
+ if (errors.some(e => e.flag === flagName)) {
260
+ continue;
261
+ }
262
+ }
263
+
264
+ // Check required
265
+ if (flagSchema.required && (value === undefined || value === null || value === '')) {
266
+ errors.push({
267
+ flag: flagName,
268
+ message: `Flag --${flagName} is required`,
269
+ value,
270
+ });
271
+ continue;
272
+ }
273
+
274
+ // Apply default if value is missing
275
+ const finalValue = value !== undefined ? value : flagSchema.default;
276
+
277
+ // Skip validation if value is undefined and not required
278
+ if (finalValue === undefined && !flagSchema.required) {
279
+ continue;
280
+ }
281
+
282
+ // Validate flag
283
+ const validationResult = await validateFlag(flagName, finalValue, schemaWithName);
284
+ if (validationResult !== true) {
285
+ errors.push({
286
+ flag: flagName,
287
+ message: validationResult,
288
+ value: finalValue,
289
+ });
290
+ continue;
291
+ }
292
+
293
+ // Apply transform if present
294
+ let transformedValue = coerceValue(finalValue, flagSchema.type);
295
+ const transform = flagSchema.transform;
296
+ if (transform) {
297
+ transformedValue = await transform(transformedValue);
298
+ }
299
+
300
+ // Store value
301
+ result[flagName] = transformedValue;
302
+ }
303
+
304
+ // Check for unknown flags (optional - could be enabled via option)
305
+ // for (const flagName of Object.keys(rawFlags)) {
306
+ // if (!(flagName in schema)) {
307
+ // errors.push({
308
+ // flag: flagName,
309
+ // message: `Unknown flag: --${flagName}`,
310
+ // value: rawFlags[flagName],
311
+ // });
312
+ // }
313
+ // }
314
+
315
+ if (errors.length > 0) {
316
+ const firstError = errors[0]!;
317
+ throw new FlagValidationError(
318
+ firstError.flag,
319
+ firstError.message,
320
+ firstError.value,
321
+ schema as FlagSchemaDefinition // Pass schema for usage generation
322
+ );
323
+ }
324
+
325
+ return result as InferFlags<T>;
326
+ }
327
+
328
+ /**
329
+ * Safe version of validateFlags that doesn't throw
330
+ */
331
+ export async function validateFlagsSafe<T extends FlagSchemaDefinition>(
332
+ rawFlags: Record<string, unknown>,
333
+ schemaDefinition: { schema: T } | T
334
+ ): Promise<SafeValidationResult<InferFlags<T>>> {
335
+ try {
336
+ const data = await validateFlags(rawFlags, schemaDefinition);
337
+ return {
338
+ success: true,
339
+ data,
340
+ errors: [],
341
+ };
342
+ } catch (error) {
343
+ if (error instanceof FlagValidationError) {
344
+ return {
345
+ success: false,
346
+ errors: [
347
+ {
348
+ flag: error.flag,
349
+ message: error.message,
350
+ value: error.value,
351
+ },
352
+ ],
353
+ };
354
+ }
355
+ throw error;
356
+ }
357
+ }
358
+
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @module @kb-labs/shared-command-kit/helpers/context
3
+ * Context helpers and re-exports
4
+ */
5
+
6
+ // Re-export PluginContextV3 type
7
+ export type { PluginContextV3 } from '@kb-labs/plugin-contracts';
8
+
@@ -0,0 +1,84 @@
1
+ /**
2
+ * @module @kb-labs/shared-command-kit/helpers/flags
3
+ * Helper functions for working with command flags (optional utilities)
4
+ *
5
+ * These functions are completely optional - you can access flags directly.
6
+ * They are provided for convenience when you need type-safe flag access.
7
+ */
8
+
9
+ /**
10
+ * Get a flag value with type safety
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const name = getFlag<string>(flags, 'name'); // string | undefined
15
+ * const count = getFlag<number>(flags, 'count', 0); // number (with default)
16
+ * ```
17
+ */
18
+ export function getFlag<T>(
19
+ flags: Record<string, unknown>,
20
+ name: string,
21
+ defaultValue?: T
22
+ ): T | undefined {
23
+ const value = flags[name];
24
+ if (value === undefined) {
25
+ return defaultValue;
26
+ }
27
+ return value as T;
28
+ }
29
+
30
+ /**
31
+ * Require a flag value (throws if missing)
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * const name = requireFlag<string>(flags, 'name'); // string (throws if missing)
36
+ * ```
37
+ */
38
+ export function requireFlag<T>(
39
+ flags: Record<string, unknown>,
40
+ name: string
41
+ ): T {
42
+ const value = flags[name];
43
+ if (value === undefined) {
44
+ throw new Error(`Required flag '${name}' is missing`);
45
+ }
46
+ return value as T;
47
+ }
48
+
49
+ /**
50
+ * Check if a flag is present
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * if (hasFlag(flags, 'dry-run')) {
55
+ * // Flag is present
56
+ * }
57
+ * ```
58
+ */
59
+ export function hasFlag(
60
+ flags: Record<string, unknown>,
61
+ name: string
62
+ ): boolean {
63
+ return flags[name] !== undefined;
64
+ }
65
+
66
+ /**
67
+ * Get multiple flags at once
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * const { name, count } = getFlags(flags, ['name', 'count']);
72
+ * ```
73
+ */
74
+ export function getFlags<T extends string[]>(
75
+ flags: Record<string, unknown>,
76
+ names: T
77
+ ): Record<T[number], unknown> {
78
+ const result: Record<string, unknown> = {};
79
+ for (const name of names) {
80
+ result[name] = flags[name];
81
+ }
82
+ return result as Record<T[number], unknown>;
83
+ }
84
+
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @module @kb-labs/shared-command-kit/helpers
3
+ * Global platform access helpers
4
+ *
5
+ * Provides clean, type-safe access to platform services without context drilling.
6
+ * Similar to React hooks pattern, but for KB Labs platform.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { useLLM, useLogger, useAnalytics } from '@kb-labs/shared-command-kit/helpers';
11
+ *
12
+ * async handler(ctx, argv, flags) {
13
+ * const logger = useLogger();
14
+ * const llm = useLLM();
15
+ *
16
+ * await logger.info('Processing started');
17
+ *
18
+ * if (llm) {
19
+ * const result = await llm.complete('prompt');
20
+ * await logger.info('LLM result', { length: result.content.length });
21
+ * }
22
+ * }
23
+ * ```
24
+ */
25
+
26
+ // Platform singleton
27
+ export { usePlatform, isPlatformConfigured } from './use-platform';
28
+
29
+ // Context types and helpers
30
+ export type { PluginContextV3 } from './context';
31
+
32
+ // Config access
33
+ export { useConfig } from './use-config';
34
+
35
+ // Core services
36
+ export { useLogger, useLoggerWithContext } from './use-logger.js';
37
+ export { useLLM, isLLMAvailable, getLLMTier, type LLMTier, type UseLLMOptions } from './use-llm.js';
38
+ export { useEmbeddings, isEmbeddingsAvailable } from './use-embeddings.js';
39
+ export { useVectorStore, isVectorStoreAvailable } from './use-vector-store.js';
40
+ export { useAnalytics, trackAnalyticsEvent } from './use-analytics.js';
41
+ export { useStorage } from './use-storage.js';
42
+ export { useCache, isCacheAvailable } from './use-cache.js';