@aid-framework/cli 0.1.39 → 0.1.48
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 +217 -38
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,46 +1,225 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@aid-framework/cli`
|
|
2
2
|
|
|
3
|
-
The command
|
|
4
|
-
|
|
3
|
+
The `aid` command: scaffold, validate, compile, index and evaluate an AI application from a single declarative
|
|
4
|
+
spec file.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
You write `app/app.spec.yaml`. `aid` checks it, compiles it into a runnable service, and gates its behaviour.
|
|
7
|
+
Nothing in the generated tree is hand-written, and nothing you write is ever overwritten.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
`eval`, `trace`, `cost`, `guard`, `migrate`, `eject`, `doctor`.
|
|
10
|
-
- Orchestrate `spec → IR → generator plugins`, and surface validation and capability errors with actionable
|
|
11
|
-
file/line context.
|
|
12
|
-
- Provide `--dry-run` (via the generator SDK's pure `plan()` phase) and `--force`, plus `--json` on every
|
|
13
|
-
command and CI-friendly exit codes.
|
|
9
|
+
## Install
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
```sh
|
|
12
|
+
npm install --save-dev @aid-framework/cli
|
|
13
|
+
```
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
> specified in [`docs/design.md` §11](../../docs/design.md); the framework name and architecture are in
|
|
20
|
-
> [`§5`](../../docs/design.md).
|
|
15
|
+
Requires Node.js 24 or newer. Or run it without installing:
|
|
21
16
|
|
|
22
|
-
|
|
17
|
+
```sh
|
|
18
|
+
npx @aid-framework/cli --help
|
|
19
|
+
```
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npx aid init my-app
|
|
25
|
+
cd my-app
|
|
26
|
+
npx aid validate
|
|
27
|
+
npx aid gen
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Three commands, and the generated app exists:
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
my-app/
|
|
34
|
+
README.md
|
|
35
|
+
app/
|
|
36
|
+
app.spec.yaml
|
|
37
|
+
business/ ← yours: prompts, tool handlers, domain logic
|
|
38
|
+
generated/ ← the compiler's: never hand-edit
|
|
39
|
+
aid.manifest.json ← provenance and per-file hashes
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Serving it
|
|
43
|
+
|
|
44
|
+
The generator owns the routes; the application decides what to mount and how to run it. That entry point is
|
|
45
|
+
yours, and it is a few lines — `app/main.py`:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from fastapi import FastAPI
|
|
49
|
+
|
|
50
|
+
from app.generated.routes.answer_ticket import answer_ticket_router
|
|
51
|
+
|
|
52
|
+
app = FastAPI(title="my-app", version="0.1.0")
|
|
53
|
+
app.include_router(answer_ticket_router)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Install the runtime and the server, then run it:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
pip install aid-runtime-python "fastapi>=0.115,<1" "uvicorn>=0.30,<1"
|
|
60
|
+
uvicorn app.main:app --reload
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`aid gen` never writes `main.py` or a `pyproject.toml`: mounting and packaging are application decisions, and a
|
|
64
|
+
generator that guessed them would overwrite your choice on the next run.
|
|
65
|
+
|
|
66
|
+
The starter spec declares no pipelines, so a fresh `init` generates the structure but no routes. Add a pipeline
|
|
67
|
+
to `app/app.spec.yaml` and regenerate, and one router module appears per HTTP-served pipeline.
|
|
68
|
+
|
|
69
|
+
## Commands
|
|
70
|
+
|
|
71
|
+
| Command | What it does |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `aid init [dir]` | Scaffold a project: a starter spec and a README |
|
|
74
|
+
| `aid validate [spec]` | Parse the spec, build the IR, run every build-time gate |
|
|
75
|
+
| `aid gen [spec]` | Compile the spec and reconcile the tree with it |
|
|
76
|
+
| `aid ingest [spec]` | Index a retriever corpus into the store the spec declares |
|
|
77
|
+
| `aid eval [spec]` | Run the eval gate, or record the baseline it measures against |
|
|
78
|
+
|
|
79
|
+
Every command accepts `--help` for its own options and `--json` for machine-readable output.
|
|
80
|
+
|
|
81
|
+
## Exit codes
|
|
82
|
+
|
|
83
|
+
The exit code is the contract, because it is what CI reads:
|
|
84
|
+
|
|
85
|
+
| Code | Meaning |
|
|
25
86
|
| --- | --- |
|
|
26
|
-
| `
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
no
|
|
45
|
-
|
|
46
|
-
|
|
87
|
+
| `0` | The project is as asked |
|
|
88
|
+
| `1` | The spec, a gate, the target, or the working tree said no |
|
|
89
|
+
| `2` | The invocation was wrong, or the CLI failed internally |
|
|
90
|
+
|
|
91
|
+
`1` and `2` are deliberately distinct. A red build caused by a spec problem is fixed in the spec; a red build
|
|
92
|
+
caused by `2` is fixed in the invocation or is a bug. Collapsing them sends people to the wrong file.
|
|
93
|
+
|
|
94
|
+
## `--json`
|
|
95
|
+
|
|
96
|
+
Every command answers `--json` with **exactly one** JSON document on stdout. Human-readable text and progress go
|
|
97
|
+
to stderr, so `aid validate --json | jq` works without filtering.
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
npx aid validate --json | jq -r '.diagnostics[] | select(.severity=="error") | .code'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## The spec path
|
|
104
|
+
|
|
105
|
+
Given no argument, a command reads `app/app.spec.yaml` — the path `aid init` creates and the path the generators
|
|
106
|
+
compile into. Pass a path to override it:
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
npx aid validate path/to/other.spec.yaml
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Targets
|
|
113
|
+
|
|
114
|
+
A spec declares the targets it is for, and `aid gen` emits one target at a time:
|
|
115
|
+
|
|
116
|
+
```yaml
|
|
117
|
+
project:
|
|
118
|
+
name: my-app
|
|
119
|
+
targets:
|
|
120
|
+
- py-fastapi
|
|
121
|
+
- ts-vercel
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
npx aid gen # the first declared target
|
|
126
|
+
npx aid gen --target ts-vercel # any other declared target
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`--target` selects among the targets the spec declares; it cannot invent one. Naming a target the spec does not
|
|
130
|
+
declare fails with `target-unsupported` and lists what is declared. That is stricter on purpose — a spec is the
|
|
131
|
+
statement of intent, and silently generating a stack it did not ask for would make the spec a lie.
|
|
132
|
+
|
|
133
|
+
| Target | Emits | Runtime |
|
|
134
|
+
| --- | --- | --- |
|
|
135
|
+
| `py-fastapi` | FastAPI service, Pydantic models | [`aid-runtime-python`](https://pypi.org/project/aid-runtime-python/) |
|
|
136
|
+
| `ts-vercel` | Next.js / Vercel functions, zod models | [`@aid-framework/runtime`](https://www.npmjs.com/package/@aid-framework/runtime) |
|
|
137
|
+
|
|
138
|
+
## `aid gen`: planning, then reconciling
|
|
139
|
+
|
|
140
|
+
`gen` never writes blindly. It plans the full file set from the IR, compares that plan against the tree, and only
|
|
141
|
+
then reconciles.
|
|
142
|
+
|
|
143
|
+
| Tier | Owner | On regeneration |
|
|
144
|
+
| --- | --- | --- |
|
|
145
|
+
| `app/generated/` | the compiler | overwritten — never hand-edit |
|
|
146
|
+
| `app/business/` | you | created once if missing, then never touched |
|
|
147
|
+
|
|
148
|
+
Preview without writing anything:
|
|
149
|
+
|
|
150
|
+
```sh
|
|
151
|
+
npx aid gen --dry-run
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```text
|
|
155
|
+
py-fastapi: dry run, 16 created, 0 updated, 0 unchanged, 0 kept
|
|
156
|
+
created:
|
|
157
|
+
app/business/prompts/__init__.py
|
|
158
|
+
app/generated/deps.py
|
|
159
|
+
app/generated/requests.py
|
|
160
|
+
app/generated/schemas.py
|
|
161
|
+
...
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The counts are `created` / `updated` / `unchanged` / `kept`, so a no-op regeneration says so instead of looking
|
|
165
|
+
like progress.
|
|
166
|
+
|
|
167
|
+
### When a generated file has been edited by hand
|
|
168
|
+
|
|
169
|
+
`gen` records a SHA-256 for every file it writes in `aid.manifest.json`. If a `generated/` file on disk no
|
|
170
|
+
longer matches its recorded hash, a human edited it, and `gen` **refuses to overwrite** it. It writes the new
|
|
171
|
+
content beside it as `<path>.aid-rej` and reports the conflict with both hashes.
|
|
172
|
+
|
|
173
|
+
The choice is yours to make explicitly:
|
|
174
|
+
|
|
175
|
+
```sh
|
|
176
|
+
npx aid gen --force # discard the hand edit and overwrite
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
or move the file into `business/` and point the business seam at it. Either way the edit is not lost silently,
|
|
180
|
+
which is the failure mode that matters: a generator that overwrites unconditionally destroys the only signal it
|
|
181
|
+
has that a human disagreed with it.
|
|
182
|
+
|
|
183
|
+
## `aid eval`: gating behaviour, not bytes
|
|
184
|
+
|
|
185
|
+
A generated service varies between runs — the model is not deterministic, and pretending otherwise produces tests
|
|
186
|
+
that are either always red or measuring nothing. `aid eval` therefore samples and compares distributions:
|
|
187
|
+
|
|
188
|
+
```sh
|
|
189
|
+
npx aid eval --record # record a baseline from the current behaviour
|
|
190
|
+
npx aid eval # gate the current behaviour against it
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## What the CLI is not
|
|
194
|
+
|
|
195
|
+
It is not a runtime. It reads the spec, compiles it, and reports; the generated app imports the runtime package
|
|
196
|
+
for its target and runs without the CLI installed. `@aid-framework/cli` is a `devDependency` for exactly that
|
|
197
|
+
reason.
|
|
198
|
+
|
|
199
|
+
## API
|
|
200
|
+
|
|
201
|
+
The CLI is also importable, so a build tool can drive it without a subprocess:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
import { main, runCli } from '@aid-framework/cli';
|
|
205
|
+
|
|
206
|
+
process.exitCode = await runCli(process.argv.slice(2));
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
| Export | Purpose |
|
|
210
|
+
| --- | --- |
|
|
211
|
+
| `runCli(argv)` | Run a command and return its exit code |
|
|
212
|
+
| `main` | The command dispatcher |
|
|
213
|
+
| `EXIT_OK`, `EXIT_REJECTED`, `EXIT_USAGE` | The exit-code constants (`0`, `1`, `2`) |
|
|
214
|
+
|
|
215
|
+
## Related packages
|
|
216
|
+
|
|
217
|
+
- [`@aid-framework/spec`](https://www.npmjs.com/package/@aid-framework/spec) — the spec language
|
|
218
|
+
- [`@aid-framework/ir`](https://www.npmjs.com/package/@aid-framework/ir) — the IR and its build-time gates
|
|
219
|
+
- [`@aid-framework/generator-py-fastapi`](https://www.npmjs.com/package/@aid-framework/generator-py-fastapi)
|
|
220
|
+
- [`@aid-framework/generator-ts-vercel`](https://www.npmjs.com/package/@aid-framework/generator-ts-vercel)
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
[Apache-2.0](https://github.com/aid-framework/aid-framework/blob/main/LICENSE). Code generated by AID Framework is
|
|
225
|
+
not subject to this license.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aid-framework/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.48",
|
|
4
4
|
"description": "The `aid` command: scaffold a project, validate its spec, generate its code, and run its eval gate.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@aid-framework/generator-py-fastapi": "0.1.
|
|
37
|
-
"@aid-framework/generator-sdk": "0.1.
|
|
38
|
-
"@aid-framework/generator-ts-vercel": "0.1.
|
|
39
|
-
"@aid-framework/ir": "0.1.
|
|
40
|
-
"@aid-framework/spec": "0.1.
|
|
36
|
+
"@aid-framework/generator-py-fastapi": "0.1.48",
|
|
37
|
+
"@aid-framework/generator-sdk": "0.1.48",
|
|
38
|
+
"@aid-framework/generator-ts-vercel": "0.1.48",
|
|
39
|
+
"@aid-framework/ir": "0.1.48",
|
|
40
|
+
"@aid-framework/spec": "0.1.48"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^26.6.5"
|