@kubb/cli 5.0.0-alpha.9 → 5.0.0-beta.2

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 (135) hide show
  1. package/README.md +4 -2
  2. package/bin/kubb.js +6 -0
  3. package/dist/{agent-BKphjOIF.cjs → agent-0Nk--lcr.cjs} +5 -5
  4. package/dist/agent-0Nk--lcr.cjs.map +1 -0
  5. package/dist/agent-B_pirbeB.cjs +116 -0
  6. package/dist/agent-B_pirbeB.cjs.map +1 -0
  7. package/dist/{agent-5mmp7QzF.js → agent-DKeVuiUC.js} +5 -5
  8. package/dist/agent-DKeVuiUC.js.map +1 -0
  9. package/dist/agent-Ev5hU5hH.js +112 -0
  10. package/dist/agent-Ev5hU5hH.js.map +1 -0
  11. package/dist/{constants-D0XHAHeZ.cjs → constants-CnDXa1R6.cjs} +30 -60
  12. package/dist/constants-CnDXa1R6.cjs.map +1 -0
  13. package/dist/{constants-DJM9zCXm.js → constants-aL3CP_Wq.js} +23 -59
  14. package/dist/constants-aL3CP_Wq.js.map +1 -0
  15. package/dist/define-Bdn8j5VM.cjs +54 -0
  16. package/dist/define-Bdn8j5VM.cjs.map +1 -0
  17. package/dist/define-Ctii4bel.js +43 -0
  18. package/dist/define-Ctii4bel.js.map +1 -0
  19. package/dist/{errors-DBW0N9w4.cjs → errors-CLCjoSg0.cjs} +22 -6
  20. package/dist/errors-CLCjoSg0.cjs.map +1 -0
  21. package/dist/errors-CjPmyZHy.js +43 -0
  22. package/dist/errors-CjPmyZHy.js.map +1 -0
  23. package/dist/{generate-Rly1EXBN.js → generate-B3PZ6Dp-.js} +3 -3
  24. package/dist/generate-B3PZ6Dp-.js.map +1 -0
  25. package/dist/{generate-Cq5RDTBL.cjs → generate-B3jl4ukb.cjs} +559 -265
  26. package/dist/generate-B3jl4ukb.cjs.map +1 -0
  27. package/dist/{generate-DU5zzc54.cjs → generate-DL_7a7Wd.cjs} +3 -3
  28. package/dist/generate-DL_7a7Wd.cjs.map +1 -0
  29. package/dist/{generate-BHNyeQXl.js → generate-Dt_r0ELY.js} +552 -258
  30. package/dist/generate-Dt_r0ELY.js.map +1 -0
  31. package/dist/index.cjs +40 -18
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.js +40 -18
  34. package/dist/index.js.map +1 -1
  35. package/dist/{init-iN7e1XwI.js → init-Bj94Nvt8.js} +4 -4
  36. package/dist/init-Bj94Nvt8.js.map +1 -0
  37. package/dist/{init-BK6inBTR.cjs → init-CZ5Xq2Hd.cjs} +45 -58
  38. package/dist/init-CZ5Xq2Hd.cjs.map +1 -0
  39. package/dist/{init-CN1JFyGX.cjs → init-CyN1oyTF.cjs} +4 -4
  40. package/dist/init-CyN1oyTF.cjs.map +1 -0
  41. package/dist/{init-BQ6zfsnw.js → init-eNRlotJK.js} +41 -54
  42. package/dist/init-eNRlotJK.js.map +1 -0
  43. package/dist/{mcp-eP1S40LZ.js → mcp-BzW703d7.js} +4 -4
  44. package/dist/{mcp-eP1S40LZ.js.map → mcp-BzW703d7.js.map} +1 -1
  45. package/dist/{mcp-CONmm_xT.cjs → mcp-CLcDV4Jm.cjs} +5 -6
  46. package/dist/mcp-CLcDV4Jm.cjs.map +1 -0
  47. package/dist/{mcp-BiGUvbWP.js → mcp-D7EIR9fR.js} +4 -5
  48. package/dist/mcp-D7EIR9fR.js.map +1 -0
  49. package/dist/{mcp-T7Q4nWbT.cjs → mcp-ZY-ONTOp.cjs} +4 -4
  50. package/dist/{mcp-T7Q4nWbT.cjs.map → mcp-ZY-ONTOp.cjs.map} +1 -1
  51. package/dist/{package-BJ6ionm6.cjs → package-D8wlStAg.cjs} +2 -2
  52. package/dist/package-D8wlStAg.cjs.map +1 -0
  53. package/dist/package-Yo-9_m5C.js +6 -0
  54. package/dist/package-Yo-9_m5C.js.map +1 -0
  55. package/dist/{shell-7HPrTCJ5.cjs → shell-475fQKaX.cjs} +8 -3
  56. package/dist/shell-475fQKaX.cjs.map +1 -0
  57. package/dist/{shell-DqqWsHCD.js → shell-DLzN4fRo.js} +8 -3
  58. package/dist/shell-DLzN4fRo.js.map +1 -0
  59. package/dist/{telemetry-DZ7IrLAU.cjs → telemetry-DN95_2pF.cjs} +50 -8
  60. package/dist/telemetry-DN95_2pF.cjs.map +1 -0
  61. package/dist/{telemetry-BF3SMlH6.js → telemetry-LgT_sdPe.js} +48 -6
  62. package/dist/telemetry-LgT_sdPe.js.map +1 -0
  63. package/dist/{validate-DAZdX_0i.js → validate-Dplr99xO.js} +4 -4
  64. package/dist/validate-Dplr99xO.js.map +1 -0
  65. package/dist/{validate-DucFMytl.cjs → validate-_8mBa63G.cjs} +4 -4
  66. package/dist/validate-_8mBa63G.cjs.map +1 -0
  67. package/dist/{validate-BImbbx1t.js → validate-kLJoT_hi.js} +5 -13
  68. package/dist/validate-kLJoT_hi.js.map +1 -0
  69. package/dist/{validate-ujLCYSWU.cjs → validate-yKKzqEZ5.cjs} +6 -14
  70. package/dist/validate-yKKzqEZ5.cjs.map +1 -0
  71. package/package.json +47 -46
  72. package/src/commands/agent/start.ts +20 -4
  73. package/src/commands/generate.ts +35 -6
  74. package/src/commands/init.ts +6 -1
  75. package/src/commands/validate.ts +6 -1
  76. package/src/constants.ts +19 -38
  77. package/src/index.ts +7 -10
  78. package/src/loggers/clackLogger.ts +54 -46
  79. package/src/loggers/fileSystemLogger.ts +15 -16
  80. package/src/loggers/githubActionsLogger.ts +23 -24
  81. package/src/loggers/plainLogger.ts +22 -23
  82. package/src/runners/agent.ts +81 -34
  83. package/src/runners/generate.ts +90 -100
  84. package/src/runners/init.ts +24 -51
  85. package/src/runners/mcp.ts +17 -4
  86. package/src/runners/validate.ts +19 -15
  87. package/src/utils/executeHooks.ts +15 -15
  88. package/src/utils/flags.ts +1 -2
  89. package/src/utils/getConfig.ts +10 -0
  90. package/src/utils/getCosmiConfig.ts +21 -12
  91. package/src/utils/getSummary.ts +1 -1
  92. package/src/utils/runHook.ts +29 -13
  93. package/src/utils/telemetry.ts +16 -3
  94. package/bin/kubb.cjs +0 -18
  95. package/dist/agent-5mmp7QzF.js.map +0 -1
  96. package/dist/agent-BKphjOIF.cjs.map +0 -1
  97. package/dist/agent-BapvKB4r.cjs +0 -92
  98. package/dist/agent-BapvKB4r.cjs.map +0 -1
  99. package/dist/agent-CBrpIMMU.js +0 -88
  100. package/dist/agent-CBrpIMMU.js.map +0 -1
  101. package/dist/constants-D0XHAHeZ.cjs.map +0 -1
  102. package/dist/constants-DJM9zCXm.js.map +0 -1
  103. package/dist/define--M_JMcDC.js +0 -25
  104. package/dist/define--M_JMcDC.js.map +0 -1
  105. package/dist/define-D6Kfm7-Z.cjs +0 -36
  106. package/dist/define-D6Kfm7-Z.cjs.map +0 -1
  107. package/dist/errors-6mF_WKxg.js +0 -27
  108. package/dist/errors-6mF_WKxg.js.map +0 -1
  109. package/dist/errors-DBW0N9w4.cjs.map +0 -1
  110. package/dist/generate-BHNyeQXl.js.map +0 -1
  111. package/dist/generate-Cq5RDTBL.cjs.map +0 -1
  112. package/dist/generate-DU5zzc54.cjs.map +0 -1
  113. package/dist/generate-Rly1EXBN.js.map +0 -1
  114. package/dist/init-BK6inBTR.cjs.map +0 -1
  115. package/dist/init-BQ6zfsnw.js.map +0 -1
  116. package/dist/init-CN1JFyGX.cjs.map +0 -1
  117. package/dist/init-iN7e1XwI.js.map +0 -1
  118. package/dist/jiti-Cd3S0xwr.cjs +0 -16
  119. package/dist/jiti-Cd3S0xwr.cjs.map +0 -1
  120. package/dist/jiti-e08mD2Ph.js +0 -11
  121. package/dist/jiti-e08mD2Ph.js.map +0 -1
  122. package/dist/mcp-BiGUvbWP.js.map +0 -1
  123. package/dist/mcp-CONmm_xT.cjs.map +0 -1
  124. package/dist/package-BJ6ionm6.cjs.map +0 -1
  125. package/dist/package-BKZ0H3Zf.js +0 -6
  126. package/dist/package-BKZ0H3Zf.js.map +0 -1
  127. package/dist/shell-7HPrTCJ5.cjs.map +0 -1
  128. package/dist/shell-DqqWsHCD.js.map +0 -1
  129. package/dist/telemetry-BF3SMlH6.js.map +0 -1
  130. package/dist/telemetry-DZ7IrLAU.cjs.map +0 -1
  131. package/dist/validate-BImbbx1t.js.map +0 -1
  132. package/dist/validate-DAZdX_0i.js.map +0 -1
  133. package/dist/validate-DucFMytl.cjs.map +0 -1
  134. package/dist/validate-ujLCYSWU.cjs.map +0 -1
  135. package/src/utils/jiti.ts +0 -9
