Skip to content

Feature Request: Support for passthrough/arbitrary CLI flags #161

@haydenbleasel

Description

@haydenbleasel

Use Case

We're building Ultracite, a CLI wrapper around Biome (a linter/formatter). We want to allow users to pass any Biome CLI flag through our commands without having to explicitly define every possible flag in our tRPC schema.

Current Behavior

When using z.record(z.string(), z.unknown()) or z.object({}).passthrough(), trpc-cli doesn't accept unknown flags. For example:

check: t.procedure
  .input(
    z.tuple([
      z.array(z.string()).optional().default([]),
      z.object({}).passthrough().optional().default({}),
    ])
  )
  .query(({ input }) => check(input))

Running mycli check --verbose results in:

error: unknown option '--verbose'

Desired Behavior

We'd like a way to accept arbitrary flags that get passed through to the underlying tool. Something like:

  1. Schema-level option: z.object({}).catchall(z.unknown()) or similar that tells trpc-cli to accept any flags
  2. Meta configuration: Add something like allowUnknownOptions: true to procedure meta
  3. createCli option: A global setting to allow passthrough for certain procedures

Workaround

Currently, we have to explicitly define every flag we want to support:

z.object({
  verbose: z.boolean().optional(),
  'diagnostic-level': z.enum(['info', 'warn', 'error']).optional(),
  'max-diagnostics': z.number().optional(),
  // ... etc
})

This works but means we can't support new Biome flags without updating our CLI.

Related

I noticed allowUnknownOption() is called in the trpc-cli source, but it seems to only apply at the program level, not for individual commands with schemas.

Proposed Solution

Perhaps something like:

check: t.procedure
  .meta({
    description: 'Run linter',
    allowUnknownOptions: true, // new option
  })
  .input(z.tuple([
    z.array(z.string()).optional(),
    z.record(z.string(), z.unknown()), // would now accept any --flag
  ]))

This would maintain type safety for known options while allowing flexibility for pass-through scenarios.

Would love to hear if this is something you'd consider adding, or if there's an existing pattern I'm missing!

Repository

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions