@aventara/cli 1.0.0-rc.1 → 1.0.0-rc.10
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/CHANGELOG.md +231 -1
- package/README.md +55 -6
- package/dist/apply/conflict.confirmer.js +1 -1
- package/dist/catalog/adapter.catalog.generated.js +1 -1
- package/dist/catalog/host-entry.interface.js +10 -0
- package/dist/catalog/host.catalog.generated.js +32 -0
- package/dist/cli.js +15 -2
- package/dist/command/command.parser.js +91 -5
- package/dist/plan/host-plan.interface.js +1 -0
- package/dist/plan/nest-host.planner.js +131 -0
- package/dist/plan/next-host.planner.js +133 -0
- package/dist/plan/project.planner.js +94 -165
- package/dist/project/project.inspector.js +53 -14
- package/dist/run/command.runner.js +37 -19
- package/dist/run/init.orchestrator.js +47 -30
- package/dist/run/new.orchestrator.js +102 -27
- package/dist/run/scaffold.committer.js +1 -1
- package/dist/run/skill.orchestrator.js +54 -0
- package/dist/skill/agent-skill.generated.js +7 -0
- package/dist/skill/agent-skill.planner.js +116 -0
- package/dist/templates/docs.links.js +1 -1
- package/dist/templates/next-server.entry.js +5 -0
- package/dist/templates/prisma7/prisma7-nest.templates.js +242 -0
- package/dist/templates/prisma7/prisma7-next.templates.js +189 -0
- package/dist/templates/prisma7/prisma7-orm.templates.js +180 -0
- package/dist/templates/prisma7/prisma7.templates.js +7 -386
- package/dist/templates/template.registry.js +1 -1
- package/dist/wizard/wizard.questions.js +2 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,236 @@
|
|
|
1
1
|
# @aventara/cli
|
|
2
2
|
|
|
3
|
+
## 1.0.0-rc.10
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 82afc5e: The scaffolded configuration's `diagnostics` sink logs `String(error)` when an error's `stack` is empty or missing,
|
|
8
|
+
instead of an empty `raw error` line.
|
|
9
|
+
|
|
10
|
+
`aventara` prints a defect as `String(error)` when its `stack` is empty or missing, instead of a blank or `undefined`
|
|
11
|
+
line.
|
|
12
|
+
|
|
13
|
+
- 09585dd: The agent skill's configuration reference covers `managedElsewhere`: a model whose table another service owns, and
|
|
14
|
+
the Prisma 7 external-tables setup that keeps `prisma migrate` off it.
|
|
15
|
+
- 04bb39a: `engines.node` is now `^22.23.4 || >=24.22.1`, the Node security releases of 2026-10-20 (CVE-2026-105481,
|
|
16
|
+
CVE-2026-105482, CVE-2026-105483, CVE-2026-106069, CVE-2026-106072, CVE-2026-106073, CVE-2026-106074,
|
|
17
|
+
CVE-2026-111497). The bins refuse an older Node in one sentence naming that range.
|
|
18
|
+
- 4f0fd29: The Prisma 7 adapter's peer floor is now `^7.10.1` (`prisma`, `@prisma/client`, `@prisma/adapter-pg`,
|
|
19
|
+
`@prisma/adapter-better-sqlite3`). `prisma` 7.10.0 pins `mysql2` 3.15.3 (GHSA-3f6p-5ww8-9rcr, GHSA-rgwj-5xj2-c3m3)
|
|
20
|
+
and, through `@prisma/config`, `deepmerge-ts` 7.1.5 (GHSA-ggr8-5vv4-36mx); 7.10.1 pins `mysql2` 3.24.4 and
|
|
21
|
+
`deepmerge-ts` 8.0.2. `aventara new` and `aventara init` install Prisma at the floor, so new projects get 7.10.1, and
|
|
22
|
+
`aventara init` refuses a project whose Prisma is below it.
|
|
23
|
+
- 10341d5: The Next.js scaffold's `src/prisma.ts` keeps one PrismaClient per generated schema: after `aventara:prepare` regenerates
|
|
24
|
+
the client, the next call makes a new one and disconnects the old one, so `next dev` needs no restart.
|
|
25
|
+
- 88263a1: Startup checks, tightened:
|
|
26
|
+
|
|
27
|
+
- An `mcp.authorization.servers` entry with a query or a fragment is refused for https URLs too, not only for loopback
|
|
28
|
+
http ones: an authorization server's issuer has neither (RFC 8414).
|
|
29
|
+
- A `client.resources` or `agent.resources` entry for a Resource managed elsewhere is refused (`COMPILER_UNKNOWN_RESOURCE`):
|
|
30
|
+
those layers never have the Resource, so nothing configured there would run. Configure it at the root or in
|
|
31
|
+
`application`.
|
|
32
|
+
|
|
33
|
+
- 88263a1: Documentation: `$access.can([])` answers `[]`, not a refusal; the MCP README states the query and fragment rule for
|
|
34
|
+
every authorization server.
|
|
35
|
+
|
|
36
|
+
## 1.0.0-rc.9
|
|
37
|
+
|
|
38
|
+
### Patch Changes
|
|
39
|
+
|
|
40
|
+
- a0bb05e: The agent skill that `aventara new`, `aventara init` and `aventara skill` write has a new section, "Calling the API over
|
|
41
|
+
HTTP", with a working curl example: the operation path uses the model's name as written in the Prisma schema
|
|
42
|
+
(`/api/_resources/User/create/one`), every request sends the `Aventara-Protocol-Version: 1` and
|
|
43
|
+
`Aventara-Contract-Hash` headers (the hash comes from `GET /api/_contract`), and a create's body is `{ "data": … }`. It
|
|
44
|
+
lists the answers each mistake gets. The printed next steps point to it. To refresh the skill in an existing
|
|
45
|
+
project, run `aventara skill`.
|
|
46
|
+
- 8c40b81: `aventara new` and `aventara init` now scaffold Next.js apps.
|
|
47
|
+
|
|
48
|
+
- `aventara new <name> --host next` runs `create-next-app@latest` (TypeScript, the App Router under `src/`, the `@/*`
|
|
49
|
+
alias, no install of its own), then the same `init` steps, then the install. create-next-app asks its own questions
|
|
50
|
+
on your terminal; with `--yes` it takes its defaults. Its own flags go after `--`, as they are:
|
|
51
|
+
`aventara new my-app --host next --yes -- --tailwind`. `--skip-git` becomes its `--disable-git`. Without a terminal,
|
|
52
|
+
`--yes` is required. `--host` defaults to `nest`: a NestJS project is scaffolded exactly as before.
|
|
53
|
+
- `aventara init` tells the host by `package.json`: `@nestjs/core` is NestJS 12, `next` is Next.js 16, and a project
|
|
54
|
+
that depends on both or neither is refused in one sentence, with nothing written. A Next.js app needs `src/app/`
|
|
55
|
+
and the `@/*` → `./src/*` path in `tsconfig.json`, and no ORM yet.
|
|
56
|
+
- In a Next.js app, init writes `src/aventara/config.ts`, `src/aventara/adapter.ts`, `src/aventara/server.ts`
|
|
57
|
+
(`Aventara()`, exporting `handlers`, `avClient`, `avApp` and `ready`), `src/app/api/[...aventara]/route.ts`,
|
|
58
|
+
`src/instrumentation.ts` (awaiting `ready()` on the Node.js runtime) and `src/prisma.ts`, with Prisma's schema and
|
|
59
|
+
config. `aventara:prepare` ends with `aventara-build --client` and runs on `postinstall` and `prebuild`; the files it
|
|
60
|
+
generates (`src/aventara/generated/`, `src/aventara/client.ts`, `src/aventara/universal.ts`) are added to
|
|
61
|
+
`.gitignore`. An instrumentation file of your own is never replaced: the lines to add are printed.
|
|
62
|
+
- Each host package now declares its project layout in its manifest (`"aventara": { "host": … }`). The CLI scaffolds
|
|
63
|
+
from it and `aventara-build` reads its paths from it, so the two always agree.
|
|
64
|
+
|
|
65
|
+
- 501b832: `aventara new --host next` and `aventara init` in a Next.js app add `serverExternalPackages: ["@aventara/core",
|
|
66
|
+
"@aventara/prisma7-adapter"]` to `next.config.ts`, so Node.js loads them once per server process: every route and
|
|
67
|
+
page shares one core, one Framework and one Prisma client. The line goes into create-next-app's config, or any config
|
|
68
|
+
whose `const nextConfig = {` object has no `serverExternalPackages`; an app with no config gets a `next.config.ts`. A
|
|
69
|
+
config that already names `serverExternalPackages`, or is written another way, is never changed, even with `--yes`:
|
|
70
|
+
the line to add is printed, with a warning. The agent skill's Next.js section says to keep it.
|
|
71
|
+
- The agent skill's Next.js section says what a page that calls `avClient` or `avApp` needs under Cache Components
|
|
72
|
+
(create-next-app's default): the calling component inside `<Suspense>` (or a `loading.tsx`), `"use cache"` on the
|
|
73
|
+
function that makes the call, or `export const instant = false`.
|
|
74
|
+
|
|
75
|
+
## 1.0.0-rc.8
|
|
76
|
+
|
|
77
|
+
1.0.0-rc.5, rc.6 and rc.7 were tagged but never published; 1.0.0-rc.8 contains everything listed under them and the changes below.
|
|
78
|
+
|
|
79
|
+
## 1.0.0-rc.7
|
|
80
|
+
|
|
81
|
+
1.0.0-rc.5 and 1.0.0-rc.6 were tagged but never published; 1.0.0-rc.7 contains everything listed under them and the changes below.
|
|
82
|
+
|
|
83
|
+
## 1.0.0-rc.6
|
|
84
|
+
|
|
85
|
+
1.0.0-rc.5 was tagged but never published. 1.0.0-rc.6 contains everything listed under 1.0.0-rc.5 and the fix below.
|
|
86
|
+
|
|
87
|
+
## 1.0.0-rc.5
|
|
88
|
+
|
|
89
|
+
## 1.0.0-rc.4
|
|
90
|
+
|
|
91
|
+
### Patch Changes
|
|
92
|
+
|
|
93
|
+
- 7c3b65f: Build-time Contracts for serverless and edge cold starts.
|
|
94
|
+
|
|
95
|
+
- New package `@aventara/build`, bin `aventara-build`, a development dependency. It runs `src/aventara.config.ts` and
|
|
96
|
+
`src/aventara.adapter.ts` without a database client, compiles the Contracts with the project's `@aventara/core` and
|
|
97
|
+
writes `src/generated/aventara/contracts.generated.ts` (`aventaraContracts`), which a serverless or edge entry passes
|
|
98
|
+
to `createFramework` as `prebuilt`. A configuration that startup would refuse fails the build with the same
|
|
99
|
+
`CONFIG_…`/`COMPILER_…` diagnostics. `aventara-build --check` writes nothing and exits 1 when the file is missing or
|
|
100
|
+
stale, naming the Resources that changed.
|
|
101
|
+
- `@aventara/prisma7-adapter`: `describePrismaAdapter({ discovery, provider, driver })` states the adapter's passive
|
|
102
|
+
facts (the model, the driver's transaction capability, the referential actions, the computed fields) with no client.
|
|
103
|
+
`createPrismaAdapter({ ...description, client })` builds the adapter from the same facts, and still checks the client
|
|
104
|
+
against the discovery artifact.
|
|
105
|
+
- `@aventara/cli`: the scaffold's `src/aventara.config.ts` exports the configuration as a plain value
|
|
106
|
+
(`export const aventaraConfig = { … } as const`) with no `adapter`. A new `src/aventara.adapter.ts` default-exports
|
|
107
|
+
`describePrismaAdapter(…)`, and `app.module.ts` joins the two:
|
|
108
|
+
`config: { ...aventaraConfig, adapter: await createPrismaAdapter({ ...adapter, client: prisma }) }`. The Nest server
|
|
109
|
+
still compiles its Contracts at startup and passes no `prebuilt`. `aventara:prepare` ends with `&& aventara-build`,
|
|
110
|
+
and `@aventara/build` is an exact development dependency.
|
|
111
|
+
|
|
112
|
+
- 2b1b17e: `aventara-build`'s files no longer land inside a Prisma client generated into `src/generated`.
|
|
113
|
+
|
|
114
|
+
When `aventara init` reuses a project whose Prisma client is generated into `src/generated`, it already puts the
|
|
115
|
+
discovery artifact in `src/aventara/`, because `prisma generate` deletes every file in its output directory that it did
|
|
116
|
+
not write. `aventara-build` still wrote its three files to `src/generated/aventara/`, so each `prisma generate` deleted
|
|
117
|
+
them and the project stopped compiling until the next `aventara:prepare`. Now all of Aventara's generated files are
|
|
118
|
+
in one place.
|
|
119
|
+
|
|
120
|
+
- `@aventara/build`: `aventara-build` writes `resources.generated.ts`, `computed-fields.generated.ts` and
|
|
121
|
+
`contracts.generated.ts` to the directory the configuration imports `aventaraResources` from, so the tree is always
|
|
122
|
+
written where the configuration reads it. A configuration that imports no tree keeps `src/generated/aventara/`.
|
|
123
|
+
The files of a project whose client is generated anywhere else are the same bytes, in the same place.
|
|
124
|
+
- `@aventara/prisma7-adapter`: with `--config`, the artifact imports the computed fields from its sibling,
|
|
125
|
+
`./computed-fields.generated`, wherever `--out` puts it. `--config` is still required for that import and the step
|
|
126
|
+
still checks that the file exists, but the step no longer uses its location. A scaffold's artifact is unchanged.
|
|
127
|
+
- `@aventara/cli`: in that project, `src/aventara.config.ts` imports the tree from `./aventara/resources.generated`,
|
|
128
|
+
and `.gitignore` gets the one `/src/aventara/` line that covers all four files (no separate
|
|
129
|
+
`/src/generated/aventara/contracts.generated.ts` line). The agent skill says where the generated files are in
|
|
130
|
+
both cases.
|
|
131
|
+
|
|
132
|
+
- 99169d2: Policy engines, reads first. `createFramework` takes an optional root `policy: { engine, principal }`: an engine that
|
|
133
|
+
answers three questions (`can`, `scopeOf`, `fieldsOf`, each now or as a promise), and a function that derives a
|
|
134
|
+
request's principal from its `ctx.$state`. Core never imports an engine; one satisfies `AvPolicyEngine` by its shape,
|
|
135
|
+
and a scope or grant is a fragment of Aventara's own `where` (`AvPolicyWhere`).
|
|
136
|
+
|
|
137
|
+
- Client and agent requests are checked (an MCP tool call is an agent request). An application call is checked only
|
|
138
|
+
when it names a principal: `framework.application.Note.find.many(args, { as: user })`. A call a stage makes through
|
|
139
|
+
`ctx` never is.
|
|
140
|
+
- Before any guard, `can` and the fields the request names or receives (`fieldsOf`); a refusal is `403 A4002`. At the
|
|
141
|
+
adapter call, after every developer stage, the read scope is ANDed into the root `where`, every to-many include and
|
|
142
|
+
its `$count`, and every relation filter, at every level. A to-one include of a row out of scope refuses the request;
|
|
143
|
+
it is never nulled.
|
|
144
|
+
- Governed fields (the engine's `governed` list) are left out of every default projection and carry `governed: true`
|
|
145
|
+
in the Contracts. Name one to get it: `select: ["$defaults", "email"]`, where `$defaults` is the level's default
|
|
146
|
+
projection. It is refused where a returned row does not grant it, and a filter or sort by it is checked before the
|
|
147
|
+
query runs, so it cannot reveal values the caller may not read.
|
|
148
|
+
- The engine's `governed` and `paths` lists are checked against the Core Contract at startup. A rule's paths are lists
|
|
149
|
+
of segments, as other paths in core are: `["Note", "campaign", "members", "some", "userId", "equals"]`.
|
|
150
|
+
- Writes are checked as well; see the policy writes entry.
|
|
151
|
+
|
|
152
|
+
Without `policy`, nothing changes: the Contracts and their hashes are the same bytes.
|
|
153
|
+
|
|
154
|
+
- 154e440: Policy engines check writes. Every create, update, delete and upsert a client or agent makes (or an application call
|
|
155
|
+
made `{ as }` a principal) is now checked by the configuration's `policy`, where before it was refused.
|
|
156
|
+
|
|
157
|
+
- The fields a write sends are held to `fieldsOf({ access: "write" })` before any guard; a field outside it answers
|
|
158
|
+
`403 A4002`. A field writable on some rows only is checked against every row an update reaches, and against the
|
|
159
|
+
row a create inserts, after the insert and inside its transaction.
|
|
160
|
+
- A create asks `can({ action: "create", row })` about each new row on its final data. Its rules can look rows up
|
|
161
|
+
through `exists`, which runs inside the write's transaction, so a plan that creates a campaign and then its first
|
|
162
|
+
note sees the campaign.
|
|
163
|
+
- An update or delete acts only on rows the caller may both change (`scopeOf` for the action) and read. A single-row
|
|
164
|
+
one that matches nothing answers `A4002` when the caller can read the row, and `A2003` when it cannot, so the
|
|
165
|
+
answer does not reveal that the row exists. A `many` or `count` one answers for the caller's rows only.
|
|
166
|
+
- An update that moves a row out of what the caller reads is rolled back with `A4002`. So is one that fails an `after`
|
|
167
|
+
condition: inside the transaction core asks `can({ action: "update", row })` about each changed row of every
|
|
168
|
+
checked update. The engine's new `afterChecks` member (`{ Note: ["update"] }`) names the updates whose grants carry
|
|
169
|
+
such a condition, which are the ones refused where there is no interactive transaction.
|
|
170
|
+
- Where the operation's `transactionsStyle` is `"interactive"`, a checked write runs in a transaction of its own. Where
|
|
171
|
+
it is `"none"` (Cloudflare D1), a write that can only be checked after it runs is refused before it runs; every other
|
|
172
|
+
write runs, a create looking up before it inserts, and an unlisted update without the after check.
|
|
173
|
+
- Nested writes (a relation directive in a write's data) are refused under a policy for now.
|
|
174
|
+
|
|
175
|
+
`afterChecks` is checked against the Core Contract at startup and changes no Contract and no prebuilt fingerprint.
|
|
176
|
+
|
|
177
|
+
- 3813207: The Resource tree and the computed fields come from `aventara-build`.
|
|
178
|
+
|
|
179
|
+
- `@aventara/build`: before it compiles the Contracts, `aventara-build` writes two more files under
|
|
180
|
+
`src/generated/aventara/`. `resources.generated.ts` exports `aventaraResources`: every `defineResource(…)` result
|
|
181
|
+
that the files `src/resources/**/*.resource.ts` export, found by its type (never run), one `resources` tree per
|
|
182
|
+
layer, written as the plain spreads you wrote by hand (`root` always, `application`, `client` and `agent` when some
|
|
183
|
+
Resource has a block for them). Two definitions of one Resource stop the build, naming both. Imports are spelled as
|
|
184
|
+
the project spells its own. `computed-fields.generated.ts` exports `computedFields` and its type `AvComputedFields`:
|
|
185
|
+
which fields the configuration's write compute fills, read off its type after the tree exists, so a fresh install is
|
|
186
|
+
typed right on its first `aventara:prepare`. `--check` covers all three files. `typescript` (`>=5.7.0 <7`) is now a
|
|
187
|
+
peer dependency.
|
|
188
|
+
- `@aventara/prisma7-adapter`: the generate step no longer reads the configuration. With `--config`, the discovery
|
|
189
|
+
artifact imports `computedFields` and `AvComputedFields` from the file `aventara-build` writes beside the
|
|
190
|
+
configuration (`generated/aventara/computed-fields.generated.ts`); a `--config` file that does not exist is still
|
|
191
|
+
refused. `readConfigComputedFields`, `AvPrismaConfigReaderError` and `ConfigComputedFieldsReading` are removed from
|
|
192
|
+
`@aventara/prisma7-adapter/generate`, as are `PrismaGenerateToolchain.readComputedFields` and
|
|
193
|
+
`PrismaGenerateResult.computedFields`.
|
|
194
|
+
- `@aventara/cli`: the scaffold's `src/aventara.config.ts` takes its Resources from the tree,
|
|
195
|
+
`resources: aventaraResources.root`, so a new Resource file needs no configuration edit; the agent skill says so.
|
|
196
|
+
|
|
197
|
+
## 1.0.0-rc.3
|
|
198
|
+
|
|
199
|
+
### Patch Changes
|
|
200
|
+
|
|
201
|
+
- b38034d: A refusal that quotes a failed child's output (`npm install`, `nest new`) ends with that output as the child wrote it.
|
|
202
|
+
It printed a period of its own after the child's last line (`npm ERR! the registry said no.`).
|
|
203
|
+
|
|
204
|
+
## 1.0.0-rc.2
|
|
205
|
+
|
|
206
|
+
### Patch Changes
|
|
207
|
+
|
|
208
|
+
- `aventara init`'s refusal of a TypeScript 7 project names the supported range: "use TypeScript 5.7 to 6" (it said
|
|
209
|
+
"use TypeScript 6 or 5"). The Prisma adapter's `typescript` peer range starts at 5.7.0 now.
|
|
210
|
+
- An agent skill for your AI coding agent. `aventara new` and `aventara init` write `.claude/skills/aventara/`:
|
|
211
|
+
`SKILL.md` (the Agent Skills format Claude Code reads: when to use it, and one rule per task) and
|
|
212
|
+
`reference/config.md`, `reference/stages.md`, `reference/recipes.md` and `reference/errors.md` (`@aventara/core`'s
|
|
213
|
+
`ERRORS.md`), for the Aventara version they install. They also add a short Aventara section to `AGENTS.md`, between
|
|
214
|
+
`<!-- aventara:start -->` and `<!-- aventara:end -->`, for agents that read `AGENTS.md`: an existing `AGENTS.md`
|
|
215
|
+
keeps everything else, and `CLAUDE.md` is never written.
|
|
216
|
+
- `aventara skill` refreshes those files for the `@aventara/core` installed in the project (its version and its
|
|
217
|
+
`ERRORS.md`; it runs none of the project's packages). It rewrites the skill files it wrote, keeps one you changed
|
|
218
|
+
and says so (`--force` replaces it), and replaces only the marked section of `AGENTS.md`.
|
|
219
|
+
- `aventara new` and `aventara init` write an `aventara:prepare` script that also passes
|
|
220
|
+
`--config src/aventara.config.ts`, so the generated types know which fields your configuration computes: a
|
|
221
|
+
variant's guards, pipes and filters may leave exactly those out of a create. A project set up before keeps working;
|
|
222
|
+
add the flag to its script to get the precise types.
|
|
223
|
+
- The agent skill says that a `Date` or bytes value a stage is told is that stage's own copy, so changing it in place
|
|
224
|
+
changes nothing another stage sees, what is written or what the caller receives; and that a `compute.read` stage with
|
|
225
|
+
no `dependsOn` computes from the same inputs for every row, so its value usually belongs in response metadata or
|
|
226
|
+
`ctx.$state`. In rc.1 it said neither.
|
|
227
|
+
- The agent skill says that a `compute.read` stage is told the row frozen: it returns its field's value, and writing
|
|
228
|
+
into `record` answers `A3000`. Its error reference lists a nested write that writes its relation back (`A2004`,
|
|
229
|
+
`V1012`).
|
|
230
|
+
- The agent skill says that a computed value its field's `format` refuses (a relative path for `format: "url"`)
|
|
231
|
+
answers `A3000`, as a value of the wrong type does.
|
|
232
|
+
- Released with the other Aventara packages, and `aventara new` and `aventara init` install them at 1.0.0-rc.2.
|
|
233
|
+
|
|
3
234
|
## 1.0.0-rc.1
|
|
4
235
|
|
|
5
236
|
### Patch Changes
|
|
@@ -65,7 +296,6 @@
|
|
|
65
296
|
nullable: false,
|
|
66
297
|
list: false,
|
|
67
298
|
lifecycle: ["VIRTUAL"],
|
|
68
|
-
format: "url",
|
|
69
299
|
},
|
|
70
300
|
pipelines: {
|
|
71
301
|
compute: {
|
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# `@aventara/cli`
|
|
2
2
|
|
|
3
|
-
The `aventara` command: it scaffolds an Aventara server on NestJS 12.
|
|
3
|
+
The `aventara` command: it scaffolds an Aventara server on NestJS 12 or Next.js 16.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm i -g @aventara/cli@rc # or run it once: npx @aventara/cli@rc <command>
|
|
7
7
|
aventara new my-api # a new NestJS project with Aventara
|
|
8
|
-
aventara
|
|
8
|
+
aventara new my-app --host next # a new Next.js app with Aventara (create-next-app, then init)
|
|
9
|
+
aventara init # Aventara added to the NestJS 12 or Next.js 16 project in this directory
|
|
10
|
+
aventara skill # refresh the agent skill for the installed @aventara/core
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
It is run once, globally or through `npx`; it is not a dependency of the projects it writes. The frontend's side
|
|
@@ -22,16 +24,29 @@ without `.env` (it is in `.gitignore`). No commit is made, and one line says why
|
|
|
22
24
|
`user.name`/`user.email`, or when the directory is already inside a git repository (a monorepo: commit it there).
|
|
23
25
|
`--skip-git` makes no repository and no commit.
|
|
24
26
|
|
|
27
|
+
### `aventara new <name> --host next`
|
|
28
|
+
|
|
29
|
+
Runs `create-next-app@latest <name> --ts --app --src-dir --import-alias "@/*" --use-<npm|pnpm> --skip-install`, then
|
|
30
|
+
`aventara init`'s pipeline over the new app, then the install. create-next-app asks its own questions on your terminal;
|
|
31
|
+
with `--yes` it takes its defaults instead (and is given `--yes` too), which is also what a run with no terminal needs.
|
|
32
|
+
Its own flags go after `--`, as they are: `aventara new my-app --host next --yes -- --tailwind --eslint`. `--skip-git`
|
|
33
|
+
becomes its `--disable-git`. `--host` defaults to `nest`.
|
|
34
|
+
|
|
25
35
|
## `aventara init`
|
|
26
36
|
|
|
27
|
-
Runs inside an existing NestJS 12
|
|
37
|
+
Runs inside an existing NestJS 12 or Next.js 16 project, telling them apart by `package.json`: `@nestjs/core` is
|
|
38
|
+
NestJS, `next` is Next.js, and a project depending on both or neither is refused in one sentence. A Next.js app must use
|
|
39
|
+
the App Router under `src/app/` and map `@/*` to `./src/*` in `tsconfig.json` (as create-next-app writes it), and have
|
|
40
|
+
no ORM yet. The rest of this section is NestJS's; a Next.js app gets [its own files](#what-it-writes-in-a-nextjs-app).
|
|
41
|
+
|
|
42
|
+
In a NestJS project, it refuses another Nest major,
|
|
28
43
|
TypeScript 7, a yarn-only lockfile, or a project already initialized.
|
|
29
44
|
|
|
30
45
|
In a project with no ORM yet, the ORM and the database are asked (or taken from the flags), and everything below is
|
|
31
46
|
written.
|
|
32
47
|
|
|
33
48
|
In a project that already uses Prisma, the installed Prisma must be one an Aventara adapter supports, or the run is
|
|
34
|
-
refused in one sentence with nothing written. Today that is Prisma 7 (`^7.10.
|
|
49
|
+
refused in one sentence with nothing written. Today that is Prisma 7 (`^7.10.1`) on SQLite or PostgreSQL, with a driver
|
|
35
50
|
adapter and the `prisma-client` generator. Your schema, Prisma config and service are reused and never written, and
|
|
36
51
|
your `.env` only gains a `CORS_ORIGINS` line at its end when it has none. Init finds the class extending `PrismaClient`
|
|
37
52
|
and the module exporting it, or asks.
|
|
@@ -45,6 +60,7 @@ possible answer is shown, not asked.
|
|
|
45
60
|
| Flag | Question | Default |
|
|
46
61
|
|---|---|---|
|
|
47
62
|
| `<name>` (`new` only) | The project directory. | required |
|
|
63
|
+
| `--host <nest\|next>` (`new` only) | The framework to build on; `init` reads it from `package.json`. Not asked. | `nest` |
|
|
48
64
|
| `--orm <alias>` | The ORM, as an adapter's alias: `prisma7` (Prisma 7, `@aventara/prisma7-adapter`). `--help` lists the aliases that exist. | the only one there is |
|
|
49
65
|
| `--db <provider>` | The database: the adapter's providers (`sqlite`, `postgresql`). | `sqlite` |
|
|
50
66
|
| `--package-manager <npm\|pnpm>` | The package manager. | the lockfile's, else the one that launched the CLI, else npm |
|
|
@@ -54,6 +70,7 @@ possible answer is shown, not asked.
|
|
|
54
70
|
| `--skip-git` (`new`) | Passed to `nest new`. | |
|
|
55
71
|
| `-y`, `--yes` | Accept every unanswered default, and replace existing content that differs. | |
|
|
56
72
|
| `--verbose` | Show the output of `nest new` and the install as they run. | |
|
|
73
|
+
| `-- <args>` (`new --host next`) | Passed to create-next-app as they are, after its own. | |
|
|
57
74
|
|
|
58
75
|
While it works, each step is shown on stderr, such as `[3/4] Installing dependencies (npm install)`. On a terminal it
|
|
59
76
|
has a spinner and the seconds so far; in a pipe, in CI (`CI`) or with `NO_COLOR` it is one plain line per step
|
|
@@ -77,6 +94,30 @@ when you confirm, or with `--yes`; where nobody can be asked, the run stops and
|
|
|
77
94
|
| `package.json`: `@aventara/*` at this CLI's version, `prisma`, `@prisma/client` and the driver at `7.10.0`; `aventara:prepare`, run by `postinstall` and by `prebuild`, `prestart`, `prestart:dev` and `prestart:debug`, and the `.env` on the start scripts and `test:e2e` (`--env-file`, `--env-file-if-exists`) | you |
|
|
78
95
|
| `pnpm-workspace.yaml` (pnpm only): `allowBuilds` for Prisma's and SQLite's install scripts | you |
|
|
79
96
|
| `src/generated/prisma/**`, `src/generated/aventara/discovery.artifact.ts` | generated by `aventara:prepare` (run on every install, build and start, so a schema change needs only a restart); never edit |
|
|
97
|
+
| `.claude/skills/aventara/` (`SKILL.md`, `reference/*.md`, `.aventara-skill.json`): the agent skill your AI coding agent reads | Aventara's: `aventara skill` refreshes it, and keeps a file you changed unless `--force` |
|
|
98
|
+
| `AGENTS.md`: an Aventara section between `<!-- aventara:start -->` and `<!-- aventara:end -->`, appended to an existing file | you, except the marked section |
|
|
99
|
+
|
|
100
|
+
### What it writes in a Next.js app
|
|
101
|
+
|
|
102
|
+
Everything Aventara is in one folder, `src/aventara/`, at the paths `@aventara/next` declares and `aventara-build`
|
|
103
|
+
reads.
|
|
104
|
+
|
|
105
|
+
| Path | Owner afterwards |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `prisma/schema.prisma`, `prisma.config.ts` | you (as in a NestJS project) |
|
|
108
|
+
| `src/prisma.ts`: `prisma()`, the app's one Prisma client with the driver adapter, made on first use and kept across `next dev` reloads | you |
|
|
109
|
+
| `src/aventara/config.ts`: `aventaraConfig`, as in a NestJS project, logging through `console.error` | you |
|
|
110
|
+
| `src/aventara/adapter.ts`: the adapter's description | you |
|
|
111
|
+
| `src/aventara/server.ts`: `Aventara({ ...aventaraConfig, adapter, prebuilt, state })` exporting `handlers`, `avClient`, `avApp` and `ready`; `@/aventara/server` | you |
|
|
112
|
+
| `src/app/api/[...aventara]/route.ts`: every method of `handlers` | you |
|
|
113
|
+
| `src/instrumentation.ts`: awaits `ready()` on the Node.js runtime, so a bad configuration stops `next start`. An instrumentation file of yours is never replaced, even with `--yes`: the lines to add are printed | you |
|
|
114
|
+
| `next.config.ts`: `serverExternalPackages: ["@aventara/core", "@aventara/prisma7-adapter"]` added to its configuration object, so Node.js loads them once per server process (one core and one Prisma client for every route and page). Added to create-next-app's config, or to any whose `const nextConfig = {` object has no `serverExternalPackages`; written as a new `next.config.ts` when the app has none. A config that already names `serverExternalPackages`, or is written another way, is never changed, even with `--yes`: the line to add is printed | you |
|
|
115
|
+
| `package.json`: `@aventara/core`, `@aventara/next`, the adapter and `@aventara/build` at this CLI's version, `server-only`, Prisma at `7.10.0`; `aventara:prepare` (ending in `aventara-build --client`), run by `postinstall` and `prebuild` | you |
|
|
116
|
+
| `.env` (`DATABASE_URL`), `.gitignore` (`/src/generated/`, `/dev.db*`, `/src/aventara/generated/`, `/src/aventara/client.ts`, `/src/aventara/universal.ts`, `.env`) | you |
|
|
117
|
+
| `src/aventara/client.ts`, `src/aventara/universal.ts`, `src/aventara/generated/` | generated by `aventara:prepare`; never edit |
|
|
118
|
+
|
|
119
|
+
No CORS (the browser calls the same origin), no Pages Router, no proxy, and no MCP route: an `mcp` endpoint you add on
|
|
120
|
+
Next.js needs `authorization`. Next steps: `npx prisma db push`, then `npm run dev`.
|
|
80
121
|
|
|
81
122
|
### Next steps it prints
|
|
82
123
|
|
|
@@ -120,15 +161,23 @@ lower-cased or pluralized.
|
|
|
120
161
|
Results are read-only. A list result is a `readonly` array, so type it `readonly User[]`:
|
|
121
162
|
`const users: readonly User[] = await avClient.User.find.many({});`.
|
|
122
163
|
|
|
164
|
+
## `aventara skill`
|
|
165
|
+
|
|
166
|
+
Writes the agent skill for the `@aventara/core` installed in the project in this directory: its version, and its
|
|
167
|
+
`ERRORS.md` as `reference/errors.md`. It reads those files and runs none of the project's packages. A skill file it
|
|
168
|
+
wrote before is replaced; one you changed is kept, with a warning, unless you pass `--force`. In `AGENTS.md` only the
|
|
169
|
+
text between the Aventara markers is replaced (or the section appended), and `CLAUDE.md` is never written. Run it with
|
|
170
|
+
the CLI at your `@aventara/core` version: `npx @aventara/cli@<version> skill`.
|
|
171
|
+
|
|
123
172
|
## Supported
|
|
124
173
|
|
|
125
174
|
- NestJS 12.
|
|
126
|
-
- Node `^22.
|
|
175
|
+
- Node `^22.23.4 || >=24.22.1`. On any other Node the bin refuses in one sentence naming that range. On Node 22 use
|
|
127
176
|
npm ≥ 11, because Node 22's bundled npm 10 cannot install Nest 12's own scaffold. A CommonJS project's jest e2e needs
|
|
128
177
|
Node ≥ 24.9, which is Nest's and Jest's limit.
|
|
129
178
|
- npm and pnpm. Yarn is not supported.
|
|
130
179
|
- ESM and CommonJS projects.
|
|
131
|
-
- Prisma 7 (`^7.10.
|
|
180
|
+
- Prisma 7 (`^7.10.1`) on SQLite and PostgreSQL.
|
|
132
181
|
|
|
133
182
|
The ORMs, majors, databases and drivers on offer are generated from the Aventara adapter packages that exist
|
|
134
183
|
(`src/catalog/adapter.catalog.generated.ts`), never listed by hand, so a future `@aventara/prisma8-adapter` appears
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export class AvConflictsNotConfirmedError extends Error {
|
|
2
2
|
name = "AvConflictsNotConfirmedError";
|
|
3
3
|
}
|
|
4
|
-
|
|
4
|
+
function conflictWarning(conflicts) {
|
|
5
5
|
return `${conflicts.join(", ")} already ${conflicts.length === 1 ? "has" : "have"} other content, and initializing will replace ${conflicts.length === 1 ? "it" : "them"}`;
|
|
6
6
|
}
|
|
7
7
|
export async function confirmConflicts(conflicts, consent) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { HOST_CATALOG } from "./host.catalog.generated.js";
|
|
2
|
+
import { lowestMajorOf } from "./range.reader.js";
|
|
3
|
+
export const HOSTS = HOST_CATALOG;
|
|
4
|
+
export const DEFAULT_HOST = "nest";
|
|
5
|
+
export function hostOf(alias) {
|
|
6
|
+
return HOST_CATALOG.find((entry) => entry.alias === alias);
|
|
7
|
+
}
|
|
8
|
+
export function frameworkMajorOf(host) {
|
|
9
|
+
return lowestMajorOf(host.framework.range);
|
|
10
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const HOST_CATALOG = [
|
|
2
|
+
{
|
|
3
|
+
"alias": "nest",
|
|
4
|
+
"label": "NestJS",
|
|
5
|
+
"package": "@aventara/nest",
|
|
6
|
+
"framework": {
|
|
7
|
+
"package": "@nestjs/core",
|
|
8
|
+
"range": "^12.0.0"
|
|
9
|
+
},
|
|
10
|
+
"layout": {
|
|
11
|
+
"config": "src/aventara.config",
|
|
12
|
+
"adapter": "src/aventara.adapter",
|
|
13
|
+
"generated": "src/generated/aventara"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"alias": "next",
|
|
18
|
+
"label": "Next.js",
|
|
19
|
+
"package": "@aventara/next",
|
|
20
|
+
"framework": {
|
|
21
|
+
"package": "next",
|
|
22
|
+
"range": ">=16.4 <17"
|
|
23
|
+
},
|
|
24
|
+
"layout": {
|
|
25
|
+
"config": "src/aventara/config",
|
|
26
|
+
"adapter": "src/aventara/adapter",
|
|
27
|
+
"generated": "src/aventara/generated",
|
|
28
|
+
"client": "src/aventara/client.ts",
|
|
29
|
+
"universal": "src/aventara/universal.ts"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
];
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { AvProjectRefusedError } from "./project/project.inspector.js";
|
|
|
6
6
|
import { runCommand } from "./run/command.runner.js";
|
|
7
7
|
import { AvInstallFailedError, runInit } from "./run/init.orchestrator.js";
|
|
8
8
|
import { runNew } from "./run/new.orchestrator.js";
|
|
9
|
+
import { AvSkillRefusedError, runSkill } from "./run/skill.orchestrator.js";
|
|
9
10
|
import { progressModeOf } from "./run/step.reporter.js";
|
|
10
11
|
import { AvUnansweredQuestionsError, } from "./wizard/answer.resolver.js";
|
|
11
12
|
import { AvWizardCancelledError, createReadlinePrompter, } from "./wizard/readline.prompter.js";
|
|
@@ -18,6 +19,7 @@ const REFUSALS = [
|
|
|
18
19
|
AvProjectRefusedError,
|
|
19
20
|
AvConflictsNotConfirmedError,
|
|
20
21
|
AvInstallFailedError,
|
|
22
|
+
AvSkillRefusedError,
|
|
21
23
|
];
|
|
22
24
|
const MANIFEST = new URL("../package.json", import.meta.url);
|
|
23
25
|
function ownVersion() {
|
|
@@ -38,6 +40,14 @@ export async function runCli(argv, io) {
|
|
|
38
40
|
io.stdout(`${ownVersion()}\n`);
|
|
39
41
|
return 0;
|
|
40
42
|
}
|
|
43
|
+
if (command.command === "skill") {
|
|
44
|
+
return runSkill(command, {
|
|
45
|
+
directory: io.cwd,
|
|
46
|
+
stdout: io.stdout,
|
|
47
|
+
stderr: io.stderr,
|
|
48
|
+
cliVersion: ownVersion(),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
41
51
|
if (command.command === "init") {
|
|
42
52
|
return await runInit(command, {
|
|
43
53
|
directory: io.cwd,
|
|
@@ -65,10 +75,13 @@ export async function runCli(argv, io) {
|
|
|
65
75
|
}
|
|
66
76
|
catch (error) {
|
|
67
77
|
if (REFUSALS.some((refusal) => error instanceof refusal)) {
|
|
68
|
-
|
|
78
|
+
const { message } = error;
|
|
79
|
+
io.stderr(message.includes("\n")
|
|
80
|
+
? `aventara: ${message.trimEnd()}\n`
|
|
81
|
+
: `aventara: ${message}.\n`);
|
|
69
82
|
return 1;
|
|
70
83
|
}
|
|
71
|
-
io.stderr(`${
|
|
84
|
+
io.stderr(`${(error instanceof Error && error.stack) || String(error)}\n`);
|
|
72
85
|
return 1;
|
|
73
86
|
}
|
|
74
87
|
}
|