@@ -4,25 +4,13 @@ import process from 'node:process'
4
4
  import { styleText } from 'node:util'
5
5
  import * as clack from '@clack/prompts'
6
6
  import type { AsyncEventEmitter } from '@internals/utils'
7
- import { AsyncEventEmitter as AsyncEventEmitterClass, executeIfOnline, toError } from '@internals/utils'
8
- import {
9
- type CLIOptions,
10
- type Config,
11
- detectFormatter,
12
- detectLinter,
13
- formatters,
14
- getConfigs,
15
- isInputPath,
16
- type KubbEvents,
17
- linters,
18
- logLevel as logLevelMap,
19
- safeBuild,
20
- setup,
21
- } from '@kubb/core'
7
+ import { AsyncEventEmitter as AsyncEventEmitterClass, detectFormatter, detectLinter, executeIfOnline, formatters, linters, toError } from '@internals/utils'
8
+ import { type CLIOptions, type Config, createKubb, isInputPath, type KubbHooks, logLevel as logLevelMap } from '@kubb/core'
22
9
  import { version } from '../../package.json'
23
10
  import { KUBB_NPM_PACKAGE_URL } from '../constants.ts'
24
11
  import { setupLogger } from '../loggers/utils.ts'
25
12
  import { executeHooks } from '../utils/executeHooks.ts'
