anhedral 0.4.2 → 0.6.0

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 (89) hide show
  1. package/README.md +29 -269
  2. package/assets/anhedral-cli-init-technical.svg +322 -0
  3. package/assets/anhedral-cli-init.svg +257 -173
  4. package/dist/bin.js +28 -191
  5. package/dist/dependencies.d.ts +7 -57
  6. package/dist/dependencies.js +96 -308
  7. package/dist/index.d.ts +2 -7
  8. package/dist/index.js +1 -4
  9. package/dist/platforms/desktop.js +401 -0
  10. package/dist/platforms/extension.js +401 -0
  11. package/dist/platforms/mobile.js +232 -0
  12. package/dist/project.d.ts +0 -26
  13. package/dist/standard-products.d.ts +2 -0
  14. package/dist/standard-products.js +6 -0
  15. package/dist/standard.d.ts +18 -0
  16. package/dist/standard.js +578 -0
  17. package/dist/version.d.ts +1 -1
  18. package/dist/version.js +1 -1
  19. package/docs/application-stack-standard.md +700 -0
  20. package/package.json +34 -51
  21. package/CHANGELOG.md +0 -178
  22. package/CONTRIBUTING.md +0 -77
  23. package/SECURITY.md +0 -37
  24. package/SKILL.md +0 -339
  25. package/assets/images/svg/ably-symbol-color.svg +0 -8
  26. package/bin/anhedral.js +0 -2
  27. package/dist/architecture/contributions.d.ts +0 -28
  28. package/dist/architecture/contributions.js +0 -125
  29. package/dist/architecture/index.d.ts +0 -12
  30. package/dist/architecture/index.js +0 -6
  31. package/dist/architecture/manifest.d.ts +0 -53
  32. package/dist/architecture/manifest.js +0 -283
  33. package/dist/architecture/modules.d.ts +0 -41
  34. package/dist/architecture/modules.js +0 -187
  35. package/dist/architecture/plan.d.ts +0 -58
  36. package/dist/architecture/plan.js +0 -145
  37. package/dist/architecture/templates.d.ts +0 -10
  38. package/dist/architecture/templates.js +0 -23
  39. package/dist/cli.d.ts +0 -32
  40. package/dist/cli.js +0 -281
  41. package/dist/composers/root.d.ts +0 -6
  42. package/dist/composers/root.js +0 -35
  43. package/dist/print.d.ts +0 -8
  44. package/dist/print.js +0 -48
  45. package/dist/prompts.d.ts +0 -6
  46. package/dist/prompts.js +0 -41
  47. package/dist/scaffold.d.ts +0 -64
  48. package/dist/scaffold.js +0 -2102
  49. package/dist/template-source.d.ts +0 -20
  50. package/dist/template-source.js +0 -180
  51. package/dist/templates/api.d.ts +0 -2
  52. package/dist/templates/api.js +0 -3187
  53. package/dist/templates/assets-private-proxy.d.ts +0 -3
  54. package/dist/templates/assets-private-proxy.js +0 -277
  55. package/dist/templates/desktop.js +0 -792
  56. package/dist/templates/electron-updater.d.ts +0 -3
  57. package/dist/templates/electron-updater.js +0 -358
  58. package/dist/templates/extension.js +0 -856
  59. package/dist/templates/first-run.d.ts +0 -2
  60. package/dist/templates/first-run.js +0 -167
  61. package/dist/templates/mobile.js +0 -861
  62. package/dist/templates/shared.d.ts +0 -2
  63. package/dist/templates/shared.js +0 -577
  64. package/dist/templates/web.d.ts +0 -2
  65. package/dist/templates/web.js +0 -569
  66. package/dist/toolchain.d.ts +0 -3
  67. package/dist/toolchain.js +0 -8
  68. package/dist/ui.d.ts +0 -33
  69. package/dist/ui.js +0 -199
  70. package/dist/workspace-config.d.ts +0 -11
  71. package/dist/workspace-config.js +0 -372
  72. package/docs/NORTHSTAR.md +0 -114
  73. package/docs/conventions.md +0 -94
  74. package/docs/engineering-principles.md +0 -88
  75. package/docs/master-stack-map.md +0 -199
  76. package/docs/output-tree-contract.md +0 -493
  77. package/docs/references/manual-scaffolding.md +0 -421
  78. package/docs/references/provisioning.md +0 -577
  79. package/scripts/render-master-stack-map.mjs +0 -510
  80. package/templates/api-fastify/apps/api/tsconfig.json +0 -20
  81. package/templates/catalog.json +0 -23
  82. package/templates/db-drizzle/packages/db/tsconfig.json +0 -14
  83. package/templates/desktop-electron/apps/desktop/tsconfig.json +0 -37
  84. package/templates/extension-wxt/apps/extension/tsconfig.json +0 -41
  85. package/templates/mobile-expo/apps/mobile/expo-env.d.ts +0 -1
  86. package/templates/web-next/apps/web/next-env.d.ts +0 -6
  87. /package/dist/{templates → platforms}/desktop.d.ts +0 -0
  88. /package/dist/{templates → platforms}/extension.d.ts +0 -0
  89. /package/dist/{templates → platforms}/mobile.d.ts +0 -0
