Skip to content

Generate API commands from the OpenAPI spec - #29

Open
dennis-chen-vanta wants to merge 4 commits into
dennis-chen-vanta/cli-command-structurefrom
dennis-chen-vanta/command-codegen
Open

dennis-chen-vanta wants to merge 4 commits into
dennis-chen-vanta/cli-command-structurefrom
dennis-chen-vanta/command-codegen

Conversation

@dennis-chen-vanta

@dennis-chen-vanta dennis-chen-vanta commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #28.

Summary

Adds the generator that produced the commands in #28, so they're regenerated from api-spec.json instead of edited by hand.

  • scripts/codegen/generate-commands.ts derives every API command in src/commands/ from the OpenAPI spec using the conventions in AGENTS.md. It reads each operation, names it (<group> <verb>-<noun>), turns its inputs into flags, writes help text, and renders one file per command group plus index.ts.
  • scripts/codegen/config.ts holds what the rules can't derive: top-level order, display names, the words that mark an action, the operations that attach or detach an existing resource (named add/remove), 2 command-name overrides, and help text that reads better than the generated default. Each entry says why it exists.
  • pnpm generate:commands regenerates src/commands/.
  • CI fails if the committed commands differ from the generator's output.
  • AGENTS.md says the commands are generated and where exceptions go.

The generator fails on duplicate command names and on help-text overrides that are stale or repeat the default.

Command changes

None. The only change to src/commands/ is a // Code generated by scripts/codegen/generate-commands.ts. DO NOT EDIT. header on each generated file. The generator uses that header to find and remove files for groups that no longer exist.

Test plan

  • On a clean checkout: pnpm generate:commands leaves src/commands/ unchanged (the new CI check), and pnpm typecheck and pnpm test (21/21) pass.
  • Command behavior was regression tested against main in Document command structure conventions and restructure commands to match #28, including dry-run, a local mock API, and the staging API (reads compared with main; write lifecycles on test resources). This PR's output is identical apart from the headers.

🤖 Generated with Claude Code

@semgrep2-openvanta

Copy link
Copy Markdown

Semgrep found 6 detect-non-literal-regexp findings:

🟡 Medium severity issue identified in your code:

RegExp() called with a word function argument, this might allow an attacker to cause a Regular Expression Denial-of-Service (ReDoS) within your application as RegExP blocks the main thread. For this reason, it is recommended to use hardcoded regexes instead. If your regex is run on user-controlled input, consider performing input validation or use a regex checking/sanitization library such as https://www.npmjs.com/package/recheck to verify that the regex does not appear vulnerable to ReDoS.

View Dataflow Graph
flowchart LR
    classDef invis fill:white, stroke: none
    classDef default fill:#e7f5ff, color:#1c7fd6, stroke: none

    subgraph File0["<b>scripts/codegen/generate-commands.ts</b>"]
        direction LR
        %% Source

        subgraph Source
            direction LR

            v0["<a href=https://github.com/OpenVanta/cli/blob/17b4866451a6c346bce42c8d5d32f49600520186/scripts/codegen/generate-commands.ts#L141 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 141] word</a>"]
        end
        %% Intermediate

        subgraph Traces0[Traces]
            direction TB

            v2["<a href=https://github.com/OpenVanta/cli/blob/17b4866451a6c346bce42c8d5d32f49600520186/scripts/codegen/generate-commands.ts#L141 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 141] word</a>"]

            v3["<a href=https://github.com/OpenVanta/cli/blob/17b4866451a6c346bce42c8d5d32f49600520186/scripts/codegen/generate-commands.ts#L152 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 152] singularize</a>"]

            v4["<a href=https://github.com/OpenVanta/cli/blob/17b4866451a6c346bce42c8d5d32f49600520186/scripts/codegen/generate-commands.ts#L152 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 152] words</a>"]

            v5["<a href=https://github.com/OpenVanta/cli/blob/17b4866451a6c346bce42c8d5d32f49600520186/scripts/codegen/generate-commands.ts#L489 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 489] singular</a>"]

            v6["<a href=https://github.com/OpenVanta/cli/blob/17b4866451a6c346bce42c8d5d32f49600520186/scripts/codegen/generate-commands.ts#L489 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 489] own</a>"]

            v7["<a href=https://github.com/OpenVanta/cli/blob/17b4866451a6c346bce42c8d5d32f49600520186/scripts/codegen/generate-commands.ts#L490 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 490] `</a>"]
        end
            v2 --> v3
            v3 --> v4
            v4 --> v5
            v5 --> v6
            v6 --> v7
        %% Sink

        subgraph Sink
            direction LR

            v1["<a href=https://github.com/OpenVanta/cli/blob/17b4866451a6c346bce42c8d5d32f49600520186/scripts/codegen/generate-commands.ts#L490 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 490] new RegExp(`-(to|from|for)-${own}$`)</a>"]
        end
    end
    %% Class Assignment
    Source:::invis
    Sink:::invis

    Traces0:::invis
    File0:::invis

    %% Connections

    Source --> Traces0
    Traces0 --> Sink

Loading

@dennis-chen-vanta
dennis-chen-vanta force-pushed the dennis-chen-vanta/cli-command-structure branch from 680d6c8 to aebb6f2 Compare October 21, 2026 17:46
@dennis-chen-vanta
dennis-chen-vanta force-pushed the dennis-chen-vanta/command-codegen branch from 9172332 to 0a3dbec Compare October 21, 2026 17:46
dennis-chen-vanta and others added 4 commits October 21, 2026 19:11
Add scripts/codegen/generate-commands.ts, which derives every API command
in src/commands/ from api-spec.json using the conventions in AGENTS.md.
Exceptions to the rules and help text overrides live in
scripts/codegen/config.ts.

- `pnpm generate:commands` regenerates src/commands/
- CI fails if the generated commands are out of date
- Generated files are marked with a "Code generated" header; the
  generator removes generated files for groups that no longer exist

The generated output matches the previous commit apart from the header.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Semgrep flags `new RegExp` built from a path segment
(detect-non-literal-regexp). Compare the suffix as a string instead;
generated output is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Resolve every $ref once when the spec loads, instead of calling
  deref() wherever a schema is read
- Type the parts of the OpenAPI spec the generator reads; no `any`
- Keep groups in a map keyed by CLI path instead of a tree with a fake
  root node
- Clearer names (`placement`, `topGroup`, `valueSchema`)

Generated commands are byte-for-byte identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `links` in config.ts lists operations that attach or detach an existing
  resource; the generator names them add/remove. The spec can't mark these
  itself, since its summaries also say "Add" for creates.
- GETs that return a file use `download`.
- `commandPlural` lets `uploads` read as `files` in list commands.
- Drops the overrides that shortened names repeating the group
  (`list-library`, `list-deactivated`, `add-document`, `add-test`).
- Removes mounts: every group is its path's first segment, so the
  generator no longer nests groups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dennis-chen-vanta
dennis-chen-vanta force-pushed the dennis-chen-vanta/command-codegen branch from 0a3dbec to 80902d0 Compare October 21, 2026 19:11
@dennis-chen-vanta
dennis-chen-vanta force-pushed the dennis-chen-vanta/cli-command-structure branch from aebb6f2 to 866696d Compare October 21, 2026 19:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant