@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.
Files changed (65) hide show
  1. package/AGENTS.md +11 -19
  2. package/README.md +4 -0
  3. package/agents/cookbook-apply.md +65 -0
  4. package/agents/kitchen.md +111 -0
  5. package/agents/process-fixture-isolation.md +29 -0
  6. package/cookbooks/bakery.cookbook.json +187 -0
  7. package/cookbooks/mechanic.cookbook.json +191 -0
  8. package/cookbooks/welder.cookbook.json +177 -0
  9. package/cookbooks/yoga-studio.cookbook.json +173 -0
  10. package/dist/{AuditLogCollection-WDHL-_AF-9sLdjeZF.js → AuditLogCollection-WDHL-_AF-ClQNu_VR.js} +1 -1
  11. package/dist/{OidcIdentity-BUblgQLs-QX0f44sA.js → OidcIdentity-BUblgQLs-br8RyLkA.js} +2 -2
  12. package/dist/{OidcIdentityCollection-ClOyaHeF-BfIHh4aA.js → OidcIdentityCollection-ClOyaHeF-BpQXB3mz.js} +1 -1
  13. package/dist/{ProfileAssetCollection-CRMcjLJ--Dobq-AgM.js → ProfileAssetCollection-CRMcjLJ--C5ytBKfF.js} +3 -3
  14. package/dist/{ProfileCollection-C4INKh7Y-DtWNBhg3.js → ProfileCollection-C4INKh7Y-CfkyJilm.js} +17 -17
  15. package/dist/{ProfileLinkCollection-DFfZRsgv-DNiBQtmL.js → ProfileLinkCollection-DFfZRsgv-MQ2zhD9z.js} +2 -2
  16. package/dist/{ProfileMetadataCollection-BIYKy4Y3-CeQxYDL4.js → ProfileMetadataCollection-BIYKy4Y3-DXu5L2yx.js} +1 -1
  17. package/dist/{ProfileMetafieldCollection-Buz7gzag-DP2ZYYv7.js → ProfileMetafieldCollection-Buz7gzag-C2tZc1PF.js} +1 -1
  18. package/dist/{ProfileRelationshipCollection-C4g_QLI4-VbnpFG2C.js → ProfileRelationshipCollection-C4g_QLI4-vTnkAIAh.js} +1 -1
  19. package/dist/{ProfileTypeCollection-B7kwi1mU-DWalSOyP.js → ProfileTypeCollection-B7kwi1mU-KwPGcUkI.js} +2 -2
  20. package/dist/apply-BNDIdixP.js +947 -0
  21. package/dist/cli-generator.d.ts.map +1 -1
  22. package/dist/commands/cookbook/apply.d.ts +102 -0
  23. package/dist/commands/cookbook/apply.d.ts.map +1 -0
  24. package/dist/commands/cookbook/recipe-index.d.ts +108 -0
  25. package/dist/commands/cookbook/recipe-index.d.ts.map +1 -0
  26. package/dist/commands/cookbook/registry.d.ts +69 -0
  27. package/dist/commands/cookbook/registry.d.ts.map +1 -0
  28. package/dist/commands/cookbook/shared.d.ts +23 -0
  29. package/dist/commands/cookbook/shared.d.ts.map +1 -0
  30. package/dist/commands/cookbook/validate.d.ts +25 -0
  31. package/dist/commands/cookbook/validate.d.ts.map +1 -0
  32. package/dist/commands/cookbook.d.ts +6 -0
  33. package/dist/commands/cookbook.d.ts.map +1 -0
  34. package/dist/commands/index.d.ts +2 -0
  35. package/dist/commands/index.d.ts.map +1 -1
  36. package/dist/commands/kitchen/ai.d.ts +34 -0
  37. package/dist/commands/kitchen/ai.d.ts.map +1 -0
  38. package/dist/commands/kitchen/apply.d.ts +52 -0
  39. package/dist/commands/kitchen/apply.d.ts.map +1 -0
  40. package/dist/commands/kitchen/mcp/catalog.d.ts +39 -0
  41. package/dist/commands/kitchen/mcp/catalog.d.ts.map +1 -0
  42. package/dist/commands/kitchen/mcp/server.d.ts +15 -0
  43. package/dist/commands/kitchen/mcp/server.d.ts.map +1 -0
  44. package/dist/commands/kitchen/mcp/tools.d.ts +68 -0
  45. package/dist/commands/kitchen/mcp/tools.d.ts.map +1 -0
  46. package/dist/commands/kitchen/planner.d.ts +71 -0
  47. package/dist/commands/kitchen/planner.d.ts.map +1 -0
  48. package/dist/commands/kitchen/run.d.ts +40 -0
  49. package/dist/commands/kitchen/run.d.ts.map +1 -0
  50. package/dist/commands/kitchen/server.d.ts +39 -0
  51. package/dist/commands/kitchen/server.d.ts.map +1 -0
  52. package/dist/commands/kitchen.d.ts +7 -0
  53. package/dist/commands/kitchen.d.ts.map +1 -0
  54. package/dist/{commands-n_7ZXru_.js → commands-pbRVQvix.js} +759 -9
  55. package/dist/{dist-BsMQ4Itc.js → dist-BsRSQ_hE.js} +16 -16
  56. package/dist/{dist-DscvT_q_.js → dist-CYVIKmvy.js} +4 -4
  57. package/dist/dist-D_WqjwkC.js +21 -0
  58. package/dist/{dist-gZP7Tw10.js → dist-DxkSjKQQ.js} +2 -2
  59. package/dist/{dist-Bw1oCty_.js → dist-ffh0LeC4.js} +3 -3
  60. package/dist/index.js +44 -16
  61. package/dist/{oidcProvisioningPrimitives-DNP9EZlU-w2kQ8oqV.js → oidcProvisioningPrimitives-DNP9EZlU-CBYwksp0.js} +3 -3
  62. package/dist/{resolveIdentity-CkyqvctR-Cm591gNa.js → resolveIdentity-CkyqvctR-B5btFsQd.js} +14 -14
  63. package/dist/server-Dp096tZj.js +898 -0
  64. package/package.json +13 -10
  65. 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 for an STI class with a bare `true`/omitted
124
- `mcp`/`cli` config, tracked as #2624:** the *scanner's* STI method merge in
125
- `manifest-generator.ts` walks the full inheritance chain unconditionally, so
126
- an STI class's `object.methods` already (incorrectly) contains its framework
127
- base classes' own internal methods (`save`, `toJSON`, `withTransaction`,
128
- `generateEmbeddings`, `destroy`, ...). This is not only a manifest
129
- artifact concern: `ObjectRegistry.getAllMethods()`'s runtime chain walk
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
+ }