npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@maykonpaulo/maestro-cli

v0.3.3

Published

Command-line interface for the Maestro declarative engine — offline generate, validate, diff, snapshot and introspect commands over @maykonpaulo/maestro-core.

Readme

@maykonpaulo/maestro-cli

📖 Guia completo — criar o admin de qualquer sistema: está no README do pacote @maykonpaulo/maestro-server no npm → https://www.npmjs.com/package/@maykonpaulo/maestro-server

Command-line interface for the Maestro declarative engine.

Status: foundation + operational commands

The Phase 5 foundation shipped the maestro binary, --help, --version, and an extensible command architecture. maestro generate (wraps the core's Declarative Config Generator), maestro validate (wraps the core's Declarative File Loader), maestro diff (compares two declarative config files), maestro snapshot (produces a canonical, deterministic snapshot of a declarative config file) and maestro introspect (canonicalizes a local IntrospectionResult, or connects to a real datasource via --provider) are the operational commands shipped so far. maestro introspect resolves a real provider from the consumer's project via --provider <alias> (12 aliases → 9 published @maykonpaulo/maestro-provider-* packages) and connects to live databases; the CLI itself stays driver-free — the database driver lives inside the provider the consumer installs. The CLI is a pure consumer of @maykonpaulo/maestro-core; it introduces no capability that doesn't already exist there.

