agentleFS
Sign inSign up

openapi-to-cli / rules

EvilFreelancer/openapi-to-cli/.cursor/rules/architecture.mdc

Architecture of openapi-to-cli (ocli)

Cursor rule260 starsChanged 27 days ago
---
description: Architecture of openapi-to-cli (ocli)
globs: src/**/*.ts
alwaysApply: true
---

# Architecture of openapi-to-cli (ocli)

`ocli` is a TypeScript/Node CLI that converts OpenAPI/Swagger specs into runtime CLI commands. No code generation: at every invocation it loads a cached spec and builds command definitions on the fly.

## Data flow

```
ocli profiles add <name> --api-base-url ... --openapi-spec ...
       |
       v
ConfigLocator -> .ocli/ dir (global or local)
       |
       v
ProfileStore  -> profiles.ini (read/write/select)
       |
       v
OpenapiLoader -> fetches spec with the profile auth headers, caches under .ocli/specs/<profile>.json
       |
       v
OpenapiToCommands -> parses spec, applies include/exclude filters,
                     builds CliCommand[] (name, method, path, options, body schema)
       |
       v
CommandSearch (BM25)  -> ranks commands by NL query or regex
       |
       v
cli.ts (yargs) -> resolves profile + spec, dispatches: profiles | use | commands | <toolName>
       |
       v
HttpClient (axios)    -> performs the real HTTP request to API_BASE_URL
```

## Components (mapping to `src/`)

- `config.ts` - `ConfigLocator`. Finds `.ocli/` (global `~/.ocli/`, local in CWD), resolves `profiles.ini` paths.
- `profile-store.ts` - `ProfileStore`, `Profile`. Reads/writes `profiles.ini`, tracks current profile, validates fields.
- `openapi-loader.ts` - `OpenapiLoader`. Loads spec from URL or local file, caches it to `.ocli/specs/<profile>.json`, refreshes on demand. Resolves external `$ref` across multi-file specs. Remote fetches (the spec and every external `$ref` document) carry the headers passed by `cli.ts` through `loadSpec(profile, { headers })`: the profile custom headers plus the Basic/Bearer `Authorization` built by `buildProfileAuthHeaders`.
- `openapi-to-commands.ts` - `OpenapiToCommands`, `CliCommand`, `CliCommandOption`. Walks the spec, applies include/exclude filters, expands path-level params, resolves local `$ref`, builds command names with optional prefix, expands `enum`/`default`/`nullable`/`oneOf` schema hints for `--help`.
- `command-search.ts` - `CommandSearch`. BM25 over `(name, method, path, description, options[].name)`, plus regex fallback. Same engine used by both `ocli commands` and any future agent skill.
- `command-args.ts` - `findUnknownFlags`, `acceptsFreeFormBody`, `formatUnknownFlagsError`. Validates parsed flags of a dynamic command against `CliCommand.options`, suggests the closest declared name, and keeps the free-form body passthrough for body-capable operations whose spec declares no body.
- `bm25.ts` - tokenizer + BM25 scorer, no I/O.
- `cli.ts` - `ocli` entry point. yargs command tree: `profiles add|remove|list`, `use`, `commands`, and dynamic per-spec commands. Builds the `axios` request from a `CliCommand` + parsed args; injects auth, custom headers, server URL overrides.
- `version.ts` - resolves `VERSION` at runtime from `OCLI_VERSION` or `package.json`; there is no generation step.

## Design principles

1. **OpenAPI-driven**: commands and their options come from the spec. No hand-maintained registry.
2. **Profiles**: every API connection is named; `profiles.ini` is the source of truth. Global vs local `.ocli/` priority is decided by `ConfigLocator`.
3. **Spec cache**: never re-download a spec on every invocation. Refresh is explicit (`onboard`/profile add or refresh flag).
4. **Pure transform layer**: `bm25.ts`, `openapi-to-commands.ts`, `command-search.ts`, and `command-args.ts` perform no I/O; they take inputs and return outputs. This keeps them trivially unit-testable.
5. **Side effects at the edges**: filesystem in `config.ts`/`profile-store.ts`/`openapi-loader.ts`, network in `cli.ts` via `HttpClient`. Inject these via constructors (`fs`, `httpClient`) so tests can swap them.
6. **TypeScript strict**: `strict: true` in `tsconfig.json`. Explicit types for exported functions and public interfaces.
7. **No surprise breaking changes**: every CLI-visible change must be reflected in `README.md`. This includes changes that add no flag, such as which requests carry profile credentials.

## Layers and allowed dependencies

```
Layer 0 (pure)         bm25.ts, version.ts, types in openapi-to-commands.ts
Layer 1 (I/O wrappers) config.ts, profile-store.ts, openapi-loader.ts
Layer 2 (transform)    openapi-to-commands.ts (uses Profile), command-search.ts (uses CliCommand + bm25),
                       command-args.ts (uses CliCommand)
Layer 3 (entry)        cli.ts (uses everything above; only this layer talks to yargs/axios/process)
```

Lower layers must not import from higher layers. New behavior should live in the lowest layer where it makes sense - prefer adding to Layer 2 over expanding `cli.ts`.

## Repository layout

- `src/` - production code (see components above).
- `tests/` - Jest test files (`*.test.ts`), fixtures under `tests/fixtures/`, recorded results under `tests/results/`.
- `examples/skill-ocli-api.md` - example Claude Code skill describing the agent workflow.
- `skills/ocli-api/SKILL.md` - portable OpenClaw skill.
- `benchmarks/benchmark.ts` - token-overhead comparison (MCP variants vs CLI).
- `.ocli/` - working dir at runtime (not part of source). Never committed.
- `dist/` - `tsc` build output.

## When extending the spec parser

Real-world OpenAPI/Swagger documents drift from any single example. Before changing `openapi-to-commands.ts` or `openapi-loader.ts`:

- Add a minimal fixture under `tests/fixtures/` that reproduces the case (don't hand-edit `box-api-yaml.test.ts` or `github-api.test.ts` fixtures - those are real specs).
- Cover both OAS 3 (`requestBody`, `components/schemas`) and Swagger 2 (`body`/`formData`, `definitions`) when the change affects request building.
- Mention the new spec feature in the README "Broader spec support" or "Better request generation" section.

Discussion

Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.

Posts are public.Sign in to post

No one has posted yet. Be the first.