13
+ import { getConfigs } from '../utils/getConfig.ts'
26
14
  import { getCosmiConfig } from '../utils/getCosmiConfig.ts'
27
15
  import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
28
16
  import { startWatcher } from '../utils/watcher.ts'
@@ -30,7 +18,7 @@ import { startWatcher } from '../utils/watcher.ts'
30
18
  type GenerateProps = {
31
19
  input?: string
32
20
  config: Config
33
- events: AsyncEventEmitter<KubbEvents>
21
+ hooks: AsyncEventEmitter<KubbHooks>
34
22
  logLevel: number
35
23
  }
36
24
 
@@ -38,7 +26,7 @@ type ToolMap = typeof formatters | typeof linters
38
26
 
39
27
  type RunToolPassOptions = {
40
28
  toolValue: string
41
- detect: () => Promise<string | undefined>
29
+ detect: () => Promise<string | null>
42
30
  toolMap: ToolMap
43
31
  /** Short noun used in "Auto-detected <toolLabel>:" message, e.g. "formatter" or "linter". */
44
32
  toolLabel: string
@@ -48,7 +36,7 @@ type RunToolPassOptions = {
48
36
  configName: string | undefined
49
37
  outputPath: string
50
38
  logLevel: number
51
- events: AsyncEventEmitter<KubbEvents>
39
+ hooks: AsyncEventEmitter<KubbHooks>
52
40
  onStart: () => Promise<void>
53
41
  onEnd: () => Promise<void>
54
42
  }
@@ -63,7 +51,7 @@ async function runToolPass({
63
51
  configName,
64
52
  outputPath,
65
53
  logLevel,
66
- events,
54
+ hooks,
67
55
  onStart,
68
56
  onEnd,
69
57
  }: RunToolPassOptions) {
@@ -73,13 +61,15 @@ async function runToolPass({
73
61
  if (resolvedTool === 'auto') {
74
62
  const detected = await detect()
75
63
  if (!detected) {
76
- await events.emit('warn', noToolMessage)
64
+ await hooks.emit('kubb:warn', { message: noToolMessage })
77
65
  } else {
78
66
  resolvedTool = detected
79
- await events.emit('info', `Auto-detected ${toolLabel}: ${styleText('dim', resolvedTool)}`)
67
+ await hooks.emit('kubb:info', { message: `Auto-detected ${toolLabel}: ${styleText('dim', resolvedTool)}` })
80
68
  }
81
69
  }
82
70
 
71
+ let toolError: Error | undefined
72
+
83
73
  if (resolvedTool && resolvedTool !== 'auto' && resolvedTool in toolMap) {
84
74
  const toolConfig = toolMap[resolvedTool as keyof ToolMap]
85
75
 
@@ -87,33 +77,32 @@ async function runToolPass({
87
77
  const hookId = createHash('sha256').update([configName, resolvedTool].filter(Boolean).join('-')).digest('hex')
88
78
 
89
79
  // Wire up the hook:end listener BEFORE emitting hook:start to avoid the race condition
90
- // where hook:end fires synchronously inside emit('hook:start') before the listener is registered.
80
+ // where hook:end fires synchronously inside emit('kubb:hook:start') before the listener is registered.
91
81
  const hookEndPromise = new Promise<void>((resolve, reject) => {
92
- const handler = ({ id, success, error }: { id?: string; command: string; args?: readonly string[]; success: boolean; error: Error | null }) => {
93
- if (id !== hookId) return
94
- events.off('hook:end', handler)
95
- if (!success) {
96
- reject(error ?? new Error(`${toolConfig.errorMessage}`))
82
+ const handler = (ctx: { id?: string; command: string; args?: readonly string[]; success: boolean; error: Error | null }) => {
83
+ if (ctx.id !== hookId) return
84
+ hooks.off('kubb:hook:end', handler)
85
+ if (!ctx.success) {
86
+ reject(ctx.error ?? new Error(`${toolConfig.errorMessage}`))
97
87
  return
98
88
  }
99
- events
100
- .emit(
101
- 'success',
102
- [
89
+ hooks
90
+ .emit('kubb:success', {
91
+ message: [
103
92
  `${successPrefix} with ${styleText('dim', resolvedTool)}`,
104
93
  logLevel >= logLevelMap.info ? `on ${styleText('dim', outputPath)}` : undefined,
105
94
  'successfully',
106
95
  ]
107
96
  .filter(Boolean)
108
97
  .join(' '),
109
- )
98
+ })
110
99
  .then(resolve)
111
100
  .catch(reject)
112
101
  }
113
- events.on('hook:end', handler)
102
+ hooks.on('kubb:hook:end', handler)
114
103
  })
115
104
 
116
- await events.emit('hook:start', {
105
+ await hooks.emit('kubb:hook:start', {
117
106
  id: hookId,
118
107
  command: toolConfig.command,
119
108
  args: toolConfig.args(outputPath),
@@ -121,59 +110,52 @@ async function runToolPass({
121
110
 
122
111
  await hookEndPromise
123
112
  } catch (caughtError) {
124
- const err = new Error(toolConfig.errorMessage)
125
- err.cause = caughtError
126
- await events.emit('error', err)
113
+ // Use the actual error from the hook. toolConfig.errorMessage (e.g. "Oxlint not found")
114
+ // is misleading when the binary was found and ran but exited with a non-zero code.
115
+ // runHook already emitted kubb:error for binary-not-found cases; here we surface the
116
+ // real reason (e.g. "Hook execute failed: oxlint --fix …") for non-zero exits.
117
+ const err = toError(caughtError)
118
+ await hooks.emit('kubb:error', { error: err })
119
+ toolError = err
127
120
  }
128
121
  }
129
122
 
130
123
  await onEnd()
124
+
125
+ if (toolError) {
126
+ throw toolError
127
+ }
131
128
  }
132
129
 
133
- async function generate({ input, config: userConfig, events, logLevel }: GenerateProps): Promise<void> {
134
- const inputPath = input ?? ('path' in userConfig.input ? userConfig.input.path : undefined)
130
+ async function generate(options: GenerateProps): Promise<void> {
131
+ const { input, hooks, logLevel } = options
132
+
135
133
  const hrStart = process.hrtime()
134
+ const inputPath = input ?? ('path' in options.config.input ? options.config.input.path : undefined)
136
135
 
137
136
  const config: Config = {
138
- ...userConfig,
139
- root: userConfig.root || process.cwd(),
137
+ ...options.config,
140
138
  input: inputPath
141
139
  ? {
142
- ...userConfig.input,
140
+ ...options.config.input,
143
141
  path: inputPath,
144
142
  }
145
- : userConfig.input,
146
- output: {
147
- write: true,
148
- barrelType: 'named',
149
- extension: {
150
- '.ts': '.ts',
151
- },
152
- format: 'prettier',
153
- ...userConfig.output,
154
- },
155
- }
143
+ : options.config.input,
144
+ ...options.config.output,
145
+ } satisfies Config
156
146
 
157
- await events.emit('generation:start', config)
147
+ const kubb = createKubb(config, { hooks })
148
+ await kubb.setup()
158
149
 
159
- await events.emit('info', config.name ? `Setup generation ${styleText('bold', config.name)}` : 'Setup generation', inputPath)
150
+ await hooks.emit('kubb:generation:start', { config })
160
151
 
161
- const { sources, fabric, driver } = await setup({
162
- config,
163
- events,
164
- })
152
+ await hooks.emit('kubb:info', { message: config.name ? `Setup generation ${styleText('bold', config.name)}` : 'Setup generation', info: inputPath })
165
153
 
166
- await events.emit('info', config.name ? `Build generation ${styleText('bold', config.name)}` : 'Build generation', inputPath)
154
+ await hooks.emit('kubb:info', { message: config.name ? `Build generation ${styleText('bold', config.name)}` : 'Build generation', info: inputPath })
167
155
 
168
- const { files, failedPlugins, pluginTimings, error } = await safeBuild(
169
- {
170
- config,
171
- events,
172
- },
173
- { driver, fabric, events, sources },
174
- )
156
+ const { files, failedPlugins, pluginTimings, error, driver } = await kubb.safeBuild()
175
157
 
176
- await events.emit('info', 'Load summary')
158
+ await hooks.emit('kubb:info', { message: 'Load summary' })
177
159
 
178
160
  // Handle build failures (either from failed plugins or general errors)
179
161
 
@@ -188,12 +170,13 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
188
170
  ].filter(Boolean)
189
171
 
190
172
  for (const err of allErrors) {
191
- await events.emit('error', err)
173
+ await hooks.emit('kubb:error', { error: err })
192
174
  }
193
175
 
194
- await events.emit('generation:end', config, files, sources)
176
+ await hooks.emit('kubb:generation:end', { config, files, sources: kubb.sources })
195
177
 
196
- await events.emit('generation:summary', config, {
178
+ await hooks.emit('kubb:generation:summary', {
179
+ config,
197
180
  failedPlugins,
198
181
  filesCreated: files.length,
199
182
  status: 'failed',
@@ -205,7 +188,10 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
205
188
  buildTelemetryEvent({
206
189
  command: 'generate',
207
190
  kubbVersion: version,
208
- plugins: driver.plugins.map((p) => ({ name: p.name, options: p.options as Record<string, unknown> })),
191
+ plugins: Array.from(driver.plugins.values(), (p) => ({
192
+ name: p.name,
193
+ options: p.options as Record<string, unknown>,
194
+ })),
209
195
  hrStart,
210
196
  filesCreated: files.length,
211
197
  status: 'failed',
@@ -215,8 +201,8 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
215
201
  process.exit(1)
216
202
  }
217
203
 
218
- await events.emit('success', 'Generation successfully', inputPath)
219
- await events.emit('generation:end', config, files, sources)
204
+ await hooks.emit('kubb:success', { message: 'Generation successfully', info: inputPath })
205
+ await hooks.emit('kubb:generation:end', { config, files, sources: kubb.sources })
220
206
 
221
207
  const outputPath = path.resolve(config.root, config.output.path)
222
208
 
@@ -227,13 +213,13 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
227
213
  toolMap: formatters,
228
214
  toolLabel: 'formatter',
229
215
  successPrefix: 'Formatting',
230
- noToolMessage: 'No formatter found (biome, prettier, or oxfmt). Skipping formatting.',
216
+ noToolMessage: 'No formatter found (oxfmt, biome, or prettier). Skipping formatting.',
231
217
  configName: config.name,
232
218
  outputPath,
233
219
  logLevel,
234
- events,
235
- onStart: () => events.emit('format:start'),
236
- onEnd: () => events.emit('format:end'),
220
+ hooks,
221
+ onStart: () => hooks.emit('kubb:format:start'),
222
+ onEnd: () => hooks.emit('kubb:format:end'),
237
223
  })
238
224
  }
239
225
 
@@ -244,25 +230,26 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
244
230
  toolMap: linters,
245
231
  toolLabel: 'linter',
246
232
  successPrefix: 'Linting',
247
- noToolMessage: 'No linter found (biome, oxlint, or eslint). Skipping linting.',
233
+ noToolMessage: 'No linter found (oxlint, biome, or eslint). Skipping linting.',
248
234
  configName: config.name,
249
235
  outputPath,
250
236
  logLevel,
251
- events,
252
- onStart: () => events.emit('lint:start'),
253
- onEnd: () => events.emit('lint:end'),
237
+ hooks,
238
+ onStart: () => hooks.emit('kubb:lint:start'),
239
+ onEnd: () => hooks.emit('kubb:lint:end'),
254
240
  })
255
241
  }
256
242
 
257
243
  if (config.hooks) {
258
- await events.emit('hooks:start')
259
- await executeHooks({ hooks: config.hooks, events })
244
+ await hooks.emit('kubb:hooks:start')
245
+ await executeHooks({ configHooks: config.hooks, hooks })
260
246
 
261
- await events.emit('hooks:end')
247
+ await hooks.emit('kubb:hooks:end')
262
248
  }
263
249
 
264
250
  // Only reached when there are no failures (process.exit(1) is called above otherwise)
265
- await events.emit('generation:summary', config, {
251
+ await hooks.emit('kubb:generation:summary', {
252
+ config,
266
253
  failedPlugins,
267
254
  filesCreated: files.length,
268
255
  status: 'success',
@@ -273,7 +260,10 @@ async function generate({ input, config: userConfig, events, logLevel }: Generat
273
260
  const telemetryEvent = buildTelemetryEvent({
274
261
  command: 'generate',
275
262
  kubbVersion: version,
276
- plugins: driver.plugins.map((p) => ({ name: p.name, options: p.options as Record<string, unknown> })),
263
+ plugins: Array.from(driver.plugins.values(), (p) => ({
264
+ name: p.name,
265
+ options: p.options as Record<string, unknown>,
266
+ })),
277
267
  hrStart,
278
268
  filesCreated: files.length,
279
269
  status: 'success',
@@ -291,9 +281,9 @@ type GenerateCommandOptions = {
291
281
 
292
282
  export async function runGenerateCommand({ input, configPath, logLevel: logLevelKey, watch }: GenerateCommandOptions): Promise<void> {
293
283
  const logLevel = logLevelMap[logLevelKey as keyof typeof logLevelMap] ?? logLevelMap.info
294
- const events = new AsyncEventEmitterClass<KubbEvents>()
284
+ const hooks = new AsyncEventEmitterClass<KubbHooks>()
295
285
 
296
- await setupLogger(events, { logLevel })
286
+ await setupLogger(hooks, { logLevel })
297
287
 
298
288
  await executeIfOnline(async () => {
299
289
  try {
@@ -302,7 +292,7 @@ export async function runGenerateCommand({ input, configPath, logLevel: logLevel
302
292
  const latestVersion = data.version
303
293
 
304
294
  if (latestVersion && version < latestVersion) {
305
- await events.emit('version:new', version, latestVersion)
295
+ await hooks.emit('kubb:version:new', { currentVersion: version, latestVersion })
306
296
  }
307
297
  } catch {
308
298
  // Ignore network errors for version check
@@ -313,31 +303,31 @@ export async function runGenerateCommand({ input, configPath, logLevel: logLevel
313
303
  const result = await getCosmiConfig('kubb', configPath)
314
304
  const configs = await getConfigs(result.config, { input } as CLIOptions)
315
305
 
316
- await events.emit('config:start')
317
- await events.emit('info', 'Config loaded', path.relative(process.cwd(), result.filepath))
318
- await events.emit('success', 'Config loaded successfully', path.relative(process.cwd(), result.filepath))
319
- await events.emit('config:end', configs)
306
+ await hooks.emit('kubb:config:start')
307
+ await hooks.emit('kubb:info', { message: 'Config loaded', info: path.relative(process.cwd(), result.filepath) })
308
+ await hooks.emit('kubb:success', { message: 'Config loaded successfully', info: path.relative(process.cwd(), result.filepath) })
309
+ await hooks.emit('kubb:config:end', { configs })
320
310
 
321
- await events.emit('lifecycle:start', version)
311
+ await hooks.emit('kubb:lifecycle:start', { version })
322
312
 
323
313
  for (const config of configs) {
324
314
  if (isInputPath(config) && watch) {
325
315
  await startWatcher([input || config.input.path], async (paths) => {
326
316
  // remove to avoid duplicate listeners after each change
327
- events.removeAll()
317
+ hooks.removeAll()
328
318
 
329
- await generate({ input, config, logLevel, events })
319
+ await generate({ input, config, logLevel, hooks })
330
320
 
331
321
  clack.log.step(styleText('yellow', `Watching for changes in ${paths.join(' and ')}`))
332
322
  })
333
323
  } else {
334
- await generate({ input, config, logLevel, events })
324
+ await generate({ input, config, logLevel, hooks })
335
325
  }
336
326
  }
337
327
 
338
- await events.emit('lifecycle:end')
328
+ await hooks.emit('kubb:lifecycle:end')
339
329
  } catch (error) {
340
- await events.emit('error', toError(error))
330
+ await hooks.emit('kubb:error', { error: toError(error) })
341
331
  process.exit(1)
342
332
  }
343
333
  }
@@ -5,7 +5,7 @@ import { styleText } from 'node:util'
5
5
  import * as clack from '@clack/prompts'
6
6
  import type { PackageManagerInfo } from '@internals/utils'
7
7
  import { detectPackageManager } from '@internals/utils'
8
- import { initDefaults, pluginDefaultConfigs } from '../constants.ts'
8
+ import { initDefaults, KUBB_CONFIG_FILENAME, pluginDefaultConfigs } from '../constants.ts'
9
9
  import { hasPackageJson, initPackageJson, installPackages } from '../utils/packageManager.ts'
10
10
 
11
11
  type PluginOption = {
@@ -14,67 +14,38 @@ type PluginOption = {
14
14
  hint?: string
15
15
  packageName: string
16
16
  importName: string
17
- category: 'core' | 'typescript' | 'query' | 'validation' | 'testing' | 'mocking' | 'docs'
17
+ category: 'types' | 'client' | 'framework' | 'validation' | 'testing' | 'mocks' | 'documentation' | 'ai'
18
18
  }
19
19
 
20
20
  const availablePlugins: PluginOption[] = [
21
- {
22
- value: 'plugin-oas',
23
- label: 'OpenAPI Parser',
24
- hint: 'Required',
25
- packageName: '@kubb/plugin-oas',
26
- importName: 'pluginOas',
27
- category: 'core',
28
- },
29
21
  {
30
22
  value: 'plugin-ts',
31
23
  label: 'TypeScript',
32
24
  hint: 'Recommended',
33
25
  packageName: '@kubb/plugin-ts',
34
26
  importName: 'pluginTs',
35
- category: 'typescript',
27
+ category: 'types',
36
28
  },
37
29
  {
38
30
  value: 'plugin-client',
39
31
  label: 'Client (Fetch/Axios)',
40
32
  packageName: '@kubb/plugin-client',
41
33
  importName: 'pluginClient',
42
- category: 'typescript',
34
+ category: 'client',
43
35
  },
44
36
  {
45
37
  value: 'plugin-react-query',
46
38
  label: 'React Query / TanStack Query',
47
39
  packageName: '@kubb/plugin-react-query',
48
40
  importName: 'pluginReactQuery',
49
- category: 'query',
50
- },
51
- {
52
- value: 'plugin-solid-query',
53
- label: 'Solid Query',
54
- packageName: '@kubb/plugin-solid-query',
55
- importName: 'pluginSolidQuery',
56
- category: 'query',
57
- },
58
- {
59
- value: 'plugin-svelte-query',
60
- label: 'Svelte Query',
61
- packageName: '@kubb/plugin-svelte-query',
62
- importName: 'pluginSvelteQuery',
63
- category: 'query',
41
+ category: 'framework',
64
42
  },
65
43
  {
66
44
  value: 'plugin-vue-query',
67
45
  label: 'Vue Query',
68
46
  packageName: '@kubb/plugin-vue-query',
69
47
  importName: 'pluginVueQuery',
70
- category: 'query',
71
- },
72
- {
73
- value: 'plugin-swr',
74
- label: 'SWR',
75
- packageName: '@kubb/plugin-swr',
76
- importName: 'pluginSwr',
77
- category: 'query',
48
+ category: 'framework',
78
49
  },
79
50
  {
80
51
  value: 'plugin-zod',
@@ -88,14 +59,14 @@ const availablePlugins: PluginOption[] = [
88
59
  label: 'Faker.js Mocks',
89
60
  packageName: '@kubb/plugin-faker',
90
61
  importName: 'pluginFaker',
91
- category: 'mocking',
62
+ category: 'mocks',
92
63
  },
93
64
  {
94
65
  value: 'plugin-msw',
95
66
  label: 'MSW Handlers',
96
67
  packageName: '@kubb/plugin-msw',
97
68
  importName: 'pluginMsw',
98
- category: 'mocking',
69
+ category: 'mocks',
99
70
  },
100
71
  {
101
72
  value: 'plugin-cypress',
@@ -104,12 +75,19 @@ const availablePlugins: PluginOption[] = [
104
75
  importName: 'pluginCypress',
105
76
  category: 'testing',
106
77
  },
78
+ {
79
+ value: 'plugin-mcp',
80
+ label: 'MCP Server (AI / Model Context Protocol)',
81
+ packageName: '@kubb/plugin-mcp',
82
+ importName: 'pluginMcp',
83
+ category: 'ai',
84
+ },
107
85
  {
108
86
  value: 'plugin-redoc',
109
87
  label: 'ReDoc Documentation',
110
88
  packageName: '@kubb/plugin-redoc',
111
89
  importName: 'pluginRedoc',
112
- category: 'docs',
90
+ category: 'documentation',
113
91
  },
114
92
  ]
115
93
 
@@ -123,7 +101,7 @@ function generateConfigFile(selectedPlugins: PluginOption[], inputPath: string,
123
101
  })
124
102
  .join('\n')
125
103
 
126
- return `import { defineConfig } from '@kubb/core'
104
+ return `import { defineConfig } from 'kubb'
127
105
  ${imports}
128
106
 
129
107
  export default defineConfig({
@@ -251,13 +229,8 @@ export async function runInit({ yes, version }: InitOptions): Promise<void> {
251
229
  selectedPlugins = availablePlugins.filter((plugin) => (selectedPluginValues as string[]).includes(plugin.value))
252
230
  }
253
231
 
254
- // Ensure plugin-oas is always included
255
- if (!selectedPlugins.find((p) => p.value === 'plugin-oas')) {
256
- selectedPlugins.unshift(availablePlugins.find((p) => p.value === 'plugin-oas')!)
257
- }
258
-
259
232
  // Install packages
260
- const packagesToInstall = ['@kubb/core', '@kubb/cli', '@kubb/agent', ...selectedPlugins.map((p) => p.packageName)]
233
+ const packagesToInstall = ['kubb', ...selectedPlugins.map((p) => p.packageName)]
261
234
 
262
235
  const spinner = clack.spinner()
263
236
  spinner.start(`Installing ${packagesToInstall.length} packages with ${packageManager.name}`)
@@ -272,17 +245,17 @@ export async function runInit({ yes, version }: InitOptions): Promise<void> {
272
245
 
273
246
  // Generate config file
274
247
  const configSpinner = clack.spinner()
275
- configSpinner.start('Creating kubb.config.ts')
248
+ configSpinner.start(`Creating ${KUBB_CONFIG_FILENAME}`)
276
249
 
277
250
  const configContent = generateConfigFile(selectedPlugins, inputPath, outputPath)
278
- const configPath = path.join(cwd, 'kubb.config.ts')
251
+ const configPath = path.join(cwd, KUBB_CONFIG_FILENAME)
279
252
 
280
253
  if (fs.existsSync(configPath)) {
281
- configSpinner.stop('kubb.config.ts already exists')
254
+ configSpinner.stop(`${KUBB_CONFIG_FILENAME} already exists`)
282
255
 
283
256
  if (!yes) {
284
257
  const shouldOverwrite = await clack.confirm({
285
- message: 'kubb.config.ts already exists. Overwrite?',
258
+ message: `${KUBB_CONFIG_FILENAME} already exists. Overwrite?`,
286
259
  initialValue: false,
287
260
  })
288
261
 
@@ -291,12 +264,12 @@ export async function runInit({ yes, version }: InitOptions): Promise<void> {
291
264
  }
292
265
  }
293
266
 
294
- configSpinner.start('Overwriting kubb.config.ts')
267
+ configSpinner.start(`Overwriting ${KUBB_CONFIG_FILENAME}`)
295
268
  }
296
269
 
297
270
  fs.writeFileSync(configPath, configContent, 'utf-8')
298
271
 
299
- configSpinner.stop('Created kubb.config.ts')
272
+ configSpinner.stop(`Created ${KUBB_CONFIG_FILENAME}`)
300
273
 
301
274
  clack.outro(
302
275
  styleText('green', '✓ All set!') +
@@ -2,7 +2,6 @@ import process from 'node:process'
2
2
  import { styleText } from 'node:util'
3
3
  import { getErrorMessage } from '@internals/utils'
4
4
  import type * as McpModule from '@kubb/mcp'
5
- import { jiti } from '../utils/jiti.ts'
6
5
  import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
7
6
 
8
7
  type McpOptions = {
@@ -12,7 +11,7 @@ type McpOptions = {
12
11
  export async function runMcp({ version }: McpOptions): Promise<void> {
13
12
  let mod: typeof McpModule
14
13
  try {
15
- mod = (await jiti.import('@kubb/mcp', { default: true })) as typeof McpModule
14
+ mod = (await import('@kubb/mcp')) as typeof McpModule
16
15
  } catch (_e) {
17
16
  console.error(`Import of '@kubb/mcp' is required to start the MCP server`)
18
17
  process.exit(1)
@@ -24,9 +23,23 @@ export async function runMcp({ version }: McpOptions): Promise<void> {
24
23
  console.log('⏳ Starting MCP server...')
25
24
  console.warn(styleText('yellow', 'This feature is still under development — use with caution'))
26
25
  run()
27
- await sendTelemetry(buildTelemetryEvent({ command: 'mcp', kubbVersion: version, hrStart, status: 'success' }))
26
+ await sendTelemetry(
27
+ buildTelemetryEvent({
28
+ command: 'mcp',
29
+ kubbVersion: version,
30
+ hrStart,
31
+ status: 'success',
32
+ }),
33
+ )
28
34
  } catch (error) {
29
- await sendTelemetry(buildTelemetryEvent({ command: 'mcp', kubbVersion: version, hrStart, status: 'failed' }))
35
+ await sendTelemetry(
36
+ buildTelemetryEvent({
37
+ command: 'mcp',
38
+ kubbVersion: version,
39
+ hrStart,
40
+ status: 'failed',
41
+ }),
42
+ )
30
43
  console.error(getErrorMessage(error))
31
44
  }
32
45
  }
@@ -1,7 +1,6 @@
1
1
  import process from 'node:process'
2
2
  import { getErrorMessage } from '@internals/utils'
3
- import type * as OasModule from '@kubb/oas'
4
- import { jiti } from '../utils/jiti.ts'
3
+ import { parseDocument, validateDocument } from '@kubb/adapter-oas'
5
4
  import { buildTelemetryEvent, sendTelemetry } from '../utils/telemetry.ts'
6
5
 
7
6
  type ValidateOptions = {
@@ -10,24 +9,29 @@ type ValidateOptions = {
10
9
  }
11
10
 
12
11
  export async function runValidate({ input, version }: ValidateOptions): Promise<void> {
13
- let mod: typeof OasModule
14
- try {
15
- mod = (await jiti.import('@kubb/oas', { default: true })) as typeof OasModule
16
- } catch (_e) {
17
- console.error(`Import of '@kubb/oas' is required to do validation`)
18
- process.exit(1)
19
- }
20
-
21
- const { parse } = mod
22
12
  const hrStart = process.hrtime()
23
13
  try {
24
- const oas = await parse(input)
25
- await oas.validate()
14
+ const document = await parseDocument(input)
15
+ await validateDocument(document, { throwOnError: true })
26
16
 
27
- await sendTelemetry(buildTelemetryEvent({ command: 'validate', kubbVersion: version, hrStart, status: 'success' }))
17
+ await sendTelemetry(
18
+ buildTelemetryEvent({
19
+ command: 'validate',
20
+ kubbVersion: version,
21
+ hrStart,
22
+ status: 'success',
23
+ }),
24
+ )
28
25
  console.log('✅ Validation success')
29
26
  } catch (error) {
30
- await sendTelemetry(buildTelemetryEvent({ command: 'validate', kubbVersion: version, hrStart, status: 'failed' }))
27
+ await sendTelemetry(
28
+ buildTelemetryEvent({
29
+ command: 'validate',
30
+ kubbVersion: version,
31
+ hrStart,
32
+ status: 'failed',
33
+ }),
34
+ )
31
35
  console.error('❌ Validation failed')
32
36
  console.error(getErrorMessage(error))
33
37
  process.exit(1)