package/SKILL.md DELETED
@@ -1,339 +0,0 @@
1
- ---
2
- name: anhedral-init
3
- description: Guide a user through creating, provisioning, extending, explaining, or diagnosing a complete Anhedral TypeScript stack. Use for skill-led new/init/add/ui/doctor workflows; Computer Use or subagent-assisted cloud setup; domains, DNS, Vercel, Cloudflare, Neon, Clerk, R2, billing, stores, and Electron updates; development inside a generated workspace; ownership interpretation; manual reproduction; or verification.
4
- ---
5
-
6
- # Anhedral Init
7
-
8
- Anhedral is a stack generator, not an application runtime or programming language. Generated projects use ordinary framework source and expose the complete integration code. Do not invent Anhedral-specific models, routes, queries, components, or client hooks when the selected framework already has a documented convention.
9
-
10
- For every new project or provisioning request, begin by asking for the project
11
- name and whether the user already owns a custom domain for it. Do not run init
12
- or create provider resources until those answers establish canonical names.
13
-
14
- `new` creates a destination directory; `init` works in an empty current
15
- directory where `.git`, `.gitignore`, and `.DS_Store` are allowed.
16
-
17
- ## Bootstrap a blank workstation
18
-
19
- Do not assume Node.js, npm, pnpm, Git, a browser, provider CLIs, or native build
20
- tools are installed. After collecting the project name and domain status, audit
21
- the workstation before generation:
22
-
23
- ```sh
24
- node --version
25
- npm --version
26
- pnpm --version
27
- git --version
28
- ```
29
-
30
- For a new installation, recommend the current Node.js 24 LTS release. The
31
- generator itself accepts `^20.19.0 || >=22.12.0`, but mobile projects require
32
- `^22.13.0 || ^24.3.0 || >=25`; Node.js 24 LTS is the single supported choice
33
- that covers every Anhedral module without using an end-of-life line.
34
-
35
- If Node.js is absent, use Computer Use to open `https://nodejs.org/en/download`,
36
- select the current Node.js 24 LTS installer for the user's operating system and
37
- architecture, and stop for the user at the installer, administrator-password,
38
- or operating-system approval boundary. npm is bundled with Node.js and may be
39
- used to install pnpm, but npm is not the package manager for a generated
40
- workspace. Installing the public Anhedral package does not require an npm
41
- account or `npm login`.
42
-
43
- Install the exact pnpm release recorded by this generator's
44
- `package.json#packageManager` (currently `pnpm@10.34.5`):
45
-
46
- ```sh
47
- npm install --global pnpm@10.34.5
48
- ```
49
-
50
- Ask before a global or administrator-level installation. Verify the four
51
- commands above again after installation. Install Git with the operating-system
52
- package manager or official installer when it is missing; stop for UAC,
53
- administrator credentials, Xcode Command Line Tools approval, or a license
54
- prompt. Configure the user's real Git name and email only after asking what
55
- identity they want commits to use.
56
-
57
- A modern browser is required for OAuth device flows and provider dashboards.
58
- No global Vercel, Neon, Wrangler, EAS, shadcn, or Anhedral installation is
59
- required: generated scripts invoke reviewed CLI versions through `pnpm dlx`.
60
- GitHub CLI is recommended for repository creation. Clerk CLI and Ably CLI are
61
- optional supplemental control planes; use them only after the corresponding
62
- module is selected, record the resolved version, and do not run `clerk init`
63
- inside an Anhedral project because the integration source already exists.
64
-
65
- Native tools are conditional:
66
-
67
- - EAS cloud builds do not require local Xcode or Android Studio.
68
- - Local iOS builds and manual App Store uploads require macOS and Xcode.
69
- - Local Android builds/emulators require Android Studio and a compatible JDK.
70
- - Signed Electron releases must be built on each target OS; macOS notarization
71
- needs Xcode tooling and Apple credentials, while Windows distribution needs
72
- an organization-owned code-signing certificate and Windows runner.
73
- - Extension development requires a Chromium browser; store publication does
74
- not require another build CLI beyond the generated WXT scripts.
75
-
76
- Anhedral intentionally requires neither Docker nor local Postgres. Database
77
- modules use managed Neon.
78
-
79
- ## Choose the construction path
80
-
81
- - Use the manual path whenever the user asks to avoid the Anhedral CLI, the CLI is unavailable, or the task requires explaining/reproducing the scaffold itself.
82
- - Before creating or changing files manually, read [docs/references/manual-scaffolding.md](docs/references/manual-scaffolding.md) completely and follow its ordered procedure. Treat it as the canonical manual checklist, including its source-file matrix and feature gates.
83
- - Use the CLI path only when the user permits it. Do not silently substitute `anhedral init`, `anhedral add`, imported generator code, or a generated demo for the manual path.
84
- - For an exact-version scaffold inside the Anhedral source repository, render from the current `src/templates/*.ts`, `src/scaffold.ts`, and `src/dependencies.ts`; do not copy stale demo output. Outside that repository, implement the behavior and file contract in the manual reference and pin a mutually compatible toolchain.
85
-
86
- The manual path must leave a complete working workspace, not merely a directory tree or list of commands. Write the source, configuration, tests, environment examples, CI, deployment configuration, and database migration gate selected by the module plan. Install and verify when the environment permits it.
87
-
88
- ## Select modules
89
-
90
- Choose app surfaces from `web`, `mobile`, `api`, `desktop`, and `extension`.
91
-
92
- Choose features from `db`, `auth`, `billing`, `storage`, `native-subscriptions`, and `electron-updater`.
93
-
94
- Apply these dependency rules automatically:
95
-
96
- ```text
97
- auth -> api + db
98
- billing -> auth
99
- storage -> auth
100
- native-subscriptions -> mobile + billing
101
- electron-updater -> desktop
102
- ```
103
-
104
- With no module flags in an interactive terminal, Anhedral prompts and suggests
105
- a focused web app. In a noninteractive environment, no flags retain the full
106
- stack for compatibility. Use `--all` when the complete stack is intentional,
107
- or pass explicit modules for reproducible automation.
108
-
109
- ## Master stack map
110
-
111
- Read [docs/master-stack-map.md](docs/master-stack-map.md) completely before
112
- explaining the whole platform, selecting modules for a complete product, or
113
- planning cross-provider provisioning. Treat it as the canonical map of the
114
- stack, command surface, runtime connections, agent workflow, and release path.
115
-
116
- ```text
117
- project + domain intake
118
- |
119
- v
120
- 1 select + plan -> 2 generate safely -> 3 one pnpm/Turborepo workspace
121
- |
122
- v
123
- 4 connected product capabilities -> 5 provision cloud + DNS
124
- |
125
- v
126
- 6 generated package.json commands -> 7 developer/agent/Computer Use
127
- |
128
- v
129
- 8 verify, release, and operate
130
- ```
131
-
132
- Use the full fixed-width ASCII map from the document when the user asks for the
133
- master stack map. Do not redraw it from memory or omit unselected modules when
134
- describing Anhedral's complete capability surface; mark modules as selected or
135
- unselected separately.
136
-
137
- ## Run the guided init workflow
138
-
139
- Read [docs/references/provisioning.md](docs/references/provisioning.md) completely before
140
- starting a new project or provisioning any provider. Follow its intake,
141
- capability discovery, Computer Use, subagent, authentication, secret-handling,
142
- domain, provider-order, and verification gates.
143
-
144
- The lead agent must:
145
-
146
- 1. Ask for project name and existing/custom-domain status first.
147
- 2. Audit and bootstrap the workstation, then discover whether Computer
148
- Use/browser control and subagents are available.
149
- 3. Resolve modules, required accounts, and canonical project/resource names.
150
- 4. Run the explicit CLI command, then read the generated `PRODUCTION.md`,
151
- `anhedral.json`, root scripts, and package-local environment examples.
152
- 5. Provision only selected providers in dependency order, pausing for sign-in, MFA, secret
153
- generation, purchases, and final submissions exactly as the reference says.
154
- 6. Keep secrets out of chat and tool output; have the user paste them directly
155
- into uncommitted environment files and protected provider fields.
156
- 7. Verify code, cloud resources, DNS, TLS, and provider health before handoff.
157
-
158
- ## Initialize
159
-
160
- ```sh
161
- pnpm dlx anhedral@latest new my-product
162
- cd my-product
163
- pnpm first-run
164
- pnpm ready
165
- ```
166
-
167
- `first-run` creates only missing package-local environment files and never
168
- overwrites existing configuration. `ready` reports missing filenames and
169
- required variable names without printing values; resolve every blocker before
170
- starting affected services.
171
-
172
- Prefer explicit modules when the user requests a smaller stack:
173
-
174
- ```sh
175
- pnpm dlx anhedral@latest new my-product --web --api --db --auth
176
- pnpm dlx anhedral@latest new my-product --web --mobile --ui button,dialog --native-styling nativewind
177
- pnpm dlx anhedral@latest new my-api --api --skip-install
178
- ```
179
-
180
- Use the stable toolchain for normal generation. The `latest` value is retained only as a metadata compatibility channel for maintainer investigations:
181
-
182
- ```sh
183
- pnpm dlx anhedral@latest new my-product --toolchain stable
184
- pnpm dlx anhedral@latest new my-product --toolchain latest
185
- ```
186
-
187
- Both channels generate the same exact verified dependencies and integrity-checked bundled templates; `latest` only records investigation intent in `anhedral.json`. Upstream refreshes happen outside user projects and are shipped only after review and checksum verification. Neither channel invokes mutable upstream framework generators inside a user project.
188
-
189
- Do not run any command in this section during the manual path.
190
-
191
- ## Develop inside a generated project
192
-
193
- Read these files before changing product code:
194
-
195
- 1. `README.md` for the selected modules, first run, and source-location map.
196
- 2. `docs/DEVELOPMENT.md` for end-to-end feature recipes.
197
- 3. `docs/STACK.md` for responsibility boundaries and official tool documentation.
198
- 4. `PRODUCTION.md` only for provisioning, deployment, DNS, stores, or release work.
199
-
200
- Use the native source conventions:
201
-
202
- - Next.js code belongs in `apps/web/app`, `apps/web/components`, and `apps/web/lib`.
203
- - Expo Router code belongs in `apps/mobile/app` and `apps/mobile/components`.
204
- - Product Fastify route registration belongs in `apps/api/src/routes/app.ts`. Server-only feature modules live alongside the managed integrations in `apps/api/src`.
205
- - Product Zod network schemas belong in `packages/contracts/src/app.ts`.
206
- - Product client-safe HTTP methods belong in `packages/api-client/src/app.ts`.
207
- - Product Drizzle tables belong in `packages/db/src/app-schema.ts`; generate and review SQL migrations.
208
-
209
- Implement an end-to-end feature as `contracts -> database/service -> route -> API client -> frontend`. The generated `items` feature demonstrates that same path on every selected client—Next.js, Expo, Electron, and WXT—using one contract and API client with platform-native UI. Frontends may import contracts and client-safe packages, never API services, database connections, or server environments. This stack uses managed Neon and intentionally has no local Postgres service.
210
-
211
- ## Add modules safely
212
-
213
- Run `add` only from a project containing `anhedral.json` schema v5 at the current generator version. If `doctor` identifies a supported older generator, preview and apply its transactional upgrade first:
214
-
215
- ```sh
216
- pnpm dlx anhedral@latest upgrade --dry-run
217
- pnpm dlx anhedral@latest upgrade
218
- pnpm dlx anhedral@latest add desktop extension
219
- pnpm dlx anhedral@latest add storage --dry-run
220
- ```
221
-
222
- Use `--dry-run` before a consequential add. Use `--json` when another program needs the plan; JSON failures include a stable `code` and a human-readable `error`. Use `--verbose` for interactive child-command diagnostics. Anhedral preserves recorded user-owned extension files and intentionally refuses modified managed files, symlinks, and unowned collisions; do not bypass those conflicts.
223
-
224
- For a 0.3 upgrade blocked by a modified managed file, preserve the change in source control, restore that file to its recorded 0.3 content, apply the upgrade, and port the product behavior into the corresponding 0.4 user-owned extension seam. Never approve the upgrade by rewriting manifest hashes.
225
-
226
- Require the manifest generator version to match the running CLI. Regenerate projects created by another version; do not add compatibility branches.
227
-
228
- Do not rewrite a generated project's README or custom workflows while adding modules. Preserve custom package fields and mergeable root configuration.
229
-
230
- `README.md` and `PRODUCTION.md` are user-owned and are not regenerated by
231
- `anhedral add`. After adding modules, do not assume those two files describe the
232
- new provider set. Reconcile them against `anhedral.json`, the managed project
233
- `SKILL.md`, root `package.json` scripts, and every package-local `.env.example`;
234
- then update the user-owned documentation explicitly with the developer's
235
- approval.
236
-
237
- For manual projects without a trustworthy schema-v5 `anhedral.json`, add modules with the manual reference instead. Never fabricate manifest ownership records, template provenance, UI installation records, or hashes. A manually authored workspace is valid as an application workspace but is not CLI-managed until it has been regenerated by a matching Anhedral version.
238
-
239
- ## Add UI components
240
-
241
- Use the unified UI wrapper for source-owned components:
242
-
243
- ```sh
244
- pnpm dlx anhedral@latest ui add button dialog
245
- pnpm dlx anhedral@latest ui add select --target mobile
246
- pnpm dlx anhedral@latest ui add data-table --target web --dry-run
247
- ```
248
-
249
- Anhedral routes web, desktop, and extension targets to shadcn/ui. It routes Expo mobile to React Native Reusables using the manifest's NativeWind or Uniwind selection. Omit `--target` to add the provider-specific implementation to every selected UI client. Provider writes occur in transaction staging and must pass the same ownership checks as module additions.
250
-
251
- ## Diagnose
252
-
253
- Dry-runs never perform transaction recovery. If a recovery journal is pending, rerun the original command without `--dry-run` before requesting a new preview.
254
-
255
- Check recorded files and hashes with:
256
-
257
- ```sh
258
- pnpm dlx anhedral@latest doctor
259
- pnpm dlx anhedral@latest doctor --json
260
- ```
261
-
262
- Explain that warnings can represent mergeable or user-owned drift, while modified or missing managed files make the project unhealthy.
263
-
264
- ## Configure the generated project
265
-
266
- Create only missing local environment files, fill selected provider values, and
267
- verify readiness:
268
-
269
- ```sh
270
- pnpm install
271
- pnpm first-run
272
- pnpm ready
273
- pnpm verify
274
- ```
275
-
276
- `first-run` copies only examples that exist for selected surfaces and never
277
- overwrites an existing file. Client packages use package-local runtime files
278
- (for example, `apps/web/.env.local` and `apps/desktop/.env`); the root
279
- `.env.example` is an inventory, not a runtime environment file.
280
-
281
- Common selected keys are:
282
-
283
- - Neon: `DATABASE_URL`
284
- - Clerk server: `CLERK_PUBLISHABLE_KEY`, `CLERK_SECRET_KEY`
285
- - Clerk clients: `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY`, `EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY`, `VITE_CLERK_PUBLISHABLE_KEY`
286
- - RevenueCat: `RC_WEBHOOK_SECRET`, `RC_SECRET_API_KEY`, `RC_ENTITLEMENT_ID`, `EXPO_PUBLIC_RC_*`
287
- - Ably billing synchronization: server-only `ABLY_API_KEY`; clients obtain scoped tokens from the API
288
- - R2: `BASE_URL`, `R2_ACCOUNT_ID`, `R2_ACCESS_KEY_ID`, `R2_SECRET_ACCESS_KEY`, `R2_BUCKET_NAME`, `R2_PREFIX=storage`, `R2_PROXY_READ_URL_TTL_SECONDS=600`; keep `CLOUDFLARE_API_TOKEN` operations/CI-only
289
- - Internal billing/storage jobs: high-entropy server-only `CRON_SECRET`
290
- - Electron updates: `DESKTOP_UPDATE_BASE_URL` in `apps/desktop/electron-builder.env`; the private R2 binding needs no credential in the packaged application
291
-
292
- Never commit real `.env` files. Keep `ANHEDRAL_DEMO=false` in production.
293
-
294
- Run only scripts present for installed modules. `pnpm dev` starts the primary
295
- product loop—web plus API when web is selected, otherwise the first selected
296
- surface. A multi-surface project also exposes `pnpm dev:all`; use it only when
297
- the user wants every selected development server. Typical focused commands
298
- include `dev:web`, `dev:mobile`, `dev:api`, `dev:desktop`, `dev:extension`,
299
- corresponding `verify:*` scripts, and database scripts for `db` projects.
300
-
301
- ## Understand deployment
302
-
303
- Treat the root `vercel.json` as the only Vercel Services configuration. It uses the current `services` schema, declares `apps/api` and `apps/web` as independent service roots, and routes `/api/(.*)` before the web catch-all. Vercel preserves the original path, so Fastify mounts its routes under `/api` locally and in deployment. Select the Services framework preset before deploying.
304
-
305
- For billing, keep Neon authoritative: RevenueCat reconciliation writes the subscription revision and realtime outbox atomically, Ably carries only per-user invalidations, and every client refetches the entitlement. Schedule the authenticated outbox flush every five minutes and verify purchase, webhook, reconnect, and foreground recovery paths.
306
-
307
- The generated billing substrate does not implement Stripe Checkout, Stripe
308
- webhooks, or a web purchase screen. RevenueCat's Test Store can exercise the
309
- entitlement flow without Apple, Google, or Stripe accounts. Native purchases
310
- require `native-subscriptions` plus Apple/Google store setup. If the user wants
311
- web subscriptions through RevenueCat Billing or a manual Stripe integration,
312
- treat that as an explicit product feature with additional account, checkout,
313
- webhook, tax, and customer-portal work; do not claim the generated billing
314
- module already completed it.
315
-
316
- Use `apps/mobile` for EAS, `pnpm desktop:build` for a current-host Electron artifact, matching platform-specific desktop scripts in CI, and `pnpm extension:zip` for the WXT archive.
317
-
318
- For `electron-updater`, provision the generated private R2 bucket and
319
- `desktop-updater` Worker at `updates.<domain>`. Keep the Worker custom domain and
320
- `DESKTOP_UPDATE_BASE_URL` identical. Build signed artifacts on each native
321
- platform and publish artifacts before mutable channel metadata.
322
-
323
- For storage, generate `apps/assets-private-proxy` plus `cloudflare/r2-cors.template.json` and keep the R2 bucket private. The Worker must be named `assets-private-proxy`, bind the normalized `<project>-assets` bucket as `ASSETS`, disable `workers.dev`, and own `assets.<domain>` as a Worker Custom Domain. Presigned uploads continue on the R2 S3 API hostname. Root every key below `R2_PREFIX=storage`; expose only `storage/confirmed/` through the Worker and reject staging plus `generation-inputs`. Authenticated private reads must verify upload ownership and return a presigned GET URL bounded by `R2_PROXY_READ_URL_TTL_SECONDS`.
324
-
325
- When explaining domains, distinguish DNS delegation from registrar transfer. A domain can use Cloudflare authoritative nameservers while remaining with its current registrar; transferring registration/billing to Cloudflare Registrar is optional and subject to eligibility locks. Keep Vercel app/API A or CNAME records DNS-only in Cloudflare, while Worker Custom Domains stay Cloudflare-managed and proxied.
326
-
327
- ## Verify outcomes
328
-
329
- After generation or repair, run the strongest practical checks:
330
-
331
- ```sh
332
- pnpm typecheck
333
- pnpm verify
334
- pnpm build
335
- ```
336
-
337
- Use `pnpm --filter ./apps/api test` when the API exists. If verification fails, report the exact package and command rather than replacing generated ownership records manually.
338
-
339
- For exact representative trees, consult `docs/output-tree-contract.md` in the Anhedral repository. During manual construction, also compare the finished workspace with the file matrix in [docs/references/manual-scaffolding.md](docs/references/manual-scaffolding.md); every selected conditional file must exist and every unselected provider integration must be absent.
@@ -1,8 +0,0 @@
1
- <svg width="78" height="64" viewBox="0 0 78 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <path d="M38.5717 0L6.29575 59.0738L0 54.6586L29.8639 0H38.5717ZM39.0214 0L71.2973 59.0738L77.5931 54.6586L47.7292 0H39.0214Z" fill="url(#ably-symbol-a)"/>
3
- <path d="M70.8477 59.4213L38.7966 34.3201L6.74548 59.4213L13.2865 64L38.7966 44.0294L64.3066 64L70.8477 59.4213Z" fill="url(#ably-symbol-b)"/>
4
- <defs>
5
- <linearGradient id="ably-symbol-a" x1="10.9472" y1="74.8439" x2="64.9206" y2="14.9005" gradientUnits="userSpaceOnUse"><stop stop-color="#FF5416"/><stop offset="0.461" stop-color="#FF4712"/><stop offset="1" stop-color="#FF0000"/></linearGradient>
6
- <linearGradient id="ably-symbol-b" x1="21.4169" y1="78.7187" x2="53.3166" y2="43.2904" gradientUnits="userSpaceOnUse"><stop stop-color="#FF5416"/><stop offset="0.461" stop-color="#FF4712"/><stop offset="1" stop-color="#FF0000"/></linearGradient>
7
- </defs>
8
- </svg>
package/bin/anhedral.js DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- import '../dist/bin.js';
@@ -1,28 +0,0 @@
1
- import { type ModuleId } from './modules.js';
2
- export type EnvironmentContribution = {
3
- readonly owner: ModuleId;
4
- readonly name: string;
5
- readonly defaultValue: string;
6
- };
7
- export type CronContribution = {
8
- readonly owner: ModuleId;
9
- readonly id: string;
10
- readonly path: string;
11
- readonly schedule: string;
12
- };
13
- export type ModuleCompositionContribution = {
14
- readonly module: ModuleId;
15
- readonly environment: readonly Omit<EnvironmentContribution, 'owner'>[];
16
- readonly crons: readonly Omit<CronContribution, 'owner'>[];
17
- };
18
- export type CompositionModel = {
19
- readonly modules: readonly ModuleId[];
20
- readonly environment: readonly EnvironmentContribution[];
21
- readonly crons: readonly CronContribution[];
22
- };
23
- export type CompositionErrorCode = 'DUPLICATE_ENVIRONMENT_CONTRIBUTION' | 'DUPLICATE_CRON_CONTRIBUTION';
24
- export declare class CompositionError extends Error {
25
- readonly code: CompositionErrorCode;
26
- constructor(code: CompositionErrorCode, message: string);
27
- }
28
- export declare function collectModuleContributions(requestedModules: readonly ModuleId[], overrides?: Readonly<Partial<Record<ModuleId, ModuleCompositionContribution>>>): CompositionModel;
@@ -1,125 +0,0 @@
1
- import { resolveModules } from './modules.js';
2
- export class CompositionError extends Error {
3
- code;
4
- constructor(code, message) {
5
- super(message);
6
- this.code = code;
7
- this.name = 'CompositionError';
8
- }
9
- }
10
- const EMPTY = Object.freeze([]);
11
- const MODULE_CONTRIBUTIONS = Object.freeze({
12
- web: Object.freeze({ module: 'web', environment: EMPTY, crons: EMPTY }),
13
- mobile: Object.freeze({ module: 'mobile', environment: EMPTY, crons: EMPTY }),
14
- api: Object.freeze({
15
- module: 'api',
16
- environment: Object.freeze([
17
- { name: 'ANHEDRAL_DEMO', defaultValue: 'false' },
18
- { name: 'PORT', defaultValue: '8787' },
19
- { name: 'TRUST_PROXY_HOPS', defaultValue: '0' },
20
- { name: 'CORS_ORIGINS', defaultValue: '' },
21
- ]),
22
- crons: EMPTY,
23
- }),
24
- desktop: Object.freeze({ module: 'desktop', environment: EMPTY, crons: EMPTY }),
25
- extension: Object.freeze({ module: 'extension', environment: EMPTY, crons: EMPTY }),
26
- db: Object.freeze({
27
- module: 'db',
28
- environment: Object.freeze([{ name: 'DATABASE_URL', defaultValue: 'YOUR_NEON_POSTGRES_URL' }]),
29
- crons: EMPTY,
30
- }),
31
- auth: Object.freeze({
32
- module: 'auth',
33
- environment: Object.freeze([
34
- { name: 'CLERK_PUBLISHABLE_KEY', defaultValue: 'pk_test_***' },
35
- { name: 'CLERK_SECRET_KEY', defaultValue: 'sk_test_***' },
36
- ]),
37
- crons: EMPTY,
38
- }),
39
- billing: Object.freeze({
40
- module: 'billing',
41
- environment: Object.freeze([
42
- { name: 'RC_WEBHOOK_SECRET', defaultValue: '' },
43
- { name: 'RC_SECRET_API_KEY', defaultValue: '' },
44
- { name: 'RC_ENTITLEMENT_ID', defaultValue: 'pro' },
45
- { name: 'ABLY_API_KEY', defaultValue: '' },
46
- { name: 'CRON_SECRET', defaultValue: '' },
47
- ]),
48
- crons: Object.freeze([
49
- { id: 'realtime-outbox', path: '/api/internal/realtime/flush', schedule: '*/5 * * * *' },
50
- ]),
51
- }),
52
- storage: Object.freeze({
53
- module: 'storage',
54
- environment: Object.freeze([
55
- { name: 'BASE_URL', defaultValue: 'http://localhost:8787' },
56
- { name: 'R2_ACCOUNT_ID', defaultValue: '' },
57
- { name: 'R2_ACCESS_KEY_ID', defaultValue: '' },
58
- { name: 'R2_SECRET_ACCESS_KEY', defaultValue: '' },
59
- { name: 'R2_BUCKET_NAME', defaultValue: '' },
60
- { name: 'R2_PREFIX', defaultValue: 'storage' },
61
- { name: 'R2_PROXY_READ_URL_TTL_SECONDS', defaultValue: '600' },
62
- { name: 'CLOUDFLARE_API_TOKEN', defaultValue: '' },
63
- { name: 'CRON_SECRET', defaultValue: '' },
64
- ]),
65
- crons: Object.freeze([
66
- { id: 'storage-cleanup', path: '/api/internal/storage/cleanup', schedule: '0 3 * * *' },
67
- ]),
68
- }),
69
- 'native-subscriptions': Object.freeze({
70
- module: 'native-subscriptions',
71
- environment: EMPTY,
72
- crons: EMPTY,
73
- }),
74
- 'electron-updater': Object.freeze({
75
- module: 'electron-updater',
76
- environment: Object.freeze([
77
- { name: 'DESKTOP_UPDATE_BASE_URL', defaultValue: 'https://updates.example.com' },
78
- ]),
79
- crons: EMPTY,
80
- }),
81
- });
82
- function collectEnvironmentFromRegistry(modules, registry) {
83
- const byName = new Map();
84
- for (const module of modules) {
85
- for (const contribution of registry[module].environment) {
86
- const candidate = Object.freeze({ owner: module, ...contribution });
87
- const previous = byName.get(candidate.name);
88
- if (previous && previous.defaultValue !== candidate.defaultValue) {
89
- throw new CompositionError('DUPLICATE_ENVIRONMENT_CONTRIBUTION', `Environment variable ${candidate.name} has incompatible defaults from ${previous.owner} and ${module}.`);
90
- }
91
- if (!previous)
92
- byName.set(candidate.name, candidate);
93
- }
94
- }
95
- return Object.freeze([...byName.values()]);
96
- }
97
- function collectCronsFromRegistry(modules, registry) {
98
- const byId = new Map();
99
- for (const module of modules) {
100
- for (const contribution of registry[module].crons) {
101
- const candidate = Object.freeze({ owner: module, ...contribution });
102
- const previous = byId.get(candidate.id);
103
- if (previous && (previous.path !== candidate.path || previous.schedule !== candidate.schedule)) {
104
- throw new CompositionError('DUPLICATE_CRON_CONTRIBUTION', `Cron ${candidate.id} has incompatible definitions from ${previous.owner} and ${module}.`);
105
- }
106
- if (!previous)
107
- byId.set(candidate.id, candidate);
108
- }
109
- }
110
- return Object.freeze([...byId.values()]);
111
- }
112
- export function collectModuleContributions(requestedModules, overrides = {}) {
113
- const modules = resolveModules(requestedModules).resolvedModules;
114
- const contributions = Object.freeze({ ...MODULE_CONTRIBUTIONS, ...overrides });
115
- for (const module of modules) {
116
- if (contributions[module].module !== module) {
117
- throw new Error(`Composition contribution key ${module} must declare the same module ID.`);
118
- }
119
- }
120
- return Object.freeze({
121
- modules,
122
- environment: collectEnvironmentFromRegistry(modules, contributions),
123
- crons: collectCronsFromRegistry(modules, contributions),
124
- });
125
- }
@@ -1,12 +0,0 @@
1
- export { APP_MODULES, DEFAULT_MODULE_DEFINITIONS, DEFAULT_MODULE_REGISTRY, FEATURE_MODULES, MODULE_IDS, ModuleRegistryError, ModuleResolutionError, createModuleRegistry, isModuleId, resolveModules, } from './modules.js';
2
- export type { AppModule, DependencyEdge, FeatureModule, ModuleDefinition, ModuleId, ModuleKind, ModuleRegistry, ModuleRegistryErrorCode, ModuleResolution, ModuleResolutionErrorCode, } from './modules.js';
3
- export { FILE_OWNERSHIP_CLASSES, PlanBuildError, buildGenerationPlan, hashContent, isFileOwnershipClass, isSha256, normalizePlanPath, writePolicyForOwnership, } from './plan.js';
4
- export type { BuildGenerationPlanInput, FileOwner, FileOwnershipClass, FileWritePolicy, GenerationOperation, GenerationPlan, ModulePlanContribution, PlanBuildErrorCode, PlanFileInput, PlannedFile, } from './plan.js';
5
- export { MANIFEST_SCHEMA_VERSION, ManifestValidationError, createManifest, readManifest, serializeManifest, } from './manifest.js';
6
- export { TEMPLATE_IDS, isTemplateId, templateIdsForModules, } from './templates.js';
7
- export type { TemplateId, TemplateProvenance, TemplateProvenanceMap, } from './templates.js';
8
- export { CompositionError, collectModuleContributions, } from './contributions.js';
9
- export type { CompositionErrorCode, CompositionModel, CronContribution, EnvironmentContribution, ModuleCompositionContribution, } from './contributions.js';
10
- export type { CreateManifestInput, ManifestFileRecord, ManifestToolchain, ManifestValidationErrorCode, ProjectManifest, } from './manifest.js';
11
- export { NATIVE_STYLING_LIBRARIES, UI_PROVIDERS, UI_TARGETS, buildUiInstallCommands, isNativeStylingLibrary, isUiProvider, isUiTarget, mergeUiInstalls, normalizeUiComponentName, parseUiComponentList, providerForTarget, registrySourceFor, resolveUiInstalls, uiInstallKey, } from '../ui.js';
12
- export type { NativeStylingLibrary, UiComponentInstall, UiInstallCommand, UiProvider, UiTarget, } from '../ui.js';
@@ -1,6 +0,0 @@
1
- export { APP_MODULES, DEFAULT_MODULE_DEFINITIONS, DEFAULT_MODULE_REGISTRY, FEATURE_MODULES, MODULE_IDS, ModuleRegistryError, ModuleResolutionError, createModuleRegistry, isModuleId, resolveModules, } from './modules.js';
2
- export { FILE_OWNERSHIP_CLASSES, PlanBuildError, buildGenerationPlan, hashContent, isFileOwnershipClass, isSha256, normalizePlanPath, writePolicyForOwnership, } from './plan.js';
3
- export { MANIFEST_SCHEMA_VERSION, ManifestValidationError, createManifest, readManifest, serializeManifest, } from './manifest.js';
4
- export { TEMPLATE_IDS, isTemplateId, templateIdsForModules, } from './templates.js';
5
- export { CompositionError, collectModuleContributions, } from './contributions.js';
6
- export { NATIVE_STYLING_LIBRARIES, UI_PROVIDERS, UI_TARGETS, buildUiInstallCommands, isNativeStylingLibrary, isUiProvider, isUiTarget, mergeUiInstalls, normalizeUiComponentName, parseUiComponentList, providerForTarget, registrySourceFor, resolveUiInstalls, uiInstallKey, } from '../ui.js';
@@ -1,53 +0,0 @@
1
- import { type ModuleId, type ModuleRegistry } from './modules.js';
2
- import { type FileOwner, type FileOwnershipClass, type GenerationPlan } from './plan.js';
3
- import { type TemplateProvenanceMap } from './templates.js';
4
- import { type NativeStylingLibrary, type UiComponentInstall } from '../ui.js';
5
- export declare const MANIFEST_SCHEMA_VERSION: 5;
6
- export type ManifestToolchain = 'stable' | 'latest';
7
- export type ManifestFileRecord = {
8
- readonly owner: FileOwner;
9
- readonly ownership: FileOwnershipClass;
10
- readonly hash: string;
11
- /** Normalized Unix permission bits (0o000-0o777), or null on platforms without portable modes. */
12
- readonly mode: number | null;
13
- };
14
- export type ProjectManifest = {
15
- readonly schemaVersion: typeof MANIFEST_SCHEMA_VERSION;
16
- readonly generatorVersion: string;
17
- readonly project: {
18
- readonly name: string;
19
- readonly displayName: string;
20
- };
21
- /** The complete, canonically ordered module dependency closure. */
22
- readonly modules: readonly ModuleId[];
23
- readonly toolchain: ManifestToolchain;
24
- /** Immutable template catalog entries used to seed the generated workspace. */
25
- readonly templates: TemplateProvenanceMap;
26
- readonly ui: {
27
- readonly nativeStyling: NativeStylingLibrary;
28
- readonly components: readonly UiComponentInstall[];
29
- };
30
- readonly files: Readonly<Record<string, ManifestFileRecord>>;
31
- };
32
- export type CreateManifestInput = {
33
- readonly generatorVersion: string;
34
- readonly project: {
35
- readonly name: string;
36
- readonly displayName: string;
37
- };
38
- readonly plan: GenerationPlan;
39
- readonly toolchain: ManifestToolchain;
40
- readonly templates: TemplateProvenanceMap;
41
- readonly nativeStyling?: NativeStylingLibrary;
42
- readonly components?: readonly UiComponentInstall[];
43
- readonly registry?: ModuleRegistry;
44
- };
45
- export type ManifestValidationErrorCode = 'INVALID_JSON' | 'INVALID_MANIFEST' | 'INVALID_SCHEMA_VERSION' | 'FUTURE_SCHEMA_VERSION' | 'UNKNOWN_MODULE' | 'DUPLICATE_MODULE' | 'INVALID_MODULE_CLOSURE' | 'INVALID_FILE_RECORD';
46
- export declare class ManifestValidationError extends Error {
47
- readonly code: ManifestValidationErrorCode;
48
- readonly path: string;
49
- constructor(code: ManifestValidationErrorCode, message: string, path: string);
50
- }
51
- export declare function readManifest(input: string | unknown, registry?: ModuleRegistry): ProjectManifest;
52
- export declare function createManifest(input: CreateManifestInput): ProjectManifest;
53
- export declare function serializeManifest(manifest: ProjectManifest): string;