@kubb/cli 4.22.0 → 4.22.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 (47) hide show
  1. package/dist/{generate-Bjj-OR20.js → generate-CBsaUUvV.js} +49 -674
  2. package/dist/generate-CBsaUUvV.js.map +1 -0
  3. package/dist/{generate-BJyvkuLI.cjs → generate-CLDVb4hB.cjs} +53 -678
  4. package/dist/generate-CLDVb4hB.cjs.map +1 -0
  5. package/dist/getCosmiConfig-Co29x0Wv.cjs +458 -0
  6. package/dist/getCosmiConfig-Co29x0Wv.cjs.map +1 -0
  7. package/dist/getCosmiConfig-y2n_oW_y.js +438 -0
  8. package/dist/getCosmiConfig-y2n_oW_y.js.map +1 -0
  9. package/dist/index.cjs +9 -7
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.js +9 -7
  12. package/dist/index.js.map +1 -1
  13. package/dist/{init-CxLDdq2k.cjs → init-Bs1GxKWV.cjs} +3 -3
  14. package/dist/{init-CxLDdq2k.cjs.map → init-Bs1GxKWV.cjs.map} +1 -1
  15. package/dist/{init-D37jPiGV.js → init-D3zCeqU-.js} +3 -3
  16. package/dist/{init-D37jPiGV.js.map → init-D3zCeqU-.js.map} +1 -1
  17. package/dist/{mcp-By2JUPa8.js → mcp-BdwwUv36.js} +1 -1
  18. package/dist/{mcp-By2JUPa8.js.map → mcp-BdwwUv36.js.map} +1 -1
  19. package/dist/{mcp-l-_Z-WU6.cjs → mcp-Hy_PYnFp.cjs} +1 -1
  20. package/dist/{mcp-l-_Z-WU6.cjs.map → mcp-Hy_PYnFp.cjs.map} +1 -1
  21. package/dist/package-BFidNEkS.js +6 -0
  22. package/dist/package-BFidNEkS.js.map +1 -0
  23. package/dist/{package-Cnl_fuIC.cjs → package-CiB5tqr0.cjs} +2 -2
  24. package/dist/package-CiB5tqr0.cjs.map +1 -0
  25. package/dist/start-BeoZd1fL.cjs +128 -0
  26. package/dist/start-BeoZd1fL.cjs.map +1 -0
  27. package/dist/start-Ohz4V8k2.js +125 -0
  28. package/dist/start-Ohz4V8k2.js.map +1 -0
  29. package/dist/{validate-DkHCd-Tl.cjs → validate-Cvb5aOEb.cjs} +1 -1
  30. package/dist/{validate-DkHCd-Tl.cjs.map → validate-Cvb5aOEb.cjs.map} +1 -1
  31. package/dist/{validate-0Qu8SxVA.js → validate-YI4YkVTl.js} +1 -1
  32. package/dist/{validate-0Qu8SxVA.js.map → validate-YI4YkVTl.js.map} +1 -1
  33. package/package.json +4 -4
  34. package/src/commands/generate.ts +0 -38
  35. package/src/commands/init.ts +3 -1
  36. package/src/commands/start.ts +149 -0
  37. package/src/index.ts +2 -1
  38. package/src/loggers/clackLogger.ts +32 -12
  39. package/src/loggers/githubActionsLogger.ts +21 -8
  40. package/src/loggers/plainLogger.ts +20 -8
  41. package/dist/generate-BJyvkuLI.cjs.map +0 -1
  42. package/dist/generate-Bjj-OR20.js.map +0 -1
  43. package/dist/package-Cnl_fuIC.cjs.map +0 -1
  44. package/dist/package-Qh9BQXK6.js +0 -6
  45. package/dist/package-Qh9BQXK6.js.map +0 -1
  46. package/src/loggers/streamLogger.ts +0 -93
  47. package/src/utils/streamServer.ts +0 -163
