@maykonpaulo/maestro-cli 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +457 -0
- package/dist/chunk-CSDLIL7L.js +1309 -0
- package/dist/chunk-CSDLIL7L.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +13 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +338 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
# @maykonpaulo/maestro-cli
|
|
2
|
+
|
|
3
|
+
Command-line interface for the Maestro declarative engine.
|
|
4
|
+
|
|
5
|
+
## Status: foundation + operational commands
|
|
6
|
+
|
|
7
|
+
The **Phase 5 foundation** shipped the `maestro` binary, `--help`, `--version`, and an extensible
|
|
8
|
+
command architecture. `maestro generate` (wraps the core's Declarative Config Generator),
|
|
9
|
+
`maestro validate` (wraps the core's Declarative File Loader), `maestro diff` (compares two
|
|
10
|
+
declarative config files), `maestro snapshot` (produces a canonical, deterministic snapshot of a
|
|
11
|
+
declarative config file) and `maestro introspect` (validates and canonicalizes a local
|
|
12
|
+
`IntrospectionResult`, **offline**) are the operational commands shipped so far. Connecting to a real
|
|
13
|
+
datasource is a separate, not-yet-implemented step. The CLI is a pure consumer of
|
|
14
|
+
`@maykonpaulo/maestro-core`; it introduces no capability that doesn't already exist there.
|
|
15
|
+
|
|
16
|
+
The package is now **publishable** (`private` removed, `publishConfig.access: public`); its first
|
|
17
|
+
public release is prepared via a Changeset and ships through the normal release flow
|
|
18
|
+
(`next` → `rc` → `latest`). The CLI is **not** the owner of the repo-wide GitHub Release `latest`
|
|
19
|
+
pointer — that stays with `@maykonpaulo/maestro-core` (the CLI's releases are tagged with
|
|
20
|
+
`--latest=false`). Publishing happens only via merge/promotion in the release workflow, never from a
|
|
21
|
+
local command. See [`RELEASE.md`](../../RELEASE.md).
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
maestro --help
|
|
27
|
+
maestro --version
|
|
28
|
+
maestro generate --input ./metadata.json --type metadata --format json
|
|
29
|
+
maestro validate --input ./maestro.config.yaml
|
|
30
|
+
maestro diff --from ./maestro.config.old.yaml --to ./maestro.config.new.yaml
|
|
31
|
+
maestro snapshot --input ./maestro.config.yaml --out ./maestro.snapshot.json
|
|
32
|
+
maestro introspect --input ./introspection.json --out ./maestro.introspection.json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
$ maestro --help
|
|
37
|
+
maestro — Maestro CLI
|
|
38
|
+
|
|
39
|
+
Command-line interface for the Maestro declarative engine.
|
|
40
|
+
|
|
41
|
+
Usage:
|
|
42
|
+
maestro <command> [options]
|
|
43
|
+
|
|
44
|
+
Options:
|
|
45
|
+
-h, --help Show this help message
|
|
46
|
+
-v, --version Show the CLI version
|
|
47
|
+
|
|
48
|
+
Available commands:
|
|
49
|
+
generate Generate a declarative config from a local metadata/schema JSON file
|
|
50
|
+
validate Validate a declarative config file (YAML or JSON)
|
|
51
|
+
diff Compare two declarative config files (YAML or JSON)
|
|
52
|
+
snapshot Produce a canonical snapshot of a declarative config file (YAML or JSON)
|
|
53
|
+
introspect Validate and canonicalize a local IntrospectionResult JSON file (offline)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Running an unrecognized command exits with a non-zero status and a message pointing back to
|
|
57
|
+
`--help`:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
$ maestro unknown-thing
|
|
61
|
+
Unknown command 'unknown-thing'. Run "maestro --help" to see available commands.
|
|
62
|
+
$ echo $?
|
|
63
|
+
1
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## `maestro generate`
|
|
67
|
+
|
|
68
|
+
Reads a local JSON file containing either `EntityMetadata[]` or `EntitySchema[]` (plus optional
|
|
69
|
+
relations/operations) and generates a declarative config (`EntityDeclaration[]` + optional
|
|
70
|
+
consumers) using the core's `generateDeclarativeConfigFromMetadata`/`generateDeclarativeConfigFromSchema`
|
|
71
|
+
and `serializeDeclarativeConfig`.
|
|
72
|
+
|
|
73
|
+
**What it does NOT do:** no introspection of a live datasource, no database/provider connection, no
|
|
74
|
+
`createMaestro()` call. It only reads a file, transforms it via the core's public APIs, and writes
|
|
75
|
+
text (stdout or a file). Introspecting a real datasource is a separate, not-yet-implemented command
|
|
76
|
+
(`maestro introspect`).
|
|
77
|
+
|
|
78
|
+
Run `maestro generate --help` for the full flag reference and examples inline.
|
|
79
|
+
|
|
80
|
+
### What input is expected
|
|
81
|
+
|
|
82
|
+
- `--type metadata` expects the JSON file to be an object matching
|
|
83
|
+
`{ entities: EntityMetadata[], relations?: RelationMetadata[], operations?: OperationMetadata[] }`
|
|
84
|
+
— typically produced by serializing `engine.getMetadata()` (or the `entities` subset of it).
|
|
85
|
+
- `--type schema` expects `{ entities: EntitySchema[], relations?: RelationSchema[] }` — the
|
|
86
|
+
low-level config shape you'd otherwise write by hand or get from introspection tooling.
|
|
87
|
+
|
|
88
|
+
Minimal examples of both shapes are checked into
|
|
89
|
+
[`packages/cli/examples/metadata.json`](./examples/metadata.json) and
|
|
90
|
+
[`packages/cli/examples/schema.json`](./examples/schema.json).
|
|
91
|
+
|
|
92
|
+
### JSON output
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
$ maestro generate --input packages/cli/examples/metadata.json --type metadata --format json
|
|
96
|
+
{
|
|
97
|
+
"entities": [
|
|
98
|
+
{
|
|
99
|
+
"entity": "user",
|
|
100
|
+
"label": "User",
|
|
101
|
+
"pluralLabel": "Users",
|
|
102
|
+
"fields": {
|
|
103
|
+
"email": { "type": "email", "label": "Email", "required": true, "searchable": true },
|
|
104
|
+
"id": { "type": "uuid", "label": "Id", "readonly": true, "primary": true }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### YAML output
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
$ maestro generate --input packages/cli/examples/schema.json --type schema --format yaml
|
|
115
|
+
entities:
|
|
116
|
+
- entity: user
|
|
117
|
+
label: User
|
|
118
|
+
pluralLabel: Users
|
|
119
|
+
fields:
|
|
120
|
+
email:
|
|
121
|
+
type: email
|
|
122
|
+
label: Email
|
|
123
|
+
required: true
|
|
124
|
+
searchable: true
|
|
125
|
+
id:
|
|
126
|
+
type: uuid
|
|
127
|
+
label: Id
|
|
128
|
+
readonly: true
|
|
129
|
+
primary: true
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Writing to a file
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
maestro generate --input ./metadata.json --type metadata --format yaml --out ./maestro.config.yaml
|
|
136
|
+
# Declarative config written to ./maestro.config.yaml
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
| Flag | Required | Description |
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| `--input <path>` | yes | Path to a local JSON file |
|
|
142
|
+
| `--type <metadata\|schema>` | yes | Whether the input matches `EntityMetadata[]` or `EntitySchema[]` shape |
|
|
143
|
+
| `--format <json\|yaml>` | no (default `json`) | Output format |
|
|
144
|
+
| `--out <path>` | no (default: stdout) | Writes the result to a file instead of printing it |
|
|
145
|
+
|
|
146
|
+
Errors (missing flags, unreadable file, invalid JSON, malformed shape) print a single readable line
|
|
147
|
+
to stderr prefixed with `maestro generate:` and exit with status `1`.
|
|
148
|
+
|
|
149
|
+
### Recommended flow: metadata/schema → generate → maestro.config → createMaestro
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
EntityMetadata[] / EntitySchema[] (already exists)
|
|
153
|
+
↓ maestro generate --input ... --type ... --out maestro.config.yaml
|
|
154
|
+
maestro.config.yaml
|
|
155
|
+
↓ loadDeclarativeConfigFromFile('./maestro.config.yaml', { yamlParser })
|
|
156
|
+
DeclarativeFileConfig (validated)
|
|
157
|
+
↓
|
|
158
|
+
createMaestro({ datasources, declarations })
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The file `maestro generate` writes is a normal declarative config — it round-trips through the
|
|
162
|
+
existing [Declarative File Loader](../../docs/specs/declarative-file-loader.md) and
|
|
163
|
+
`createMaestro({ declarations })` unchanged, exactly like a hand-written `maestro.config.yaml`. See
|
|
164
|
+
[`docs/specs/cli-generate.md`](../../docs/specs/cli-generate.md) for the full walkthrough, including
|
|
165
|
+
how to validate the generated file end-to-end.
|
|
166
|
+
|
|
167
|
+
## `maestro validate`
|
|
168
|
+
|
|
169
|
+
Reads a local declarative config file (`maestro.config.yaml`/`.yml`/`.json`) and validates its
|
|
170
|
+
structure using the core's `loadDeclarativeConfigFromString`. It is the natural counterpart to
|
|
171
|
+
`maestro generate`: `generate` writes the file, `validate` confirms it is well-formed before you use
|
|
172
|
+
it at runtime.
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
$ maestro validate --input packages/cli/examples/maestro.config.yaml
|
|
176
|
+
Valid declarative config: packages/cli/examples/maestro.config.yaml
|
|
177
|
+
Entities: 2
|
|
178
|
+
Consumers: 1
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The format is detected from the file extension (`.json` → JSON, `.yaml`/`.yml` → YAML) unless
|
|
182
|
+
`--format` overrides it. YAML is parsed by the `yaml` package the CLI provides — the core has no
|
|
183
|
+
bundled YAML dependency.
|
|
184
|
+
|
|
185
|
+
**What it does NOT do:** no `createMaestro()` call, no database/provider connection, and it does NOT
|
|
186
|
+
require declared operations to have a real handler binding. It only reads a file and validates its
|
|
187
|
+
declarative structure. Wiring operations to code and starting the runtime is what `createMaestro()`
|
|
188
|
+
does separately — see the [validate spec](../../docs/specs/cli-validate.md#diferença-entre-validar-o-arquivo-e-inicializar-o-engine)
|
|
189
|
+
for the exact difference.
|
|
190
|
+
|
|
191
|
+
| Flag | Required | Description |
|
|
192
|
+
|---|---|---|
|
|
193
|
+
| `--input`, `-i <path>` | yes | Path to a local declarative config file (`.yaml`, `.yml` or `.json`) |
|
|
194
|
+
| `--format <json\|yaml>` | no | Overrides the format detected from the file extension |
|
|
195
|
+
|
|
196
|
+
Errors (missing flag, unreadable file, unknown extension, invalid format, parse failure, invalid
|
|
197
|
+
structure) print a single readable line to stderr prefixed with `maestro validate:` and exit with a
|
|
198
|
+
non-zero status. Run `maestro validate --help` for the full reference, and see
|
|
199
|
+
[`docs/specs/cli-validate.md`](../../docs/specs/cli-validate.md) for the complete walkthrough.
|
|
200
|
+
|
|
201
|
+
## `maestro diff`
|
|
202
|
+
|
|
203
|
+
Compares two local declarative config files and prints a readable, deterministic report of what
|
|
204
|
+
changed. Both files are loaded and validated with the core's `loadDeclarativeConfigFromString` (the
|
|
205
|
+
same validation as `maestro validate`), then their normalised structures are compared. It's the tool
|
|
206
|
+
for reviewing, promoting or versioning a declarative change: `validate` confirms one file is
|
|
207
|
+
well-formed; `diff` shows exactly what moved between two versions.
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
$ maestro diff --from ./before.json --to ./after.json
|
|
211
|
+
Differences found.
|
|
212
|
+
|
|
213
|
+
Entities:
|
|
214
|
+
+ invoice
|
|
215
|
+
- legacyCustomer
|
|
216
|
+
~ user
|
|
217
|
+
fields:
|
|
218
|
+
+ email
|
|
219
|
+
~ name
|
|
220
|
+
label: "Full name" → "Name"
|
|
221
|
+
operations:
|
|
222
|
+
- archive
|
|
223
|
+
|
|
224
|
+
Consumers:
|
|
225
|
+
+ admin:user
|
|
226
|
+
~ backoffice:user
|
|
227
|
+
list: {"fields":["id"]} → {"fields":["id","name"]}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
When the two files are structurally equal it prints `No differences found.`. The format of each file
|
|
231
|
+
is detected from its extension (`.json` → JSON, `.yaml`/`.yml` → YAML) unless `--from-format` /
|
|
232
|
+
`--to-format` override it, so you can diff a `.json` against a `.yaml`.
|
|
233
|
+
|
|
234
|
+
**What it compares:** entities (label/pluralLabel/description/capabilities), fields
|
|
235
|
+
(type/label/required/readonly/description/enumOptions/relationEntity/…), operations, relationships,
|
|
236
|
+
and consumers (list/detail/forms/actions). **What it does NOT do:** no `createMaestro()`, no
|
|
237
|
+
database/provider connection, no operation-binding requirement, and it never compares against a live
|
|
238
|
+
schema — it diffs **declarative files**, not introspection metadata. The core's `DiffEngine` operates
|
|
239
|
+
on `IntrospectionResult` (real DB schema), which is a different, not-yet-wired path.
|
|
240
|
+
|
|
241
|
+
| Flag | Required | Description |
|
|
242
|
+
|---|---|---|
|
|
243
|
+
| `--from`, `-f <path>` | yes | Baseline declarative config file |
|
|
244
|
+
| `--to`, `-t <path>` | yes | Changed declarative config file |
|
|
245
|
+
| `--from-format <json\|yaml>` | no | Overrides the format detected from the `--from` extension |
|
|
246
|
+
| `--to-format <json\|yaml>` | no | Overrides the format detected from the `--to` extension |
|
|
247
|
+
|
|
248
|
+
**Exit codes:** `0` = valid files, no differences; `1` = valid files, with differences; `2` = usage,
|
|
249
|
+
read, parse or validation error. Errors print a single readable line to stderr prefixed with
|
|
250
|
+
`maestro diff:`. Run `maestro diff --help` for the full reference, and see
|
|
251
|
+
[`docs/specs/cli-diff.md`](../../docs/specs/cli-diff.md) for the complete walkthrough.
|
|
252
|
+
|
|
253
|
+
## `maestro snapshot`
|
|
254
|
+
|
|
255
|
+
Loads a local declarative config file, validates it with the core's `loadDeclarativeConfigFromString`
|
|
256
|
+
(the same validation as `maestro validate`), and emits a **canonical, deterministic JSON snapshot** of
|
|
257
|
+
its declarations. The snapshot is a stable, versionable artifact: commit it alongside your config, review
|
|
258
|
+
it in PRs, and use it as the baseline for future comparison. The same (semantically equivalent) input
|
|
259
|
+
always produces byte-identical output.
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
$ maestro snapshot --input packages/cli/examples/maestro.config.yaml
|
|
263
|
+
{
|
|
264
|
+
"declarations": {
|
|
265
|
+
"consumers": [ ... ],
|
|
266
|
+
"entities": [ ... ],
|
|
267
|
+
"relations": [ { "fromEntity": "order", "fromField": "userId", "toEntity": "user", "toField": "id", "type": "many-to-one" } ]
|
|
268
|
+
},
|
|
269
|
+
"kind": "maestro.declarative.snapshot",
|
|
270
|
+
"schemaVersion": 1,
|
|
271
|
+
"source": { "format": "yaml", "path": "packages/cli/examples/maestro.config.yaml" },
|
|
272
|
+
"summary": { "consumers": 1, "entities": 2, "relations": 1 }
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Writing to a file prints a readable summary instead of the JSON:
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
$ maestro snapshot --input packages/cli/examples/maestro.config.yaml --out ./maestro.snapshot.json
|
|
280
|
+
Snapshot written: ./maestro.snapshot.json
|
|
281
|
+
Entities: 2
|
|
282
|
+
Consumers: 1
|
|
283
|
+
Relations: 1
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
The snapshot is **canonical**: entities are sorted by name, consumers by name, relationships (and the
|
|
287
|
+
flattened top-level `relations` index, each tagged with its `fromEntity`) by derived id, and every object
|
|
288
|
+
key is sorted. It is **deterministic by design** — it never includes a timestamp, an absolute path, a
|
|
289
|
+
random hash or any value that varies between runs of the same input, so re-running it only changes the
|
|
290
|
+
output when the config actually changed.
|
|
291
|
+
|
|
292
|
+
**Declarative snapshot vs. introspection snapshot.** This command snapshots a **declarative file**, not a
|
|
293
|
+
live database or runtime introspection. The core's Snapshot Engine (`IntrospectionSnapshot` /
|
|
294
|
+
`SnapshotRepository`) captures an `IntrospectionResult` from a real datasource and intentionally carries
|
|
295
|
+
volatile fields (`id`, `timestamp`) — a different, not-yet-wired path. `maestro snapshot` is local,
|
|
296
|
+
offline and reproducible.
|
|
297
|
+
|
|
298
|
+
**What it does NOT do:** no `createMaestro()`, no database/provider connection, no operation execution,
|
|
299
|
+
and it does NOT require declared operations to have a real handler binding — operations are captured as
|
|
300
|
+
declared. It never modifies the input file.
|
|
301
|
+
|
|
302
|
+
| Flag | Required | Description |
|
|
303
|
+
|---|---|---|
|
|
304
|
+
| `--input`, `-i <path>` | yes | Path to a local declarative config file (`.yaml`, `.yml` or `.json`) |
|
|
305
|
+
| `--input-format <json\|yaml>` | no | Overrides the format detected from the `--input` extension |
|
|
306
|
+
| `--format json` | no (default `json`) | Snapshot output format. Only `json` is supported for now |
|
|
307
|
+
| `--out <path>` | no (default: stdout) | Writes the snapshot to a file instead of printing it |
|
|
308
|
+
|
|
309
|
+
**Exit codes:** `0` = snapshot produced; `1` = usage, read, parse, validation or write error. Errors
|
|
310
|
+
print a single readable line to stderr prefixed with `maestro snapshot:`. Run `maestro snapshot --help`
|
|
311
|
+
for the full reference, and see [`docs/specs/cli-snapshot.md`](../../docs/specs/cli-snapshot.md) for the
|
|
312
|
+
complete walkthrough, including how `validate`, `diff` and `snapshot` relate.
|
|
313
|
+
|
|
314
|
+
## `maestro introspect`
|
|
315
|
+
|
|
316
|
+
Reads a local JSON file describing an `IntrospectionResult` — the core's introspection contract
|
|
317
|
+
(`{ entities: EntityIntrospectionSchema[], relations: RelationIntrospectionSchema[] }`) — validates it
|
|
318
|
+
against that contract, and emits a **canonical, deterministic JSON artifact**. The same (semantically
|
|
319
|
+
equivalent) input always produces byte-identical output, so the artifact is a stable, versionable input
|
|
320
|
+
for future comparison, generation or review.
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
$ maestro introspect --input packages/cli/examples/introspection.json
|
|
324
|
+
{
|
|
325
|
+
"kind": "maestro.introspection",
|
|
326
|
+
"result": {
|
|
327
|
+
"entities": [ { "fields": [ ... ], "table": "orders" }, { "fields": [ ... ], "table": "users" } ],
|
|
328
|
+
"relations": [ { "confidence": "definite", "from": { ... }, "id": "orders.user_id->users.id", ... } ]
|
|
329
|
+
},
|
|
330
|
+
"schemaVersion": 1,
|
|
331
|
+
"source": { "path": "packages/cli/examples/introspection.json" },
|
|
332
|
+
"summary": { "entities": 2, "relations": 1 }
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Writing to a file prints a readable summary instead of the JSON:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
$ maestro introspect --input packages/cli/examples/introspection.json --out ./maestro.introspection.json
|
|
340
|
+
Introspection written: ./maestro.introspection.json
|
|
341
|
+
Entities: 2
|
|
342
|
+
Relations: 1
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**Offline / local only — this is NOT a database connector (yet).** It does **not** connect to
|
|
346
|
+
PostgreSQL, MySQL, MongoDB or any database, does **not** read a connection string or `.env`, does **not**
|
|
347
|
+
load an ORM/driver, and does **not** execute any query. It operates only on the local JSON file passed
|
|
348
|
+
with `--input`, which is an intermediate/offline representation of an introspection — not a live schema
|
|
349
|
+
read. Connecting to real datasources (via `IntrospectionProvider` implementations) is a separate,
|
|
350
|
+
not-yet-implemented step; keeping the contract stable first is the point of this command.
|
|
351
|
+
|
|
352
|
+
The artifact is **canonical**: entities are sorted by `table`, relations by `id`, and every object key is
|
|
353
|
+
sorted. Field (column) order within an entity is preserved. It is **deterministic by design** — no
|
|
354
|
+
timestamp, absolute path or hash is ever included, so re-running it only changes the output when the input
|
|
355
|
+
actually changed.
|
|
356
|
+
|
|
357
|
+
**`introspect` vs. `snapshot` vs. `diff`.** All three are offline, read-only and never touch a database or
|
|
358
|
+
the engine, but they operate on different inputs:
|
|
359
|
+
|
|
360
|
+
- `introspect` validates/canonicalizes an **`IntrospectionResult`** (the real DB-schema contract), offline.
|
|
361
|
+
- `snapshot` canonicalizes a **declarative config file** (`maestro.config.yaml`/`.json`).
|
|
362
|
+
- `diff` compares **two declarative config files**.
|
|
363
|
+
|
|
364
|
+
The core's Introspection Runtime (`DiffEngine`, Snapshot Engine, `ReportGenerator`) also operates on
|
|
365
|
+
`IntrospectionResult`, but those paths assume a live/real introspection and carry volatile data (e.g.
|
|
366
|
+
`IntrospectionSnapshot`'s `id`/`timestamp`, the report's `completedAt`). `maestro introspect` intentionally
|
|
367
|
+
stays offline and reproducible, so it does not use them yet — see the
|
|
368
|
+
[spec](../../docs/specs/cli-introspect.md#apis-do-core-e-limitações) for the exact gap.
|
|
369
|
+
|
|
370
|
+
**What it does NOT do:** no `createMaestro()`, no database/provider connection, no `.env`/connection-string
|
|
371
|
+
read, no query execution, no operation execution, and no operation-binding requirement. It never modifies
|
|
372
|
+
the input file.
|
|
373
|
+
|
|
374
|
+
| Flag | Required | Description |
|
|
375
|
+
|---|---|---|
|
|
376
|
+
| `--input`, `-i <path>` | yes | Path to a local `IntrospectionResult` JSON file |
|
|
377
|
+
| `--out <path>` | no (default: stdout) | Writes the artifact to a file instead of printing it |
|
|
378
|
+
|
|
379
|
+
**Exit codes:** `0` = artifact produced; `1` = usage, read, parse, validation or write error. Errors print a
|
|
380
|
+
single readable line to stderr prefixed with `maestro introspect:`. Run `maestro introspect --help` for the
|
|
381
|
+
full reference, and see [`docs/specs/cli-introspect.md`](../../docs/specs/cli-introspect.md) for the
|
|
382
|
+
complete walkthrough.
|
|
383
|
+
|
|
384
|
+
## Architectural rule: the CLI consumes, it doesn't implement
|
|
385
|
+
|
|
386
|
+
The CLI never re-implements a capability that already exists in `@maykonpaulo/maestro-core`. It
|
|
387
|
+
calls `generateDeclarativeConfigFromMetadata`/`generateDeclarativeConfigFromSchema` and
|
|
388
|
+
`serializeDeclarativeConfig` directly — there is no parallel generation or serialization logic in
|
|
389
|
+
this package. `maestro diff` reuses the core's `loadDeclarativeConfigFromString` for loading and
|
|
390
|
+
validation, adding only the small, local, deterministic structural comparison of two
|
|
391
|
+
`DeclarativeFileConfig` objects (the core's `DiffEngine` targets `IntrospectionResult`, not
|
|
392
|
+
declarative files). `maestro snapshot` likewise reuses `loadDeclarativeConfigFromString` for
|
|
393
|
+
load+validation and adds only a small, local, deterministic canonicalizer over the resulting
|
|
394
|
+
`DeclarativeFileConfig` (the core's Snapshot Engine, `IntrospectionSnapshot`/`SnapshotRepository`,
|
|
395
|
+
targets a runtime `IntrospectionResult` with volatile `id`/`timestamp` fields — incompatible with a
|
|
396
|
+
reproducible declarative snapshot). `maestro introspect` consumes the core's `IntrospectionResult` **type**
|
|
397
|
+
(the introspection contract) and adds only a small, local, deterministic validator + canonicalizer over it;
|
|
398
|
+
the core exposes no public `IntrospectionResult` validator yet, and its runtime introspection helpers
|
|
399
|
+
(`DiffEngine`, Snapshot Engine, `ReportGenerator`) all carry volatile fields, so an offline/reproducible
|
|
400
|
+
artifact can't reuse them here — see [`docs/specs/cli-introspect.md`](../../docs/specs/cli-introspect.md).
|
|
401
|
+
|
|
402
|
+
## Command architecture
|
|
403
|
+
|
|
404
|
+
- `Command` — interface a command implements: `name`, `description`, optional `helpText` (shown for
|
|
405
|
+
`maestro <name> --help`; falls back to the global help when omitted), `run(context)`.
|
|
406
|
+
- `CommandRegistry` — a simple map of registered commands (`register`, `get`, `list`).
|
|
407
|
+
- `parseArgs` — minimal global parser: detects `--help`/`-h` and `--version`/`-v`, and the first
|
|
408
|
+
non-flag argument as the command name; everything else (including a command's own flags) passes
|
|
409
|
+
through untouched.
|
|
410
|
+
- `runCli` — orchestrates parsing → dispatch → exit code; when `--help` is combined with a known
|
|
411
|
+
command, prints that command's own `helpText` instead of the global help. Accepts an injectable
|
|
412
|
+
`stdout`/`stderr` and `CommandRegistry`, so it's fully testable without spawning a process.
|
|
413
|
+
- `createGenerateCommand` — builds the `generate` `Command`; accepts injectable `readFile`/`writeFile`
|
|
414
|
+
for testing without touching the real filesystem.
|
|
415
|
+
- `createValidateCommand` — builds the `validate` `Command`; accepts an injectable `readFile` for
|
|
416
|
+
testing without touching the real filesystem.
|
|
417
|
+
- `createDiffCommand` — builds the `diff` `Command`; accepts an injectable `readFile` for testing
|
|
418
|
+
without touching the real filesystem. The pure comparison lives in `diffDeclarativeConfigs`
|
|
419
|
+
(`diffDeclarativeConfigs` + `formatDeclarativeDiff`), tested in isolation from file I/O.
|
|
420
|
+
- `createSnapshotCommand` — builds the `snapshot` `Command`; accepts injectable `readFile`/`writeFile`
|
|
421
|
+
for testing without touching the real filesystem. The pure canonicalization lives in
|
|
422
|
+
`buildDeclarativeSnapshot` (`buildDeclarativeSnapshot` + `serializeSnapshot`), tested in isolation
|
|
423
|
+
from file I/O.
|
|
424
|
+
- `createIntrospectCommand` — builds the `introspect` `Command`; accepts injectable `readFile`/`writeFile`
|
|
425
|
+
for testing without touching the real filesystem. The pure validation/canonicalization lives in
|
|
426
|
+
`buildIntrospection` (`validateIntrospectionResult` + `buildIntrospectionArtifact` +
|
|
427
|
+
`serializeIntrospection`), tested in isolation from file I/O.
|
|
428
|
+
|
|
429
|
+
Adding a future command means implementing `Command` (with its own `helpText`) and calling
|
|
430
|
+
`defaultRegistry.register(...)` in `runCli.ts` — no change to `runCli`, `parseArgs`, or the
|
|
431
|
+
entrypoint is needed.
|
|
432
|
+
|
|
433
|
+
## Development
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
pnpm --filter @maykonpaulo/maestro-cli build
|
|
437
|
+
pnpm --filter @maykonpaulo/maestro-cli test
|
|
438
|
+
node packages/cli/dist/cli.js --help
|
|
439
|
+
node packages/cli/dist/cli.js generate --help
|
|
440
|
+
node packages/cli/dist/cli.js generate --input packages/cli/examples/metadata.json --type metadata
|
|
441
|
+
node packages/cli/dist/cli.js validate --help
|
|
442
|
+
node packages/cli/dist/cli.js validate --input packages/cli/examples/maestro.config.yaml
|
|
443
|
+
node packages/cli/dist/cli.js diff --help
|
|
444
|
+
node packages/cli/dist/cli.js diff --from packages/cli/examples/maestro.config.yaml --to packages/cli/examples/maestro.config.yaml
|
|
445
|
+
node packages/cli/dist/cli.js snapshot --help
|
|
446
|
+
node packages/cli/dist/cli.js snapshot --input packages/cli/examples/maestro.config.yaml
|
|
447
|
+
node packages/cli/dist/cli.js introspect --help
|
|
448
|
+
node packages/cli/dist/cli.js introspect --input packages/cli/examples/introspection.json
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
See [`docs/cli.md`](../../docs/cli.md) for the consolidated CLI overview (recommended flow, command
|
|
452
|
+
table, limitations and next steps), [`docs/specs/cli-foundation.md`](../../docs/specs/cli-foundation.md)
|
|
453
|
+
for the CLI foundation architecture, [`docs/specs/cli-generate.md`](../../docs/specs/cli-generate.md) for the full
|
|
454
|
+
`maestro generate` walkthrough, [`docs/specs/cli-validate.md`](../../docs/specs/cli-validate.md) for
|
|
455
|
+
`maestro validate`, [`docs/specs/cli-diff.md`](../../docs/specs/cli-diff.md) for `maestro diff`,
|
|
456
|
+
[`docs/specs/cli-snapshot.md`](../../docs/specs/cli-snapshot.md) for `maestro snapshot`, and
|
|
457
|
+
[`docs/specs/cli-introspect.md`](../../docs/specs/cli-introspect.md) for `maestro introspect`.
|