Skip to content

Agent UX: importer/exporter registered without options yields an unusable CLI command (no arg/flag contracts, confusing "Too many arguments", no doctor diagnostic) #504

Closed

Description

@unbraind

Summary

When an extension registers an importer/exporter with only a handler —
api.registerImporter("todos", handler) — and omits the optional third
options argument ({ arguments, flags, description, ... }), the derived
<name> import / <name> export command is completely unusable from the CLI
,
even though the handler reads ctx.args[0] and ctx.options.* and works fine
through the SDK/MCP action path. Nothing warns the author, and the runtime
errors give an agent no actionable hint.

This is adjacent to #503 (flattened aliases dropping contracts on bundled
groups) but a distinct code path: here the canonical derived command of an
installed importer/exporter carries no argument or flag contract at all.

Reproduction

An importer registered the bare way:

api.registerImporter("todos", async (ctx) => {
  const file = ctx.args?.[0] ?? readStringOption(ctx.options, "file");
  const format = readStringOption(ctx.options, "format");
  // ... handler fully supports a positional file + --file/--format/...
});

Then, against pm-cli 2026.7.10:

$ pm todos import backlog.jsonl
Error: Too many arguments for extension command 'todos import': backlog.jsonl.

$ pm todos import --file backlog.jsonl --format jsonl
Error: Unknown option '--file' for extension command 'todos import'.

$ pm todos import --help
Usage: pm todos import [options]
Options:
  -h, --help  display help for command      # <-- no flags, no <file> arg

The same handler invoked via the SDK works:
await runAction({ action: "todos-import", options: { file: "backlog.jsonl", format: "jsonl" } }) → OK.

By contrast, a registerCommand({ name: "todos sync", flags: [...] }) in the
same extension exposes its flags and accepts a positional file perfectly, and a
bundled importer that does pass options works too — confirming the derived
command faithfully reflects declared contracts, and the gap is purely the
missing declaration going undetected.

Why this is agent-hostile

  • pm <name> import --help is empty, so an agent can't discover how to call it.
  • Too many arguments for a positional file (the one thing an importer always
    needs) gives no hint that the file must come via declared arguments/--file.
  • Unknown option '--file' contradicts the handler, which reads --file.
  • Green unit tests hide it entirely (handlers are tested directly, bypassing the
    CLI parser), so it ships silently. (Real example: pm-todos import/export were
    unusable via CLI for multiple releases; fixed in fix(todos): make CLI import/export usable + harden status/jsonl/sync semantics pm-todos#22 by
    declaring the contracts.)

Proposed improvements (any subset)

  1. pm package doctor diagnostic: warn when a derived importer/exporter
    command declares zero arguments and flags while its handler reads
    ctx.args/ctx.options — the exact authoring footgun above. This would have
    caught it. (Today doctor reports collisions but nothing here.)
  2. Sensible default for importers/exporters: since an importer inherently
    consumes a source, have the derived <name> import command accept an optional
    positional file and a --file flag by default, even when options is
    omitted. (Exporters similarly default --output.)
  3. Better error text: on Too many arguments for a derived import command
    that declares no positional, hint: "this importer declares no file argument;
    pass the source via its declared options or add arguments:[{name:'file'}]."

Improvement (1) is the highest-leverage: a single doctor rule turns a silent,
ships-to-production footgun into an author-time warning.

Environment

Activity

  1. coderabbitai commented on Jul 10, 2026

    @coderabbitai
    🔗 Related PRs

    #36 - [codex] Improve agent UX from manual pm audit [closed]
    #51 - Polish CLI ergonomics, delete safety, and templates dedup [closed]
    #418 - Refactor residual CodeFactor complexity hotspots [merged]
    #422 - Eliminate remaining CodeFactor SDK and CLI issues [merged]
    #428 - Harden SDK extension runtime packaging [merged]


    📝 Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    🧪 Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  2. unbraind commented on Jul 11, 2026

    @unbraind
    OwnerAuthor

    Fixed by #511 and merged to main in f1dafef. The implementation passed 5,533 tests at exact 100/100/100/100 coverage plus all required CI, security, static, smoke, and bot-review gates.

  3. unbraind commented on Jul 11, 2026

    @unbraind
    OwnerAuthor

    pm tracker item:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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