Skip to content

bug: kebab-case Zod schema keys silently fail — ctx.args['dry-run'] is always undefined #177

Description

@zrosenbauer

Summary

When defining command options with kebab-case keys in a Zod schema (e.g., 'dry-run'), --help correctly displays --dry-run but ctx.args['dry-run'] is always undefined at runtime. Only the camelCase variant ctx.args.dryRun works. Both forms should be valid accessors since the schema key is the user's source of truth.

Current behavior

const options = z.object({
  'dry-run': z.boolean().default(false).describe('Preview without writing'),
})

export default command({
  options,
  handler: (ctx) => {
    ctx.args['dry-run'] // undefined — always falls through to default
    ctx.args.dryRun     // works — but doesn't match the schema key
  },
})

--help shows --dry-run correctly, but the value is never accessible via the schema key.

Root cause

cleanParsedArgs in packages/core/src/runtime/args/parser.ts:79-83 strips all keys containing -:

function cleanParsedArgs(argv: Record<string, unknown>): Record<string, unknown> {
  return Object.fromEntries(
    Object.entries(argv).filter(([key]) => key !== '_' && key !== '$0' && !key.includes('-'))
  )
}

Yargs provides both dry-run and dryRun on argv. This filter removes dry-run, keeping only the camelCase dryRun. When the Zod schema expects dry-run, validation falls back to the .default() value since the key is missing.

Expected behavior

ctx.args['dry-run'] and ctx.args.dryRun should both return the parsed value. Either:

  1. Keep both — Don't strip kebab keys from cleaned args, or
  2. Normalize schema keys — Map kebab Zod keys to their camelCase equivalents before validation, then provide both on ctx.args

Location

  • packages/core/src/runtime/args/parser.tscleanParsedArgs (~line 79)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions