@happyvertical/smrt-cli 0.55.10 → 0.55.11
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/AGENTS.md +11 -19
- package/README.md +4 -0
- package/agents/cookbook-apply.md +65 -0
- package/agents/kitchen.md +111 -0
- package/agents/process-fixture-isolation.md +29 -0
- package/cookbooks/bakery.cookbook.json +187 -0
- package/cookbooks/mechanic.cookbook.json +191 -0
- package/cookbooks/welder.cookbook.json +177 -0
- package/cookbooks/yoga-studio.cookbook.json +173 -0
- package/dist/{AuditLogCollection-WDHL-_AF-9sLdjeZF.js → AuditLogCollection-WDHL-_AF-ClQNu_VR.js} +1 -1
- package/dist/{OidcIdentity-BUblgQLs-QX0f44sA.js → OidcIdentity-BUblgQLs-br8RyLkA.js} +2 -2
- package/dist/{OidcIdentityCollection-ClOyaHeF-BfIHh4aA.js → OidcIdentityCollection-ClOyaHeF-BpQXB3mz.js} +1 -1
- package/dist/{ProfileAssetCollection-CRMcjLJ--Dobq-AgM.js → ProfileAssetCollection-CRMcjLJ--C5ytBKfF.js} +3 -3
- package/dist/{ProfileCollection-C4INKh7Y-DtWNBhg3.js → ProfileCollection-C4INKh7Y-CfkyJilm.js} +17 -17
- package/dist/{ProfileLinkCollection-DFfZRsgv-DNiBQtmL.js → ProfileLinkCollection-DFfZRsgv-MQ2zhD9z.js} +2 -2
- package/dist/{ProfileMetadataCollection-BIYKy4Y3-CeQxYDL4.js → ProfileMetadataCollection-BIYKy4Y3-DXu5L2yx.js} +1 -1
- package/dist/{ProfileMetafieldCollection-Buz7gzag-DP2ZYYv7.js → ProfileMetafieldCollection-Buz7gzag-C2tZc1PF.js} +1 -1
- package/dist/{ProfileRelationshipCollection-C4g_QLI4-VbnpFG2C.js → ProfileRelationshipCollection-C4g_QLI4-vTnkAIAh.js} +1 -1
- package/dist/{ProfileTypeCollection-B7kwi1mU-DWalSOyP.js → ProfileTypeCollection-B7kwi1mU-KwPGcUkI.js} +2 -2
- package/dist/apply-BNDIdixP.js +947 -0
- package/dist/cli-generator.d.ts.map +1 -1
- package/dist/commands/cookbook/apply.d.ts +102 -0
- package/dist/commands/cookbook/apply.d.ts.map +1 -0
- package/dist/commands/cookbook/recipe-index.d.ts +108 -0
- package/dist/commands/cookbook/recipe-index.d.ts.map +1 -0
- package/dist/commands/cookbook/registry.d.ts +69 -0
- package/dist/commands/cookbook/registry.d.ts.map +1 -0
- package/dist/commands/cookbook/shared.d.ts +23 -0
- package/dist/commands/cookbook/shared.d.ts.map +1 -0
- package/dist/commands/cookbook/validate.d.ts +25 -0
- package/dist/commands/cookbook/validate.d.ts.map +1 -0
- package/dist/commands/cookbook.d.ts +6 -0
- package/dist/commands/cookbook.d.ts.map +1 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/kitchen/ai.d.ts +34 -0
- package/dist/commands/kitchen/ai.d.ts.map +1 -0
- package/dist/commands/kitchen/apply.d.ts +52 -0
- package/dist/commands/kitchen/apply.d.ts.map +1 -0
- package/dist/commands/kitchen/mcp/catalog.d.ts +39 -0
- package/dist/commands/kitchen/mcp/catalog.d.ts.map +1 -0
- package/dist/commands/kitchen/mcp/server.d.ts +15 -0
- package/dist/commands/kitchen/mcp/server.d.ts.map +1 -0
- package/dist/commands/kitchen/mcp/tools.d.ts +68 -0
- package/dist/commands/kitchen/mcp/tools.d.ts.map +1 -0
- package/dist/commands/kitchen/planner.d.ts +71 -0
- package/dist/commands/kitchen/planner.d.ts.map +1 -0
- package/dist/commands/kitchen/run.d.ts +40 -0
- package/dist/commands/kitchen/run.d.ts.map +1 -0
- package/dist/commands/kitchen/server.d.ts +39 -0
- package/dist/commands/kitchen/server.d.ts.map +1 -0
- package/dist/commands/kitchen.d.ts +7 -0
- package/dist/commands/kitchen.d.ts.map +1 -0
- package/dist/{commands-n_7ZXru_.js → commands-pbRVQvix.js} +759 -9
- package/dist/{dist-BsMQ4Itc.js → dist-BsRSQ_hE.js} +16 -16
- package/dist/{dist-DscvT_q_.js → dist-CYVIKmvy.js} +4 -4
- package/dist/dist-D_WqjwkC.js +21 -0
- package/dist/{dist-gZP7Tw10.js → dist-DxkSjKQQ.js} +2 -2
- package/dist/{dist-Bw1oCty_.js → dist-ffh0LeC4.js} +3 -3
- package/dist/index.js +44 -16
- package/dist/{oidcProvisioningPrimitives-DNP9EZlU-w2kQ8oqV.js → oidcProvisioningPrimitives-DNP9EZlU-CBYwksp0.js} +3 -3
- package/dist/{resolveIdentity-CkyqvctR-Cm591gNa.js → resolveIdentity-CkyqvctR-B5btFsQd.js} +14 -14
- package/dist/server-Dp096tZj.js +898 -0
- package/package.json +13 -10
- package/dist/dist-DsZbjZ9J.js +0 -21
package/AGENTS.md
CHANGED
|
@@ -40,6 +40,10 @@ smrt generate-types # Generate TypeScript declarations (alias: generate
|
|
|
40
40
|
smrt generate-routes # Generate SvelteKit API routes (aliases: routes, generate:routes)
|
|
41
41
|
smrt config:export # Export agent config for SSG
|
|
42
42
|
smrt init # Init new project
|
|
43
|
+
smrt cookbook validate|apply # Cookbook check / project create: agents/cookbook-apply.md
|
|
44
|
+
smrt kitchen [dir] # Planner on localhost -> cookbook -> apply: agents/kitchen.md
|
|
45
|
+
smrt kitchen apply <url|file> [dir] # cookbook apply, plan URLs too
|
|
46
|
+
smrt kitchen mcp # stdio MCP server for the local Kitchen plugin: agents/kitchen.md
|
|
43
47
|
smrt gnode # Scaffold gnode site
|
|
44
48
|
smrt dispatch:* # Dispatch management (list/process/retry/cleanup)
|
|
45
49
|
smrt app <op> # App install/setup/start/migrate/worker…: agents/app-commands.md
|
|
@@ -120,25 +124,13 @@ included using the generator's own exhaustive-include rule.
|
|
|
120
124
|
`renderAgentSurfaceReport(report)` returns the printed lines; both are exported
|
|
121
125
|
so the shape is testable without running the command.
|
|
122
126
|
|
|
123
|
-
**Known live over-exposure
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
`
|
|
129
|
-
|
|
130
|
-
(`registry/inheritance-resolver.ts`) never actually filters it back out —
|
|
131
|
-
its `SmrtObject`/`SmrtClass`/`SmrtCollection` ancestor-skip only fires for a
|
|
132
|
-
chain entry literally named one of those three, and `getInheritanceChain()`
|
|
133
|
-
already stops *before* adding such an ancestor to the chain, so that name
|
|
134
|
-
never appears there to be skipped. The STI class itself is always the last
|
|
135
|
-
chain entry, contributing its own (already-polluted) `methods` map
|
|
136
|
-
unfiltered. So `CLIGenerator`/`MCPGenerator` genuinely register
|
|
137
|
-
`<object>:save`, `<object>:withTransaction`, etc. as real, callable
|
|
138
|
-
commands/tools **today, in a running application** — `configuredSurfaces()`
|
|
139
|
-
here is reporting a real exposed surface, not fabricating one. #2624 tracks
|
|
140
|
-
narrowing the scanner's STI method merge (or making the runtime skip
|
|
141
|
-
actually reachable).
|
|
127
|
+
**Known live over-exposure (#2624):** the scanner's STI method merge walks the full
|
|
128
|
+
inheritance chain, so an STI class's `object.methods` contains its framework base
|
|
129
|
+
classes' internal methods (`save`, `toJSON`, `withTransaction`, `destroy`, ...), and
|
|
130
|
+
`ObjectRegistry.getAllMethods()` never filters them back out (its ancestor skip
|
|
131
|
+
cannot fire: `getInheritanceChain()` stops before adding those ancestors). So
|
|
132
|
+
`CLIGenerator`/`MCPGenerator` really register `<object>:save` etc. in a running
|
|
133
|
+
app; `configuredSurfaces()` reports a real surface. #2624 tracks the fix.
|
|
142
134
|
|
|
143
135
|
Sample output:
|
|
144
136
|
|
package/README.md
CHANGED
|
@@ -198,6 +198,10 @@ knowledge index; documentation snapshots use lightweight package discovery.
|
|
|
198
198
|
| `smrt config:export` | Export agent config for SSG |
|
|
199
199
|
| `smrt export` | Export data in various formats |
|
|
200
200
|
| `smrt init` | Initialize s-m-r-t in an existing SvelteKit project |
|
|
201
|
+
| `smrt cookbook validate <file\|url>` | Check a cookbook against cookbook/v1 and the recipe manifests |
|
|
202
|
+
| `smrt cookbook apply <file\|url> [dir]` | Create a project from a cookbook and the smrt-start template (`--into`, `--template`, `--dry-run`, `--no-install`) |
|
|
203
|
+
| `smrt kitchen [dir]` | Serve the planner on localhost, chat to a cookbook, **Send to kitchen** applies it into `dir` (`--planner`, `--template`, `--no-open`, `--no-install`) |
|
|
204
|
+
| `smrt kitchen apply <url\|file> [dir]` | `cookbook apply` that also takes a plan URL |
|
|
201
205
|
|
|
202
206
|
`smrt export` reads the `export` section of `smrt.config.js`. A listed type that
|
|
203
207
|
is not registered fails the command (non-zero exit, naming the type) instead of
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# `smrt cookbook validate|apply` (#3748)
|
|
2
|
+
|
|
3
|
+
Code: `src/commands/cookbook.ts` (registration), `src/commands/cookbook/`
|
|
4
|
+
(`recipe-index.ts`, `validate.ts`, `apply.ts`). Contract of the document:
|
|
5
|
+
[`core/agents/cookbook.md`](../../core/agents/cookbook.md).
|
|
6
|
+
|
|
7
|
+
## Recipe index
|
|
8
|
+
|
|
9
|
+
Recipes and the objects policies point at come from each package's published
|
|
10
|
+
manifest (`recipes[]`, `objects`), found through its `package.json`
|
|
11
|
+
`exports['./manifest']` (`dist/lib/manifest.json` for SvelteKit-library packages such as
|
|
12
|
+
products), falling back to `dist/manifest.json`, `dist/lib/manifest.json`, `manifest.json`. Read in order:
|
|
13
|
+
|
|
14
|
+
1. `--manifests <file|dir>` (repeatable): a manifest, a dir of manifests, or a dir of package dirs.
|
|
15
|
+
2. The SMRT workspace around the target (`packages/*`).
|
|
16
|
+
3. `node_modules/@happyvertical/smrt-*` walking up from the target.
|
|
17
|
+
4. The package's registry, resolved as npm does (`@scope:registry` from the `.npmrc` chain or `npm config`,
|
|
18
|
+
then `npm_config_registry`, then npmjs; an `_authToken` for that host is sent). `@happyvertical` resolves to
|
|
19
|
+
npm.happyvertical.com, since npmjs lags. Only for
|
|
20
|
+
packages still unresolved: `@happyvertical/smrt-<recipe-id prefix>` and the
|
|
21
|
+
packages named by `features`/`policies`. The tarball is fetched and only
|
|
22
|
+
the manifest is extracted; nothing is installed. `--no-registry` skips it.
|
|
23
|
+
A cookbook cannot choose what is fetched: only names matching
|
|
24
|
+
`@happyvertical/smrt-<name>` are looked up (a package of another scope must
|
|
25
|
+
already be installed locally), the fetched manifest must name the package that
|
|
26
|
+
was asked for, the registry version must be a plain semver, the token is sent
|
|
27
|
+
only to the registry's own origin (a foreign tarball host gets none; foreign
|
|
28
|
+
plain-http is refused), `dist.integrity` is verified when given, and a tarball
|
|
29
|
+
is capped at 300 MB (a cookbook URL at 2 MB).
|
|
30
|
+
|
|
31
|
+
`validate` fails (exit 1, `--json` for machines) on: structural errors (core's
|
|
32
|
+
`validateCookbook`), unknown recipe ids, a `requires` id missing from the
|
|
33
|
+
cookbook, a `requiresAny` group with none present, a `features`/`policies`
|
|
34
|
+
object that is not in its package's manifest, or a policy field the object lacks.
|
|
35
|
+
|
|
36
|
+
## `apply <file|url> [dir]`
|
|
37
|
+
|
|
38
|
+
- **New project** (default): `[dir]` (or the slug of `cookbook.name`) must be absent or empty. The template is
|
|
39
|
+
copied (`--template <path|github:owner/repo[#ref]|git URL>`, default
|
|
40
|
+
`github:happyvertical/smrt-start`, unpinned; a remote template is shallow-fetched, `.git` dropped,
|
|
41
|
+
and its commit printed). A local template that is a git checkout contributes only tracked plus
|
|
42
|
+
untracked-but-unignored files (`git ls-files -co --exclude-standard`), so git-ignored generated output
|
|
43
|
+
(e.g. a stale `src/routes/api/notes`) never reaches the project; uncommitted edits still apply, which
|
|
44
|
+
`git archive HEAD` would drop. A non-git directory is copied whole minus `.git`/`node_modules`.
|
|
45
|
+
`package.json` `name` becomes the slug of `cookbook.name` when set, else of the target directory's
|
|
46
|
+
basename (never the template's name: the runtime derives the app identity, hence the OS data
|
|
47
|
+
directory, from it).
|
|
48
|
+
- **Existing project**: `--into [dir]`, or re-running on a directory that already holds `smrt.cookbook.json`.
|
|
49
|
+
- **Writes**: missing packages (from the recipes, features and policies) into `dependencies`,
|
|
50
|
+
sorted; `smrt.cookbook.json` (the validated, normalized cookbook). Nothing else. No sample data.
|
|
51
|
+
- **Versions**: the cookbook carries none (epic #3747). A new dependency takes the range the project already
|
|
52
|
+
uses for `@happyvertical/smrt-core` (smrt packages release as one group), else `^<this CLI's version>`.
|
|
53
|
+
Already-declared packages are never changed. Removing a recipe does not remove its package.
|
|
54
|
+
- **Idempotent**: re-applying leaves an identical project untouched; an edited cookbook updates the
|
|
55
|
+
config file and adds packages without rewriting anything else.
|
|
56
|
+
- `--dry-run` prints the plan (no writes; a remote template is still fetched to a temp dir).
|
|
57
|
+
`--no-install` skips `<pm> install` (pnpm, or npm/yarn by lockfile). `--json` prints the plan.
|
|
58
|
+
- Next steps printed: install if skipped, then the project's `app:setup` and `app:doctor` scripts
|
|
59
|
+
(else `npx smrt app setup` / `npx smrt doctor`).
|
|
60
|
+
|
|
61
|
+
## Intake contract (smrt-start#4, app shell #3749)
|
|
62
|
+
|
|
63
|
+
`smrt.cookbook.json` at the project root is the cookbook, verbatim v1. An app reads it at
|
|
64
|
+
startup/setup to select recipes, apply policies and layout/theme. The CLI only places
|
|
65
|
+
dependencies and the file; the project is responsible for acting on it.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# `smrt kitchen` (#3750)
|
|
2
|
+
|
|
3
|
+
Code: `src/commands/kitchen.ts` (registration, Ctrl-C), `src/commands/kitchen/`
|
|
4
|
+
(`run.ts` orchestration, `planner.ts` locating the planner, `server.ts` HTTP,
|
|
5
|
+
`ai.ts` the model, `apply.ts` the cookbook hand-off). Planner side:
|
|
6
|
+
`happyvertical/smrt-planner` `docs/inference-host.md` ("Send to kitchen").
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
smrt kitchen [dir] [--planner <dir>] [--template <t>] [--no-open] [--no-install]
|
|
10
|
+
[--manifests <p>]... [--no-registry] [--port <n>]
|
|
11
|
+
smrt kitchen apply <url|file> [dir] # = cookbook apply (any URL that returns cookbook JSON)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`dir` must be absent, empty, or already hold `smrt.cookbook.json` (then it is updated).
|
|
15
|
+
Checked before anything starts.
|
|
16
|
+
|
|
17
|
+
## Flow
|
|
18
|
+
|
|
19
|
+
1. Find the planner (below), import its plain-Node `./core`.
|
|
20
|
+
2. Resolve the model; start the server on `127.0.0.1` (random port, or `--port`) with a random
|
|
21
|
+
one-time token; open the browser (`--no-open` to skip).
|
|
22
|
+
3. The page chats through `/api/planner/chat`, the user builds a cookbook and chooses
|
|
23
|
+
**Send to kitchen**, which POSTs it to `/api/kitchen/cookbook`.
|
|
24
|
+
4. The server validates (`validateCookbook` + recipe index, as `cookbook validate`), runs the
|
|
25
|
+
`cookbook apply` engine into `dir` (install progress goes to the terminal), answers the page
|
|
26
|
+
with the result, then the CLI prints the next steps and exits. A refused cookbook (422) or an
|
|
27
|
+
unusable directory (409) leaves the server running so the user can fix and send again.
|
|
28
|
+
|
|
29
|
+
## Server
|
|
30
|
+
|
|
31
|
+
| Path | |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| `GET /planner.config.json` | `{ inference, kitchen: { endpoint: "/api/kitchen/cookbook", token } }`; `inference` is `{ mode: "host", host: { endpoint: "/api/planner/chat" } }`, or `{ mode: "browser" }` with no model |
|
|
34
|
+
| `POST /api/planner/chat` | host contract v1: `parseHostRequest` -> `buildHostPrompt` -> model -> `parseHostReply` -> reply JSON. 400 bad request, 502 model failure (message not forwarded), 503 no model |
|
|
35
|
+
| `POST /api/kitchen/cookbook` | header `x-kitchen-token`, same-origin JSON; 200 `{ ok, dir, mode, installed, added, nextSteps }`, 4xx `{ ok: false, errors }` |
|
|
36
|
+
| everything else (GET/HEAD) | the planner's `app/` directory; `/` serves `index.html`; no path leaves `app/` |
|
|
37
|
+
|
|
38
|
+
Guards: every request's `Host` must be this server (DNS rebinding); the cookbook POST needs a
|
|
39
|
+
matching `Origin` header (chat: matching if present), `content-type: application/json`, a
|
|
40
|
+
timing-safe token match, and is refused after a success. Bodies are capped (chat 256 KB,
|
|
41
|
+
cookbook 2 MB). No CORS headers are sent. Only one cookbook POST applies at a time
|
|
42
|
+
(the slot is claimed before the body is read). The token is served from
|
|
43
|
+
`/planner.config.json`, which any local process that sends this server's `Host` can read:
|
|
44
|
+
the kitchen defends against browsers, not against other users on the same machine, so run
|
|
45
|
+
it on a machine you trust. Keeping the token out of that endpoint needs a planner contract
|
|
46
|
+
change (token in the opened URL's fragment).
|
|
47
|
+
|
|
48
|
+
## Model
|
|
49
|
+
|
|
50
|
+
`ai.ts` loads `smrt.config.*` (from `dir` if it exists, else the cwd) and resolves the provider
|
|
51
|
+
with smrt-config's `tryResolveConfiguredAIProvider` (config `ai` block, `SMRT_AI_*`, then a
|
|
52
|
+
provider key variable such as `OPENAI_API_KEY`), then builds it with `getAI` from
|
|
53
|
+
`@happyvertical/ai`. Calls are `chat([system, ...messages], { temperature: 0 })`. With no provider
|
|
54
|
+
the config is served in `browser` mode and the terminal says so.
|
|
55
|
+
|
|
56
|
+
## Finding the planner
|
|
57
|
+
|
|
58
|
+
`@happyvertical/smrt-planner` carries a ~70 MB static app, so it is not a dependency. In order:
|
|
59
|
+
|
|
60
|
+
1. `--planner <dir>`: a packaged checkout (`pnpm package` in the planner repo).
|
|
61
|
+
2. `<cache>/<version>/package/`, where `<cache>` is `$SMRT_CACHE_DIR/planner`, else
|
|
62
|
+
`$XDG_CACHE_HOME/smrt/planner`, `~/Library/Caches/smrt/planner` (macOS),
|
|
63
|
+
`%LOCALAPPDATA%\smrt\planner` (Windows) or `~/.cache/smrt/planner`.
|
|
64
|
+
3. The registry that owns the `@happyvertical` scope (`cookbook/registry.ts`, the same scope and
|
|
65
|
+
token resolution as the recipe index): the latest version is downloaded and extracted into the
|
|
66
|
+
cache. If the registry cannot be reached, the newest cached version is used.
|
|
67
|
+
|
|
68
|
+
Otherwise it stops with an error naming `--planner`.
|
|
69
|
+
|
|
70
|
+
## Tests
|
|
71
|
+
|
|
72
|
+
`src/commands/__tests__/kitchen.test.ts` uses a generated fake planner package and a stub model.
|
|
73
|
+
Set `SMRT_PLANNER_DIR=<packaged planner checkout>` to also run the suite against the real `./core`.
|
|
74
|
+
|
|
75
|
+
## `smrt kitchen mcp` (#3752)
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
smrt kitchen mcp [--template <t>] [--manifests <p>]... [--no-registry] [--packages <name>]... [--library <dir>]...
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
A stdio MCP server (`@modelcontextprotocol/server`, `serveStdio`) for the local
|
|
82
|
+
agent plugin in [`plugins/smrt-kitchen`](../../../plugins/smrt-kitchen/README.md).
|
|
83
|
+
The host's model reasons; the server holds plans and calls `createCookbookEngine`
|
|
84
|
+
(`smrt-core/cookbook/engine`). It never calls a model and takes no credential.
|
|
85
|
+
**stdout is the protocol stream**: every notice goes to stderr (this is why the
|
|
86
|
+
`.smrt/register.js` warning in `cli-generator.ts` uses `console.warn`).
|
|
87
|
+
|
|
88
|
+
Code: `src/commands/kitchen/mcp/` (`tools.ts` definitions + `KitchenSession`, `catalog.ts`,
|
|
89
|
+
`server.ts`). Tests: `__tests__/kitchen-mcp.test.ts` (spawns the CLI over stdio with the SDK
|
|
90
|
+
client), `__tests__/kitchen-plugin.test.ts` (plugin files against the Agent Plugins schemas).
|
|
91
|
+
|
|
92
|
+
| Tool | Hints | |
|
|
93
|
+
| --- | --- | --- |
|
|
94
|
+
| `list_cookbooks`, `search_recipes` | read-only | library (`cookbooks/*.cookbook.json`, kept equal to core's fixtures by a test; `--library` adds dirs) and catalog recipes |
|
|
95
|
+
| `start_plan` | not read-only, not destructive | empty, or from a library cookbook; returns `planId` |
|
|
96
|
+
| `run_commands` | idempotent | one atomic `engine.batch` (`id`, `expectedRevision` pass through); a lone `undo` runs as a command |
|
|
97
|
+
| `get_plan`, `validate_plan`, `export_plan` | read-only | `validate_plan` = engine `validate` + `validateCookbookText` against the catalog's manifests |
|
|
98
|
+
| `apply_plan` | destructive, idempotent, open-world | `createCookbookApplier` (= `cookbook apply`); absolute `dir`; **writes only with `confirm: true` + the preview's `confirmCode`** (sha256 of dir, server template and exported plan, so a changed plan or dir needs a new preview), otherwise (or with `dryRun`) a preview + `confirmation_required`; template is the server's `--template` only; `install` defaults to false and its output goes to stderr; templates containing symlinks are refused |
|
|
99
|
+
|
|
100
|
+
- **Plans** live in the server process, keyed by `plan-<8 hex>` (20 kept, oldest dropped); a restart
|
|
101
|
+
forgets them. `smrt.cookbook.json` in the project (written by apply) is the durable copy.
|
|
102
|
+
- **Catalog** is built lazily on first use, once: manifests are resolved for the library's recipes
|
|
103
|
+
exactly like `cookbook validate` (`--manifests`, workspace, `node_modules`, registry unless
|
|
104
|
+
`--no-registry`; `--packages` adds more) and turned into `EngineCatalog` (`catalog.ts`: recipes from
|
|
105
|
+
`manifest.recipes`, models from `manifest.objects`, `exposed` from `decoratorConfig.api`). A package
|
|
106
|
+
that cannot be found shows up in `warnings` and as `snapshot.unavailable`. Tool schemas are static
|
|
107
|
+
(no recipe-id enums) so `tools/list` never waits on the network; `search_recipes` is the id source.
|
|
108
|
+
- **Output**: every tool returns compact text for the model plus `structuredContent` that matches its
|
|
109
|
+
`outputSchema` (`ok` always; `error {code,message,path?,details?}` when false, with `isError`).
|
|
110
|
+
- Not here: the MCP Apps planner widget (see the plugin README), remote/HTTP transport.
|
|
111
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Process fixture isolation (#3762)
|
|
2
|
+
|
|
3
|
+
The CLI process fixture must have no ancestor `node_modules` directory. Node's
|
|
4
|
+
normal hoisted dependency lookup otherwise lets the missing-Vite case find an
|
|
5
|
+
unrelated installed Vite after removing the fixture's local dependency. The
|
|
6
|
+
fixture uses the existing packed-consumer and dev-mcp admission pattern: prefer
|
|
7
|
+
the temporary directory, fall back to the home directory, and require a clean
|
|
8
|
+
TMPDIR when neither parent is isolated. Runtime lookup and assertions stay intact.
|
|
9
|
+
|
|
10
|
+
| Behavior / invariant | Trigger | Positive | Negative / failure | Actor / context | Executor / transaction | Runtime / dialect | External edge | Test level / validation |
|
|
11
|
+
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
12
|
+
| Missing Vite is truly absent | Remove fixture node_modules | Launcher reports missing Vite with status 1 | Unrelated ancestor Vite cannot satisfy the fixture | Real CLI child process | N/A: disposable filesystem fixture | Node26; SQL N/A | Normal Node dependency lookup | Existing CLI process test |
|
|
13
|
+
| Fixture retains process behavior | Dev/build/vite commands | Arguments, environment, stderr and signal contracts hold | Unsafe/missing executable remains rejected | Fixture application | N/A: isolated child processes | Node26; SQL N/A | Fake local Vite and built CLI entry | All eight process tests |
|
|
14
|
+
|
|
15
|
+
Baseline ordinary execution reproduced seven passes and one failure: the absent
|
|
16
|
+
Vite case found the unowned `/tmp/node_modules/vite`, returning status 0 instead
|
|
17
|
+
of 1. An owned TMPDIR with clean ancestors passes all eight tests before the fix,
|
|
18
|
+
confirming fixture contamination. The corrected fixture passes the same eight
|
|
19
|
+
tests under the ordinary environment. No assertion, runtime dependency lookup,
|
|
20
|
+
unowned installation or packaged application artifact is changed.
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
pnpm --filter @happyvertical/smrt-cli exec vitest run src/app/__tests__/cli-process.test.ts
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Full baseline, isolated diagnostic and corrected outputs are retained in release
|
|
27
|
+
evidence under `search/cli-fixture-base.log`,
|
|
28
|
+
`search/cli-fixture-clean-ancestor.log` and `search/cli-fixture-fixed-r2.log`.
|
|
29
|
+
The release coordinator owns combined source acceptance.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://s-m-r-t.dev/schemas/cookbook/v1.json",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"theme": {
|
|
5
|
+
"custom": {
|
|
6
|
+
"primary": "#b45309",
|
|
7
|
+
"fontFamily": "Georgia"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"recipes": [
|
|
11
|
+
"commerce.customers",
|
|
12
|
+
"commerce.invoicing",
|
|
13
|
+
"commerce.purchases",
|
|
14
|
+
"commerce.sales",
|
|
15
|
+
"commerce.vendors",
|
|
16
|
+
"commerce.wholesale",
|
|
17
|
+
"inventory.stock",
|
|
18
|
+
"ledgers.bookkeeping",
|
|
19
|
+
"products.ingredients",
|
|
20
|
+
"products.simple"
|
|
21
|
+
],
|
|
22
|
+
"features": [
|
|
23
|
+
"@happyvertical/smrt-commerce:ProductionOrder"
|
|
24
|
+
],
|
|
25
|
+
"policies": [
|
|
26
|
+
{
|
|
27
|
+
"objectRef": "@happyvertical/smrt-commerce:Customer",
|
|
28
|
+
"fieldName": "paymentTerms",
|
|
29
|
+
"scopeType": "app",
|
|
30
|
+
"defaultValue": "\"Net 15\""
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"objectRef": "@happyvertical/smrt-commerce:Invoice",
|
|
34
|
+
"fieldName": "currency",
|
|
35
|
+
"scopeType": "app",
|
|
36
|
+
"defaultValue": "\"USD\""
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"objectRef": "@happyvertical/smrt-commerce:Invoice",
|
|
40
|
+
"fieldName": "terms",
|
|
41
|
+
"scopeType": "app",
|
|
42
|
+
"defaultValue": "\"Net 15\""
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"objectRef": "@happyvertical/smrt-commerce:Order",
|
|
46
|
+
"fieldName": "terms",
|
|
47
|
+
"scopeType": "app",
|
|
48
|
+
"defaultValue": "\"Net 15\""
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"objectRef": "@happyvertical/smrt-commerce:ProductionOrder",
|
|
52
|
+
"fieldName": "channelId",
|
|
53
|
+
"scopeType": "app",
|
|
54
|
+
"visibility": "hidden"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"objectRef": "@happyvertical/smrt-commerce:ProductionOrder",
|
|
58
|
+
"fieldName": "customerId",
|
|
59
|
+
"scopeType": "app",
|
|
60
|
+
"visibility": "hidden"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"objectRef": "@happyvertical/smrt-commerce:ProductionOrder",
|
|
64
|
+
"fieldName": "expiryDate",
|
|
65
|
+
"scopeType": "app",
|
|
66
|
+
"visibility": "hidden"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"objectRef": "@happyvertical/smrt-commerce:ProductionOrder",
|
|
70
|
+
"fieldName": "vendorId",
|
|
71
|
+
"scopeType": "app",
|
|
72
|
+
"visibility": "hidden"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"objectRef": "@happyvertical/smrt-commerce:WholesaleOrder",
|
|
76
|
+
"fieldName": "terms",
|
|
77
|
+
"scopeType": "app",
|
|
78
|
+
"defaultValue": "\"Net 15\""
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
"layout": {
|
|
82
|
+
"version": 1,
|
|
83
|
+
"sectionOrder": [
|
|
84
|
+
"custom:shop",
|
|
85
|
+
"custom:wholesale",
|
|
86
|
+
"custom:kitchen",
|
|
87
|
+
"custom:buying",
|
|
88
|
+
"section:accounting"
|
|
89
|
+
],
|
|
90
|
+
"itemOrder": {
|
|
91
|
+
"custom:shop": [
|
|
92
|
+
"item:commerce:Order",
|
|
93
|
+
"item:commerce:Customer"
|
|
94
|
+
],
|
|
95
|
+
"custom:wholesale": [
|
|
96
|
+
"item:commerce:WholesaleOrder",
|
|
97
|
+
"item:commerce:Invoice",
|
|
98
|
+
"item:commerce:Payment"
|
|
99
|
+
],
|
|
100
|
+
"custom:kitchen": [
|
|
101
|
+
"item:products:Product",
|
|
102
|
+
"item:products:Product:ingredients",
|
|
103
|
+
"item:inventory:StockLevel",
|
|
104
|
+
"item:commerce:ProductionOrder"
|
|
105
|
+
],
|
|
106
|
+
"custom:buying": [
|
|
107
|
+
"item:commerce:Vendor",
|
|
108
|
+
"item:commerce:PurchaseOrder"
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
"hidden": [
|
|
112
|
+
"item:inventory:InventoryLocation",
|
|
113
|
+
"item:inventory:StockMovement"
|
|
114
|
+
],
|
|
115
|
+
"moved": {
|
|
116
|
+
"item:commerce:Order": "custom:shop",
|
|
117
|
+
"item:commerce:Customer": "custom:shop",
|
|
118
|
+
"item:commerce:WholesaleOrder": "custom:wholesale",
|
|
119
|
+
"item:commerce:Invoice": "custom:wholesale",
|
|
120
|
+
"item:commerce:Payment": "custom:wholesale",
|
|
121
|
+
"item:products:Product": "custom:kitchen",
|
|
122
|
+
"item:inventory:StockLevel": "custom:kitchen",
|
|
123
|
+
"item:commerce:ProductionOrder": "custom:kitchen",
|
|
124
|
+
"item:commerce:Vendor": "custom:buying",
|
|
125
|
+
"item:commerce:PurchaseOrder": "custom:buying",
|
|
126
|
+
"item:products:Product:ingredients": "custom:kitchen"
|
|
127
|
+
},
|
|
128
|
+
"customSections": [
|
|
129
|
+
{
|
|
130
|
+
"id": "custom:shop",
|
|
131
|
+
"label": "Shop"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"id": "custom:wholesale",
|
|
135
|
+
"label": "Wholesale"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "custom:kitchen",
|
|
139
|
+
"label": "Kitchen"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"id": "custom:buying",
|
|
143
|
+
"label": "Buying"
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
"items": {
|
|
147
|
+
"item:commerce:Order": {
|
|
148
|
+
"label": "Orders",
|
|
149
|
+
"description": "Orders from your shop and website customers."
|
|
150
|
+
},
|
|
151
|
+
"item:commerce:WholesaleOrder": {
|
|
152
|
+
"label": "Cafe orders",
|
|
153
|
+
"description": "Standing and one-off orders from cafes and shops."
|
|
154
|
+
},
|
|
155
|
+
"item:inventory:StockLevel": {
|
|
156
|
+
"label": "Stock",
|
|
157
|
+
"description": "Baked goods and supplies on hand today."
|
|
158
|
+
},
|
|
159
|
+
"item:commerce:ProductionOrder": {
|
|
160
|
+
"label": "Batches",
|
|
161
|
+
"description": "What you plan to bake, with the amounts and the day."
|
|
162
|
+
},
|
|
163
|
+
"item:commerce:Vendor": {
|
|
164
|
+
"label": "Suppliers",
|
|
165
|
+
"description": "The mills, dairies and suppliers you buy from."
|
|
166
|
+
},
|
|
167
|
+
"item:products:Product:ingredients": {
|
|
168
|
+
"label": "Ingredients",
|
|
169
|
+
"description": "Flour, butter, sugar and the rest, with costs and units."
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"sections": {
|
|
173
|
+
"custom:shop": {
|
|
174
|
+
"icon": "shoppingBag"
|
|
175
|
+
},
|
|
176
|
+
"custom:wholesale": {
|
|
177
|
+
"icon": "briefcase"
|
|
178
|
+
},
|
|
179
|
+
"custom:kitchen": {
|
|
180
|
+
"icon": "package"
|
|
181
|
+
},
|
|
182
|
+
"custom:buying": {
|
|
183
|
+
"icon": "truck"
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://s-m-r-t.dev/schemas/cookbook/v1.json",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"theme": {
|
|
5
|
+
"preset": "smrt"
|
|
6
|
+
},
|
|
7
|
+
"recipes": [
|
|
8
|
+
"commerce.customers",
|
|
9
|
+
"commerce.estimates",
|
|
10
|
+
"commerce.invoicing",
|
|
11
|
+
"commerce.purchases",
|
|
12
|
+
"commerce.sales",
|
|
13
|
+
"commerce.vendors",
|
|
14
|
+
"events.calendar",
|
|
15
|
+
"inventory.stock",
|
|
16
|
+
"ledgers.bookkeeping",
|
|
17
|
+
"products.simple"
|
|
18
|
+
],
|
|
19
|
+
"features": [],
|
|
20
|
+
"policies": [
|
|
21
|
+
{
|
|
22
|
+
"objectRef": "@happyvertical/smrt-commerce:ContractLineItem",
|
|
23
|
+
"fieldName": "taxRate",
|
|
24
|
+
"scopeType": "app",
|
|
25
|
+
"defaultValue": "0.0825"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"objectRef": "@happyvertical/smrt-commerce:Customer",
|
|
29
|
+
"fieldName": "creditLimit",
|
|
30
|
+
"scopeType": "app",
|
|
31
|
+
"visibility": "hidden"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"objectRef": "@happyvertical/smrt-commerce:Customer",
|
|
35
|
+
"fieldName": "customerType",
|
|
36
|
+
"scopeType": "app",
|
|
37
|
+
"visibility": "hidden"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"objectRef": "@happyvertical/smrt-commerce:Customer",
|
|
41
|
+
"fieldName": "paymentTerms",
|
|
42
|
+
"scopeType": "app",
|
|
43
|
+
"defaultValue": "\"Due on receipt\""
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"objectRef": "@happyvertical/smrt-commerce:Estimate",
|
|
47
|
+
"fieldName": "terms",
|
|
48
|
+
"scopeType": "app",
|
|
49
|
+
"defaultValue": "\"Due on receipt\""
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"objectRef": "@happyvertical/smrt-commerce:Invoice",
|
|
53
|
+
"fieldName": "currency",
|
|
54
|
+
"scopeType": "app",
|
|
55
|
+
"defaultValue": "\"USD\""
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"objectRef": "@happyvertical/smrt-commerce:Invoice",
|
|
59
|
+
"fieldName": "terms",
|
|
60
|
+
"scopeType": "app",
|
|
61
|
+
"defaultValue": "\"Due on receipt\""
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"objectRef": "@happyvertical/smrt-commerce:InvoiceLineItem",
|
|
65
|
+
"fieldName": "taxRate",
|
|
66
|
+
"scopeType": "app",
|
|
67
|
+
"defaultValue": "0.0825"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"objectRef": "@happyvertical/smrt-commerce:Order",
|
|
71
|
+
"fieldName": "terms",
|
|
72
|
+
"scopeType": "app",
|
|
73
|
+
"defaultValue": "\"Due on receipt\""
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"layout": {
|
|
77
|
+
"version": 1,
|
|
78
|
+
"sectionOrder": [
|
|
79
|
+
"custom:jobs",
|
|
80
|
+
"custom:parts",
|
|
81
|
+
"custom:billing",
|
|
82
|
+
"custom:schedule",
|
|
83
|
+
"section:accounting"
|
|
84
|
+
],
|
|
85
|
+
"itemOrder": {
|
|
86
|
+
"custom:jobs": [
|
|
87
|
+
"item:commerce:Estimate",
|
|
88
|
+
"item:commerce:Order",
|
|
89
|
+
"item:commerce:Customer"
|
|
90
|
+
],
|
|
91
|
+
"custom:parts": [
|
|
92
|
+
"item:products:Product",
|
|
93
|
+
"item:inventory:StockLevel",
|
|
94
|
+
"item:commerce:Vendor",
|
|
95
|
+
"item:commerce:PurchaseOrder"
|
|
96
|
+
],
|
|
97
|
+
"custom:billing": [
|
|
98
|
+
"item:commerce:Invoice",
|
|
99
|
+
"item:commerce:Payment"
|
|
100
|
+
],
|
|
101
|
+
"custom:schedule": [
|
|
102
|
+
"item:events:Event",
|
|
103
|
+
"item:events:EventType"
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
"hidden": [
|
|
107
|
+
"item:events:EventSeries",
|
|
108
|
+
"item:inventory:InventoryLocation",
|
|
109
|
+
"item:inventory:StockMovement"
|
|
110
|
+
],
|
|
111
|
+
"moved": {
|
|
112
|
+
"item:commerce:Estimate": "custom:jobs",
|
|
113
|
+
"item:commerce:Order": "custom:jobs",
|
|
114
|
+
"item:commerce:Customer": "custom:jobs",
|
|
115
|
+
"item:products:Product": "custom:parts",
|
|
116
|
+
"item:inventory:StockLevel": "custom:parts",
|
|
117
|
+
"item:commerce:Vendor": "custom:parts",
|
|
118
|
+
"item:commerce:PurchaseOrder": "custom:parts",
|
|
119
|
+
"item:commerce:Invoice": "custom:billing",
|
|
120
|
+
"item:commerce:Payment": "custom:billing",
|
|
121
|
+
"item:events:Event": "custom:schedule",
|
|
122
|
+
"item:events:EventType": "custom:schedule"
|
|
123
|
+
},
|
|
124
|
+
"customSections": [
|
|
125
|
+
{
|
|
126
|
+
"id": "custom:jobs",
|
|
127
|
+
"label": "Jobs"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"id": "custom:parts",
|
|
131
|
+
"label": "Parts"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"id": "custom:billing",
|
|
135
|
+
"label": "Billing"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "custom:schedule",
|
|
139
|
+
"label": "Schedule"
|
|
140
|
+
}
|
|
141
|
+
],
|
|
142
|
+
"items": {
|
|
143
|
+
"item:commerce:Estimate": {
|
|
144
|
+
"label": "Quotes",
|
|
145
|
+
"description": "Quotes for repairs, ready to become work orders once approved."
|
|
146
|
+
},
|
|
147
|
+
"item:commerce:Order": {
|
|
148
|
+
"label": "Work orders",
|
|
149
|
+
"description": "Every job in the shop, from drop-off to pick-up."
|
|
150
|
+
},
|
|
151
|
+
"item:products:Product": {
|
|
152
|
+
"label": "Parts",
|
|
153
|
+
"description": "The parts you sell and fit, with prices."
|
|
154
|
+
},
|
|
155
|
+
"item:inventory:StockLevel": {
|
|
156
|
+
"label": "Parts on hand",
|
|
157
|
+
"description": "Parts on the shelf, so you know what to reorder."
|
|
158
|
+
},
|
|
159
|
+
"item:commerce:Vendor": {
|
|
160
|
+
"label": "Suppliers",
|
|
161
|
+
"description": "The parts suppliers you buy from, with terms and contacts."
|
|
162
|
+
},
|
|
163
|
+
"item:commerce:PurchaseOrder": {
|
|
164
|
+
"label": "Parts orders",
|
|
165
|
+
"description": "Parts you have ordered and when they arrive."
|
|
166
|
+
},
|
|
167
|
+
"item:events:Event": {
|
|
168
|
+
"label": "Appointments",
|
|
169
|
+
"description": "Bookings on the shop calendar, by day and bay."
|
|
170
|
+
},
|
|
171
|
+
"item:events:EventType": {
|
|
172
|
+
"label": "Appointment types",
|
|
173
|
+
"description": "The kinds of service you book, like an oil change or inspection."
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
"sections": {
|
|
177
|
+
"custom:jobs": {
|
|
178
|
+
"icon": "wrench"
|
|
179
|
+
},
|
|
180
|
+
"custom:parts": {
|
|
181
|
+
"icon": "package"
|
|
182
|
+
},
|
|
183
|
+
"custom:billing": {
|
|
184
|
+
"icon": "receipt"
|
|
185
|
+
},
|
|
186
|
+
"custom:schedule": {
|
|
187
|
+
"icon": "calendar"
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|