@@ -1 +1 @@
1
- {"version":3,"file":"validate-0Qu8SxVA.js","names":[],"sources":["../src/commands/validate.ts"],"sourcesContent":["import process from 'node:process'\nimport type { ArgsDef } from 'citty'\nimport { defineCommand, showUsage } from 'citty'\nimport { createJiti } from 'jiti'\n\nconst jiti = createJiti(import.meta.url, {\n sourceMaps: true,\n})\n\nconst args = {\n input: {\n type: 'string',\n description: 'Path to Swagger/OpenAPI file',\n alias: 'i',\n },\n help: {\n type: 'boolean',\n description: 'Show help',\n alias: 'h',\n default: false,\n },\n} as const satisfies ArgsDef\n\nconst command = defineCommand({\n meta: {\n name: 'validate',\n description: 'Validate a Swagger/OpenAPI file',\n },\n args,\n async run(commandContext) {\n const { args } = commandContext\n\n if (args.help) {\n return showUsage(command)\n }\n\n if (args.input) {\n let mod: any\n try {\n mod = await jiti.import('@kubb/oas', { default: true })\n } catch (_e) {\n console.error(`Import of '@kubb/oas' is required to do validation`)\n process.exit(1)\n }\n\n const { parse } = mod\n try {\n const oas = await parse(args.input)\n await oas.validate()\n\n console.log('✅ Validation success')\n } catch (error) {\n console.error('❌ Validation failed')\n console.log((error as Error)?.message)\n process.exit(1)\n }\n }\n },\n})\n\nexport default command\n"],"mappings":";;;;;;AAKA,MAAM,OAAO,WAAW,OAAO,KAAK,KAAK,EACvC,YAAY,MACb,CAAC;AAgBF,MAAM,UAAU,cAAc;CAC5B,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAnBW;EACX,OAAO;GACL,MAAM;GACN,aAAa;GACb,OAAO;GACR;EACD,MAAM;GACJ,MAAM;GACN,aAAa;GACb,OAAO;GACP,SAAS;GACV;EACF;CAQC,MAAM,IAAI,gBAAgB;EACxB,MAAM,EAAE,SAAS;AAEjB,MAAI,KAAK,KACP,QAAO,UAAU,QAAQ;AAG3B,MAAI,KAAK,OAAO;GACd,IAAI;AACJ,OAAI;AACF,UAAM,MAAM,KAAK,OAAO,aAAa,EAAE,SAAS,MAAM,CAAC;YAChD,IAAI;AACX,YAAQ,MAAM,qDAAqD;AACnE,YAAQ,KAAK,EAAE;;GAGjB,MAAM,EAAE,UAAU;AAClB,OAAI;AAEF,WADY,MAAM,MAAM,KAAK,MAAM,EACzB,UAAU;AAEpB,YAAQ,IAAI,uBAAuB;YAC5B,OAAO;AACd,YAAQ,MAAM,sBAAsB;AACpC,YAAQ,IAAK,OAAiB,QAAQ;AACtC,YAAQ,KAAK,EAAE;;;;CAItB,CAAC;AAEF,uBAAe"}
1
+ {"version":3,"file":"validate-YI4YkVTl.js","names":[],"sources":["../src/commands/validate.ts"],"sourcesContent":["import process from 'node:process'\nimport type { ArgsDef } from 'citty'\nimport { defineCommand, showUsage } from 'citty'\nimport { createJiti } from 'jiti'\n\nconst jiti = createJiti(import.meta.url, {\n sourceMaps: true,\n})\n\nconst args = {\n input: {\n type: 'string',\n description: 'Path to Swagger/OpenAPI file',\n alias: 'i',\n },\n help: {\n type: 'boolean',\n description: 'Show help',\n alias: 'h',\n default: false,\n },\n} as const satisfies ArgsDef\n\nconst command = defineCommand({\n meta: {\n name: 'validate',\n description: 'Validate a Swagger/OpenAPI file',\n },\n args,\n async run(commandContext) {\n const { args } = commandContext\n\n if (args.help) {\n return showUsage(command)\n }\n\n if (args.input) {\n let mod: any\n try {\n mod = await jiti.import('@kubb/oas', { default: true })\n } catch (_e) {\n console.error(`Import of '@kubb/oas' is required to do validation`)\n process.exit(1)\n }\n\n const { parse } = mod\n try {\n const oas = await parse(args.input)\n await oas.validate()\n\n console.log('✅ Validation success')\n } catch (error) {\n console.error('❌ Validation failed')\n console.log((error as Error)?.message)\n process.exit(1)\n }\n }\n },\n})\n\nexport default command\n"],"mappings":";;;;;;AAKA,MAAM,OAAO,WAAW,OAAO,KAAK,KAAK,EACvC,YAAY,MACb,CAAC;AAgBF,MAAM,UAAU,cAAc;CAC5B,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAnBW;EACX,OAAO;GACL,MAAM;GACN,aAAa;GACb,OAAO;GACR;EACD,MAAM;GACJ,MAAM;GACN,aAAa;GACb,OAAO;GACP,SAAS;GACV;EACF;CAQC,MAAM,IAAI,gBAAgB;EACxB,MAAM,EAAE,SAAS;AAEjB,MAAI,KAAK,KACP,QAAO,UAAU,QAAQ;AAG3B,MAAI,KAAK,OAAO;GACd,IAAI;AACJ,OAAI;AACF,UAAM,MAAM,KAAK,OAAO,aAAa,EAAE,SAAS,MAAM,CAAC;YAChD,IAAI;AACX,YAAQ,MAAM,qDAAqD;AACnE,YAAQ,KAAK,EAAE;;GAGjB,MAAM,EAAE,UAAU;AAClB,OAAI;AAEF,WADY,MAAM,MAAM,KAAK,MAAM,EACzB,UAAU;AAEpB,YAAQ,IAAI,uBAAuB;YAC5B,OAAO;AACd,YAAQ,MAAM,sBAAsB;AACpC,YAAQ,IAAK,OAAiB,QAAQ;AACtC,YAAQ,KAAK,EAAE;;;;CAItB,CAAC;AAEF,uBAAe"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/cli",
3
- "version": "4.22.0",
3
+ "version": "4.22.2",
4
4
  "description": "Command-line interface for Kubb, enabling easy generation of TypeScript, React-Query, Zod, and other code from OpenAPI specifications.",
5
5
  "keywords": [
6
6
  "cli",
@@ -66,14 +66,14 @@
66
66
  "seedrandom": "^3.0.5",
67
67
  "semver": "^7.7.4",
68
68
  "string-argv": "^0.3.2",
69
- "@kubb/core": "4.22.0"
69
+ "@kubb/core": "4.22.2"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/seedrandom": "^3.0.8",
73
73
  "@types/semver": "^7.7.1",
74
74
  "source-map-support": "^0.5.21",
75
- "@kubb/mcp": "4.22.0",
76
- "@kubb/oas": "4.22.0"
75
+ "@kubb/mcp": "4.22.2",
76
+ "@kubb/oas": "4.22.2"
77
77
  },
78
78
  "engines": {
79
79
  "node": ">=20"
@@ -13,7 +13,6 @@ import { setupLogger } from '../loggers/utils.ts'
13
13
  import { generate } from '../runners/generate.ts'
14
14
  import { getConfigs } from '../utils/getConfigs.ts'
15
15
  import { getCosmiConfig } from '../utils/getCosmiConfig.ts'
16
- import { startStreamServer } from '../utils/streamServer.ts'
17
16
  import { startWatcher } from '../utils/watcher.ts'
18
17
 
19
18
  const args = {
@@ -53,21 +52,6 @@ const args = {
53
52
  alias: 's',
54
53
  default: false,
55
54
  },
56
- stream: {
57
- type: 'boolean',
58
- description: 'Start HTTP server with SSE streaming',
59
- default: false,
60
- },
61
- port: {
62
- type: 'string',
63
- description: 'Port for stream server (requires --stream). If not specified, an available port is automatically selected.',
64
- alias: 'p',
65
- },
66
- host: {
67
- type: 'string',
68
- description: 'Host for stream server (requires --stream)',
69
- default: 'localhost',
70
- },
71
55
  help: {
72
56
  type: 'boolean',
73
57
  description: 'Show help',
@@ -122,28 +106,6 @@ const command = defineCommand({
122
106
  const result = await getCosmiConfig('kubb', args.config)
123
107
  const configs = await getConfigs(result, args)
124
108
 
125
- // Start stream server if --stream flag is provided
126
- if (args.stream) {
127
- if (configs.length > 1) {
128
- clack.log.warn(pc.yellow('Stream mode only supports a single configuration. Only the first config will be used.'))
129
- }
130
-
131
- const port = args.port ? Number.parseInt(args.port, 10) : 0
132
- const host = args.host
133
-
134
- await startStreamServer({
135
- port,
136
- host,
137
- configPath: result.filepath,
138
- config: configs[0]!,
139
- input,
140
- args,
141
- })
142
-
143
- // Server is running, don't exit
144
- return
145
- }
146
-
147
109
  await events.emit('config:start')
148
110
 
149
111
  await events.emit('info', 'Config loaded', path.relative(process.cwd(), result.filepath))
@@ -307,7 +307,9 @@ const command = defineCommand({
307
307
  '\n' +
308
308
  pc.cyan(` 1. Make sure your OpenAPI spec is at: ${inputPath}`) +
309
309
  '\n' +
310
- pc.cyan(' 2. Run: npx kubb generate') +
310
+ pc.cyan(' 2. Generate code with: npx kubb generate') +
311
+ '\n' +
312
+ pc.cyan(' Or start a stream server with: npx kubb start') +
311
313
  '\n' +
312
314
  pc.cyan(` 3. Find generated files in: ${outputPath}`) +
313
315
  '\n\n' +
@@ -0,0 +1,149 @@
1
+ import * as process from 'node:process'
2
+ import * as clack from '@clack/prompts'
3
+ import { LogLevel, type ServerEvents, startServer } from '@kubb/core'
4
+ import { AsyncEventEmitter } from '@kubb/core/utils'
5
+ import type { ArgsDef } from 'citty'
6
+ import { defineCommand, showUsage } from 'citty'
7
+ import pc from 'picocolors'
8
+ import { version } from '../../package.json'
9
+ import { generate } from '../runners/generate.ts'
10
+ import { getConfigs } from '../utils/getConfigs.ts'
11
+ import { getCosmiConfig } from '../utils/getCosmiConfig.ts'
12
+ import type { Args as GenerateArgs } from './generate.ts'
13
+
14
+ const args = {
15
+ config: {
16
+ type: 'string',
17
+ description: 'Path to the Kubb config',
18
+ alias: 'c',
19
+ },
20
+ logLevel: {
21
+ type: 'string',
22
+ description: 'Info, silent, verbose or debug',
23
+ alias: 'l',
24
+ default: 'info',
25
+ valueHint: 'silent|info|verbose|debug',
26
+ },
27
+ debug: {
28
+ type: 'boolean',
29
+ description: 'Override logLevel to debug',
30
+ alias: 'd',
31
+ default: false,
32
+ },
33
+ verbose: {
34
+ type: 'boolean',
35
+ description: 'Override logLevel to verbose',
36
+ alias: 'v',
37
+ default: false,
38
+ },
39
+ silent: {
40
+ type: 'boolean',
41
+ description: 'Override logLevel to silent',
42
+ alias: 's',
43
+ default: false,
44
+ },
45
+ port: {
46
+ type: 'string',
47
+ description: 'Port for the server. If not specified, an available port is automatically selected.',
48
+ alias: 'p',
49
+ },
50
+ host: {
51
+ type: 'string',
52
+ description: 'Host for the server',
53
+ default: 'localhost',
54
+ },
55
+ help: {
56
+ type: 'boolean',
57
+ description: 'Show help',
58
+ alias: 'h',
59
+ default: false,
60
+ },
61
+ } as const satisfies ArgsDef
62
+
63
+ const command = defineCommand({
64
+ meta: {
65
+ name: 'start',
66
+ description: "[input] Start HTTP server with SSE streaming based on a 'kubb.config.ts' file",
67
+ },
68
+ args,
69
+ async run(commandContext) {
70
+ const { args } = commandContext
71
+ const input = args._[0]
72
+
73
+ if (args.help) {
74
+ return showUsage(command)
75
+ }
76
+
77
+ if (args.debug) {
78
+ args.logLevel = 'debug'
79
+ }
80
+
81
+ if (args.verbose) {
82
+ args.logLevel = 'verbose'
83
+ }
84
+
85
+ if (args.silent) {
86
+ args.logLevel = 'silent'
87
+ }
88
+
89
+ try {
90
+ const result = await getCosmiConfig('kubb', args.config)
91
+ // Create args compatible with getConfigs/startStreamServer (needs watch property)
92
+ const generateArgs = { ...args, watch: false } as unknown as GenerateArgs
93
+ const configs = await getConfigs(result, generateArgs)
94
+
95
+ if (configs.length > 1) {
96
+ clack.log.warn(pc.yellow('Stream mode only supports a single configuration. Only the first config will be used.'))
97
+ }
98
+
99
+ const port = args.port ? Number.parseInt(args.port, 10) : 0
100
+ const host = args.host
101
+ const config = configs[0]!
102
+ const logLevel = LogLevel[args.logLevel as keyof typeof LogLevel] || 3
103
+
104
+ const events = new AsyncEventEmitter<ServerEvents>()
105
+
106
+ events.on('server:start', (serverUrl: string, configPath: string) => {
107
+ clack.log.success(pc.green(`Stream server started on ${pc.bold(serverUrl)}`))
108
+ clack.log.info(pc.dim(`Config: ${configPath}`))
109
+ clack.log.info(pc.dim(`Connect: ${serverUrl}/api/info`))
110
+ clack.log.info(pc.dim(`Generate: ${serverUrl}/api/generate`))
111
+ clack.log.info(pc.dim(`Health: ${serverUrl}/api/health`))
112
+ clack.log.step(pc.yellow('Waiting for requests... (Press Ctrl+C to stop)'))
113
+ })
114
+
115
+ events.on('server:shutdown', () => {
116
+ clack.log.info('Shutting down stream server...')
117
+ })
118
+
119
+ events.on('server:stopped', () => {
120
+ clack.log.success('Server stopped')
121
+ })
122
+
123
+ await startServer({
124
+ port,
125
+ host,
126
+ configPath: result.filepath,
127
+ config,
128
+ version,
129
+ events,
130
+ onGenerate: async () => {
131
+ await generate({
132
+ input,
133
+ config,
134
+ logLevel,
135
+ events,
136
+ })
137
+ },
138
+ })
139
+
140
+ // Server is running, don't exit
141
+ } catch (error) {
142
+ clack.log.error(pc.red('Failed to start stream server'))
143
+ console.error(error)
144
+ process.exit(1)
145
+ }
146
+ },
147
+ })
148
+
149
+ export default command
package/src/index.ts CHANGED
@@ -20,7 +20,7 @@ const main = defineCommand({
20
20
  process.exit(0)
21
21
  }
22
22
 
23
- if (!['generate', 'validate', 'mcp', 'init'].includes(rawArgs[0] as string)) {
23
+ if (!['generate', 'validate', 'mcp', 'init', 'start'].includes(rawArgs[0] as string)) {
24
24
  // generate is not being used
25
25
  const generateCommand = await import('./commands/generate.ts').then((r) => r.default)
26
26
 
@@ -34,6 +34,7 @@ const main = defineCommand({
34
34
  validate: () => import('./commands/validate.ts').then((r) => r.default),
35
35
  mcp: () => import('./commands/mcp.ts').then((r) => r.default),
36
36
  init: () => import('./commands/init.ts').then((r) => r.default),
37
+ start: () => import('./commands/start.ts').then((r) => r.default),
37
38
  },
38
39
  })
39
40
 
@@ -3,7 +3,7 @@ import process from 'node:process'
3
3
  import * as clack from '@clack/prompts'
4
4
  import { defineLogger, LogLevel } from '@kubb/core'
5
5
  import { formatHrtime, formatMs } from '@kubb/core/utils'
6
- import { execa } from 'execa'
6
+ import { type ExecaError, execa } from 'execa'
7
7
  import pc from 'picocolors'
8
8
  import { formatMsWithColor } from '../utils/formatMsWithColor.ts'
9
9
  import { getIntro } from '../utils/getIntro.ts'
@@ -139,7 +139,7 @@ export const clackLogger = defineLogger({
139
139
  })
140
140
 
141
141
  context.on('error', (error) => {
142
- const caused = error.cause as Error
142
+ const caused = error.cause as Error | undefined
143
143
 
144
144
  const text = [pc.red('✗'), error.message].join(' ')
145
145
 
@@ -411,22 +411,32 @@ Run \`npm install -g @kubb/cli\` to update`,
411
411
  error: null,
412
412
  })
413
413
  } catch (err) {
414
- const error = new Error('Hook execute failed')
415
- error.cause = err
414
+ const error = err as ExecaError
415
+ const stderr = typeof error.stderr === 'string' ? error.stderr : String(error.stderr)
416
+ const stdout = typeof error.stdout === 'string' ? error.stdout : String(error.stdout)
416
417
 
417
418
  await context.emit('debug', {
418
419
  date: new Date(),
419
- logs: [(err as any).stdout],
420
+ logs: [stdout, stderr].filter(Boolean),
420
421
  })
421
422
 
423
+ if (stderr) {
424
+ console.error(stderr)
425
+ }
426
+ if (stdout) {
427
+ console.log(stdout)
428
+ }
429
+
430
+ const errorMessage = new Error(`Hook execute failed: ${commandWithArgs}`)
431
+
422
432
  await context.emit('hook:end', {
423
433
  command,
424
434
  args,
425
435
  id,
426
436
  success: false,
427
- error,
437
+ error: errorMessage,
428
438
  })
429
- await context.emit('error', error)
439
+ await context.emit('error', errorMessage)
430
440
  }
431
441
 
432
442
  return
@@ -454,16 +464,26 @@ Run \`npm install -g @kubb/cli\` to update`,
454
464
 
455
465
  await context.emit('hook:end', { command, args, id, success: true, error: null })
456
466
  } catch (err) {
457
- const error = new Error('Hook execute failed')
458
- error.cause = err
467
+ const error = err as ExecaError
468
+ const stderr = typeof error.stderr === 'string' ? error.stderr : String(error.stderr)
469
+ const stdout = typeof error.stdout === 'string' ? error.stdout : String(error.stdout)
459
470
 
460
471
  await context.emit('debug', {
461
472
  date: new Date(),
462
- logs: [(err as any).stdout],
473
+ logs: [stdout, stderr].filter(Boolean),
463
474
  })
464
475
 
465
- await context.emit('hook:end', { command, args, id, success: true, error })
466
- await context.emit('error', error)
476
+ if (stderr) {
477
+ logger.error(stderr)
478
+ }
479
+ if (stdout) {
480
+ logger.message(stdout)
481
+ }
482
+
483
+ const errorMessage = new Error(`Hook execute failed: ${commandWithArgs}`)
484
+
485
+ await context.emit('hook:end', { command, args, id, success: false, error: errorMessage })
486
+ await context.emit('error', errorMessage)
467
487
  }
468
488
  })
469
489
 
@@ -1,6 +1,6 @@
1
1
  import { type Config, defineLogger, LogLevel } from '@kubb/core'
2
2
  import { formatHrtime, formatMs } from '@kubb/core/utils'
3
- import { execa } from 'execa'
3
+ import { type ExecaError, execa } from 'execa'
4
4
  import pc from 'picocolors'
5
5
  import { formatMsWithColor } from '../utils/formatMsWithColor.ts'
6
6
 
@@ -111,7 +111,7 @@ export const githubActionsLogger = defineLogger({
111
111
  })
112
112
 
113
113
  context.on('error', (error) => {
114
- const caused = error.cause as Error
114
+ const caused = error.cause as Error | undefined
115
115
 
116
116
  if (logLevel <= LogLevel.silent) {
117
117
  return
@@ -363,7 +363,9 @@ export const githubActionsLogger = defineLogger({
363
363
  logs: [result.stdout],
364
364
  })
365
365
 
366
- console.log(result.stdout)
366
+ if (logLevel > LogLevel.silent) {
367
+ console.log(result.stdout)
368
+ }
367
369
 
368
370
  await context.emit('hook:end', {
369
371
  command,
@@ -373,22 +375,33 @@ export const githubActionsLogger = defineLogger({
373
375
  error: null,
374
376
  })
375
377
  } catch (err) {
376
- const error = new Error('Hook execute failed')
377
- error.cause = err
378
+ const error = err as ExecaError
379
+ const stderr = typeof error.stderr === 'string' ? error.stderr : String(error.stderr)
380
+ const stdout = typeof error.stdout === 'string' ? error.stdout : String(error.stdout)
378
381
 
379
382
  await context.emit('debug', {
380
383
  date: new Date(),
381
- logs: [(err as any).stdout],
384
+ logs: [stdout, stderr].filter(Boolean),
382
385
  })
383
386
 
387
+ // Display stderr/stdout in GitHub Actions format
388
+ if (stderr) {
389
+ console.error(`::error::${stderr}`)
390
+ }
391
+ if (stdout) {
392
+ console.log(stdout)
393
+ }
394
+
395
+ const errorMessage = new Error(`Hook execute failed: ${commandWithArgs}`)
396
+
384
397
  await context.emit('hook:end', {
385
398
  command,
386
399
  args,
387
400
  id,
388
401
  success: false,
389
- error,
402
+ error: errorMessage,
390
403
  })
391
- await context.emit('error', error)
404
+ await context.emit('error', errorMessage)
392
405
  }
393
406
  })
394
407
 
@@ -1,7 +1,7 @@
1
1
  import { relative } from 'node:path'
2
2
  import { defineLogger, LogLevel } from '@kubb/core'
3
3
  import { formatMs } from '@kubb/core/utils'
4
- import { execa } from 'execa'
4
+ import { type ExecaError, execa } from 'execa'
5
5
  import { getSummary } from '../utils/getSummary.ts'
6
6
 
7
7
  /**
@@ -59,7 +59,7 @@ export const plainLogger = defineLogger({
59
59
  })
60
60
 
61
61
  context.on('error', (error) => {
62
- const caused = error.cause as Error
62
+ const caused = error.cause as Error | undefined
63
63
 
64
64
  const text = getMessage(['✗', error.message].join(' '))
65
65
 
@@ -233,7 +233,9 @@ export const plainLogger = defineLogger({
233
233
  logs: [result.stdout],
234
234
  })
235
235
 
236
- console.log(result.stdout)
236
+ if (logLevel > LogLevel.silent) {
237
+ console.log(result.stdout)
238
+ }
237
239
 
238
240
  await context.emit('hook:end', {
239
241
  command,
@@ -243,22 +245,32 @@ export const plainLogger = defineLogger({
243
245
  error: null,
244
246
  })
245
247
  } catch (err) {
246
- const error = new Error('Hook execute failed')
247
- error.cause = err
248
+ const error = err as ExecaError
249
+ const stderr = typeof error.stderr === 'string' ? error.stderr : String(error.stderr)
250
+ const stdout = typeof error.stdout === 'string' ? error.stdout : String(error.stdout)
248
251
 
249
252
  await context.emit('debug', {
250
253
  date: new Date(),
251
- logs: [(err as any).stdout],
254
+ logs: [stdout, stderr].filter(Boolean),
252
255
  })
253
256
 
257
+ if (stderr) {
258
+ console.error(stderr)
259
+ }
260
+ if (stdout) {
261
+ console.log(stdout)
262
+ }
263
+
264
+ const errorMessage = new Error(`Hook execute failed: ${commandWithArgs}`)
265
+
254
266
  await context.emit('hook:end', {
255
267
  command,
256
268
  args,
257
269
  id,
258
270
  success: false,
259
- error,
271
+ error: errorMessage,
260
272
  })
261
- await context.emit('error', error)
273
+ await context.emit('error', errorMessage)
262
274
  }
263
275
  })
264
276