The package is now publishable (private removed, publishConfig.access: public); its first public release is prepared via a Changeset and ships through the normal release flow (nextrclatest). The CLI is not the owner of the repo-wide GitHub Release latest pointer — that stays with @maykonpaulo/maestro-core (the CLI's releases are tagged with --latest=false). Publishing happens only via merge/promotion in the release workflow, never from a local command. See RELEASE.md.

Usage

maestro --help
maestro --version
maestro generate --input ./metadata.json --type metadata --format json
maestro validate --input ./maestro.config.yaml
maestro diff --from ./maestro.config.old.yaml --to ./maestro.config.new.yaml
maestro snapshot --input ./maestro.config.yaml --out ./maestro.snapshot.json
maestro introspect --input ./introspection.json --out ./maestro.introspection.json
$ maestro --help
maestro — Maestro CLI

Command-line interface for the Maestro declarative engine.

Usage:
  maestro <command> [options]

Options:
  -h, --help       Show this help message
  -v, --version    Show the CLI version

Available commands:
  generate     Generate a declarative config from a local metadata/schema JSON file
  validate     Validate a declarative config file (YAML or JSON)
  diff         Compare two declarative config files (YAML or JSON)
  snapshot     Produce a canonical snapshot of a declarative config file (YAML or JSON)
  introspect   Introspect a schema (offline file or real provider) into a canonical artifact

Running an unrecognized command exits with a non-zero status and a message pointing back to --help:

$ maestro unknown-thing
Unknown command 'unknown-thing'. Run "maestro --help" to see available commands.
$ echo $?
1

maestro generate

Reads a local JSON file containing either EntityMetadata[] or EntitySchema[] (plus optional relations/operations) and generates a declarative config (EntityDeclaration[] + optional consumers) using the core's generateDeclarativeConfigFromMetadata/generateDeclarativeConfigFromSchema and serializeDeclarativeConfig.

What it does NOT do: no introspection of a live datasource, no database/provider connection, no createMaestro() call. It only reads a file, transforms it via the core's public APIs, and writes text (stdout or a file). Introspecting a real datasource is a separate command (maestro introspect --provider <alias>).

Run maestro generate --help for the full flag reference and examples inline.

What input is expected

  • --type metadata expects the JSON file to be an object matching { entities: EntityMetadata[], relations?: RelationMetadata[], operations?: OperationMetadata[] } — typically produced by serializing engine.getMetadata() (or the entities subset of it).
  • --type schema expects { entities: EntitySchema[], relations?: RelationSchema[] } — the low-level config shape you'd otherwise write by hand or get from introspection tooling.

Minimal examples of both shapes are checked into packages/cli/examples/metadata.json and packages/cli/examples/schema.json.

JSON output

$ maestro generate --input packages/cli/examples/metadata.json --type metadata --format json
{
  "entities": [
    {
      "entity": "user",
      "label": "User",
      "pluralLabel": "Users",
      "fields": {
        "email": { "type": "email", "label": "Email", "required": true, "searchable": true },
        "id": { "type": "uuid", "label": "Id", "readonly": true, "primary": true }
      }
    }
  ]
}

YAML output

$ maestro generate --input packages/cli/examples/schema.json --type schema --format yaml
entities:
  - entity: user
    label: User
    pluralLabel: Users
    fields:
      email:
        type: email
        label: Email
        required: true
        searchable: true
      id:
        type: uuid
        label: Id
        readonly: true
        primary: true

Writing to a file

maestro generate --input ./metadata.json --type metadata --format yaml --out ./maestro.config.yaml
# Declarative config written to ./maestro.config.yaml

| Flag | Required | Description | |---|---|---| | --input <path> | yes | Path to a local JSON file | | --type <metadata\|schema> | yes | Whether the input matches EntityMetadata[] or EntitySchema[] shape | | --format <json\|yaml> | no (default json) | Output format | | --out <path> | no (default: stdout) | Writes the result to a file instead of printing it |

Errors (missing flags, unreadable file, invalid JSON, malformed shape) print a single readable line to stderr prefixed with maestro generate: and exit with status 1.

Recommended flow: metadata/schema → generate → maestro.config → createMaestro

EntityMetadata[] / EntitySchema[] (already exists)
        ↓ maestro generate --input ... --type ... --out maestro.config.yaml
maestro.config.yaml
        ↓ loadDeclarativeConfigFromFile('./maestro.config.yaml', { yamlParser })
DeclarativeFileConfig (validated)
        ↓
createMaestro({ datasources, declarations })

The file maestro generate writes is a normal declarative config — it round-trips through the existing Declarative File Loader and createMaestro({ declarations }) unchanged, exactly like a hand-written maestro.config.yaml. See docs/specs/cli-generate.md for the full walkthrough, including how to validate the generated file end-to-end.

maestro validate

Reads a local declarative config file (maestro.config.yaml/.yml/.json) and validates its structure using the core's loadDeclarativeConfigFromString. It is the natural counterpart to maestro generate: generate writes the file, validate confirms it is well-formed before you use it at runtime.

$ maestro validate --input packages/cli/examples/maestro.config.yaml
Valid declarative config: packages/cli/examples/maestro.config.yaml
Entities: 2
Consumers: 1

The format is detected from the file extension (.json → JSON, .yaml/.yml → YAML) unless --format overrides it. YAML is parsed by the yaml package the CLI provides — the core has no bundled YAML dependency.

What it does NOT do: no createMaestro() call, no database/provider connection, and it does NOT require declared operations to have a real handler binding. It only reads a file and validates its declarative structure. Wiring operations to code and starting the runtime is what createMaestro() does separately — see the validate spec for the exact difference.

| Flag | Required | Description | |---|---|---| | --input, -i <path> | yes | Path to a local declarative config file (.yaml, .yml or .json) | | --format <json\|yaml> | no | Overrides the format detected from the file extension |

Errors (missing flag, unreadable file, unknown extension, invalid format, parse failure, invalid structure) print a single readable line to stderr prefixed with maestro validate: and exit with a non-zero status. Run maestro validate --help for the full reference, and see docs/specs/cli-validate.md for the complete walkthrough.

maestro diff

Compares two local declarative config files and prints a readable, deterministic report of what changed. Both files are loaded and validated with the core's loadDeclarativeConfigFromString (the same validation as maestro validate), then their normalised structures are compared. It's the tool for reviewing, promoting or versioning a declarative change: validate confirms one file is well-formed; diff shows exactly what moved between two versions.

$ maestro diff --from ./before.json --to ./after.json
Differences found.

Entities:
  + invoice
  - legacyCustomer
  ~ user
    fields:
      + email
      ~ name
        label: "Full name" → "Name"
    operations:
      - archive

Consumers:
  + admin:user
  ~ backoffice:user
    list: {"fields":["id"]} → {"fields":["id","name"]}

When the two files are structurally equal it prints No differences found.. The format of each file is detected from its extension (.json → JSON, .yaml/.yml → YAML) unless --from-format / --to-format override it, so you can diff a .json against a .yaml.

What it compares: entities (label/pluralLabel/description/capabilities), fields (type/label/required/readonly/description/enumOptions/relationEntity/…), operations, relationships, and consumers (list/detail/forms/actions). What it does NOT do: no createMaestro(), no database/provider connection, no operation-binding requirement, and it never compares against a live schema — it diffs declarative files, not introspection metadata. The core's DiffEngine operates on IntrospectionResult (real DB schema), which is a different, not-yet-wired path.

| Flag | Required | Description | |---|---|---| | --from, -f <path> | yes | Baseline declarative config file | | --to, -t <path> | yes | Changed declarative config file | | --from-format <json\|yaml> | no | Overrides the format detected from the --from extension | | --to-format <json\|yaml> | no | Overrides the format detected from the --to extension |

Exit codes: 0 = valid files, no differences; 1 = valid files, with differences; 2 = usage, read, parse or validation error. Errors print a single readable line to stderr prefixed with maestro diff:. Run maestro diff --help for the full reference, and see docs/specs/cli-diff.md for the complete walkthrough.

maestro snapshot

Loads a local declarative config file, validates it with the core's loadDeclarativeConfigFromString (the same validation as maestro validate), and emits a canonical, deterministic JSON snapshot of its declarations. The snapshot is a stable, versionable artifact: commit it alongside your config, review it in PRs, and use it as the baseline for future comparison. The same (semantically equivalent) input always produces byte-identical output.

$ maestro snapshot --input packages/cli/examples/maestro.config.yaml
{
  "declarations": {
    "consumers": [ ... ],
    "entities": [ ... ],
    "relations": [ { "fromEntity": "order", "fromField": "userId", "toEntity": "user", "toField": "id", "type": "many-to-one" } ]
  },
  "kind": "maestro.declarative.snapshot",
  "schemaVersion": 1,
  "source": { "format": "yaml", "path": "packages/cli/examples/maestro.config.yaml" },
  "summary": { "consumers": 1, "entities": 2, "relations": 1 }
}

Writing to a file prints a readable summary instead of the JSON:

$ maestro snapshot --input packages/cli/examples/maestro.config.yaml --out ./maestro.snapshot.json
Snapshot written: ./maestro.snapshot.json
Entities: 2
Consumers: 1
Relations: 1

The snapshot is canonical: entities are sorted by name, consumers by name, relationships (and the flattened top-level relations index, each tagged with its fromEntity) by derived id, and every object key is sorted. It is deterministic by design — it never includes a timestamp, an absolute path, a random hash or any value that varies between runs of the same input, so re-running it only changes the output when the config actually changed.

Declarative snapshot vs. introspection snapshot. This command snapshots a declarative file, not a live database or runtime introspection. The core's Snapshot Engine (IntrospectionSnapshot / SnapshotRepository) captures an IntrospectionResult from a real datasource and intentionally carries volatile fields (id, timestamp) — a different, not-yet-wired path. maestro snapshot is local, offline and reproducible.

What it does NOT do: no createMaestro(), no database/provider connection, no operation execution, and it does NOT require declared operations to have a real handler binding — operations are captured as declared. It never modifies the input file.

| Flag | Required | Description | |---|---|---| | --input, -i <path> | yes | Path to a local declarative config file (.yaml, .yml or .json) | | --input-format <json\|yaml> | no | Overrides the format detected from the --input extension | | --format json | no (default json) | Snapshot output format. Only json is supported for now | | --out <path> | no (default: stdout) | Writes the snapshot to a file instead of printing it |

Exit codes: 0 = snapshot produced; 1 = usage, read, parse, validation or write error. Errors print a single readable line to stderr prefixed with maestro snapshot:. Run maestro snapshot --help for the full reference, and see docs/specs/cli-snapshot.md for the complete walkthrough, including how validate, diff and snapshot relate.

maestro introspect

Reads a local JSON file describing an IntrospectionResult — the core's introspection contract ({ entities: EntityIntrospectionSchema[], relations: RelationIntrospectionSchema[] }) — validates it against that contract, and emits a canonical, deterministic JSON artifact. The same (semantically equivalent) input always produces byte-identical output, so the artifact is a stable, versionable input for future comparison, generation or review.

$ maestro introspect --input packages/cli/examples/introspection.json
{
  "kind": "maestro.introspection",
  "result": {
    "entities": [ { "fields": [ ... ], "table": "orders" }, { "fields": [ ... ], "table": "users" } ],
    "relations": [ { "confidence": "definite", "from": { ... }, "id": "orders.user_id->users.id", ... } ]
  },
  "schemaVersion": 1,
  "source": { "path": "packages/cli/examples/introspection.json" },
  "summary": { "entities": 2, "relations": 1 }
}

Writing to a file prints a readable summary instead of the JSON:

$ maestro introspect --input packages/cli/examples/introspection.json --out ./maestro.introspection.json
Introspection written: ./maestro.introspection.json
Entities: 2
Relations: 1

Two modes. maestro introspect runs either offline (--input, a local IntrospectionResult JSON file) or against a real datasource (--provider <alias>), producing the same canonical artifact in both. In offline mode it does not connect to any database, read a connection string or .env, load a driver, or execute a query — it operates only on the local JSON file. In provider mode it resolves a real IntrospectionProvider from the consumer's project (via --provider <alias> or --provider-package <name>), connects to the live database and reads its schema. The CLI itself stays driver-free: the database driver lives inside the provider the consumer installs, not in the CLI's dependencies.

The CLI resolves 12 aliases → 9 published packages: sqlite, postgres, mysql and mssql all map to @maykonpaulo/maestro-provider-sql (dialect selected per alias); mongodb, redis, elasticsearch, dynamodb, cassandra, couchbase, firestore and neo4j each map to their own @maykonpaulo/maestro-provider-* package. Install the provider in your project, then:

# SQLite via the --database shortcut
maestro introspect --provider sqlite --database ./app.db --out introspection.json

# MongoDB via generic --provider-option key=value (repeatable)
maestro introspect --provider mongodb \
  --provider-option uri=mongodb://localhost:27017 \
  --provider-option database=app \
  --out introspection.json

See docs/specs/cli-provider-resolution.md for the full alias table, package resolution, provider options and error handling.

The artifact is canonical: entities are sorted by table, relations by id, and every object key is sorted. Field (column) order within an entity is preserved. It is deterministic by design — no timestamp, absolute path or hash is ever included, so re-running it only changes the output when the input actually changed.

introspect vs. snapshot vs. diff. All three are read-only and never call createMaestro() or execute operations, but they operate on different inputs:

  • introspect produces a canonical IntrospectionResult (the real DB-schema contract) — either from a local file (--input, offline) or by connecting to a real datasource (--provider).
  • snapshot canonicalizes a declarative config file (maestro.config.yaml/.json), offline.
  • diff compares two declarative config files, offline.

The core's Introspection Runtime (DiffEngine, Snapshot Engine, ReportGenerator) also operates on IntrospectionResult, but those paths assume a live/real introspection and carry volatile data (e.g. IntrospectionSnapshot's id/timestamp, the report's completedAt). maestro introspect keeps its output canonical and reproducible, so it does not use them — see the spec for the exact boundary.

What it does NOT do: no createMaestro(), no operation execution, and no operation-binding requirement. It never modifies the --input file. (In offline mode it also makes no database connection whatsoever; in --provider mode the connection is owned by the resolved provider, not the CLI.)

| Flag | Required | Description | |---|---|---| | --input, -i <path> | offline mode | Path to a local IntrospectionResult JSON file | | --provider <alias> | provider mode | Official alias (e.g. sqlite, mongodb) resolved to a published @maykonpaulo/maestro-provider-* package in your project | | --provider-package <name> | provider mode | Explicit provider package name (bypasses the alias table; supports third-party providers) | | --database <value> | no | Alias shortcut for the provider's primary connection option (e.g. databasePath for SQLite) | | --provider-option <k=v> | no | Generic, repeatable provider option passed to the provider factory | | --config <path> | no | Config file with an introspection section; flags take precedence over it | | --out <path> | no (default: stdout) | Writes the artifact to a file instead of printing it |

--input (offline) and --provider/--provider-package (real introspection) are mutually exclusive. The CLI declares no provider or database driver as a dependency — the provider is loaded from the consumer's node_modules at runtime.

Exit codes: 0 = artifact produced; 1 = usage, read, parse, validation or write error. Errors print a single readable line to stderr prefixed with maestro introspect:. Run maestro introspect --help for the full reference, and see docs/specs/cli-introspect.md for the complete walkthrough.

Architectural rule: the CLI consumes, it doesn't implement

The CLI never re-implements a capability that already exists in @maykonpaulo/maestro-core. It calls generateDeclarativeConfigFromMetadata/generateDeclarativeConfigFromSchema and serializeDeclarativeConfig directly — there is no parallel generation or serialization logic in this package. maestro diff reuses the core's loadDeclarativeConfigFromString for loading and validation, adding only the small, local, deterministic structural comparison of two DeclarativeFileConfig objects (the core's DiffEngine targets IntrospectionResult, not declarative files). maestro snapshot likewise reuses loadDeclarativeConfigFromString for load+validation and adds only a small, local, deterministic canonicalizer over the resulting DeclarativeFileConfig (the core's Snapshot Engine, IntrospectionSnapshot/SnapshotRepository, targets a runtime IntrospectionResult with volatile id/timestamp fields — incompatible with a reproducible declarative snapshot). maestro introspect consumes the core's validateIntrospectionResult and normalizeIntrospectionResult directly for validation/canonicalization, and — in --provider mode — runs the resolved provider through the core's runIntrospectionProvider (never calling introspect() raw). It adds only a small, local envelope + deterministic key-sorting for byte-stable output; the core's higher-level runtime helpers (DiffEngine, Snapshot Engine, ReportGenerator) carry volatile fields (id/timestamp/completedAt), so a reproducible artifact can't reuse them here — see docs/specs/cli-introspect.md.

Command architecture

  • Command — interface a command implements: name, description, optional helpText (shown for maestro <name> --help; falls back to the global help when omitted), run(context).
  • CommandRegistry — a simple map of registered commands (register, get, list).
  • parseArgs — minimal global parser: detects --help/-h and --version/-v, and the first non-flag argument as the command name; everything else (including a command's own flags) passes through untouched.
  • runCli — orchestrates parsing → dispatch → exit code; when --help is combined with a known command, prints that command's own helpText instead of the global help. Accepts an injectable stdout/stderr and CommandRegistry, so it's fully testable without spawning a process.
  • createGenerateCommand — builds the generate Command; accepts injectable readFile/writeFile for testing without touching the real filesystem.
  • createValidateCommand — builds the validate Command; accepts an injectable readFile for testing without touching the real filesystem.
  • createDiffCommand — builds the diff Command; accepts an injectable readFile for testing without touching the real filesystem. The pure comparison lives in diffDeclarativeConfigs (diffDeclarativeConfigs + formatDeclarativeDiff), tested in isolation from file I/O.
  • createSnapshotCommand — builds the snapshot Command; accepts injectable readFile/writeFile for testing without touching the real filesystem. The pure canonicalization lives in buildDeclarativeSnapshot (buildDeclarativeSnapshot + serializeSnapshot), tested in isolation from file I/O.
  • createIntrospectCommand — builds the introspect Command; accepts injectable readFile/writeFile for testing without touching the real filesystem. The pure validation/canonicalization lives in buildIntrospection (validateIntrospectionResult + buildIntrospectionArtifact + serializeIntrospection), tested in isolation from file I/O.

Adding a future command means implementing Command (with its own helpText) and calling defaultRegistry.register(...) in runCli.ts — no change to runCli, parseArgs, or the entrypoint is needed.

Development

pnpm --filter @maykonpaulo/maestro-cli build
pnpm --filter @maykonpaulo/maestro-cli test
node packages/cli/dist/cli.js --help
node packages/cli/dist/cli.js generate --help
node packages/cli/dist/cli.js generate --input packages/cli/examples/metadata.json --type metadata
node packages/cli/dist/cli.js validate --help
node packages/cli/dist/cli.js validate --input packages/cli/examples/maestro.config.yaml
node packages/cli/dist/cli.js diff --help
node packages/cli/dist/cli.js diff --from packages/cli/examples/maestro.config.yaml --to packages/cli/examples/maestro.config.yaml
node packages/cli/dist/cli.js snapshot --help
node packages/cli/dist/cli.js snapshot --input packages/cli/examples/maestro.config.yaml
node packages/cli/dist/cli.js introspect --help
node packages/cli/dist/cli.js introspect --input packages/cli/examples/introspection.json

See docs/cli.md for the consolidated CLI overview (recommended flow, command table, limitations and next steps), docs/specs/cli-foundation.md for the CLI foundation architecture, docs/specs/cli-generate.md for the full maestro generate walkthrough, docs/specs/cli-validate.md for maestro validate, docs/specs/cli-diff.md for maestro diff, docs/specs/cli-snapshot.md for maestro snapshot, and docs/specs/cli-introspect.md for maestro introspect.