@metaobjectsdev/sdk 0.24.4 → 0.24.5
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/agent-context/skills/metaobjects-codegen/SKILL.md +50 -6
- package/agent-context/skills/metaobjects-codegen/references/typescript.md +105 -0
- package/agent-context/skills/metaobjects-prompts/references/typescript.md +10 -1
- package/agent-context/skills/metaobjects-runtime-ui/references/tanstack.md +6 -1
- package/dist/agent-context/scaffold.d.ts.map +1 -1
- package/dist/agent-context/scaffold.js +60 -3
- package/dist/agent-context/scaffold.js.map +1 -1
- package/package.json +2 -2
- package/src/agent-context/scaffold.ts +58 -3
|
@@ -150,12 +150,13 @@ don't silently churn the existing code.
|
|
|
150
150
|
|
|
151
151
|
## Write your own generators — the built-ins rarely fit an app exactly
|
|
152
152
|
|
|
153
|
-
The built-in generators (entity, queries, routes,
|
|
154
|
-
common shape, but **real apps routinely need output the
|
|
155
|
-
— a bespoke REST contract, custom DTO/response shapes,
|
|
156
|
-
repository layer, a UI the defaults don't produce. When
|
|
157
|
-
move is **not** to abandon metadata and hand-write the
|
|
158
|
-
generator** that reads the same metadata and emits *your*
|
|
153
|
+
The built-in generators (entity, queries, routes, routes-hono, barrel, form, hooks,
|
|
154
|
+
grid, grid-hook) cover the common shape, but **real apps routinely need output the
|
|
155
|
+
built-ins don't emit as-is** — a bespoke REST contract, custom DTO/response shapes,
|
|
156
|
+
an app-specific service or repository layer, a UI the defaults don't produce. When
|
|
157
|
+
that happens the model-first move is **not** to abandon metadata and hand-write the
|
|
158
|
+
layer. Write a **custom generator** that reads the same metadata and emits *your*
|
|
159
|
+
app's shape.
|
|
159
160
|
|
|
160
161
|
Treat this as a first-class, expected activity — not an escape hatch. A custom
|
|
161
162
|
generator is still model-first: it derives from the metadata spine, so it
|
|
@@ -163,6 +164,12 @@ regenerates on change and stays consistent across every entity — the leverage
|
|
|
163
164
|
forfeit by hand-writing. Hand-rolling *away from* metadata is the anti-pattern;
|
|
164
165
|
generating *your own shape from* metadata is the point.
|
|
165
166
|
|
|
167
|
+
This is for when the *shape* itself needs to change. If a built-in's shape is
|
|
168
|
+
already right and only the *target* is wrong — a different framework than the
|
|
169
|
+
shipped reference emits for — take ownership of that generator instead of writing
|
|
170
|
+
one from scratch; see "Your framework isn't the default" below, and your language
|
|
171
|
+
reference for the command that does it.
|
|
172
|
+
|
|
166
173
|
The plugin interface is small (`@metaobjectsdev/codegen-ts`): a `Generator` is
|
|
167
174
|
`{ name, filter?, generate }`, where `generate(ctx)` returns `EmittedFile[]`
|
|
168
175
|
(`{ path, content }`). `perEntity` / `oncePerRun` wrap the common cases:
|
|
@@ -190,6 +197,31 @@ the `generators` array in `metaobjects.config.ts` next to the built-ins — it r
|
|
|
190
197
|
the same pass, writes under the same target rules, and carries the `@generated`
|
|
191
198
|
header so it round-trips like any other.
|
|
192
199
|
|
|
200
|
+
## Your framework isn't the default — the retargeting procedure
|
|
201
|
+
|
|
202
|
+
If the shipped templates do not emit for your stack, retargeting is the **normal first
|
|
203
|
+
move** — not a workaround and not a sign of a bug. Owning a generator is the supported
|
|
204
|
+
path to any framework; MetaObjects does not ship a codegen package per framework and is
|
|
205
|
+
not waiting to.
|
|
206
|
+
|
|
207
|
+
The doctrine, in order of what to try:
|
|
208
|
+
|
|
209
|
+
1. **Check config first.** Several apparent codegen failures are one config value
|
|
210
|
+
(module-specifier style, output directory, dialect, API prefix). Change it and retest
|
|
211
|
+
before writing any code.
|
|
212
|
+
2. **Own the generator, not the renderer.** Take a copy of the reference template for the
|
|
213
|
+
artifact that is wrong and edit the one step your framework disagrees about. Each
|
|
214
|
+
template's header names what its emit is coupled to and which call to swap.
|
|
215
|
+
3. **Compose, do not fork.** Call the exported render function and wrap its result where
|
|
216
|
+
you can, so you keep receiving upstream fixes. Forking a whole renderer is the thing
|
|
217
|
+
to avoid — not owning the generator.
|
|
218
|
+
4. **Server-tier output is usually already portable.** The entity module and the query
|
|
219
|
+
helpers carry no HTTP-framework coupling; retargeting is usually only needed at the
|
|
220
|
+
routes and UI tiers.
|
|
221
|
+
|
|
222
|
+
Hand-rolling *away from* metadata is the anti-pattern. Generating *your own shape from*
|
|
223
|
+
metadata is the point.
|
|
224
|
+
|
|
193
225
|
### Never read metadata through an `own*()` accessor (ADR-0039) — top bug source
|
|
194
226
|
|
|
195
227
|
When writing OR reviewing a generator, **read every field/node property and iterate
|
|
@@ -239,6 +271,18 @@ output/template · doesn't fit → write a generator that emits your shape *from
|
|
|
239
271
|
metadata* · only the genuinely un-modelable (business algorithms, external calls) is
|
|
240
272
|
hand-written outside codegen — and it still imports the generated types.
|
|
241
273
|
|
|
274
|
+
**The commands and config keys that implement the steps above differ per port, and the
|
|
275
|
+
ports differ in how much is written down.** TypeScript has the whole procedure as a
|
|
276
|
+
documented one — `meta eject`, the `metaobjects.config.ts` keys, the exported `render*`
|
|
277
|
+
functions — in this skill's `references/typescript.md`. **The other ports have no eject
|
|
278
|
+
command.** There, owning a generator means implementing that port's generator interface
|
|
279
|
+
— `com.metaobjects.generator.Generator` (Java / Kotlin),
|
|
280
|
+
`metaobjects.codegen.generator.Generator` (Python), `MetaObjects.Codegen.IGenerator`
|
|
281
|
+
(C#) — and registering it with the build tool that runs codegen for your port. Their
|
|
282
|
+
`references/` fragments document what each built-in emits, which is what you compare
|
|
283
|
+
your own emit against; they do not carry a step-by-step retargeting procedure, so do
|
|
284
|
+
not go looking for one.
|
|
285
|
+
|
|
242
286
|
## Dialects
|
|
243
287
|
|
|
244
288
|
Generated DB schema/DDL targets a SQL **dialect**:
|
|
@@ -11,6 +11,7 @@ packages. Codegen runs through the Node `meta` CLI (`@metaobjectsdev/cli`, binar
|
|
|
11
11
|
- Run
|
|
12
12
|
- Multiple output targets
|
|
13
13
|
- Field subtype → column mapping
|
|
14
|
+
- Retargeting to another framework — the TypeScript procedure
|
|
14
15
|
|
|
15
16
|
## Install
|
|
16
17
|
|
|
@@ -43,6 +44,7 @@ import { tanstackQuery, tanstackGrid } from "@metaobjectsdev/codegen-ts-tanstack
|
|
|
43
44
|
export default defineConfig({
|
|
44
45
|
outDir: "src/generated",
|
|
45
46
|
dialect: "postgres", // "postgres" | "sqlite" | "d1" (D1 is TS-only)
|
|
47
|
+
extStyle: "js", // "js" (default) for Node ESM / plain tsc; "none" for a bundler-resolution toolchain — see SKILL.md "Your framework isn't the default"
|
|
46
48
|
apiPrefix: "/api", // flows to routes AND client fetch URLs
|
|
47
49
|
columnNamingStrategy: "snake_case", // "snake_case" (default) | "literal" | "kebab-case"
|
|
48
50
|
timestampMode: "string", // "string" (default, ISO-8601 wire contract) | "date" (Drizzle native Date)
|
|
@@ -231,3 +233,106 @@ The VO type, its Zod `InsertSchema`, and this `.$type<>()` all import the VO fro
|
|
|
231
233
|
the same module (layout/package/`extStyle`-aware resolution). An opaque jsonb column
|
|
232
234
|
(`field.string @dbColumnType: jsonb`) gets no `.$type<>()` — it stays `unknown`,
|
|
233
235
|
which is the correct shape for freeform payloads with no fixed VO.
|
|
236
|
+
|
|
237
|
+
## Retargeting to another framework — the TypeScript procedure
|
|
238
|
+
|
|
239
|
+
This is the TypeScript implementation of the retargeting doctrine in SKILL.md
|
|
240
|
+
("Your framework isn't the default"). Read that first for the order of moves;
|
|
241
|
+
everything below — `meta eject`, `metaobjects.config.ts` keys, the exported
|
|
242
|
+
`render*` functions — is Node-CLI-specific and exists only on this port.
|
|
243
|
+
|
|
244
|
+
The shipped reference templates emit for **Fastify on Node** (plus a Hono variant) with
|
|
245
|
+
Drizzle and Zod. If that is not your stack, retargeting is the **normal first move** — not
|
|
246
|
+
a workaround and not a sign of a bug. Each template's header carries a `targets:` line
|
|
247
|
+
naming exactly what its emit is coupled to and which call to swap.
|
|
248
|
+
|
|
249
|
+
Work the list in order; the first two cost nothing.
|
|
250
|
+
|
|
251
|
+
**1. Check the target-shaped config first.** Several apparent codegen failures are one
|
|
252
|
+
config value in `metaobjects.config.ts`:
|
|
253
|
+
|
|
254
|
+
- **`extStyle`** — `"js"` emits `./Entity.js` specifiers, correct for Node ESM and a plain
|
|
255
|
+
`tsc` with `nodenext`. Bundlers disagree on whether they perform the TypeScript
|
|
256
|
+
`.js`→`.ts` rewrite: it fails outright under **Turbopack** — including between two
|
|
257
|
+
generated files, which makes the whole generated tree unresolvable — while Vite and
|
|
258
|
+
esbuild are documented to accept it and webpack needs `resolve.extensionAlias` to do the
|
|
259
|
+
same. **If a generated import fails to resolve, set `extStyle: "none"` and retest** for
|
|
260
|
+
your toolchain rather than assuming either setting from this list.
|
|
261
|
+
- **`clientDirective`** — `true` prepends `"use client";` to the generated form, hooks,
|
|
262
|
+
columns and grid-hook modules. Defaults to `false`. **Set it if your framework compiles
|
|
263
|
+
server and client from one tree** (React Server Components — Next.js App Router and
|
|
264
|
+
friends); leave it off otherwise, where the directive is inert and some bundlers warn
|
|
265
|
+
about it.
|
|
266
|
+
- **`outDir`** / **`targets`** — where output lands, per generator.
|
|
267
|
+
- **`apiPrefix`**, **`dialect`** — route mounting and column mapping.
|
|
268
|
+
|
|
269
|
+
**2. Ask whether your framework splits the module graph.** Some frameworks compile server
|
|
270
|
+
and client from one source tree and resolve each half under *different export conditions*
|
|
271
|
+
(React Server Components, Angular universal, Qwik). Where they do:
|
|
272
|
+
|
|
273
|
+
- a generated artifact using client-only APIs may need a **marker directive** or a distinct
|
|
274
|
+
import path, and
|
|
275
|
+
- the resulting error frequently **names a package that is installed and present** — because
|
|
276
|
+
resolution failed under the server condition, not because the dependency is missing.
|
|
277
|
+
|
|
278
|
+
Read that error as a *boundary* problem, not a dependency problem. The fix belongs in the
|
|
279
|
+
generator that emits the artifact, which you own.
|
|
280
|
+
|
|
281
|
+
**3. If the emit is wrong for your framework, own the generator.**
|
|
282
|
+
|
|
283
|
+
meta eject --list # every template you can take ownership of
|
|
284
|
+
meta eject form # copies it to codegen/generators/form.ts
|
|
285
|
+
|
|
286
|
+
Then compose the engine and replace only the step that differs. Every generator's renderer
|
|
287
|
+
is exported, so wrapping is available — but **how much that buys you differs by tier, and
|
|
288
|
+
it is worth knowing which one you are in before you start**:
|
|
289
|
+
|
|
290
|
+
- **Entity module (`entity`)** — genuinely composable. `renderDrizzleSchema`,
|
|
291
|
+
`renderZodValidators`, `renderInferredTypes`, `renderFilterAllowlist` and friends are
|
|
292
|
+
separate exported sections the template assembles into a `Code[]`. Swap or drop one and
|
|
293
|
+
keep the rest.
|
|
294
|
+
- **Routes and UI (`routes`, `routes-hono`, `form`, `hooks`, `grid`, `grid-hook`)** — one
|
|
295
|
+
whole-file renderer each, so "replace a step" really means wrap the whole output. That
|
|
296
|
+
is enough for a marker directive, a header, or a post-process, and it is what the RSC
|
|
297
|
+
case below needs. It is **not** enough to retarget the emitted framework: if you need
|
|
298
|
+
Svelte or Angular instead of React, you are writing a renderer, and the honest move is
|
|
299
|
+
to keep the generator's metadata walk and replace the render call entirely.
|
|
300
|
+
|
|
301
|
+
**`"use client"` needs no ejecting at all — it is a config knob.** The generated form,
|
|
302
|
+
hooks, columns and grid-hook modules are client components; React Server Components
|
|
303
|
+
frameworks (Next.js App Router and friends) require the directive saying so. Set it once:
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
export default defineConfig({
|
|
307
|
+
clientDirective: true, // prepend `"use client";` to generated client artifacts
|
|
308
|
+
// ...
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Defaults to `false`, because the directive is only *required* under RSC and is inert
|
|
313
|
+
(and warned about by some bundlers) everywhere else. It is applied ahead of the
|
|
314
|
+
`@generated` header, exactly once, and only to the four client artifacts — the entity
|
|
315
|
+
module, the query helpers and `<Entity>.meta.ts` are untouched, since `.meta.ts` is plain
|
|
316
|
+
data and in RSC the boundary is the importing component, not everything it reaches.
|
|
317
|
+
|
|
318
|
+
For the general wrap-the-output case — a directive or header MetaObjects does not model:
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
// codegen/generators/form.ts — OWNED
|
|
322
|
+
import { renderFormFile } from "@metaobjectsdev/codegen-ts-react";
|
|
323
|
+
|
|
324
|
+
// ...inside generate():
|
|
325
|
+
if (!ctx.renderContext) throw new Error("renderContext is required (provided by runGen)");
|
|
326
|
+
const body = renderFormFile(entity, ctx.renderContext);
|
|
327
|
+
return { path, content: `// @my-framework:client\n` + body };
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
You keep receiving upstream fixes to `renderFormFile` while owning the one line your
|
|
331
|
+
framework cares about. **Forking the whole renderer is the thing to avoid**, not owning the
|
|
332
|
+
generator.
|
|
333
|
+
|
|
334
|
+
**4. Server-tier output is usually already portable.** The entity module (a table plus
|
|
335
|
+
validation schemas) and the query helpers (which take `db` as a parameter rather than
|
|
336
|
+
importing a singleton) carry no HTTP-framework coupling — a server-rendered component can
|
|
337
|
+
call a generated query directly. Retargeting is usually only needed at the routes and UI
|
|
338
|
+
tiers.
|
|
@@ -22,7 +22,16 @@ provider/LLM-call layer — you compose the call yourself.
|
|
|
22
22
|
```ts
|
|
23
23
|
// metaobjects.config.ts
|
|
24
24
|
import { defineConfig } from "@metaobjectsdev/cli";
|
|
25
|
-
|
|
25
|
+
// The entity trio + barrel come from the OWNED copies `meta init` scaffolded into
|
|
26
|
+
// ./codegen/generators/ (ADR-0034). Importing them from the package instead is the
|
|
27
|
+
// deprecated path, and quietly hands their shape back to the package — so keep these
|
|
28
|
+
// lines as `meta init` wrote them and add only the prompt pair below.
|
|
29
|
+
import { entityFile } from "./codegen/generators/entity.js";
|
|
30
|
+
import { queriesFile } from "./codegen/generators/queries.js";
|
|
31
|
+
import { barrel } from "./codegen/generators/barrel.js";
|
|
32
|
+
// promptRender / outputParser are NOT in the ownable set — the render and parse engines
|
|
33
|
+
// are upstream-owned, so importing them from the package is the supported pattern.
|
|
34
|
+
import { promptRender, outputParser } from "@metaobjectsdev/codegen-ts/generators";
|
|
26
35
|
|
|
27
36
|
export default defineConfig({
|
|
28
37
|
outDir: "src/generated",
|
|
@@ -20,9 +20,14 @@ this package.
|
|
|
20
20
|
```bash
|
|
21
21
|
npm install @metaobjectsdev/tanstack @metaobjectsdev/runtime-web
|
|
22
22
|
npm install --save-dev @metaobjectsdev/codegen-ts-tanstack
|
|
23
|
+
npm i @tanstack/react-table@^8.21.3
|
|
23
24
|
```
|
|
24
25
|
|
|
25
|
-
Peer-deps: `@tanstack/react-query`, `@tanstack/react-table`.
|
|
26
|
+
Peer-deps: `@tanstack/react-query`, `@tanstack/react-table`. **Pin the react-table
|
|
27
|
+
major explicitly** — the registry's `latest` is v9, which removed `useReactTable`
|
|
28
|
+
and `getCoreRowModel` (both used by `<EntityGrid>`), so a bare
|
|
29
|
+
`npm i @tanstack/react-table` installs a version this package's `^8.20.0` peer range
|
|
30
|
+
rejects and poisons every later install in the project with `ERESOLVE`.
|
|
26
31
|
|
|
27
32
|
## Key exports
|
|
28
33
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/agent-context/scaffold.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEvD,mFAAmF;AACnF,eAAO,MAAM,2BAA2B,qCAAqC,CAAC;AAE9E,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,CAAC,CAAC;IACX;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,MAAM,WAAW,gBAAgB;IAC/B,sFAAsF;IACtF,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,uFAAuF;IACvF,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjE,6CAA6C;IAC7C,QAAQ,EAAE,QAAQ,CAAC;IACnB,wHAAwH;IACxH,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC5B,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAClD,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,gBAAgB,CA8BnB;
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/agent-context/scaffold.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEvD,mFAAmF;AACnF,eAAO,MAAM,2BAA2B,qCAAqC,CAAC;AAE9E,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,CAAC,CAAC;IACX;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,MAAM,WAAW,gBAAgB;IAC/B,sFAAsF;IACtF,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,uFAAuF;IACvF,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjE,6CAA6C;IAC7C,QAAQ,EAAE,QAAQ,CAAC;IACnB,wHAAwH;IACxH,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,KAAK,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC5B,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAClD,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,gBAAgB,CA8BnB;AAwDD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;CACxB,GAAG,MAAM,GAAG,IAAI,CAahB"}
|
|
@@ -39,6 +39,61 @@ export function planScaffold(opts) {
|
|
|
39
39
|
removed,
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* The ordered release coordinate of a version — `[minor, patch]` — or `null` when the
|
|
44
|
+
* version cannot be ordered as a plain release.
|
|
45
|
+
*
|
|
46
|
+
* The MAJOR is deliberately dropped. It is a per-registry constant, not information:
|
|
47
|
+
* npm/PyPI/NuGet ship `0.<m>.<p>` and Maven Central the same `<m>.<p>` on its historical
|
|
48
|
+
* major `7`, so the minor.patch IS the shared release coordinate across all four (this is
|
|
49
|
+
* the same reduction the JVM's `releaseCoordinate` has always made for equality).
|
|
50
|
+
*
|
|
51
|
+
* Returns `null` — meaning "not orderable, so nudge" — for anything that is not exactly
|
|
52
|
+
* three dot-separated integers. That deliberately covers prereleases (`0.24.5-rc.1`),
|
|
53
|
+
* build metadata (`0.24.5+abc`), and the `0.0.0` sentinel a port emits when it cannot
|
|
54
|
+
* resolve its own installed version. Each must keep nudging: an RC-scaffolded context
|
|
55
|
+
* against a final release is still worth refreshing, and an unknown install must never
|
|
56
|
+
* be allowed to assert "in sync".
|
|
57
|
+
*/
|
|
58
|
+
function releaseSeries(version) {
|
|
59
|
+
if (version === undefined)
|
|
60
|
+
return null;
|
|
61
|
+
const m = /^(\d+)\.(\d+)\.(\d+)$/.exec(version.trim());
|
|
62
|
+
if (m === null)
|
|
63
|
+
return null;
|
|
64
|
+
if (version.trim() === UNRESOLVED_VERSION)
|
|
65
|
+
return null; // never assert in-sync on unknown
|
|
66
|
+
return [Number(m[2]), Number(m[3])];
|
|
67
|
+
}
|
|
68
|
+
/** The sentinel a port stamps when it cannot resolve its own installed version. */
|
|
69
|
+
const UNRESOLVED_VERSION = "0.0.0";
|
|
70
|
+
/**
|
|
71
|
+
* True when the manifest was stamped by a release STRICTLY NEWER than the installed one.
|
|
72
|
+
*
|
|
73
|
+
* This is the one exemption from "any drift nudges", and it exists because of the
|
|
74
|
+
* publish-what-changed rule (docs/RELEASING.md): a registry publishes only when it has a
|
|
75
|
+
* changed product file, so a port legitimately sits behind npm — while `meta agent-docs`,
|
|
76
|
+
* the canonical scaffolder for EVERY port, stamps the npm version it was run from. A
|
|
77
|
+
* Python install at `0.24.4` whose context was scaffolded by npm `0.24.7` is correct, and
|
|
78
|
+
* nudging it is [#347](https://github.com/metaobjectsdev/metaobjects/issues/347) exactly:
|
|
79
|
+
* the remedy re-runs the scaffolder, which re-stamps `0.24.7`, so the advisory can never
|
|
80
|
+
* be satisfied and fires on every build forever. An advisory that cries wolf in the inner
|
|
81
|
+
* loop gets tuned out, and then it is not there for the upgrade it exists for.
|
|
82
|
+
*
|
|
83
|
+
* KNOWN BOUND, stated rather than hidden: ordering on minor.patch assumes both versions
|
|
84
|
+
* sit in the same release SERIES. That holds for every release to date and for every
|
|
85
|
+
* release after the 1.0/8.0 cut, but not ACROSS it — at that one cut a `0.24.x`-stamped
|
|
86
|
+
* context against a `1.0.0` install compares (24,x) > (0,0) and is read as "ahead", so the
|
|
87
|
+
* nudge is suppressed once when it should fire. The cost is a missed advisory, never a
|
|
88
|
+
* wrong action, and re-scaffolding at 1.0 is part of the cut anyway.
|
|
89
|
+
*/
|
|
90
|
+
function contextIsAheadOfInstall(generatedBy, currentVersion) {
|
|
91
|
+
const stamped = releaseSeries(generatedBy);
|
|
92
|
+
const installed = releaseSeries(currentVersion);
|
|
93
|
+
if (stamped === null || installed === null)
|
|
94
|
+
return false; // not orderable → nudge
|
|
95
|
+
return stamped[0] > installed[0] || (stamped[0] === installed[0] && stamped[1] > installed[1]);
|
|
96
|
+
}
|
|
42
97
|
/**
|
|
43
98
|
* A one-line nudge if the scaffolded agent context predates the installed MetaObjects
|
|
44
99
|
* (so `gen`/`verify` can remind the user to refresh the skills after an upgrade), or
|
|
@@ -49,11 +104,13 @@ export function agentContextStaleness(opts) {
|
|
|
49
104
|
const { manifest, currentVersion } = opts;
|
|
50
105
|
if (manifest === undefined)
|
|
51
106
|
return null; // no agent context here → nothing to nudge
|
|
52
|
-
// Exact-equality
|
|
53
|
-
//
|
|
54
|
-
//
|
|
107
|
+
// Exact-equality FIRST: ANY drift nudges (a re-scaffold is cheap + idempotent). A
|
|
108
|
+
// prerelease/build-metadata difference is still a reason to refresh, so this is not a
|
|
109
|
+
// semver compare — see releaseSeries() for the ONE case that is exempt.
|
|
55
110
|
if (manifest.generatedBy === currentVersion)
|
|
56
111
|
return null; // in sync
|
|
112
|
+
if (contextIsAheadOfInstall(manifest.generatedBy, currentVersion))
|
|
113
|
+
return null;
|
|
57
114
|
const from = manifest.generatedBy ?? "an older MetaObjects";
|
|
58
115
|
return (`MetaObjects agent context was generated by ${from}; you're on ${currentVersion}. ` +
|
|
59
116
|
`Re-run 'meta init --docs-only --refresh-docs' to refresh the .claude/skills docs.`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/agent-context/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,mFAAmF;AACnF,MAAM,CAAC,MAAM,2BAA2B,GAAG,kCAAkC,CAAC;AAiB9E,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAaD;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,IAO5B;IACC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IACnE,MAAM,MAAM,GAA+B,EAAE,CAAC;IAC9C,MAAM,SAAS,GAAkC,EAAE,CAAC;IACpD,MAAM,KAAK,GAA2B,EAAE,CAAC;IAEzC,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;YACpD,SAAS;QACX,CAAC;QACD,MAAM,SAAS,GAAG,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,SAAS,KAAK,SAAS,IAAI,YAAY,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;YACnE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,iCAAiC;QACxF,CAAC;aAAM,CAAC;YACN,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE5F,OAAO;QACL,MAAM;QACN,SAAS;QACT,QAAQ,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE;QAC5F,OAAO;KACR,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAGrC;IACC,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAC1C,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,CAAC,2CAA2C;IACpF,
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/agent-context/scaffold.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaobjectsdev/sdk",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.5",
|
|
4
4
|
"description": "Workspace helpers and agent-docs utilities for MetaObjects projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"access": "public"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@metaobjectsdev/metadata": "0.24.
|
|
59
|
+
"@metaobjectsdev/metadata": "0.24.5",
|
|
60
60
|
"zod": "^3.23.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
@@ -79,6 +79,60 @@ export function planScaffold(opts: {
|
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* The ordered release coordinate of a version — `[minor, patch]` — or `null` when the
|
|
84
|
+
* version cannot be ordered as a plain release.
|
|
85
|
+
*
|
|
86
|
+
* The MAJOR is deliberately dropped. It is a per-registry constant, not information:
|
|
87
|
+
* npm/PyPI/NuGet ship `0.<m>.<p>` and Maven Central the same `<m>.<p>` on its historical
|
|
88
|
+
* major `7`, so the minor.patch IS the shared release coordinate across all four (this is
|
|
89
|
+
* the same reduction the JVM's `releaseCoordinate` has always made for equality).
|
|
90
|
+
*
|
|
91
|
+
* Returns `null` — meaning "not orderable, so nudge" — for anything that is not exactly
|
|
92
|
+
* three dot-separated integers. That deliberately covers prereleases (`0.24.5-rc.1`),
|
|
93
|
+
* build metadata (`0.24.5+abc`), and the `0.0.0` sentinel a port emits when it cannot
|
|
94
|
+
* resolve its own installed version. Each must keep nudging: an RC-scaffolded context
|
|
95
|
+
* against a final release is still worth refreshing, and an unknown install must never
|
|
96
|
+
* be allowed to assert "in sync".
|
|
97
|
+
*/
|
|
98
|
+
function releaseSeries(version: string | undefined): [number, number] | null {
|
|
99
|
+
if (version === undefined) return null;
|
|
100
|
+
const m = /^(\d+)\.(\d+)\.(\d+)$/.exec(version.trim());
|
|
101
|
+
if (m === null) return null;
|
|
102
|
+
if (version.trim() === UNRESOLVED_VERSION) return null; // never assert in-sync on unknown
|
|
103
|
+
return [Number(m[2]), Number(m[3])];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** The sentinel a port stamps when it cannot resolve its own installed version. */
|
|
107
|
+
const UNRESOLVED_VERSION = "0.0.0";
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* True when the manifest was stamped by a release STRICTLY NEWER than the installed one.
|
|
111
|
+
*
|
|
112
|
+
* This is the one exemption from "any drift nudges", and it exists because of the
|
|
113
|
+
* publish-what-changed rule (docs/RELEASING.md): a registry publishes only when it has a
|
|
114
|
+
* changed product file, so a port legitimately sits behind npm — while `meta agent-docs`,
|
|
115
|
+
* the canonical scaffolder for EVERY port, stamps the npm version it was run from. A
|
|
116
|
+
* Python install at `0.24.4` whose context was scaffolded by npm `0.24.7` is correct, and
|
|
117
|
+
* nudging it is [#347](https://github.com/metaobjectsdev/metaobjects/issues/347) exactly:
|
|
118
|
+
* the remedy re-runs the scaffolder, which re-stamps `0.24.7`, so the advisory can never
|
|
119
|
+
* be satisfied and fires on every build forever. An advisory that cries wolf in the inner
|
|
120
|
+
* loop gets tuned out, and then it is not there for the upgrade it exists for.
|
|
121
|
+
*
|
|
122
|
+
* KNOWN BOUND, stated rather than hidden: ordering on minor.patch assumes both versions
|
|
123
|
+
* sit in the same release SERIES. That holds for every release to date and for every
|
|
124
|
+
* release after the 1.0/8.0 cut, but not ACROSS it — at that one cut a `0.24.x`-stamped
|
|
125
|
+
* context against a `1.0.0` install compares (24,x) > (0,0) and is read as "ahead", so the
|
|
126
|
+
* nudge is suppressed once when it should fire. The cost is a missed advisory, never a
|
|
127
|
+
* wrong action, and re-scaffolding at 1.0 is part of the cut anyway.
|
|
128
|
+
*/
|
|
129
|
+
function contextIsAheadOfInstall(generatedBy: string | undefined, currentVersion: string): boolean {
|
|
130
|
+
const stamped = releaseSeries(generatedBy);
|
|
131
|
+
const installed = releaseSeries(currentVersion);
|
|
132
|
+
if (stamped === null || installed === null) return false; // not orderable → nudge
|
|
133
|
+
return stamped[0] > installed[0] || (stamped[0] === installed[0] && stamped[1] > installed[1]);
|
|
134
|
+
}
|
|
135
|
+
|
|
82
136
|
/**
|
|
83
137
|
* A one-line nudge if the scaffolded agent context predates the installed MetaObjects
|
|
84
138
|
* (so `gen`/`verify` can remind the user to refresh the skills after an upgrade), or
|
|
@@ -91,10 +145,11 @@ export function agentContextStaleness(opts: {
|
|
|
91
145
|
}): string | null {
|
|
92
146
|
const { manifest, currentVersion } = opts;
|
|
93
147
|
if (manifest === undefined) return null; // no agent context here → nothing to nudge
|
|
94
|
-
// Exact-equality
|
|
95
|
-
//
|
|
96
|
-
//
|
|
148
|
+
// Exact-equality FIRST: ANY drift nudges (a re-scaffold is cheap + idempotent). A
|
|
149
|
+
// prerelease/build-metadata difference is still a reason to refresh, so this is not a
|
|
150
|
+
// semver compare — see releaseSeries() for the ONE case that is exempt.
|
|
97
151
|
if (manifest.generatedBy === currentVersion) return null; // in sync
|
|
152
|
+
if (contextIsAheadOfInstall(manifest.generatedBy, currentVersion)) return null;
|
|
98
153
|
const from = manifest.generatedBy ?? "an older MetaObjects";
|
|
99
154
|
return (
|
|
100
155
|
`MetaObjects agent context was generated by ${from}; you're on ${currentVersion}. ` +
|