incur 0.1.4 → 0.1.6

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.
package/src/Cli.ts CHANGED
@@ -128,7 +128,9 @@ export function create<
128
128
  const output extends z.ZodType | undefined = undefined,
129
129
  >(
130
130
  definition: create.Options<args, env, opts, output> & { name: string; run: Function },
131
- ): Cli<{ [key in (typeof definition)['name']]: { args: InferOutput<args>; options: InferOutput<opts> } }>
131
+ ): Cli<{
132
+ [key in (typeof definition)['name']]: { args: InferOutput<args>; options: InferOutput<opts> }
133
+ }>
132
134
  /** Creates a router CLI from a single options object (e.g. package.json). */
133
135
  export function create<
134
136
  const args extends z.ZodObject<any> | undefined = undefined,
@@ -162,7 +164,13 @@ export function create(
162
164
  }
163
165
  const sub = nameOrCli as Cli
164
166
  const subCommands = toCommands.get(sub)!
165
- commands.set(sub.name, { _group: true, description: sub.description, commands: subCommands })
167
+ const subOutputPolicy = toOutputPolicy.get(sub)
168
+ commands.set(sub.name, {
169
+ _group: true,
170
+ description: sub.description,
171
+ commands: subCommands,
172
+ ...(subOutputPolicy ? { outputPolicy: subOutputPolicy } : undefined),
173
+ })
166
174
  return cli
167
175
  },
168
176
 
@@ -172,6 +180,7 @@ export function create(
172
180
  description: def.description,
173
181
  format: def.format,
174
182
  mcp: def.mcp,
183
+ outputPolicy: def.outputPolicy,
175
184
  rootCommand: rootDef,
176
185
  sync: def.sync,
177
186
  version: def.version,
@@ -180,6 +189,7 @@ export function create(
180
189
  }
181
190
 
182
191
  if (rootDef) toRootDefinition.set(cli as unknown as Root, rootDef)
192
+ if (def.outputPolicy) toOutputPolicy.set(cli, def.outputPolicy)
183
193
  toCommands.set(cli, commands)
184
194
  return cli
185
195
  }
@@ -210,25 +220,37 @@ export declare namespace create {
210
220
  options?: options | undefined
211
221
  /** Zod schema for the return value. */
212
222
  output?: output | undefined
223
+ /**
224
+ * Controls when output data is displayed. Inherited by child commands when set on a group or root CLI.
225
+ *
226
+ * - `'all'` — displays to both humans and agents.
227
+ * - `'agent-only'` — suppresses data output in human/TTY mode while still returning it to agents.
228
+ *
229
+ * @default 'all'
230
+ */
231
+ outputPolicy?: OutputPolicy | undefined
213
232
  /** Alternative usage patterns shown in help output. */
214
233
  usage?: Usage<args, options>[] | undefined
215
234
  /** The root command handler. When provided, creates a leaf CLI with no subcommands. */
216
235
  run?:
217
236
  | ((context: {
218
- args: InferOutput<args>
219
- /** Parsed environment variables. */
220
- env: InferOutput<env>
221
- /** Return an error result with optional CTAs. */
222
- error: (options: {
223
- code: string
224
- cta?: CtaBlock | undefined
225
- message: string
226
- retryable?: boolean | undefined
227
- }) => never
228
- /** Return a success result with optional metadata (e.g. CTAs). */
229
- ok: (data: InferReturn<output>, meta?: { cta?: CtaBlock | undefined }) => never
230
- options: InferOutput<options>
231
- }) =>
237
+ /** Whether the consumer is an agent (stdout is not a TTY). */
238
+ agent: boolean
239
+ /** Positional arguments. */
240
+ args: InferOutput<args>
241
+ /** Parsed environment variables. */
242
+ env: InferOutput<env>
243
+ /** Return an error result with optional CTAs. */
244
+ error: (options: {
245
+ code: string
246
+ cta?: CtaBlock | undefined
247
+ message: string
248
+ retryable?: boolean | undefined
249
+ }) => never
250
+ /** Return a success result with optional metadata (e.g. CTAs). */
251
+ ok: (data: InferReturn<output>, meta?: { cta?: CtaBlock | undefined }) => never
252
+ options: InferOutput<options>
253
+ }) =>
232
254
  | InferReturn<output>
233
255
  | Promise<InferReturn<output>>
234
256
  | AsyncGenerator<InferReturn<output>, unknown, unknown>)
@@ -300,8 +322,8 @@ async function serveImpl(
300
322
  return
301
323
  }
302
324
 
303
- // Human mode: default unless --verbose or explicit --format/--json override
304
- const human = !formatExplicit && !verbose
325
+ // Human mode: stdout is a TTY.
326
+ const human = process.stdout.isTTY === true
305
327
 
306
328
  function writeln(s: string) {
307
329
  stdout(s.endsWith('\n') ? s : `${s}\n`)
@@ -377,14 +399,15 @@ async function serveImpl(
377
399
  const rest = filtered.slice(skillsIdx + 2)
378
400
  const depthArg = rest.indexOf('--depth')
379
401
  const depthEq = rest.find((t) => t.startsWith('--depth='))
380
- const depth = depthArg !== -1
381
- ? Number(rest[depthArg + 1])
382
- : depthEq
383
- ? Number(depthEq.split('=')[1])
384
- : (options.sync?.depth ?? 1)
402
+ const depth =
403
+ depthArg !== -1
404
+ ? Number(rest[depthArg + 1])
405
+ : depthEq
406
+ ? Number(depthEq.split('=')[1])
407
+ : (options.sync?.depth ?? 1)
385
408
  const global = rest.includes('--no-global') ? false : undefined
386
409
  try {
387
- if (human) stdout('Syncing...')
410
+ stdout('Syncing...')
388
411
  const result = await SyncSkills.sync(name, commands, {
389
412
  cwd: options.sync?.cwd,
390
413
  depth,
@@ -392,31 +415,30 @@ async function serveImpl(
392
415
  global,
393
416
  include: options.sync?.include,
394
417
  })
395
- if (human) {
396
- stdout('\r\x1b[K')
397
- const lines: string[] = []
398
- const skillLabel = (s: (typeof result.skills)[number]) =>
399
- s.external || s.name === name ? s.name : `${name}-${s.name}`
400
- const maxLen = Math.max(...result.skills.map((s) => skillLabel(s).length))
401
- for (const s of result.skills) {
402
- const label = skillLabel(s)
403
- const padding = s.description
404
- ? `${' '.repeat(maxLen - label.length)} ${s.description}`
405
- : ''
406
- lines.push(` ✓ ${label}${padding}`)
407
- }
408
- lines.push('')
409
- lines.push(`${result.skills.length} skill${result.skills.length === 1 ? '' : 's'} synced`)
410
- const suggestions = options.sync?.suggestions
411
- if (suggestions && suggestions.length > 0) {
412
- lines.push('')
413
- lines.push(`Your agent can now use ${name}. Try asking:`)
414
- for (const s of suggestions) lines.push(` "${s}"`)
415
- }
418
+ stdout('\r\x1b[K')
419
+ const lines: string[] = []
420
+ const skillLabel = (s: (typeof result.skills)[number]) =>
421
+ s.external || s.name === name ? s.name : `${name}-${s.name}`
422
+ const maxLen = Math.max(...result.skills.map((s) => skillLabel(s).length))
423
+ for (const s of result.skills) {
424
+ const label = skillLabel(s)
425
+ const padding = s.description
426
+ ? `${' '.repeat(maxLen - label.length)} ${s.description}`
427
+ : ''
428
+ lines.push(` ✓ ${label}${padding}`)
429
+ }
430
+ lines.push('')
431
+ lines.push(`${result.skills.length} skill${result.skills.length === 1 ? '' : 's'} synced`)
432
+ const suggestions = options.sync?.suggestions
433
+ if (suggestions && suggestions.length > 0) {
416
434
  lines.push('')
417
- lines.push(`Run \`${name} --help\` to see the full command reference.`)
418
- writeln(lines.join('\n'))
419
- } else {
435
+ lines.push(`Your agent can now use ${name}. Try asking:`)
436
+ for (const s of suggestions) lines.push(` "${s}"`)
437
+ }
438
+ lines.push('')
439
+ lines.push(`Run \`${name} --help\` to see the full command reference.`)
440
+ writeln(lines.join('\n'))
441
+ if (verbose || formatExplicit) {
420
442
  const output: Record<string, unknown> = { skills: result.paths }
421
443
  if (verbose && result.agents.length > 0) output.agents = result.agents
422
444
  writeln(Formatter.format(output, formatExplicit ? formatFlag : 'toon'))
@@ -463,27 +485,26 @@ async function serveImpl(
463
485
  }
464
486
 
465
487
  try {
466
- if (human) stdout('Registering MCP server...')
488
+ stdout('Registering MCP server...')
467
489
  const result = await SyncMcp.register(name, {
468
490
  command,
469
491
  global,
470
492
  agents,
471
493
  })
472
- if (human) {
473
- stdout('\r\x1b[K')
474
- const lines: string[] = []
475
- lines.push(`✓ Registered ${name} as MCP server`)
476
- if (result.agents.length > 0) lines.push(` Agents: ${result.agents.join(', ')}`)
494
+ stdout('\r\x1b[K')
495
+ const lines: string[] = []
496
+ lines.push(`✓ Registered ${name} as MCP server`)
497
+ if (result.agents.length > 0) lines.push(` Agents: ${result.agents.join(', ')}`)
498
+ lines.push('')
499
+ lines.push(`Agents can now use ${name} tools.`)
500
+ const suggestions = options.sync?.suggestions
501
+ if (suggestions && suggestions.length > 0) {
477
502
  lines.push('')
478
- lines.push(`Agents can now use ${name} tools.`)
479
- const suggestions = options.sync?.suggestions
480
- if (suggestions && suggestions.length > 0) {
481
- lines.push('')
482
- lines.push('Try asking:')
483
- for (const s of suggestions) lines.push(` "${s}"`)
484
- }
485
- writeln(lines.join('\n'))
486
- } else
503
+ lines.push('Try asking:')
504
+ for (const s of suggestions) lines.push(` "${s}"`)
505
+ }
506
+ writeln(lines.join('\n'))
507
+ if (verbose || formatExplicit)
487
508
  writeln(
488
509
  Formatter.format(
489
510
  { name, command: result.command, agents: result.agents },
@@ -526,7 +547,7 @@ async function serveImpl(
526
547
 
527
548
  const resolved =
528
549
  filtered.length === 0 && options.rootCommand
529
- ? ({ command: options.rootCommand, path: name, rest: [] as string[] })
550
+ ? { command: options.rootCommand, path: name, rest: [] as string[] }
530
551
  : resolveCommand(commands, filtered)
531
552
 
532
553
  // --help after a command → show help for that command
@@ -607,10 +628,22 @@ async function serveImpl(
607
628
  const resolvedFormat = 'command' in resolved && resolved.command.format
608
629
  const format = formatExplicit ? formatFlag : resolvedFormat || options.format || 'toon'
609
630
 
631
+ // Fall back to root command when no subcommand matches
632
+ const effective =
633
+ 'error' in resolved && options.rootCommand
634
+ ? { command: options.rootCommand, path: name, rest: filtered }
635
+ : resolved
636
+
637
+ // Resolve outputPolicy: command/group → CLI-level → default ('all')
638
+ const effectiveOutputPolicy =
639
+ ('command' in resolved && resolved.outputPolicy) || options.outputPolicy
640
+ const renderOutput = !(human && !formatExplicit && effectiveOutputPolicy === 'agent-only')
641
+
610
642
  function write(output: Output) {
611
643
  const cta = output.meta.cta
612
- if (human) {
613
- if (output.ok && output.data != null) writeln(Formatter.format(output.data, format))
644
+ if (human && !verbose) {
645
+ if (output.ok && output.data != null && renderOutput)
646
+ writeln(Formatter.format(output.data, format))
614
647
  else if (!output.ok) writeln(formatHumanError(output.error))
615
648
  if (cta) writeln(formatHumanCta(cta))
616
649
  return
@@ -627,16 +660,10 @@ async function serveImpl(
627
660
  writeln(Formatter.format(payload, format))
628
661
  }
629
662
 
630
- // Fall back to root command when no subcommand matches
631
- const effective =
632
- 'error' in resolved && options.rootCommand
633
- ? { command: options.rootCommand, path: name, rest: filtered }
634
- : resolved
635
-
636
663
  if ('error' in effective) {
637
664
  const helpCmd = effective.path ? `${name} ${effective.path} --help` : `${name} --help`
638
665
  const message = `'${effective.error}' is not a command. See '${helpCmd}' for a list of available commands.`
639
- if (human) {
666
+ if (human && !verbose) {
640
667
  writeln(formatHumanError({ code: 'COMMAND_NOT_FOUND', message }))
641
668
  exit(1)
642
669
  return
@@ -678,6 +705,7 @@ async function serveImpl(
678
705
  }
679
706
 
680
707
  const result = command.run({
708
+ agent: !human,
681
709
  args,
682
710
  env,
683
711
  options: parsedOptions,
@@ -694,6 +722,7 @@ async function serveImpl(
694
722
  format,
695
723
  formatExplicit,
696
724
  human,
725
+ renderOutput,
697
726
  verbose,
698
727
  write,
699
728
  writeln,
@@ -762,7 +791,7 @@ async function serveImpl(
762
791
  },
763
792
  }
764
793
 
765
- if (human && error instanceof ValidationError) {
794
+ if (human && !formatExplicit && error instanceof ValidationError) {
766
795
  writeln(formatHumanValidationError(name, path, command, error))
767
796
  exit(1)
768
797
  return
@@ -804,7 +833,12 @@ function resolveCommand(
804
833
  commands: Map<string, CommandEntry>,
805
834
  tokens: string[],
806
835
  ):
807
- | { command: CommandDefinition<any, any, any>; path: string; rest: string[] }
836
+ | {
837
+ command: CommandDefinition<any, any, any>
838
+ outputPolicy?: OutputPolicy | undefined
839
+ path: string
840
+ rest: string[]
841
+ }
808
842
  | {
809
843
  help: true
810
844
  path: string
@@ -819,8 +853,10 @@ function resolveCommand(
819
853
  let entry = commands.get(first)!
820
854
  const path = [first]
821
855
  let remaining = rest
856
+ let inheritedOutputPolicy: OutputPolicy | undefined
822
857
 
823
858
  while (isGroup(entry)) {
859
+ if (entry.outputPolicy) inheritedOutputPolicy = entry.outputPolicy
824
860
  const next = remaining[0]
825
861
  if (!next)
826
862
  return {
@@ -840,7 +876,13 @@ function resolveCommand(
840
876
  entry = child
841
877
  }
842
878
 
843
- return { command: entry, path: path.join(' '), rest: remaining }
879
+ const outputPolicy = entry.outputPolicy ?? inheritedOutputPolicy
880
+ return {
881
+ command: entry,
882
+ path: path.join(' '),
883
+ rest: remaining,
884
+ ...(outputPolicy ? { outputPolicy } : undefined),
885
+ }
844
886
  }
845
887
 
846
888
  /** @internal Options for serveImpl, extending public serve.Options with internal metadata. */
@@ -849,6 +891,8 @@ declare namespace serveImpl {
849
891
  description?: string | undefined
850
892
  /** CLI-level default output format. */
851
893
  format?: Formatter.Format | undefined
894
+ /** CLI-level default output policy. */
895
+ outputPolicy?: OutputPolicy | undefined
852
896
  mcp?:
853
897
  | {
854
898
  agents?: string[] | undefined
@@ -921,10 +965,14 @@ export type CommandsMap = Record<
921
965
  /** @internal Entry stored in a command map — either a leaf definition or a group. */
922
966
  type CommandEntry = CommandDefinition<any, any, any> | InternalGroup
923
967
 
968
+ /** Controls when output data is displayed. `'all'` displays to both humans and agents. `'agent-only'` suppresses data output in human/TTY mode. */
969
+ export type OutputPolicy = 'agent-only' | 'all'
970
+
924
971
  /** @internal A command group's internal storage. */
925
972
  type InternalGroup = {
926
973
  _group: true
927
974
  description?: string | undefined
975
+ outputPolicy?: OutputPolicy | undefined
928
976
  commands: Map<string, CommandEntry>
929
977
  }
930
978
 
@@ -939,6 +987,9 @@ export const toCommands = new WeakMap<Cli, Map<string, CommandEntry>>()
939
987
  /** @internal Maps root CLI instances to their command definitions. */
940
988
  const toRootDefinition = new WeakMap<Root, CommandDefinition<any, any, any>>()
941
989
 
990
+ /** @internal Maps CLI instances to their output policy. */
991
+ const toOutputPolicy = new WeakMap<Cli, OutputPolicy>()
992
+
942
993
  /** @internal Sentinel symbol for `ok()` and `error()` return values. */
943
994
  const sentinel = Symbol.for('incur.sentinel')
944
995
 
@@ -1016,16 +1067,17 @@ async function handleStreaming(
1016
1067
  format: Formatter.Format
1017
1068
  formatExplicit: boolean
1018
1069
  human: boolean
1070
+ renderOutput: boolean
1019
1071
  verbose: boolean
1020
1072
  write: (output: Output) => void
1021
1073
  writeln: (s: string) => void
1022
1074
  exit: (code: number) => void
1023
1075
  },
1024
1076
  ) {
1025
- // Incremental: human, no explicit format (default toon), or explicit jsonl
1077
+ // Incremental: no explicit format (default toon), or explicit jsonl
1026
1078
  // Buffered: explicit json/yaml/toon/md
1027
- const useJsonl = !ctx.human && ctx.formatExplicit && ctx.format === 'jsonl'
1028
- const incremental = ctx.human || useJsonl || !ctx.formatExplicit
1079
+ const useJsonl = ctx.formatExplicit && ctx.format === 'jsonl'
1080
+ const incremental = useJsonl || !ctx.formatExplicit
1029
1081
 
1030
1082
  if (incremental) {
1031
1083
  // Incremental output: write each chunk as it arrives
@@ -1060,7 +1112,7 @@ async function handleStreaming(
1060
1112
  }
1061
1113
  }
1062
1114
  if (useJsonl) ctx.writeln(JSON.stringify({ type: 'chunk', data: value }))
1063
- else ctx.writeln(Formatter.format(value, 'toon'))
1115
+ else if (ctx.renderOutput) ctx.writeln(Formatter.format(value, 'toon'))
1064
1116
  }
1065
1117
 
1066
1118
  // Handle return value — error() or ok() sentinel
@@ -1444,8 +1496,8 @@ type CommandDefinition<
1444
1496
  > = {
1445
1497
  /** Map of option names to single-char aliases. */
1446
1498
  alias?: options extends z.ZodObject<any>
1447
- ? Partial<Record<keyof z.output<options>, string>>
1448
- : Record<string, string> | undefined
1499
+ ? Partial<Record<keyof z.output<options>, string>>
1500
+ : Record<string, string> | undefined
1449
1501
  /** Zod schema for positional arguments. */
1450
1502
  args?: args | undefined
1451
1503
  /** A short description of what the command does. */
@@ -1462,10 +1514,22 @@ type CommandDefinition<
1462
1514
  options?: options | undefined
1463
1515
  /** Zod schema for the command's return value. */
1464
1516
  output?: output | undefined
1517
+ /**
1518
+ * Controls when output data is displayed. Inherited by child commands when set on a group.
1519
+ *
1520
+ * - `'all'` — displays to both humans and agents.
1521
+ * - `'agent-only'` — suppresses data output in human/TTY mode while still returning it to agents.
1522
+ *
1523
+ * @default 'all'
1524
+ */
1525
+ outputPolicy?: OutputPolicy | undefined
1465
1526
  /** Alternative usage patterns shown in help output. */
1466
1527
  usage?: Usage<args, options>[] | undefined
1467
1528
  /** The command handler. Return a value for single-return, or use `async *run` to stream chunks. */
1468
1529
  run(context: {
1530
+ /** Whether the consumer is an agent (stdout is not a TTY). */
1531
+ agent: boolean
1532
+ /** Positional arguments. */
1469
1533
  args: InferOutput<args>
1470
1534
  /** Parsed environment variables. */
1471
1535
  env: InferOutput<env>
package/src/Mcp.test.ts CHANGED
@@ -19,8 +19,8 @@ function createTestCommands() {
19
19
  options: z.object({
20
20
  upper: z.boolean().default(false).describe('Uppercase output'),
21
21
  }),
22
- run({ args, options }: any) {
23
- const msg = options.upper ? args.message.toUpperCase() : args.message
22
+ run(c: any) {
23
+ const msg = c.options.upper ? c.args.message.toUpperCase() : c.args.message
24
24
  return { result: msg }
25
25
  },
26
26
  })
@@ -34,8 +34,8 @@ function createTestCommands() {
34
34
  {
35
35
  description: 'Say hello',
36
36
  args: z.object({ name: z.string().describe('Name to greet') }),
37
- run({ args }: any) {
38
- return { greeting: `hello ${args.name}` }
37
+ run(c: any) {
38
+ return { greeting: `hello ${c.args.name}` }
39
39
  },
40
40
  },
41
41
  ],
@@ -44,8 +44,8 @@ function createTestCommands() {
44
44
 
45
45
  commands.set('fail', {
46
46
  description: 'Always fails',
47
- run({ error }: any) {
48
- return error({ code: 'BOOM', message: 'it broke' })
47
+ run(c: any) {
48
+ return c.error({ code: 'BOOM', message: 'it broke' })
49
49
  },
50
50
  })
51
51
 
package/src/SyncSkills.ts CHANGED
@@ -58,9 +58,19 @@ export async function sync(
58
58
 
59
59
  const { paths, agents } = Agents.install(tmpDir, { global, cwd })
60
60
 
61
- // Write skills hash for staleness detection
61
+ // Remove stale skills from previous installs
62
+ const currentNames = new Set(paths.map((p) => path.basename(p)))
63
+ const prev = readMeta(name)
64
+ if (prev?.skills) {
65
+ for (const old of prev.skills) {
66
+ if (currentNames.has(old)) continue
67
+ Agents.remove(old, { global, cwd })
68
+ }
69
+ }
70
+
71
+ // Write skills hash + names for staleness detection
62
72
  const hashEntries = collectEntries(commands, [])
63
- writeHash(name, Skill.hash(hashEntries))
73
+ writeMeta(name, Skill.hash(hashEntries), [...currentNames])
64
74
 
65
75
  return { skills, paths, agents }
66
76
  } finally {
@@ -158,20 +168,27 @@ function hashPath(name: string): string {
158
168
  return path.join(dir, 'incur', `${name}.json`)
159
169
  }
160
170
 
161
- /** @internal Writes the skills hash for staleness detection. */
162
- function writeHash(name: string, hash: string) {
171
+ /** @internal Writes the skills metadata for staleness detection and cleanup. */
172
+ function writeMeta(name: string, hash: string, skills: string[]) {
163
173
  const file = hashPath(name)
164
174
  const dir = path.dirname(file)
165
175
  if (!fsSync.existsSync(dir)) fsSync.mkdirSync(dir, { recursive: true })
166
- fsSync.writeFileSync(file, JSON.stringify({ hash, at: new Date().toISOString() }) + '\n')
176
+ fsSync.writeFileSync(
177
+ file,
178
+ JSON.stringify({ hash, skills, at: new Date().toISOString() }) + '\n',
179
+ )
167
180
  }
168
181
 
169
- /** Reads the stored skills hash for a CLI. Returns `undefined` if no hash exists. */
170
- export function readHash(name: string): string | undefined {
182
+ /** @internal Reads the stored metadata for a CLI. */
183
+ function readMeta(name: string): { hash: string; skills?: string[] } | undefined {
171
184
  try {
172
- const data = JSON.parse(fsSync.readFileSync(hashPath(name), 'utf-8'))
173
- return data.hash
185
+ return JSON.parse(fsSync.readFileSync(hashPath(name), 'utf-8'))
174
186
  } catch {
175
187
  return undefined
176
188
  }
177
189
  }
190
+
191
+ /** Reads the stored skills hash for a CLI. Returns `undefined` if no hash exists. */
192
+ export function readHash(name: string): string | undefined {
193
+ return readMeta(name)?.hash
194
+ }
package/src/bin.ts CHANGED
@@ -19,10 +19,10 @@ const cli = Cli.create('incur', {
19
19
  entry: z.string().optional().describe('Entrypoint path (absolute)'),
20
20
  output: z.string().optional().describe('Output path (absolute)'),
21
21
  }),
22
- async run({ options }) {
23
- const dir = options.dir ?? '.'
24
- const entry = options.entry ?? dir
25
- const output = options.output ?? path.join(dir, 'incur.generated.ts')
22
+ async run(c) {
23
+ const dir = c.options.dir ?? '.'
24
+ const entry = c.options.entry ?? dir
25
+ const output = c.options.output ?? path.join(dir, 'incur.generated.ts')
26
26
  await Typegen.generate(entry, output)
27
27
  return { dir, entry, output }
28
28
  },