From dbd705f5bdc3d80014c98eb1b5146fdaeb4d5038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adel=20Rodr=C3=ADguez?= Date: Fri, 31 Jul 2026 16:26:47 -0400 Subject: [PATCH] chore: delete completed plans and polish landing page copy --- .github/assets/init-logo.png | Bin 12828 -> 12690 bytes .plans/01-cli-correctness-fixes.md | 59 -- .plans/02-package-consolidation.md | 73 -- .plans/03-app-hygiene.md | 74 -- .plans/04-cli-manifest-and-setup.md | 250 ------- .plans/05-convex-backend-example.md | 40 - .plans/07-template-recipes.md | 298 -------- .plans/09-cli-effect-v4-and-tooling.md | 104 --- .plans/10-marketing-site.md | 185 ----- .plans/12-agents-context-docs-refactor.md | 138 ---- .plans/13-descope-cli-to-scripts.md | 174 ----- .plans/14-connect-backend-generator.md | 681 ------------------ .plans/15-files-sdk-integration.md | 166 ----- .plans/TRACKER.md | 53 -- README.md | 3 +- apps/api/package.json | 2 +- apps/api/src/routes/v1/index.ts | 4 +- apps/app/package.json | 2 +- .../src/shared/components/locale-toggle.tsx | 6 +- apps/desktop/package.json | 2 +- .../local-files/components/file-editor.tsx | 14 +- apps/desktop/src/routes/index.tsx | 8 +- .../src/shared/components/locale-toggle.tsx | 6 +- apps/docs/astro.config.ts | 2 +- apps/docs/package.json | 2 +- apps/docs/src/middleware.ts | 8 + .../components/localized-greeting.astro | 9 +- apps/extension/package.json | 2 +- .../features/demo/components/popup-demo.tsx | 10 +- apps/mobile/package.json | 2 +- apps/mobile/src/app/index.tsx | 15 +- apps/web/astro.config.ts | 2 +- apps/web/package.json | 2 +- .../landing/components/footer-controls.tsx | 24 +- .../features/landing/components/footer.astro | 4 +- .../features/landing/components/hero.astro | 39 +- .../landing/components/ship-targets.astro | 125 ++++ .../components/technology-marquee.astro | 6 +- apps/web/src/middleware.ts | 8 +- apps/web/src/pages/404.astro | 4 +- apps/web/src/pages/[lang]/404.astro | 5 +- apps/web/src/pages/[lang]/index.astro | 3 +- apps/web/src/shared/components/layout.astro | 13 +- bun.lock | 30 +- docs/internationalization.md | 48 ++ tooling/internationalization/README.md | 8 + tooling/internationalization/messages/en.json | 72 +- tooling/internationalization/messages/es.json | 72 +- tooling/internationalization/package.json | 2 +- .../project.inlang/settings.json | 3 +- 50 files changed, 385 insertions(+), 2477 deletions(-) delete mode 100644 .plans/01-cli-correctness-fixes.md delete mode 100644 .plans/02-package-consolidation.md delete mode 100644 .plans/03-app-hygiene.md delete mode 100644 .plans/04-cli-manifest-and-setup.md delete mode 100644 .plans/05-convex-backend-example.md delete mode 100644 .plans/07-template-recipes.md delete mode 100644 .plans/09-cli-effect-v4-and-tooling.md delete mode 100644 .plans/10-marketing-site.md delete mode 100644 .plans/12-agents-context-docs-refactor.md delete mode 100644 .plans/13-descope-cli-to-scripts.md delete mode 100644 .plans/14-connect-backend-generator.md delete mode 100644 .plans/15-files-sdk-integration.md delete mode 100644 .plans/TRACKER.md create mode 100644 apps/docs/src/middleware.ts create mode 100644 apps/web/src/features/landing/components/ship-targets.astro create mode 100644 docs/internationalization.md create mode 100644 tooling/internationalization/README.md diff --git a/.github/assets/init-logo.png b/.github/assets/init-logo.png index ad8044d5ed201d6169c2c11f0c6cf940bba7ec6f..162aeae3423b414c0b0b1aa8999249cd423776f0 100644 GIT binary patch literal 12690 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 literal 12828 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 diff --git a/.plans/01-cli-correctness-fixes.md b/.plans/01-cli-correctness-fixes.md deleted file mode 100644 index 8c2ce075..00000000 --- a/.plans/01-cli-correctness-fixes.md +++ /dev/null @@ -1,59 +0,0 @@ -# Plan 01 — CLI correctness fixes - -Small, independent bug fixes in `cli/` (the `init-now` CLI). No feature work — that is plan 04/06. The CLI is an Effect-based app (`@effect/cli`), bundled with bunup, published to npm. It has its own `bun.lock` and tests (`cd cli && bun test`). - -## Bugs to fix - -### 1. Scaffold overwrite is broken - -`cli/src/index.ts:64-73`: the root command prompts "directory exists, overwrite?" but never clears the directory nor passes `force: true` to giget's `downloadTemplate` (`cli/src/index.ts:76`). giget throws when the target dir is non-empty, so answering "yes" produces a `DownloadFailed` error. - -Fix: on confirmed overwrite, pass `force: true` (or `forceClean: true` if full replacement is desired — check giget docs) to `downloadTemplate`. - -### 2. Wrong `--version` output - -`cli/src/index.ts:113` hardcodes `"2.0.0"` in `Command.run` while `cli/package.json` says `2.0.2`. Read the version from `cli/package.json` at build time (bunup define/macro) or import it, so there is a single source of truth. (Plan 09 formalizes this as a Bun macro following `../adamantite/src/lib/shared/version.macro.ts` — a plain JSON import is fine here if 09 hasn't landed.) - -### 3. `compareVersions` breaks on the actual tag format - -Release tags are `init@v1.1.0` (release-please config: `tag-separator: "@"`, `include-component-in-tag: true` in `release-please-config.json`). `compareVersions` (`cli/src/utils.ts:126-146`) only strips a leading `v`, so `"init@v1.1.0".split(".")` yields `[NaN, 1, 0]` — NaN comparisons silently return equal, and the major version is ignored. - -Fix: normalize tags by stripping the `@v` prefix (regex like `/^.*@v?/`) before comparing. Also make non-numeric segments an explicit error or treat the version as unknown instead of silently comparing NaN. - -### 4. `.template-version.json` format corruption - -The file starts as `{".": "1.1.0"}` (it doubles as the release-please manifest). `updateTemplateVersion` (`cli/src/utils.ts:148-153`) writes the raw tag (`init@v1.1.0`) after an update, producing a format neither release-please nor `getVersion` (`utils.ts:83-101`) expects on the next run. - -Fix: always store the bare semver (`1.1.0`). Normalize when writing AND when reading (defensive, for projects already corrupted). - -### 5. `check` unreachable branch - -`cli/src/commands/check.ts:24-27`: `if (!latestRelease)` is dead — `getLatestRelease` fails with `VersionCheckFailed` (handled via `catchTag` at `check.ts:59`), it never succeeds with `null`. Delete the branch. - -### 6. Duplicated `updatePackageJson` - -`cli/src/commands/setup.ts:42-52` does regex string-replacement on package.json (fragile: depends on exact `"name": "init"` formatting, rewrites first `"version"` match). `cli/src/commands/rename.ts:11-21` does it properly via JSON parse/serialize. Extract the JSON-based implementation into `cli/src/utils.ts` and use it in both. - -### 7. Stale exclusion/cleanup lists - -- `cli/src/utils.ts:175`: `EXCLUDED_DIRS` contains `"scripts/template"`, which no longer exists. -- `cli/src/commands/setup.ts:101`: `cleanupInternalFiles` removes a root `__tests__` dir that no longer exists, and misses newer internal files (`.github/workflows/opencode.yml`, `cli/.claude`, `.plans/`). - -Fix: refresh both lists against the current template tree. (Plan 04 replaces these with a manifest — keep this fix minimal.) - -### 8. Minor dead code - -- `cli/src/utils.ts:78`: `export type ReleaseInfo` — unused, delete or stop exporting. -- `cli/src/commands/setup.ts:168`: `const packages = selectedPackages` pointless alias. - -## Acceptance criteria - -- `init-now ` into an existing non-empty dir with "overwrite: yes" succeeds. -- `init-now --version` prints the version from `cli/package.json`. -- `compareVersions("init@v2.0.0", "1.1.0")` reports an update available; add unit tests in `cli/src/__tests__/` covering tag formats: `1.1.0`, `v1.1.0`, `init@v1.1.0`. -- After a simulated update, `.template-version.json` contains `{".": ""}`. -- `cd cli && bun test` passes; `bun run check` at root passes. - -## Out of scope - -Manifest generation, setup/backend selection, update-command semantics (plans 04 and 06). npm publish automation (plan 08). Effect v4 migration, adamantite adoption, CLI CI (plan 09) — keep these fixes on the current Effect v3 APIs so they can land immediately. diff --git a/.plans/02-package-consolidation.md b/.plans/02-package-consolidation.md deleted file mode 100644 index 0cbbf926..00000000 --- a/.plans/02-package-consolidation.md +++ /dev/null @@ -1,73 +0,0 @@ -# Plan 02 — Package consolidation & dead-code sweep - -Consolidate micro-packages and remove dead code from `packages/`. Decisions below are final (agreed with the maintainer) unless marked "confirm first". - -Anything removed that is still _useful as copy-once code_ must be recorded in -`.plans/07-template-recipes.md` under "Template recipe backlog" instead of being -lost—append to that list as you delete. - -## 1. Merge `@init/error` into `@init/core` as `@init/core/errors` - -`packages/error` (85 LOC, faultier-based tagged errors) disappears; `packages/core` (currently a placeholder with literal `unused()` exports) becomes the home for domain primitives. - -Steps: - -1. Delete the root `packages/core/src/index.ts` placeholder. Keep a folder-based example feature at `packages/core/src/features/example/index.ts` and export `"./features/*": "./src/features/*/index.ts"`, so features expose one or more files through `@init/core/features/` without a core root barrel. -2. Move `packages/error/src/*` → `packages/core/src/errors/` (keep the domain split: `auth.ts`, `email.ts`, `utils.ts`, barrel `index.ts`). Add `faultier` to core's dependencies. -3. Core exports `"./errors"` only. Do NOT add a root barrel or replicate error's `"./*"` wildcard. -4. Update all consumers from `@init/error` → `@init/core/errors`: - - `apps/app/src/shared/server/middleware.ts` - - `apps/app/src/shared/server/serialization.ts` - - `packages/email/src/client.ts` - - `packages/backend/src/functions/models/documents.ts` - - `packages/backend/src/functions/shared/convex.ts` - - `packages/utils/src/assert.ts` — being deleted anyway (see §3) - - package.json deps in: `apps/api` (declared but unused in src — just remove the dep), `apps/app`, `packages/email`, `packages/utils`, `packages/backend` -5. Delete `packages/error/`. Update `cli/src/workspaces.ts` (remove the `error` entry, add/keep `core`) and the knip config if it references error. - -## 2. Delete `@init/storage`, decouple storage records from the implementation - -`packages/storage` (51 LOC) has no runtime consumers. Only `packages/db/src/schema.ts:1-2` imports it — types only (`StorageBucket` from `buckets.ts`, `MimeType` from `helpers.ts`). Future storage support will use `files-sdk` directly from the API composition root and its client/backend integrations; it will not recreate an `@init/storage` wrapper. - -Steps: - -1. Remove the `StorageBucket` and `MimeType` imports and their `.$type<...>()` annotations from `packages/db/src/schema.ts`; keep `bucket` and `mimeType` as plain text columns. Do not move these implementation types into db or add `mime` there. Bucket names are deployment configuration, and MIME values can include parameters such as `text/plain; charset=utf-8` that the old static type excludes. -2. Replace the `storage_provider` PostgreSQL enum with a required text `provider` column with no default. Do not encode `files-sdk`'s provider catalog as a database enum or default to S3: generated projects should choose their provider explicitly and may add their own constraint if desired. Delete the existing migration history and regenerate one baseline migration from the final template schema. -3. Remove `@init/storage` from `packages/db/package.json`. No replacement db dependency is needed. -4. Delete the unused runtime helpers rather than preserving their implementation: `files-sdk` replaces the S3 factory and provides content-type facilities; the current silent key sanitizer is not a sufficient authorization or key-policy boundary. Plan 07's template recipe backlog contains the replacement `files-sdk` integration recipe, not a copy of these helpers. -5. Delete `packages/storage/`. Remove the `s3` env preset from `packages/env/src/presets.ts` (its only purpose was this package); the future template recipe adds only the selected provider's validated variables. Update `cli/src/workspaces.ts` and knip config. - -## 3. Dead-code sweep - -Delete, updating package.json deps/exports accordingly: - -- `packages/utils/src/assert.ts` and `packages/utils/src/codec.ts` — zero importers. Add both to the template recipe backlog. -- `unstorage` dependency in `packages/utils/package.json` — nothing imports it. -- `packages/env/src/presets.ts`: delete `railway`, `openai`, `anthropic` presets (no corresponding package/consumer). KEEP `convex` (serves `packages/backend`), `posthog` (serves `packages/analytics`), `stripe` (payments), `resend` (email). Delete `s3` per §2. -- `packages/observability`: KEEP the uptime component, its `./uptime` export, and `@openstatus/react` dependency as an intentional selectable capability. -- `packages/auth`: KEEP all integrations, compatibility exports, and `createErrorHandler`. These are intentional selectable authentication capabilities even when the template has no default consumer. -- `packages/ui`: remove the `./hooks/*` export from package.json (`use-mobile.ts` is internal to sidebar — keep the file). Fix `src/components/theme.tsx` importing from its own package name `@init/ui/...` — use `#` subpath imports like the rest of the package. -- `packages/db/src/helpers.ts`: KEEP the `increment` and `decrement` helpers as useful Drizzle primitives. -- `packages/analytics`: KEEP the package and the platform-specific `useIdentifyUser` implementations in `src/product/react.ts` and `src/product/expo.ts`; do not deduplicate them. -- `packages/email/src/client.ts`: dedupe the verbatim `MOCK_RESEND` preview logic between `sendEmail` and `batchEmails`. -- `packages/payments/src/helpers.ts`: delete `createAgentToolkit` (Stripe AI Agent Toolkit) and its deps; add it to the template recipe backlog (confirmed by maintainer). Keep the rest of payments. -- `packages/ai`: KEEP as-is (selectable unit, maintainer decision). - -## 3b. Root-level dead infrastructure - -- **Delete `scripts/`** (decided — leftover from previous tooling work): `scripts/index.ts` is an empty yargs shell, `scripts/helpers.ts` a 5-line identity helper. Remove the `"scripts"` entry from root `package.json` scripts, the `scripts/**` entries in `knip.config.ts`, and mentions in `AGENTS.md`/`docs/project-structure.md` (the "scripts" folder description). -- **Prune orphaned root devDependencies**: with `scripts/` gone and the CLI standalone, verify and remove unused root devDeps — `yargs`, `@types/yargs`, and check whether `effect`, `@effect/cli`, `@effect/platform`, `@effect/platform-bun`, `@octokit/rest` are used by anything at root (turbo generators, etc.); remove those that aren't (`bun run analyze` should confirm). -- `packages/kv`: refactor the `class` wrapper to a factory function, per the repo's own "avoid classes" style rule (behavior unchanged). - -## 4. Keep `cli/src/workspaces.ts` consistent - -Every package added/removed above must be reflected in `cli/src/workspaces.ts` and its test (`cli/src/__tests__/workspaces.test.ts`) so `init-now setup` doesn't offer deleted packages. (Plan 04 replaces this file with a generated manifest; here, just keep it truthful.) - -## Acceptance criteria - -- `packages/error` and `packages/storage` no longer exist; `rg "@init/error|@init/storage" --glob '!node_modules'` returns nothing. -- Db storage records use plain text for provider, bucket, and MIME type and have no dependency on `mime`, `files-sdk`, or provider-specific types. -- The template has exactly one baseline database migration; the storage provider is required text with no enum or default. -- `@init/core/errors` contains the domain errors, with no root barrel or `unused()` placeholders. -- `bun run check`, `bun run analyze` (knip should report fewer issues, none new), `bun run check:monorepo`, `bun test`, and `cd cli && bun test` all pass. -- The template recipe backlog in `.plans/07-template-recipes.md` lists every useful deletion. diff --git a/.plans/03-app-hygiene.md b/.plans/03-app-hygiene.md deleted file mode 100644 index 1d03246d..00000000 --- a/.plans/03-app-hygiene.md +++ /dev/null @@ -1,74 +0,0 @@ -# Plan 03 — App hygiene - -Fix half-wired integrations, placeholders, and gaps across `apps/`. Guiding principle: **no external services required** — everything here must work with local dev alone (docker compose, mock modes). Sentry/PostHog/Resend remain opt-in; we add the local seams they'd plug into. - -## 1. Error boundaries (no external services needed) - -- `apps/app/src/router.tsx`: add a `defaultErrorComponent` (styled with `@init/ui`, offers "try again" via router invalidate). Currently only `defaultNotFoundComponent` is set — uncaught route errors white-screen. -- `apps/desktop/src/router` setup: same treatment. -- `apps/mobile`: export `ErrorBoundary` from the root layout (`src/app/_layout.tsx`) per expo-router convention, with a minimal fallback screen using `#shared/components/ui`. - -These are plain React fallbacks. Log through `@init/observability` logger (already local-safe); do not wire Sentry. - -## 2. Fix the forgot-password lie - -`apps/app/src/features/auth/server/functions.ts:25-37` mocks `forgotPassword` with a `setTimeout`, and the UI (`apps/app/src/features/auth/components/forgot-password-form.tsx`) shows a success toast for an email never sent — while `@init/email` sits unused. - -Fix (agreed direction): - -1. Wire better-auth's `sendResetPassword` in `apps/api/src/shared/auth.ts` using `@init/email` (`sendEmail`). `@init/email` already has a `MOCK_RESEND` mode that logs/previews instead of sending — local dev needs no Resend key. -2. Replace the mocked server function so the flow goes through better-auth's actual reset endpoint. -3. Local verification is `MOCK_RESEND` logging/preview — decided: no mailpit/SMTP catcher (`@init/email` is Resend HTTP API; an SMTP path just for dev isn't worth it). -4. Remove the stray copied comment "// Add your global server functions here" from the feature's functions file. - -## 3. Dead/broken env modules - -- `apps/web/src/shared/env.ts`: never imported and contains a literal `TEST_VAR` placeholder. Either wire it into `astro.config.ts` via `@tooling/env`'s `ensureEnv` (like other apps) with real vars, or delete the file until web has env needs. Prefer wiring it — env validation is a claimed template feature. -- `apps/extension/src/shared/env.ts`: keep the `ensureEnv` wiring and an empty schema as the standard validation seam, but remove the API URL and `.env.template`; the extension has no environment variables or API integration by default. - -## 4. Placeholder & dead-weight cleanup - -- `apps/api/src/functions/example.ts`: entirely commented-out, exports `null`. Delete (violates repo comment policy). `apps/api/src/routes/workflows.ts` already defines its own demo function. -- `apps/api`: collapse the three overlapping demos (`/v1/hello`, `hello` tRPC procedure, `/ping`) to one exemplar of each transport (one REST route with OpenAPI schema, one tRPC procedure). Keep `/health`. -- `apps/app/src/routes/api/test.ts`: delete. -- `` rendered unconditionally in `apps/app/src/routes/__root.tsx` and `apps/desktop/src/routes/__root.tsx`: gate on `import.meta.env.DEV`. -- `apps/extension`: remove the three unused `@webext-core/*` deps (`proxy-service`, `job-scheduler`, `isolated-element`) from package.json; delete the empty `src/entrypoints/content.ts` (matches `*://*.google.com/*` and does nothing) or give it a one-line real example. -- `apps/desktop/src/shared/assets/react.svg`, `apps/extension/src/shared/assets/react.svg`: delete leftover starter assets. -- `apps/mobile`: delete unused `src/shared/components/ui/alert.tsx`, `ui/toggle.tsx`, `external-link.tsx`, and stock Expo images (`react-logo*.png`, `partial-react-logo.png`). -- `apps/mobile/android/sentry.properties` (and iOS equivalent): remove hardcoded `defaults.org=metaideas` / `init-mobile` values (genericize/placeholder them). Decided: `ios/`/`android/` **stay committed** — do not gitignore them; just fix the leaked template-author values. -- `apps/docs`: minimum viable de-starterization — fix the social link in `astro.config.ts` (points at `https://github.com/withastro/starlight`), remove the unused React integration (`@astrojs/react`), replace stock example content with 1-2 short pages about the scaffolded project, and use one page or component to demonstrate the shared Paraglide catalog alongside Starlight's own navigation i18n. Keep the app. -- `apps/web`: replace the meta-refresh redirect in `src/pages/index.astro` with a proper redirect (Astro `redirects` config or middleware); add root `404.astro`; add `@astrojs/sitemap` + RSS for the blog (standard marketing-site table stakes, no external services). -- Paraglide i18n: make the shared catalog an intentional, demonstrated capability in every app. Keep the existing `app` locale toggle and `web` locale routes, then add the smallest native example for each remaining runtime: - - `desktop`: a locale toggle that translates the local-filesystem example's labels and feedback. - - `extension`: localized popup text with a locale toggle persisted through extension storage. - - `mobile`: a locale toggle and one translated screen using the generated runtime/messages. - - `docs`: one localized custom page or component backed by Paraglide; Starlight continues to own documentation-shell/navigation i18n. - - `api`: compile the shared catalog for the server and localize one exemplar REST response from `Accept-Language`, with an explicit base-locale fallback. - - Keep translation source in `tooling/internationalization/project.inlang`, keep generated output app-local, and ensure every generated runtime is imported by its app. The examples should establish the locale-detection, persistence, and fallback pattern appropriate to each runtime without requiring an external service. -- `apps/api` context wiring: the global tRPC/route context hard-sets `session: null` while `requireSession` resolves per-request — confusing double wiring. Make session resolution live in one place (the middleware) and remove the misleading context default/comment. -- `apps/mobile`: **delete the unreachable better-auth client** (`src/shared/auth.ts` — full client with admin/org plugins, imported by nothing) and drop `@init/auth` from mobile's deps. Decided: mobile ships without an auth client by default; it returns as two local template recipes (plan 07): `mobile-auth-client-api` (points at `apps/api`'s better-auth handler) and `mobile-auth-client-convex` (uses `@convex-dev/better-auth`'s client plugin + Convex site URL). Keep `@init/auth`'s expo subpaths — they serve the recipes and package selection. - -## 4b. Desktop: local-first direction (decided) - -`apps/desktop` stays and gets **smart, local-first investment** — desktop apps often do local filesystem work with no API at all, so outbound connections are unnecessary by default: - -- Replace the `greet` demo (`src-tauri` command + `features/demo`) with a small, genuinely useful **local filesystem example**: e.g., a feature that picks a directory/file via the Tauri dialog plugin, reads/writes it through a Rust command or the fs plugin, and shows the Tauri `invoke` + TanStack Query `mutationOptions` pattern on something real. -- Remove the API-oriented wiring that exists "by default": `PUBLIC_API_URL` in `src/shared/env.ts` and the unused URL builder in `shared/utils.ts`. Connecting desktop to the API/auth becomes an opt-in template recipe later (plan 07), not template default. -- Keep: the Tauri/Vite config (`TAURI_*` handling), theme toggle, router shell, error boundary (§1). -- No auth or tRPC client by default. Keep the self-contained i18n example from §4. - -## 5. Turbo/CI wiring - -- `apps/api/turbo.json`: delete phantom `build:types` and `deploy` tasks (no corresponding package.json scripts). -- Add `turbo.json` with `build` `outputs` for `apps/web` and `apps/docs` (Astro `dist/`) so builds cache. -- Root `turbo.json` build env: move API-only vars (`INNGEST_*`, `REDIS_URL`) out of the global build env into `apps/api`'s task config. -- `apps/app/src/shared/env.ts` + `apps/app/turbo.json`: the frontend extends `db()` preset and lists `DATABASE_URL`/`RESEND_API_KEY` in build env, but app talks to the DB only via the API. Remove server-side presets/vars that belong to `api`. -- `infra/local/docker-compose.yml`: add `healthcheck` blocks to redis/postgres/minio/inngest, and bootstrap the default `assets` MinIO bucket declaratively with an `mc`-based init container. Bucket names are deployment configuration after plan 02 removed the old storage enum; the files-sdk template recipe may extend this list for its contract suite. Today the bucket only exists because it was created by hand in the gitignored `.data` dir. - -## Acceptance criteria - -- `bun run check`, `bun run analyze`, and `bun test` all pass locally. -- No route in `app` can white-screen without a styled fallback. -- Forgot-password flow completes against local stack with no external keys (email visible via MOCK_RESEND logging/preview). -- Every app imports its generated Paraglide output and demonstrates locale selection or negotiation with a working base-locale fallback. -- `rg "TEST_VAR|@webext-core" apps --glob '!node_modules'` returns nothing. diff --git a/.plans/04-cli-manifest-and-setup.md b/.plans/04-cli-manifest-and-setup.md deleted file mode 100644 index 3116f61d..00000000 --- a/.plans/04-cli-manifest-and-setup.md +++ /dev/null @@ -1,250 +0,0 @@ -# Plan 04 — CLI manifest & setup rework - -Make the TanStack Start app independently useful, then replace the hardcoded, drifted workspace list in `cli/src/workspaces.ts` with a **generated manifest**, add transitive dependency resolution and workspace compatibility guidance to `init-now setup`, and support non-interactive use. - -Prereqs: plan 01 (correctness fixes), plan 02 (final package set), and plan 09 (Effect v4 + adamantite + service structure) should land first — write this feature work against the v4 APIs and the `lib/services` structure plan 09 introduces. - -## Problem summary - -- `cli/src/workspaces.ts` is a handwritten `as const` list of apps/packages with dependency arrays. It has already drifted from reality (~12 packages list wrong `@init/*` deps), and the package-level `dependencies` arrays are never read by any code. -- `setup` (`cli/src/commands/setup.ts:137-171`) resolves app→package dependencies only one level deep. Concrete failure: selecting the `api` app keeps `db` but drops packages `db` itself depends on → dangling `workspace:*` deps → broken install. -- The CLI test (`cli/src/__tests__/workspaces.test.ts`) validates apps only — exactly not where the drift is. -- `apps/app` currently imports `api/client`, sends authentication to `apps/api`, and uses API-hosted tRPC for a greeting and email-availability check. This makes the full-stack TanStack Start app unusable without also retaining Hono, even though TanStack Start supports its own server routes and server functions. -- Everything is prompt-driven; unusable in CI. - -## 1. Make `apps/app` standalone - -The default TanStack Start app must run independently with its own server capabilities. `apps/api` (Hono), `packages/backend` (Convex), and TanStack Start's built-in server runtime are three valid choices, not a hierarchy where the app implicitly requires Hono. - -### Local authentication - -Move the Better Auth composition needed by the web app into `apps/app`. Mount it on a TanStack Start server route and point the existing auth client at the same-origin route. - -`apps/app/src/features/auth/server/auth.ts`: - -```ts -import { AUTH_APP_NAME, AUTH_COOKIE_PREFIX } from "@init/auth/constants" -import { tanstackStartCookies } from "@init/auth/integrations/start" -import { createAuth, databaseAdapter } from "@init/auth/server" -import { admin, organization } from "@init/auth/server/plugins" -import { database } from "@init/db/client" -import { sendEmail } from "@init/email/client" -import PasswordReset from "@init/email/templates/password-reset" -import { seconds } from "qte" -import env from "#shared/env.ts" - -export const auth = createAuth({ - advanced: { - cookiePrefix: AUTH_COOKIE_PREFIX, - database: { generateId: false }, - }, - appName: AUTH_APP_NAME, - basePath: "/api/auth", - baseURL: env.PUBLIC_BASE_URL, - database: databaseAdapter(database()), - emailAndPassword: { - autoSignIn: true, - enabled: true, - sendResetPassword: async ({ user, url }) => { - await sendEmail(PasswordReset({ resetUrl: url }), { - emails: [user.email], - subject: `Reset your ${AUTH_APP_NAME} password`, - }) - }, - }, - plugins: [admin(), organization(), tanstackStartCookies()], - secret: env.AUTH_SECRET, - session: { - expiresIn: seconds("30d"), - updateAge: seconds("15d"), - }, - socialProviders: { - github: { - clientId: env.GITHUB_CLIENT_ID, - clientSecret: env.GITHUB_CLIENT_SECRET, - enabled: true, - }, - google: { - clientId: env.GOOGLE_CLIENT_ID, - clientSecret: env.GOOGLE_CLIENT_SECRET, - enabled: true, - }, - }, - trustedOrigins: env.AUTH_TRUSTED_ORIGINS, -}) -``` - -`apps/app/src/routes/api/auth/$.ts`: - -```ts -import { createFileRoute } from "@tanstack/react-router" -import { auth } from "#features/auth/server/auth.ts" - -export const Route = createFileRoute("/api/auth/$")({ - server: { - handlers: { - GET: ({ request }) => auth.handler(request), - POST: ({ request }) => auth.handler(request), - }, - }, -}) -``` - -`apps/app/src/shared/auth.ts` keeps the UI-facing Better Auth interface stable: - -```ts -import { createAuthClient } from "@init/auth/client" -import { adminClient, organizationClient } from "@init/auth/client/plugins" -import { buildUrl } from "#shared/utils.ts" - -export const authClient = createAuthClient(buildUrl("/api/auth"), [ - adminClient(), - organizationClient(), -]) - -export const { useSession, signIn, signOut, signUp } = authClient -``` - -Extend `apps/app/src/shared/env.ts` with the `auth()`, provider, `db()`, and `resend()` presets needed by the local composition. Add `@init/db`, `@init/email`, and `qte` to `apps/app/package.json`. - -### Local server functions - -Replace the API-hosted tRPC demo and email check with TanStack Start server functions. Keep database/auth helpers server-only and expose only validated functions to routes/components. - -```ts -import { database } from "@init/db/client" -import * as z from "@init/utils/schema" -import { getRequestHeaders } from "@tanstack/react-start/server" -import { auth } from "#features/auth/server/auth.ts" -import { publicFunction } from "#shared/server/functions.ts" - -async function getCurrentSession() { - return auth.api.getSession({ headers: getRequestHeaders() }) -} - -export const validateSession = publicFunction.handler(getCurrentSession) - -export const getGreeting = publicFunction.handler(async () => { - const session = await getCurrentSession() - if (!session) throw new Error("Unauthorized") - - return { message: `Hello, ${session.user.name}!` } -}) - -export const checkEmailAvailability = publicFunction - .validator(z.object({ email: z.email() })) - .handler(async ({ data }) => { - const user = await database().query.users.findFirst({ - where: (table, { eq }) => eq(table.email, data.email), - }) - - return { isAvailable: !user } - }) -``` - -The dashboard loader calls `getGreeting()` directly, and the sign-up form calls `checkEmailAvailability()` instead of tRPC. Protect each server function that reads private data inside the function; route guards are navigation UX, not an authorization boundary. - -### Remove the hard coupling - -- Delete the app's `api: "workspace:*"` dependency and both `TRPCRouter` imports. -- Remove the app-wide tRPC provider/context and the now-unused `@trpc/client` and `@trpc/tanstack-react-query` dependencies. -- Remove `PUBLIC_API_URL` from the default app env schema/template. The local template recipes in plan 07 add it when a project opts into a remote Hono API. -- Keep `apps/api` fully functional for mobile, desktop, extensions, third-party clients, or projects that deliberately choose a separately deployed Hono backend. -- Do not add a local/remote abstraction before installing a second adapter. TanStack server functions are the default implementation; plan 07 installs the real remote adapters at the seam when requested. - -## 2. Generated manifest - -Create a generator (suggested: `cli/scripts/generate-manifest.ts`, runnable via `bun run --cwd cli generate:manifest`) that walks the template's `apps/*/package.json`, `packages/*/package.json`, `tooling/*/package.json` and emits `manifest.json` at the repo root containing, per workspace: - -- `name` (npm name), `dir` (e.g. `packages/db`), `type` (`app` | `package` | `tooling`) -- `description` (from package.json `description` — add descriptions to workspace package.jsons where missing, migrating the prose currently in `workspaces.ts`) -- `relationships`: normalized edges to other workspaces. Each edge has a `target`, a `kind` (`required` or `recommended`), its dependency section when applicable, and a human-readable `reason` for recommendations. -- template metadata that today lives in hardcoded CLI lists: files/dirs that are template-internal (the `cleanupInternalFiles` list from `setup.ts:95-110`, `EXCLUDED_DIRS` from `utils.ts:163-180`) — a single `internalPaths` array in the manifest so setup/update share one source of truth (update uses it in plan 06). - -Workspace relationships must be colocated with the workspace that owns them, not hardcoded in CLI commands: - -- Every `workspace:*` dependency is generated as `required` by default. -- An optional `init.relationships` field in a workspace's `package.json` can declare a non-dependency recommendation or override a workspace dependency from `required` to `recommended`, with a reason shown to the user. -- The generator validates that every relationship target exists, every kind is supported, every recommendation has a reason, and workspace names/directories are unique. - -Example workspace metadata: - -```json -{ - "init": { - "relationships": { - "worker": { - "kind": "recommended", - "reason": "Background jobs run inline unless the worker app is selected." - } - } - } -} -``` - -The relationship model is workspace-type agnostic. It must handle app→app, app→package, package→package, and package→app edges without adding named special cases such as `if (workspace === "app")`. - -Wiring: - -- Commit `manifest.json`; add a CI check (in `adamantite.yml` or `tests.yml`) that regenerates and diffs it, failing when stale. This replaces the drift-prone test. -- The CLI reads the manifest from the _downloaded/cloned template tree_ at runtime (giget result for create/setup, clone for update) — never from a bundled copy — so a published CLI always matches the template snapshot it's operating on. -- Compatibility check (enabled by lockstep versioning, plan 08): after fetching a template snapshot, compare the CLI's own version against the snapshot's version and warn on **major** mismatch, suggesting `bunx init-now@latest`. Guards stale globally-installed CLIs against manifest/layout changes. -- Delete `cli/src/workspaces.ts` and its test; add tests for the manifest reader + resolver instead. - -## 3. Generic workspace selection resolver - -Implement one pure module used by both `setup` and `add`. Its interface accepts the manifest and the workspaces explicitly selected by the user and returns a selection plan: - -- selected workspaces after the transitive `required` closure -- why each automatically included workspace is required, so the UI can explain and lock it -- all omitted `recommended` relationships, grouped for one confirmation -- package-manifest dependency entries to remove when the user confirms omission of a recommended workspace -- undeclared dangling relationships that must fail validation - -The resolver owns graph traversal, cycle handling, relationship validation, and omission planning. Commands must not reproduce this logic or inspect specific workspace names. A visited set makes required cycles safe; recommendation cycles never force selection. - -This is the seam for tests: table-driven graph fixtures exercise the resolver through this interface. Adding a fixture with a new workspace relationship must work without changing the resolver or either command. - -## 4. Setup: workspace selection + transitive resolution - -Rework `cli/src/commands/setup.ts`: - -Keep the existing two-phase mental model. Setup must not ask users to choose a backend or present TanStack Start, Hono, and Convex as named architecture modes. - -1. **App multiselect**: ask which apps to keep and present every app uniformly. `app` and `api` are ordinary app choices. -2. **Package multiselect**: ask which packages to keep. Preselect and lock the transitive required closure of the selected apps; `backend` is an ordinary package choice. Resolve again after package selection so dependencies of newly selected packages are included. -3. **Check recommendations**: after all selections, use the resolver's grouped omissions to explain every missing recommendation and ask once whether to return to selection or continue. Do not silently select, lock, or retain recommended workspaces. -4. **Post-prune validation**: after deleting unselected workspaces, apply the resolver's planned package-manifest edits so `bun install` can complete when recommended workspaces are deliberately omitted. Scan remaining `package.json`s for other `workspace:*` deps pointing at deleted workspaces and scan for imports from deleted workspaces. Fail on undeclared dangling relationships; report confirmed, missing recommendations as warnings with the affected source imports so users know what they must adapt. -5. Use the shared `internalPaths` from the manifest for cleanup instead of the hardcoded list. - -## 5. Create-time version pinning - -The root create command (`cli/src/index.ts:76`) downloads `github:metaideas/init` — the tip of `main` — so scaffolded projects can contain unreleased content while `.template-version.json` records the last release cut on `main`. Fix: - -- Default the create command to the **latest release tag**: resolve it via the existing `getLatestRelease` logic and pass it as giget's ref (`github:metaideas/init#`). Fall back to `main` with a printed warning if the release lookup fails (offline/rate-limited). -- Add `--ref ` to override (mirrors plan 06's update flag). -- `setup` stamps `.template-version.json` with the version actually scaffolded (bare semver, per plan 01's format fix) instead of trusting the committed value. - -## 6. Non-interactive mode - -Add flags to `setup` (and the root create command where relevant): `--name `, `--apps `, `--packages `, `--yes` (accept defaults and recommendation warnings, skip confirmations), `--no-install`, `--no-git`. Every prompt must have a flag equivalent. Validate flag values against the manifest, and print any unfulfilled recommendations even when `--yes` acknowledges them. - -## 7. `add` command alignment - -`cli/src/commands/add.ts` currently offers workspaces from the hardcoded list and copies from `main`. Update it to: - -- Read available workspaces from the manifest (fetched from the template at the project's recorded version — see plan 06's ref-pinning; until then, `main` with a warning). -- Feed the requested workspace and the project's existing workspaces through the same selection resolver used by setup. Offer to add the returned required closure and show the same grouped recommendation warnings. -- After copying: rewrite `@init/*` → project scope if the project was renamed (reuse `replaceProjectNameInProjectFiles`, scoped to the new workspace dirs), and run `bun install`. -- Make `add app` and `add package` consistent (both should handle scope prefixing and `--destination` the same way). - -## Acceptance criteria - -- `manifest.json` generated, committed, CI-checked for staleness; `cli/src/workspaces.ts` deleted. -- Scaffolding with only `app` selected produces a working TanStack Start app with local Better Auth, session protection, password reset, email-availability validation, and the greeting server function; it has no `api` workspace dependency or tRPC client dependencies. -- Scaffolding with only `api` selected produces a project where `bun install && bun run check` passes (transitive closure kept `db`'s deps). -- `init-now setup --yes --apps app --name demo` completes with zero prompts. -- Setup contains only the app and package selection phases; it has no backend question, backend mode, or conditional backend-specific flow. -- Selecting `app`, `api`, or `backend` never silently selects either of the others; any combination can be retained. -- Adding a new required or recommended workspace relationship requires only package metadata plus a regenerated manifest; neither `setup` nor `add` changes. -- `cd cli && bun test` covers manifest parsing and validation, arbitrary transitive graphs, required and recommendation cycles, grouped recommendation warnings, omission pruning, the api→db closure, independent app/API/Convex selection, and flag validation. App-level tests are intentionally out of scope for now. diff --git a/.plans/05-convex-backend-example.md b/.plans/05-convex-backend-example.md deleted file mode 100644 index 5e45bdc8..00000000 --- a/.plans/05-convex-backend-example.md +++ /dev/null @@ -1,40 +0,0 @@ -# Plan 05 — Convex backend example & conventions - -**Status:** Completed -**Note:** Durable auth and backend conventions retained; the always-on mobile demo is -superseded by Plan 14. - -`packages/backend` (Convex + `@convex-dev/better-auth`) is an intentional backend option alongside `apps/api`. The TanStack Start app is independently full-stack through its own server routes/functions (plan 04); projects can keep that default, add a self-managed Hono API, or adopt Convex (e.g., a mobile app that doesn't want to run a server). Decision: Convex **stays in `packages/`** — it is consumed like a library (React client + generated types) and deploys to Convex cloud, not our infra; this also matches Convex's own Turborepo conventions. - -The problem: today it has **zero consumers**, so the alternative is asserted but never demonstrated, and drift between the two auth setups goes unnoticed. - -## 1. Minimal consumption example in `apps/mobile` - -Mobile is the natural pairing (the stated use case). Add a small, clearly-optional example: - -- A screen (e.g. `src/app/convex-demo.tsx` or a `features/convex-demo` module per repo structure rules) that uses `@init/backend`'s React client (`packages/backend/src/client/index.ts`) — a `ConvexProvider` + one live query against an existing model (`packages/backend/src/functions/models/documents.ts`). -- Env wiring: `EXPO_PUBLIC_CONVEX_URL` via the existing `convex` preset in `packages/env/src/presets.ts`, validated in `apps/mobile/src/shared/env.ts`. -- Add `@init/backend` to `apps/mobile/package.json`. -- Keep it deletable: the example must be self-contained (one feature folder + one route + one provider wrapper) so omitting `packages/backend` in `init-now setup` (plan 04) deletes this example folder cleanly. Document the hard workspace relationship in the generated manifest. - -Constraint: **no external service required to pass CI** — `convex dev` needs an account, so the example must typecheck and build without a running deployment (guard the provider on env presence, render a "Convex not configured" state otherwise). Same pattern as Sentry: wired seam, opt-in service. - -## 2. Reduce duplication between the two backends - -Don't force-share code between alternatives (they must delete cleanly), but eliminate accidental drift: - -- Auth config: `packages/backend/src/functions/shared/auth.ts` vs `apps/api/src/shared/auth.ts` duplicate better-auth settings (session/user config). Extract genuinely shared, service-free constants (session TTLs, cookie names, plugin lists if identical) into `@init/auth` so both consume one source. Leave provider-specific wiring in place. -- `LoggerCategory.CONVEX` in `@init/observability`: fine to keep (observability is cross-cutting), but verify the category is actually used by `packages/backend` logging; delete if not. - -## 3. Documentation & conventions - -- `AGENTS.md`: the Project Structure section says deployables live in `apps/`. Add a carve-out: _hosted-platform backends consumed as libraries (e.g., Convex) live in `packages/`_. This prevents relitigating the placement. -- `docs/project-structure.md`: same clarification; also fix the stale claim that `scripts/` contains the template-sync script (sync lives in the `init-now` CLI). -- Add a short `packages/backend/README.md` section (or extend the existing one): when to keep TanStack Start local server functions, choose Convex, or choose `apps/api`; document that optional app adapters are generated locally rather than shipped in the default app. - -## Acceptance criteria - -- `apps/mobile` imports `@init/backend` and renders the demo screen; app builds/typechecks with no Convex deployment configured. -- Deleting `packages/backend` + the mobile example folder leaves a green `bun run check` (verify manually; plan 04 automates it). -- No duplicated better-auth constants between the two backend stacks. -- AGENTS.md and docs updated. diff --git a/.plans/07-template-recipes.md b/.plans/07-template-recipes.md deleted file mode 100644 index 17ce6b5a..00000000 --- a/.plans/07-template-recipes.md +++ /dev/null @@ -1,298 +0,0 @@ -# Plan 07 — Local template recipes - -**Status:** Completed -**Size:** M -**Depends on:** 13, 14 - -Build snapshot-matched Turbo generator recipes so the template ships a lean runtime core -while optional, copy-once code remains one local command away. Plan 14 establishes the -generator conventions (plain Plop generators, shared helpers, `skipIfExists` idempotency, -`bun add --exact` versioning); this plan expands that machinery into the copy-once recipe -catalog. - -## Model (decided) - -- **Packages stay packages:** units with their own third-party dependencies and lifecycle - (payments, AI, analytics, KV, email, ...) remain selectable workspaces through - `bun template setup` / `bun template add`. -- **Template recipes are copy-once leaves:** utilities, optional wiring, and integration - snippets become user-owned project code after generation. They create no runtime - dependency on Init. -- Recipes MAY target files inside existing workspaces and MAY add dependencies, - environment validation, or narrowly scoped integration wiring. -- **Backend connection wiring belongs to Plan 14.** The `connect-backend` generator owns - all app↔backend transport and auth-client wiring (Convex, Hono, tRPC, and their auth - variants). This plan owns the remaining copy-once catalog. -- **Distribution is local and snapshot-matched:** recipe definitions and source templates - ship under `turbo/generators/` in every scaffold. They are renamed with the rest of the - project during setup and therefore match the scaffold's recorded template commit. -- There is no hosted registry, shadcn schema, remote installer, or independent recipe - release channel. New or corrected recipes reach an existing project through the same - agent-assisted upstream diff workflow as other template improvements. -- Generators never modify existing user-owned consumers. Recipes add files, append to - `.env.template`, make structural `package.json` changes, and perform at most narrow - anchored line merges (env `extends`/schema, preset appends). Anything requiring - broader mutation is out of scope. -- **Add-only rule (from Plan 14):** recipes may only `add` files that do not exist in - the default scaffold; behavior changes to default-scaffold files must go through - shipped seams. The sanctioned exceptions are purely additive merges: anchored env - merges, `.env.template` appends, `presets.ts` export appends, and `shared/utils.ts` - export appends. Skipped targets are always reported in generator output — never - silently omitted. - -## 1. Generator module and categorized template structure - -Follow the Plan 14 conventions: plain Plop generators registered in `config.ts`, shared -helpers in `shared/utils.ts`, no installer framework or catalog module. The generator -list itself is the catalog. - -```text -turbo/generators/ - config.ts - shared/ - utils.ts - recipes/ - utilities/ - codec.ts - assert.ts - scaffolds/ - new-feature.ts - new-package.ts - backend-clients/ # owned by Plan 14 - convex.ts - hono.ts - trpc.ts - env/ - openai.ts - anthropic.ts - s3.ts - templates/ - utilities/ - codec/ - assert/ - scaffolds/ - new-feature/ - new-package/ - backend-clients/ # owned by Plan 14 - env/ -``` - -- `config.ts` is the sole executable generator entrypoint. Public generators: - `template` (this plan's copy-once catalog, grouped by category), `connect-backend` - (Plan 14), `new-feature`, and `new-package`. -- Recipe modules export typed Plop generator definitions; `config.ts` imports and - registers them. Composition may be reorganized only if it stays typesafe without - significant type wrangling. -- Mirror the category between `recipes/` and `templates/`. Do not duplicate generated - code in docs or a second distribution directory. -- Every plan that removes restorable code must add its canonical target, dependency/env - requirements, and concrete source snippet to the owning plan before deleting it. The - generated recipe must compile. - -Recipe targets use template-relative paths such as `packages/utils/src/codec.ts`. -Canonical sources use `@init/*`; `bun template setup` rewrites them with the rest of the -scaffold, and generators discover current workspace package names via `readPackageName` -rather than assuming a scope. - -## 2. Template recipe backlog - -- `codec` — Zod JSON codec (was `packages/utils/src/codec.ts`) -- `assert` — assertion helpers importing `@init/core/errors` (was - `packages/utils/src/assert.ts`) -- ~~`stripe-agent-toolkit`~~ — **dropped**: too small to justify a dedicated recipe -- env presets not available upstream in `@t3-oss/env-core/presets-zod`: `openai`, - `anthropic`, `s3` -- ~~`files-sdk` / `files-client`~~ — **moved to Plan 15** so storage integration can - evolve independently after the core recipe catalog lands -- ~~`railway`~~ — **dropped**: available upstream in `@t3-oss/env-core/presets-zod`; - users import it directly -- ~~`email-organization-invitation`~~ — **dropped**: simple email template, not worth a - recipe -- ~~`app-api-client` / `app-api-auth` / `app-api-trpc` / `app-api` / - `desktop-api-client` / `mobile-auth-client-api` / `mobile-auth-client-convex`~~ — - **moved to Plan 14**: all backend connection and auth-client wiring is owned by - `connect-backend`. Their canonical snippets now live in Plan 14. -- ~~`@init/ui` unused components~~ — **decided: NOT template recipes.** All 57 - components stay in the template: they are customized to fit the project (base-ui port, - project theming), and the user model is "import what you need, delete the rest." - -### Utility recipe requirements - -`codec` targets `packages/utils/src/codec.ts`: - -```ts -import * as z from "#schema.ts" - -export const jsonCodec = (schema: T) => - z.codec(z.string(), schema, { - decode: (jsonString, ctx) => { - try { - return JSON.parse(jsonString) as z.input - } catch (error) { - ctx.issues.push({ - code: "invalid_format", - format: "json", - input: jsonString, - message: error instanceof Error ? error.message : "Unknown error", - }) - return z.NEVER - } - }, - encode: (value) => JSON.stringify(value), - }) -``` - -`assert` targets `packages/utils/src/assert.ts`: - -```ts -import { AssertConditionFailedError, AssertUnreachableError } from "@init/core/errors" - -/** - * Asserts that a value is never, and throws an error if it is. Use this to make sure that all cases - * in a `switch` statement are handled. - */ -export function assertUnreachable(x: never): never { - throw new AssertUnreachableError({ value: String(x) }) -} - -/** - * Throws an error if a condition is not met. - */ -export function throwUnless(condition: boolean, message: string): asserts condition is true { - if (!condition) { - throw new AssertConditionFailedError({ condition: "throwUnless" }).withMessage(message) - } -} - -/** - * Throws an error if a condition is met. - */ -export function throwIf(condition: boolean, message: string): asserts condition is false { - if (condition) { - throw new AssertConditionFailedError({ condition: "throwIf" }).withMessage(message) - } -} -``` - -### Env preset recipe requirements - -Each env recipe **appends** its named export to `packages/env/src/presets.ts` using a -Plop `append` action, skipped when the export name is already present. If a user -somehow extends the same preset twice, they fix it manually. Canonical snippets: - -```ts -// openai -export const openai = () => - createEnv({ - runtimeEnv: env, - server: { - OPENAI_API_KEY: z.string(), - }, - skipValidation: isCI, - }) - -// anthropic -export const anthropic = () => - createEnv({ - runtimeEnv: env, - server: { - ANTHROPIC_API_KEY: z.string(), - }, - skipValidation: isCI, - }) - -// s3 -export const s3 = () => - createEnv({ - runtimeEnv: env, - server: { - S3_ACCESS_KEY_ID: z.string(), - S3_BUCKET: z.string().optional(), - S3_ENDPOINT: z.string().optional(), - S3_REGION: z.string().optional(), - S3_SECRET_ACCESS_KEY: z.string(), - }, - skipValidation: isCI, - }) -``` - -## 3. Generator interface and installation workflow - -Expose the copy-once catalog through the `template` generator: - -```sh -bun run generate -bun run generate template --args codec -``` - -1. Interactive use presents the catalog grouped by category via the standard Turbo - generator menu. `--args` provides the non-interactive path. -2. Preflight with `ensureWorkspaceExists` before any write; report the `bun template -add` remedy for missing workspaces and abort. -3. Apply using Plop primitives only: - - `add`/`addMany` with `skipIfExists` for source files; - - structural `package.json` updates via `addWorkspaceDependencies`; - - `bun add --exact` for third-party dependencies; - - `append` for `.env.template` values and `presets.ts` exports, with skip-functions - that check for existing content; - - install once after all package changes; format affected files with the managed - formatter. -4. Reruns: existing targets are skipped, an already-installed recipe reports as a no-op. - User-modified files are silently skipped; there is no drift detection or rollback. -5. Return a concise result listing created files, dependencies, and environment values - the user must supply. - -## 4. Recipe health (manual) - -- Verify recipes manually in disposable scaffold copies after `bun template setup`: - generated files use the renamed scope and the project passes `bun run check`. -- Test at least `codec` and one env preset. -- Run recipes against the smallest supported workspace selection and verify missing - requirements fail before any write. -- Include recipe sources and generator implementation in Adamantite/knip analysis while - excluding inert `.hbs` source templates where appropriate. - -No unit-test suite or CI harness for the generators; testing is local and manual. - -## Acceptance criteria - -- `bun run generate` lists the catalog recipes, and - `bun run generate template --args codec` works non-interactively in a scaffolded - project. -- Installing recipes into a renamed project yields workspace imports under the project - scope, and `bun run check` passes. -- Installing an env preset recipe appends exactly one named export to - `packages/env/src/presets.ts`; rerunning is a no-op. -- Every recipe sourced from removed template code has a canonical snippet in this plan - (or in Plan 14 for backend wiring), and its generated files typecheck. -- Fresh scaffold runtime workspaces contain no copies of optional recipe output; - canonical sources live only under `turbo/generators/templates/` until invoked. -- Missing workspaces fail during preflight without partial writes. -- Recipe installation is snapshot-local and makes no network request except dependency - installation. - -## Decisions (settled with maintainer) - -- No installer framework, catalog module, snapshots, drift detection, or rollback — - Plop primitives only, per Plan 14 conventions. -- Dependencies are installed at their latest versions with `bun add --exact`; version - drift across packages is resolved by the user with `bun run fix:monorepo`. -- Recipes never modify existing user-owned consumers; additions are additive and easy - to remove. -- All backend connection and auth-client wiring moved to Plan 14's `connect-backend`. -- File storage integration moved to Plan 15. -- The Stripe Agent Toolkit helper was dropped because it does not warrant a recipe. -- `railway` and `email-organization-invitation` recipes dropped. -- Recipes ship with the scaffold and follow its template commit. There is no hosted - catalog or updater. -- `@init/ui` components stay in the template in full. - -## Out of scope - -- Hosting or publishing recipe artifacts independently of the GitHub template. -- Updating previously scaffolded projects from a newer recipe catalog. -- Automatically adding a missing workspace or deploying an external service. -- Moving selectable, lifecycle-owning packages into copy-once recipes. -- Backend transport and auth-client adapters owned by Plan 14. -- File storage recipes owned by Plan 15. -- AST-based or syntax-aware file merging beyond anchored appends/modifies. diff --git a/.plans/09-cli-effect-v4-and-tooling.md b/.plans/09-cli-effect-v4-and-tooling.md deleted file mode 100644 index cb0a018a..00000000 --- a/.plans/09-cli-effect-v4-and-tooling.md +++ /dev/null @@ -1,104 +0,0 @@ -# Plan 09 — CLI: Effect v4 migration, adamantite tooling, and CI - -Modernize the `init-now` CLI (`cli/`): migrate to Effect v4 beta, adopt adamantite for lint/format/typecheck, add a dedicated GitHub Actions workflow, and guarantee all of it is stripped from scaffolded projects. - -**Reference implementation: `../adamantite`** (local checkout, `github.com/adelrodriguez/adamantite`). It is a standalone Effect v4 beta CLI with the exact target structure and tooling. When in doubt about v4 APIs or project layout, mirror what adamantite does. - -Prereq: plan 01 (correctness fixes) — land first so behavior fixes aren't tangled with the migration. This plan subsumes plan 01 item 2 (version string) via the macro pattern below. Plans 04 and 06 should be written against the migrated codebase, so this plan precedes them. - -## Context - -- CLI today: `effect@3.19.14`, `@effect/cli@0.73.0`, `@effect/platform@0.94.1`, `@effect/platform-bun@0.87.0`. Bundled with bunup, bin shim at `cli/bin/init-now`, own `bun.lock` (NOT part of root workspaces). -- The CLI is currently excluded from all repo linting: root `oxlint.config.ts` ignores `cli/**` (tsgolint panics resolving the nested standalone tsconfig). So the CLI has no lint/format/typecheck enforcement at all today. -- adamantite (the reference) uses `effect@4.0.0-beta.99` + `@effect/platform-node@4.0.0-beta.99`. `@effect/platform-bun` also has `4.0.0-beta.*` releases — prefer it since the CLI targets Bun (`bunup.config.ts` target) — but if v4-beta platform-bun lags or misbehaves, `@effect/platform-node` works under Bun (adamantite proves this). - -## 1. Migrate to Effect v4 beta - -Dependency changes in `cli/package.json`: - -- `effect` → `4.0.0-beta.x` (match adamantite's pinned beta or newer; pin exact). -- **Remove** `@effect/cli` and `@effect/platform` — in v4 these live inside `effect` itself: `effect/unstable/cli` (`Command`, `Flag`, `Argument`, `Prompt`), `effect/FileSystem`, `effect/Terminal`, `effect/unstable/process/ChildProcess` (replaces `@effect/platform` `Command`/shell execution). -- `@effect/platform-bun` → `4.0.0-beta.x` (`BunRuntime`/`BunServices`), or swap to `@effect/platform-node` (`NodeRuntime`/`NodeServices`) exactly as `../adamantite/src/index.ts` does. -- Keep `giget` and `@octokit/rest` external per `cli/bunup.config.ts`; update the `external` list (`effect` stays external, dropped packages removed). - -API migration map (see adamantite for live examples of each): - -| v3 (current CLI) | v4 (adamantite pattern) | Reference | -| ----------------------------------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------- | -| `import { Command, Prompt } from "@effect/cli"` | `effect/unstable/cli/Command`, `.../Prompt`, `.../Flag`, `.../Argument` (namespace imports) | `../adamantite/src/commands/*.ts` | -| `@effect/platform` `Command` (shell) | `effect/unstable/process/ChildProcess` + `ChildProcessSpawner` | `../adamantite/src/lib/shared/process.ts` | -| `@effect/platform` `FileSystem` | `effect/FileSystem` (interface moved into core) | `../adamantite/src/lib/shared/filesystem.ts` | -| `BunContext.layer` / `BunRuntime.runMain` | `BunServices`/`NodeServices` layer + `Runtime`/`NodeRuntime` main | `../adamantite/src/index.ts` | -| `Data.TaggedError` tagged errors (`cli/src/utils.ts`) | same concept; check `Data`/`Schema` error patterns | `../adamantite/src/lib/shared/errors.ts` | -| Hardcoded version string in `Command.run` | build-time macro: `getPackageVersion()` `with { type: "macro" }` | `../adamantite/src/lib/shared/version.macro.ts` | - -Migration is command-by-command: `index.ts` (root create), `commands/{setup,add,check,rename,update}.ts`, `utils.ts`. Behavior must not change — the existing tests in `cli/src/__tests__/` (plus any added by plan 01) are the regression net; extend them where coverage is thin before migrating a command. - -## 2. Restructure to match adamantite's layout - -Target structure (mirror `../adamantite/src`): - -``` -cli/src/ - index.ts # Command.make + withSubcommands + runtime bootstrap - commands/ # one file per subcommand (+ commands/__tests__/) - lib/ - services/ # Context.Tag services (command runner, prompter, ...) - shared/ # errors.ts, filesystem.ts, process.ts, version.macro.ts - __tests__/ -``` - -- Split the current `cli/src/utils.ts` grab-bag into `lib/shared/*` modules (errors, version/release lookup, file walking, project-name rewriting). -- Adopt `#` subpath imports (`"imports": { "#*": "./src/*" }` in `cli/package.json`), matching both adamantite and this repo's own convention. -- Wrap shell/prompt side effects in `Context.Tag` services (see `../adamantite/src/lib/services/command-runner.ts`, `prompter.ts`) so command logic is testable with stub layers — this directly benefits plans 04/06 test requirements. - -## 3. Adamantite support for the CLI - -The CLI stays outside root workspaces (own lockfile, published independently), so it gets its **own** adamantite setup, like the adamantite repo itself: - -- Add `adamantite` (+ `oxlint`, `oxfmt`, `knip` as its presets require — copy adamantite's own devDependencies approach) to `cli/package.json` devDependencies. -- `cli/oxlint.config.ts` and `cli/oxfmt.config.ts` extending `adamantite/lint` + `adamantite/lint/node` and `adamantite/format` (see the repo root configs for consumer syntax; drop the react preset). -- `cli/knip.config.ts` if adamantite's analyze needs it (root `knip.config.ts` already has a `cli` workspace entry — decide whether cli analysis moves fully local; prefer local so `cd cli && bun run analyze` is self-contained, and slim the root entry). -- Scripts in `cli/package.json`: `check`, `format`, `fix`, `analyze` → `adamantite `, plus existing `build`/`test`. -- Root `oxlint.config.ts`: keep the `cli/**` ignore (the CLI now enforces itself), but update the comment to say the CLI runs its own adamantite setup. -- Run `bun run fix` + `bun run format` inside `cli/` and resolve the initial wave of lint findings (expect real findings — this code has never been linted). - -## 4. GitHub Actions for the CLI - -Add `.github/workflows/cli.yml`, modeled on `../adamantite/.github/workflows/ci.yml`: - -- Trigger: `pull_request` and `push` to `main`, **filtered to `paths: [cli/**, .github/workflows/cli.yml]`** so template-only PRs don't pay the cost. -- Matrix jobs, all with `workdir`/`defaults.run.working-directory: cli`: `check` (adamantite), `format --check`, `test` (`bun test`), `analyze`, `build` (bunup + verify `bin/init-now` runs `dist/index.js --version` successfully as a smoke test). -- Setup: checkout, setup-bun, cache keyed on `cli/bun.lock`, `bun install --frozen-lockfile` in `cli/`. -- Plan 08 (release automation) note: its publish gate should reuse/require this workflow's jobs rather than duplicating test/build steps. - -## 4b. Robustness fixes (ride along with the restructure) - -Address these while touching the affected modules — they're behavioral hardening, not features: - -- **Stop swallowing errors**: the codebase leans on `Effect.orElse(() => Effect.void)` / broad `catchAll` (`setup.ts:37,76,107`, `update.ts:84,88,211,218`, `utils.ts:101,153`), so partial failures still print "✅". With the v4 service structure, make failures explicit: either propagate, or downgrade to a printed warning — never silent success. -- **Input validation**: `rename` accepts any string; apply the same name regex the create command uses (`index.ts:14`), npm-scope-safe. `setup`'s name prompt likewise. -- **Tool preflight checks**: verify `git` (setup/update) and `turbo` (add) exist on PATH before starting, with actionable error messages. -- **GitHub rate limits**: unauthenticated Octokit calls get 60 req/h/IP; on 403-rate-limit, print a clear message (optionally honor `GITHUB_TOKEN` env if present) instead of a generic `VersionCheckFailed`. - -## 5. Cleanup in scaffolded projects - -`init-now setup` already deletes `cli/` (`cleanupInternalFiles`, `cli/src/commands/setup.ts:95-110`). Extend the guarantee: - -- Add `.github/workflows/cli.yml` to the cleanup list (and `.github/workflows/release.yml` / `release-please-config.json` if not already covered — verify against the current list). -- When plan 04 lands, these paths move into the manifest's `internalPaths` — ensure `cli/` and its workflow are in that list so `update` (plan 06) never re-adds them either. -- Add a CLI test asserting the cleanup list covers: `cli/`, `.github/workflows/cli.yml`, `.github/workflows/release.yml`, `release-please-config.json`, `.plans/`. This is the regression net for "internal files leak into user projects". - -## Acceptance criteria - -- `cli/package.json` depends on `effect@4.0.0-beta.x` only (no `@effect/cli`, no `@effect/platform`); `cd cli && bun test` green; all commands behave identically (manual smoke: create → setup → check in a temp dir). -- `cd cli && bun run check && bun run format --check && bun run analyze && bun run build` all pass. -- `init-now --version` matches `cli/package.json` (macro-inlined, no hardcoded string). -- `.github/workflows/cli.yml` runs and passes on a PR touching `cli/`; does not trigger on template-only changes. -- A scaffolded project contains no `cli/` directory and no `.github/workflows/cli.yml` (covered by an automated test). - -## Risks / notes - -- Effect v4 is beta: pin exact versions, and expect `unstable/cli` API movement between betas — upgrading betas later is a `bump:deps` + fix cycle, acceptable for an internal tool. -- Do the restructure (§2) and migration (§1) as one PR series but separate commits: move files first (no logic change), then migrate imports/APIs — keeps diffs reviewable. -- If tsgolint/oxlint type-aware checks still panic on the standalone package, check how adamantite configures `typeAware`/`typeCheck` options in its own `oxlint.config.ts` and mirror it. diff --git a/.plans/10-marketing-site.md b/.plans/10-marketing-site.md deleted file mode 100644 index 8bde221b..00000000 --- a/.plans/10-marketing-site.md +++ /dev/null @@ -1,185 +0,0 @@ -# Plan 10 — `init.now` marketing site - -**Status:** Completed -**Size:** M -**Depends on:** 13 (soft) -**Affects:** 12 (there is no separate template-internal website context) - -Build and deploy the public `init.now` site from the existing `apps/web` workspace. The -same implementation ships in scaffolded projects as a polished, replaceable example of -the template's Astro marketing app. - -The visual reference is the original `get-convex/v1` marketing site: - -- source: https://github.com/get-convex/v1/tree/main/apps/web -- homepage: - https://github.com/get-convex/v1/blob/main/apps/web/src/app/page.tsx -- supporting UI: - https://github.com/get-convex/v1/tree/main/apps/web/src/components - -Use its visual composition as inspiration: a full-viewport hero, perspective grid, -animated headline, copyable scaffold command, compact header actions, and a technology -marquee. Do not port its Next.js implementation, branded assets, backend providers, -newsletter, analytics, or large inline logo modules. - -## Decision - -`apps/web` is the one marketing-site module: - -- In this repository, Vercel deploys it to `init.now`. -- In scaffolded projects, it is the working marketing-site example users customize or - remove through the normal workspace selection. -- It continues to participate in the root Bun workspace, Turbo graph, Adamantite - checks, and shared package conventions. - -Do not create a top-level `www/`, second lockfile, isolated dependency graph, special -cleanup path, or duplicate deployment implementation. The independent deployment -lifecycle is a Vercel project concern, not a reason to duplicate the source module. - -This intentionally dogfoods the template. A change that breaks the scaffolded marketing -app should also fail the public site's build. - -## 1. Replace the placeholder landing page - -Rework the homepage and shared layout in `apps/web` into the Init landing page: - -- Logo/wordmark -- Headline: **"Start once. Ship everywhere."** -- Description: **"Modern monorepo template for shipping TypeScript apps everywhere."** -- Copyable scaffold command: - - ```sh - bun create metaideas/init my-app - ``` - -- GitHub link -- Compact "Featuring" marquee for the core stack: Bun, Turborepo, Astro, TanStack - Start, Hono, Expo, Drizzle, Better Auth, Tailwind, and other choices that are actually - present in the template -- Minimal footer with GitHub and license - -The homepage should adopt the reference's character without becoming a clone: - -- dark, restrained visual system -- perspective grid above and below the hero -- strong display typography paired with quiet monospace details -- subtle headline reveal and continuously moving technology strip -- responsive composition that remains useful on narrow mobile screens - -Honor `prefers-reduced-motion`; reduced-motion visitors get the final headline and a -static technology strip. The copy button must be keyboard accessible, expose a useful -label, and announce its copied state. - -## 2. Keep the implementation native to the existing app - -- Keep Astro and static output. -- Reuse `@init/ui` styling and existing workspace packages where they reduce duplicate - implementation. Do not add a second design system for the landing page. -- Prefer Astro, CSS, and a small inline script for the copy interaction. Use a React - island only if it produces a materially clearer implementation; do not reproduce the - reference's client-heavy dependency graph. -- Store technology metadata in one small data structure and render the desktop/static - and mobile/marquee treatments from it. -- Keep assets small and locally owned. Do not paste the reference footer's large, - repeated inline SVG payload. -- Add no dependency on `apps/app`, `apps/api`, `packages/backend`, or an external - service. -- Preserve useful starter capabilities already established in `apps/web`—localization, - blog content infrastructure, RSS, sitemap, and 404 handling—unless a concrete conflict - requires a separate decision. They do not need promotional sections on the v1 - homepage. - -## 3. Metadata and public-site behavior - -- Set the canonical site URL to `https://init.now` in production. -- Add the Init favicon, title, description, canonical metadata, Open Graph image, and - social preview metadata. -- Keep the root URL useful and canonical. Locale routing must not leave - `https://init.now` as a meta-refresh or placeholder page. -- The production landing page must require no environment variables beyond deployment - metadata and no external account at build or runtime. -- Do not add email capture, authentication, a dashboard preview, documentation content, - analytics, or backend-powered features in v1. - -## 4. Deploy `apps/web` to Vercel - -- Create a Vercel project for `init.now` using this monorepo and the `apps/web` - workspace. -- Configure installation/code generation so shared workspace imports resolve before the - Astro build. -- Set `PUBLIC_SITE_URL=https://init.now` for production. -- Deploy `main` to production and retain Vercel preview deployments for pull requests. -- Use the Vercel build check as the primary deployment signal. If it does not cover - pull requests reliably, add one path-aware build workflow for `apps/web` and the - shared packages/tooling it consumes; do not add a `www.yml` workflow. - -## 5. Template behavior and documentation - -- Keep `apps/web` in the ordinary app-selection flow. Selecting it during - `bun template setup` retains the polished example; omitting it removes the workspace. -- Do not add `apps/web` to `init.cleanupPaths`. -- Update the root README to identify the deployed example and link to `init.now`. -- Update `apps/web/README.md` with the local development/build commands and a short list - of the branding/content users normally replace. -- Make the scaffold command, tagline, GitHub URL, and metadata consistent across the - landing page and root README. -- Record that the site demonstrates the template but does not distribute recipes or - provide an updater. Plan 07's recipes remain local and snapshot-matched under - `turbo/generators/`. - -## Verification - -```sh -bun run format -bun run check -bun run analyze -bun run check:monorepo -bun test -bun run build --filter=web -``` - -Manual: - -- Verify the page at narrow mobile, tablet, and desktop widths. -- Verify keyboard focus, copy feedback, contrast, and reduced-motion behavior. -- Verify title, canonical URL, favicon, Open Graph image, and social metadata in the - built output. -- Run `bun template setup` in a disposable scaffold: - - keeping `web` retains the complete landing page and its workspace dependencies - - omitting `web` removes it cleanly - -## Acceptance criteria - -- `init.now` serves the `apps/web` implementation from this repository. -- The page clearly follows the `get-convex/v1` visual direction without porting its - Next.js-specific or service-dependent implementation. -- The scaffold command is correct, copyable, accessible, and visually central. -- The page is responsive, honors reduced motion, and includes complete basic metadata. -- `apps/web` builds through the monorepo with zero required external services. -- Scaffolded projects receive the same polished site when they keep the `web` - workspace. -- There is no `www/`, independent site lockfile, duplicate site implementation, or - marketing-site cleanup rule. -- The site contains no hosted recipe catalog or machine-readable code-distribution - routes. - -## Out of scope - -- A second template-internal marketing app. -- Email capture or newsletter infrastructure. -- Authentication, example-dashboard sign-in, or backend integration. -- Analytics. -- A documentation portal or authored blog campaign. -- Hosted template recipes, a remote installer, or automated project updates. - -## Decisions - -- Source module: `apps/web` -- Hosting: Vercel at `init.now` -- Visual reference: `get-convex/v1/apps/web` -- Tagline: "Start once. Ship everywhere." / "Modern monorepo template for shipping - TypeScript apps everywhere." -- Scaffold command: `bun create metaideas/init my-app` -- Scaffold behavior: the branded site ships as replaceable demo content when `web` is - selected -- Optional code distribution: local Turbo recipes from Plan 07 diff --git a/.plans/12-agents-context-docs-refactor.md b/.plans/12-agents-context-docs-refactor.md deleted file mode 100644 index 988c7ddc..00000000 --- a/.plans/12-agents-context-docs-refactor.md +++ /dev/null @@ -1,138 +0,0 @@ -# Plan 12 — AGENTS.md → AGENTS.md + CONTEXT.md + docs refactor - -**Status:** Completed - -Restructure this repo's agent-facing documentation from one overloaded `AGENTS.md` into -the layered setup: **`AGENTS.md`** (standing rules and commands) + **`CONTEXT.md`** -(domain language and orientation) + **`docs/`** (agent workflows, application -architecture, and template governance). This is the layout the maintainer's engineering -skills expect (`setup-matt-pocock-skills`, consumed by `domain-modeling`, `triage`, -`grilling`, etc. — see `~/.agents/skills/setup-matt-pocock-skills/SKILL.md`), and -**`../adamantite` is the live reference implementation** (its `AGENTS.md`, `CONTEXT.md`, -and `docs/agents/` show the target shape). - -Template-governance decisions and documentation about the actual application code are -different domains. They must not share an ADR namespace or be presented as if a -scaffolded project's owner made the template maintainer's decisions. - -## The split - -Roles, per the skill's model: - -- **`AGENTS.md`** — standing rules only: quality-control commands (format/check/analyze/test), comment policy, version control, coding style, import rules. Short. Points to `CONTEXT.md` and `docs/agents/*` instead of inlining everything. Project structure does not live here. See `../adamantite/AGENTS.md` — note how it opens with skill wiring (issue tracker, triage labels, domain docs) in a few lines each, with details delegated to `docs/agents/*.md`. -- **`CONTEXT.md`** — _what things mean_, not rules: what init is, the glossary - (template vs scaffolded project, workspace, template recipe, internal cleanup path, - backend alternatives, preset, ...), architectural orientation (apps/packages/tooling - flow, unidirectional imports **as a concept**), and pointers to application - architecture. Keep detailed - project structure outside both `AGENTS.md` and `CONTEXT.md`; `CONTEXT.md` links to - `docs/architecture/project-structure.md` as the canonical reference. See - `../adamantite/CONTEXT.md` for tone: "Read this before exploring or changing code so - you use the project's own terms." -- **`docs/agents/`** — skill wiring docs: `issue-tracker.md`, `triage-labels.md`, - `domain.md` (routing rules for the two documentation domains). -- **`docs/architecture/`** — facts and guidance about the application code that ships: - workspace structure, import flow, backend topology, desktop behavior, and file-service - security/composition. These documents ship with scaffolded projects. -- **`docs/template/`** — upstream-only governance and research. It contains - `docs/template/adr/` and `docs/template/research/` and is removed during installation. -- **`docs/template/adr/`** — template decision records. Seed it with decisions already - made in these plans, phrased as template selection/governance decisions: - - Package vs template-recipe criterion (tracker / plan 07). - - Zero-external-accounts-and-hosted-services default. - - Why backend alternatives are selected as workspaces and why Convex is offered from - `packages/backend`. - - Why `apps/web` is both the Init site and the replaceable starter marketing app. - - Why Files SDK is built into a selected `apps/api` workspace while its application - clients remain optional generators. -- **`docs/adr/`** — reserved for decisions made by the owner of a scaffolded - application. Do not create or seed this directory in the template. Consumers create - it when their first project-specific decision lands. - -## Single- vs multi-context - -**Decided: single application context** (one root `CONTEXT.md` + -`docs/architecture/`). This repo is a monorepo, but its shipped workspaces form one -starter application model; per-workspace CONTEXT files would mostly restate the root. -Template governance is a separate documentation domain under `docs/template/`, not a -second application CONTEXT. `apps/web` is both the deployed Init site and a scaffolded -example, so its runtime architecture remains inside the application context while the -decision to give it both roles belongs in a template ADR. Revisit multi-context -(`CONTEXT-MAP.md` + per-context files) only if a runtime subsystem develops enough -independent vocabulary to justify it. - -Consider running the `setup-matt-pocock-skills` skill to scaffold section A–C choices interactively rather than hand-writing them. - -## Content migration map - -From the current `AGENTS.md`: - -| Current AGENTS.md section | Destination | -| ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Project Structure (folder meanings, import flow rationale) | `docs/architecture/project-structure.md`; `CONTEXT.md` provides orientation and links to it; keep only terse import-boundary enforcement in `AGENTS.md` | -| Testing, Comment Policy, Version Control, Coding Style, Imports, Naming, TS Usage, Syntax | stay in `AGENTS.md` (rules) — tighten wording | -| Database / Expo / Hono / Web UI sections | stay in `AGENTS.md`, or move to scoped `AGENTS.md` files per directory if supported tooling prefers colocation — keep whichever is shorter | -| Adamantite managed block | stays in `AGENTS.md` (it's command rules; the block is tool-managed) | -| Stale claims | fix during migration: describe `scripts/` as the local template-management command surface; remove claims about a published CLI, automated template sync, or hosted recipe registry | - -Move `docs/project-structure.md` to `docs/architecture/project-structure.md`. Update -`CONTEXT.md`, the template `README.md`, and all internal links to use the new path (plan -05 already touches the backend parts — coordinate, don't duplicate). The document ships -with scaffolded projects and is the canonical detailed project-structure reference. - -Move `docs/research/files-sdk.md` to -`docs/template/research/files-sdk.md`. Research performed to choose or evolve template -defaults is upstream context, not documentation of the generated application's runtime. -Keep consumer-operational documents such as `docs/getting-started.md`, -`docs/generators.md`, and `docs/template-commands.md` outside `docs/template/`: despite -their names, they describe commands and capabilities that remain available after -scaffolding. - -## Template implications (this repo is a template!) - -Scaffolded projects inherit these files. Decide per file: - -- `AGENTS.md`, `CONTEXT.md` — **ship to users**: new projects start with the layout, the glossary, and the standing rules; users' agents then maintain them. -- `docs/architecture/` — **ship to users**: it describes the code and runtime structure - they receive. Application facts selected by the template must be stated without - presenting the template maintainer's rationale as the consumer's own decision. -- `docs/template/` — **template-internal, stripped on install** (decided). Its ADRs - explain why the upstream template is shaped this way, and its research supports those - choices. Add the whole directory to root `package.json`'s `init.cleanupPaths` and - extend the scaffold smoke test. -- `docs/adr/` — **absent initially**. Consumers create their own directory lazily - through the `domain-modeling` skill. Never seed it with upstream template decisions. - - Consequence for `CONTEXT.md`: because it ships, its application orientation must be - self-contained and may link only to shipped `docs/architecture/*` documents. - Template ADRs and research must not be load-bearing or linked from consumer-facing - sections. -- `docs/agents/issue-tracker.md`, `triage-labels.md` — these encode _this repo's_ - tracker (`metaideas/init`). **Decided: strip during `bun template setup`**—add both to - root `package.json`'s `init.cleanupPaths` and the scaffold smoke test. Scaffolded - projects run `setup-matt-pocock-skills` themselves to generate their own; the shipped - `AGENTS.md` should note that. -- `docs/agents/domain.md` — ships, but routes conditionally: when `docs/template/` - exists, upstream template decisions go to `docs/template/adr/`; project-specific - decisions go to `docs/adr/`. After installation only the latter route remains. -- `CLAUDE.md`/`.cursorrules`-style duplicates: none exist today — keep it that way; `AGENTS.md` is the single agent entrypoint. - -## Acceptance criteria - -- `AGENTS.md` contains only standing rules + pointers and does not duplicate project - structure; `CONTEXT.md` exists with glossary and orientation and links to - `docs/architecture/project-structure.md`. -- `docs/architecture/` documents shipped application code; - `docs/template/adr/` contains the seed template decisions listed above; no seeded - `docs/adr/` exists. -- `docs/project-structure.md` has moved to - `docs/architecture/project-structure.md`, `docs/research/files-sdk.md` has moved to - `docs/template/research/files-sdk.md`, and all links use the new paths. -- No stale claims: `rg "syncing the project with the template" AGENTS.md docs/` returns nothing pointing at `scripts/`. -- The maintainer's skills work against the layout: `domain-modeling` routes template - decisions to `docs/template/adr/` and application decisions to `docs/adr/`; `triage` - finds the label vocabulary doc. -- A scaffolded project receives a coherent doc set: `AGENTS.md` + `CONTEXT.md` with no - dangling references—no seeded `docs/adr/`, `docs/template/`, tracker docs, or - references to stripped template-internal machinery (`.plans/`). Consumer-facing - docs may describe the local template recipes that remain in `turbo/generators/`, but - must not imply a hosted registry or updater exists. diff --git a/.plans/13-descope-cli-to-scripts.md b/.plans/13-descope-cli-to-scripts.md deleted file mode 100644 index 003cdc10..00000000 --- a/.plans/13-descope-cli-to-scripts.md +++ /dev/null @@ -1,174 +0,0 @@ -# 13 — Descope: delete the CLI, return to template scripts - -**Status:** Completed -**Size:** M -**Depends on:** — -**Supersedes:** 06 (update rework), 08 (release automation), 11 (`bun create init-now`) — all -three deleted from `.plans/`; their only durable ideas (rename idempotency, CI smoke test) -are folded into this plan -**Affects:** 07 becomes local Turbo template recipes; 10 remains a standalone marketing -site with no code-distribution responsibility - -## Decision - -The published `init-now` CLI is overengineering for what this template needs. The -versioning/publishing problems (plan 08) and template-sync complexity (plan 06) only -exist because the CLI is a separately published npm package. We are reverting to the -pre-`7c1b5d31` design that already worked: plain scripts inside the template, run with -`bun run`. - -Supported operations after this plan: - -| Need | Solution | -| --------------- | ----------------------------------------------------------------------------------------------------------------------- | -| create | `bun create metaideas/init` (GitHub tarball, no npm package) | -| setup | `bun template setup` — pick workspaces, set name/scope, cleanup | -| rename | `bun template rename` — rewrite `@init/` scope and project name | -| add app/package | `bun template add` — wraps `turbo gen workspace --copy `, then runs the rename pass on the copied workspace | -| check/update | **Not supported.** Agents diff against upstream using the stamped commit (see below) | - -Constraints: - -- Leverage Bun APIs (`Bun.file`, `Bun.Glob`, `Bun.spawn`, `Bun.$`) for all file/process work. -- Dependencies kept to a minimum: `yargs` for the command surface, `consola` for - prompts + logging (matching the old `scripts/template` setup at `2de75496`..`08e6ec3b`), - and `faultier` for structured errors (dogfooding a metaideas library; `tryharder` was - considered and rejected as overkill for these scripts). Nothing else — no Effect, no - clack, no octokit, no giget, no semver. -- Setup and rename logic is **rewritten from scratch**, not ported from `cli/`. The old - Effect code is sunk cost; the new scripts should be much simpler. `cli/src/commands/` - may be consulted for behavior reference only. - -## Work items - -### 1. Delete the CLI and deprecate the npm package - -- Delete `cli/` entirely (source, tests, lockfile, bin). -- Delete `.github/workflows/cli.yml`. -- Run `npm deprecate init-now "init is now a GitHub template — use: bun create metaideas/init"`. -- Remove the `init-now` devDependency from root `package.json`. - -### 2. Kill release-please and template versioning - -- Delete `release-please-config.json`, `.template-version.json`, and - `.github/workflows/release.yml`. -- Remove the `release-please` branch ignore from `.github/workflows/tests.yml` and the - `release-please-config.json` reference in root `package.json`. -- Delete `CHANGELOG.md`; release history remains available in Git history. - -### 3. Template stamp for agent-driven updates - -Since `check`/`update` are gone, projects need a way to know what template snapshot they -started from so users can point agents at "diff `metaideas/init` from `` to `HEAD` -and apply what's relevant." - -- `template setup` writes `.template.json` at the project root: - - ```json - { - "template": "metaideas/init", - "commit": "", - "createdAt": "" - } - ``` - -- The sha is fetched at setup time from - `https://api.github.com/repos/metaideas/init/commits/main` (the tarball `bun create` - downloads is HEAD of main, so this matches modulo a negligible race). If the request - fails (offline), write the file without `commit` and warn. -- Document the agent-update workflow in the README: point your agent at the upstream - repo and the stamped commit; it cherry-picks what applies. Include two gotchas the - agent must handle: upstream **deletions** should propagate, and the project rename - must be normalized before diffing (local `@/` corresponds to upstream - `@init/`) so renames don't read as edits. - -### 4. Write the scripts - -New `scripts/` folder at the repo root (excluded from workspace packages), mirroring -the structure that worked before (`2de75496`): - -``` -scripts/ - index.ts # yargs root — project-owned scripts entry, extensible by users - template/ - index.ts # `template` command group registering the subcommands - setup.ts - rename.ts - add.ts - utils.ts # shared helpers if needed; keep it flat -``` - -- `template setup` — interactive: select apps/packages to keep (delete the rest), - set project name and npm scope (delegates to the rename logic), write - `.template.json`, optional git init + `bun install`, self-cleanup of template-only - files (`.plans/`, this script's own create-time artifacts, etc.). -- `template rename` — rename project and rewrite `@init/` scope across the repo - (package.json names/deps, imports, config references). Must be runnable standalone - and scoped to a single workspace (so `add` can invoke it on just the copied package). - **Rename must be idempotent**: running it twice is a no-op, and after renaming (or - adding a workspace to a renamed project) zero `@init/` references remain. No unit - tests for the scripts (decided) — the CI smoke test is the safety net. -- `template add app ` / `template add package `: shell out to - `turbo gen workspace --copy https://github.com/metaideas/init/tree/main//`, - then run the rename pass on the copied workspace so `@init/` becomes the project scope. -- Root `package.json` scripts: replace the `init:*` entries with the old two-script - setup: - - ```json - "scripts": "bun run scripts/index.ts", - "template": "bun --bun run scripts/index.ts template" - ``` - - Usage: `bun template setup`, `bun template rename`, `bun template add package `. - The generic `scripts` entry stays as the extension point for users' own project - scripts (the yargs root's help text should say so). - -### 5. `bun create` support - -- Verify the repo is public at `metaideas/init` and `bun create metaideas/init ` - works end to end. -- Add a `"bun-create"` section to root `package.json` that prints the next step (run - `bun template setup`) after install — or runs it directly if the interactive prompts work - under `bun create`'s postinstall (verify; if not, print instructions). -- Add a CI smoke test (in `tests.yml` or a small dedicated workflow): scaffold a - project from the repo tarball, run `template setup` non-interactively (flags or - env for answers), and assert the result — workspaces pruned, scope renamed, no - `@init/` references, `.template.json` stamped. - -### 6. Docs and plan bookkeeping - -- Rewrite `README.md` quick-start: `bun create metaideas/init`, then `bun template setup`. - Replace the `init-now` command table with the new scripts. -- Rewrite or delete `docs/template-commands.md` accordingly. -- `.plans/TRACKER.md` bookkeeping is already done (statuses, context, verification). -- Plans 06, 08, 11 are superseded by this plan. -- Plan 10 (marketing site) survives but should market the template/`bun create` flow, - not an npm package. -- Plan 07 survives as snapshot-matched Turbo template recipes stored in the scaffold - itself. It reuses the existing `bun generate` command surface and does not introduce - a hosted catalog, shadcn format, or independently versioned installer. -- Plan 10 markets the GitHub template and `bun create metaideas/init` flow only; it - does not host recipe artifacts. - -## Verification - -```sh -bun run format -bun run check -bun run analyze -bun run check:monorepo -bun test -``` - -Manual: - -- `bun create metaideas/init test-proj` in a temp dir, run `bun template setup`, confirm - workspace selection, rename, and `.template.json` stamp. -- `bun template add package ` in the scaffolded project copies and rescopes correctly. -- `npm view init-now` shows the deprecation notice. - -## Out of scope - -- Any form of automated `update`/`check`. -- Publishing anything to npm. -- The local template recipe catalog and shared generator installer (plan 07). diff --git a/.plans/14-connect-backend-generator.md b/.plans/14-connect-backend-generator.md deleted file mode 100644 index 4235daa9..00000000 --- a/.plans/14-connect-backend-generator.md +++ /dev/null @@ -1,681 +0,0 @@ -# 14 — Generic backend connection generator - -**Status:** Completed -**Size:** M -**Depends on:** 05, 13 -**Affects:** 07 (establishes the shared generator conventions that Plan 07 expands) - -## Problem - -Plan 05 proved that `packages/backend` can be consumed from the mobile app, but it did -so by permanently shipping a Convex demo route, provider, environment variables, and -dependency in `apps/mobile`. That conflicts with the template's lean default: a -freshly scaffolded project should not imply that Convex, Hono, or tRPC has already been -chosen. - -Backend connection wiring is also too framework-specific at the command surface. -`hono-client` and `trpc-client` are separate generators today, while Convex has no -generator at all. The shared user intent is simpler: connect an app to one of the -backend options already present in the project. - -## Decision - -Add one Turbo generator named `connect-backend`, invoked through the standard Turbo -generator menu: - -```sh -bun run generate -bun run generate connect-backend --args mobile convex false false -``` - -Its external interface: - -- target app -- backend adapter (`convex`, `hono`, or `trpc`) -- whether to include auth client wiring (where the adapter supports it) -- whether to add an additive example - -The generator is generic at the workflow level, not at the implementation level. -Each supported app/backend combination has an explicit internal adapter that owns its -dependencies, files, environment wiring, and provider integration. Unsupported -combinations fail clearly with a message from a hardcoded adapter map. - -Production connection wiring is the default. Auth wiring and demo examples are opt-in -prompts. Examples are **additive-only**: a new route or screen that is easy to delete. -The generator never modifies existing consumers (dashboard, sign-up form, `getGreeting`, -`checkEmailAvailability`, or any other demo implementation), because users may have -already changed them. - -## Architecture conventions (shared with Plan 07) - -These conventions are established here and reused by Plan 07's recipe catalog: - -- **Plain Plop generators only.** `turbo/generators/config.ts` remains the sole - entrypoint; it imports and registers typed generator definitions from modules under - `turbo/generators/recipes//`. Each module exports a register function (or a - `PlopTypes`-typed definition object). No installer framework, no change-plan - abstraction, no catalog module, no standalone scripts. Composition may be reorganized - only if it stays typesafe without significant type wrangling. -- **Shared helpers in `turbo/generators/shared/utils.ts`:** - - `getAppChoices` / `getAvailableApps` — plain `apps/*` discovery, no framework - sniffing; - - `addWorkspaceDependencies` — idempotent structural `package.json` merge; - - `readPackageName` — discover actual package names from workspace `package.json` - files; never hardcode `@init/`, because `bun template rename` may have changed the - scope; - - `ensureWorkspaceExists` — preflight helper that aborts before any write and prints - the exact remedy (for example `bun template add package backend`). -- **Idempotency uses Plop primitives only.** `add`/`addMany` with `skipIfExists`, - skip-functions that check for existing content, and the idempotent dependency merge. - When every target already exists, report the connection as a no-op. Files the user has - modified are skipped on rerun; this is an accepted trade-off. There is no snapshot - tracking, drift detection, or rollback beyond what Plop provides. -- **Add-only rule.** Generators may only `add` files that do not exist in the default - scaffold. Any behavior change to a file that ships in the default scaffold must be - selected through a seam that also ships in the template (an env variable, the - providers file, or a designated import point) — never through modifying or replacing - the file. This is what makes `skipIfExists` safe: a skip can never silently withhold - intended behavior. The sanctioned exceptions are purely additive merges that change - no existing behavior: anchored env schema/`extends` additions, `.env.template` - appends, and appending new exports to `shared/utils.ts` (skipped when the export - already exists). -- **Skips are reported, never silent.** When a target is skipped because it already - exists, the generator's output says so explicitly. A run must never report success - while quietly omitting part of its work. -- **Versions: latest at generation time, exactly pinned.** Dependency installation uses - `bun add --exact`. Version drift between packages is the user's to resolve with - `bun run fix:monorepo`. -- **Env wiring is feature-local where possible.** Where a shared env file must be - extended (for example adding a preset to `extends: [...]`), use a narrow Plop `modify` - with a regex anchored on the known line, skipped when the preset is already present. - If a user extends the same preset twice through manual edits, they fix it manually. -- **`.env.template` changes use `append`.** No dedupe beyond a skip-function that checks - whether the key is already present. - -## Work items - -### 1. Restore the template seams - -**Provider seam.** Re-add `src/shared/components/providers.tsx` to `apps/app`, -`apps/desktop`, and `apps/mobile` (partially reversing the inlining done in #86). The -file composes the app's providers and is imported by `__root.tsx` / `main.tsx` / -`_layout.tsx`. It is the **only** file generators touch to insert providers; generators -never rewrite root layouts or arbitrary TSX. - -**API URL seam in `apps/app`.** `apps/app`'s `shared/auth.ts` and -`features/auth/server/functions.ts` exist in every fresh scaffold, so under the -add-only rule the generator can never rewrite them to point at `apps/api`. Instead the -default app becomes transport-agnostic (partially reversing plan 04's removal of -`PUBLIC_API_URL`): - -- restore `PUBLIC_API_URL` as an **optional** client env value in - `apps/app/src/shared/env.ts`: - - ```ts - PUBLIC_API_URL: z.url({ protocol: /^https?$/ }).optional(), - ``` - -- ship the fallback URL builder in `apps/app/src/shared/utils.ts` by default: - - ```ts - export const buildApiUrl = createUrlBuilder( - env.PUBLIC_API_URL ?? `${env.PUBLIC_BASE_URL}/api`, - isProduction ? "https" : "http" - ) - ``` - -- write the default `shared/auth.ts` and the session/password-reset server functions - once against `authClient` + `buildApiUrl` so the identical code serves both the local - TanStack Better Auth handler (no env var → `PUBLIC_BASE_URL/api/auth`) and a remote - `apps/api` deployment (env var set): - - ```ts - // apps/app/src/shared/auth.ts (default template, not generated) - import { createAuthClient } from "@init/auth/client" - import { adminClient, organizationClient } from "@init/auth/client/plugins" - import { buildApiUrl } from "#shared/utils.ts" - - export const authClient = createAuthClient(buildApiUrl("/auth"), [ - adminClient(), - organizationClient(), - ]) - - export const { useSession, signIn, signOut, signUp } = authClient - ``` - - ```ts - // apps/app/src/features/auth/server/functions.ts (default template, not generated) - import * as z from "@init/utils/schema" - import { createIsomorphicFn } from "@tanstack/react-start" - import { getRequestHeaders } from "@tanstack/react-start/server" - import { authClient } from "#shared/auth.ts" - import { publicFunction } from "#shared/server/functions.ts" - import { buildUrl } from "#shared/utils.ts" - - export const validateSession = createIsomorphicFn() - .client(async () => { - const { data: session } = await authClient.getSession() - return session - }) - .server(async () => { - const { data: session } = await authClient.getSession({ - fetchOptions: { headers: getRequestHeaders() }, - }) - return session - }) - - export const forgotPassword = publicFunction - .validator(z.object({ email: z.email() })) - .handler(async ({ data }) => { - const { error } = await authClient.requestPasswordReset({ - email: data.email, - fetchOptions: { headers: getRequestHeaders() }, - redirectTo: buildUrl("/reset-password"), - }) - - if (error) throw new Error(error.message) - return { success: true } - }) - ``` - -Switching `apps/app` auth to the Hono API is thereby pure configuration: setting -`PUBLIC_API_URL` retargets the same client, and removing it falls back to the local -handler. - -### 2. Introduce the `connect-backend` interface - -- Register `connect-backend` in `turbo/generators/config.ts`. -- Prompts, in this documented order (with equivalent `--args` bypass): - 1. target app (from `getAppChoices`) - 2. backend (`convex`, `hono`, or `trpc`) - 3. include auth client wiring (skipped/forced where noted below) - 4. add an example -- Verify during implementation that Turbo/Plop's `--args` bypass coerces `true`/`false` - for the confirm prompts; if it does not, use list prompts with `yes`/`no` values. -- Adapter matrix (hardcoded map; unsupported combinations fail with a clear message): - - | Backend | Targets | Auth prompt | Notes | - | ------- | -------------------- | ----------- | ---------------------------------------- | - | Convex | mobile | always on | Convex client wiring is auth-integrated | - | Hono | app, desktop, mobile | app/mobile | subsumes the old `hono-client` generator | - | tRPC | app, desktop | app only | subsumes the old `trpc-client` generator | - -- Remove the public `hono-client` and `trpc-client` generators. Their templates move - under `turbo/generators/templates/backend-clients/` as internal adapter templates. -- Adapter definitions live under `turbo/generators/recipes/backend-clients/`. - -### 3. Adapter: Convex (mobile) - -Restore `apps/mobile` to a backend-neutral default: - -- remove its always-on `@init/backend` dependency; -- remove the Convex URLs from its default environment schema and `.env.template`; -- remove the `convex-demo` route and feature; -- remove the always-on Convex provider integration. - -The adapter then generates, from templates under -`turbo/generators/templates/backend-clients/convex/`: - -- the backend workspace dependency using its discovered package name; -- env wiring via the existing `convex.expo()` preset from `@init/env/presets` - (`EXPO_PUBLIC_CONVEX_URL`, `EXPO_PUBLIC_CONVEX_SITE_URL`), merged into - `apps/mobile/src/shared/env.ts` via the anchored `extends: [` modify, plus appended - `.env.template` values: - - ```dotenv - EXPO_PUBLIC_CONVEX_URL="https://example.convex.cloud" - EXPO_PUBLIC_CONVEX_SITE_URL="https://example.convex.site" - ``` - -- the Convex provider inserted through `shared/components/providers.tsx`, with a - `ConvexQueryClient` connected to mobile's existing persisted TanStack Query client; -- the auth client at `apps/mobile/src/shared/auth.ts` (auth is always part of this - adapter): - - ```ts - import { convexClient } from "@init/backend/client/auth" - import { createAuthClient } from "@init/auth/client" - import { adminClient, organizationClient } from "@init/auth/client/plugins" - import { expoClient } from "@init/auth/expo/client" - import { accessControl, adminRole, memberRole, ownerRole } from "@init/auth/permissions" - import * as SecureStore from "expo-secure-store" - import env from "#shared/env.ts" - - export const auth = createAuthClient(env.EXPO_PUBLIC_CONVEX_SITE_URL, [ - expoClient({ storage: SecureStore }), - convexClient(), - adminClient(), - organizationClient({ - ac: accessControl, - roles: { - admin: adminRole, - member: memberRole, - owner: ownerRole, - }, - }), - ]) - - export const { useSession } = auth - ``` - -- a minimal sign-in screen and guarded route group (shared templates with the Hono mobile - variant; see §6); -- with `example` true: a small additive feature and route querying the existing public - documents API from `packages/backend`, generated inside the - `(auth)/(authenticated)` route group from §6. The example uses - `useQuery(convexQuery(...))`, so loading and error states use the normal TanStack - Query result object while Convex remains realtime. The backend client's - `useConvexQuery` export remains the native Convex hook. - -`packages/backend`'s `convex/_generated` output is committed, so generated targets -typecheck before any Convex deployment or account exists. A missing URL value is a -normal env-validation state, not special machinery. Retain the durable Plan 05 -conventions in `packages/backend`: public/shared function structure, auth constants, -logging, and the example public query. - -### 4. Adapter: Hono - -Preserves the old `hono-client` behavior: generates `src/shared/api.ts` from the -existing template and adds the `api` workspace dependency (discovered name). - -Connection wiring per app restores the API URL env value and URL builder: - -`apps/app` uses the API URL seam shipped by work item 1; the adapter only appends the -env value: - -```dotenv -PUBLIC_API_URL="http://localhost:3000" -``` - -`apps/desktop` and `apps/mobile` receive their env schema addition through the -sanctioned anchored env merge, and their URL builder through the sanctioned -`shared/utils.ts` export append (skipped when `buildApiUrl` already exists). - -`apps/desktop`: - -```ts -// apps/desktop/src/shared/env.ts — anchored client schema merge -PUBLIC_API_URL: z.url(), -``` - -```ts -// apps/desktop/src/shared/utils.ts — appended export -export const buildApiUrl = createUrlBuilder(env.PUBLIC_API_URL, isProduction ? "https" : "http") -``` - -```dotenv -PUBLIC_API_URL="http://localhost:3000" -``` - -`apps/mobile`: - -```ts -// apps/mobile/src/shared/env.ts — anchored client schema merge -EXPO_PUBLIC_API_URL: z.url(), -``` - -```ts -// apps/mobile/src/shared/utils.ts — appended export -export const buildApiUrl = createUrlBuilder( - env.EXPO_PUBLIC_API_URL, - isProduction ? "https" : "http" -) -``` - -```dotenv -EXPO_PUBLIC_API_URL="http://localhost:3000" -``` - -With auth wiring enabled: - -`apps/app` generates **no auth files** — the default scaffold's auth client and server -functions already route through the `buildApiUrl` seam (work item 1). The adapter's -auth step for `apps/app` is configuration and documentation only: ensure -`PUBLIC_API_URL` is appended to `.env.template` and print/document that the local -Better Auth handler is not deleted, that `PUBLIC_API_URL` selects the remote handler -(removing it falls back to the local `/api/auth` handler), and that both deployments -must share compatible Better Auth cookie, secret, plugin, and trusted-origin -configuration. - -`apps/mobile` targets `src/shared/auth.ts` (plus the shared sign-in screen and session -gate from §6): - -```ts -import { createAuthClient } from "@init/auth/client" -import { adminClient, organizationClient } from "@init/auth/client/plugins" -import { expoClient } from "@init/auth/expo/client" -import { accessControl, adminRole, memberRole, ownerRole } from "@init/auth/permissions" -import * as SecureStore from "expo-secure-store" -import { buildApiUrl } from "#shared/utils.ts" - -export const auth = createAuthClient(buildApiUrl("/auth"), [ - expoClient({ storage: SecureStore }), - adminClient(), - organizationClient({ - ac: accessControl, - roles: { - admin: adminRole, - member: memberRole, - owner: ownerRole, - }, - }), -]) - -export const { useSession } = auth - -export function getAuthHeaders() { - const headers = new Headers() - const cookies = auth.getCookie() - if (cookies) headers.set("Cookie", cookies) - return headers -} -``` - -### 5. Adapter: tRPC - -Adds `api` as a workspace dependency and installs `@trpc/client`, -`@trpc/tanstack-react-query`, `@tanstack/react-query`, and `superjson` with -`bun add --exact`. - -`apps/app` targets `src/shared/trpc.ts`: - -```ts -import type { TRPCRouter } from "api/client" -import { createIsomorphicFn } from "@tanstack/react-start" -import { getRequestHeaders } from "@tanstack/react-start/server" -import { createTRPCClient, httpBatchStreamLink, loggerLink } from "@trpc/client" -import { createTRPCContext } from "@trpc/tanstack-react-query" -import superjson from "superjson" -import { buildApiUrl } from "#shared/utils.ts" - -export const { useTRPC, useTRPCClient, TRPCProvider } = createTRPCContext() - -const url = buildApiUrl("/trpc") - -export const makeTRPCClient = createIsomorphicFn() - .server(() => - createTRPCClient({ - links: [ - httpBatchStreamLink({ - headers: getRequestHeaders, - transformer: superjson, - url, - }), - ], - }) - ) - .client(() => - createTRPCClient({ - links: [ - loggerLink({ - colorMode: "ansi", - enabled: () => import.meta.env.DEV, - }), - httpBatchStreamLink({ - fetch: (requestUrl, options) => - fetch(requestUrl, { - ...options, - credentials: "include", - }), - transformer: superjson, - url, - }), - ], - }) - ) -``` - -The `TRPCProvider` is inserted through `shared/components/providers.tsx`. The generator -does **not** rewrite `router.tsx` or wire `trpc` into router context, and it does not -replace existing local consumers such as `getGreeting` or `checkEmailAvailability` — -those remain user-owned. With `example` true, a new additive route demonstrates a typed -query through `useTRPC`. - -`apps/desktop` targets `src/shared/trpc.ts`: - -```ts -import type { TRPCRouter } from "api/client" -import { createTRPCClient, httpBatchStreamLink, loggerLink } from "@trpc/client" -import superjson from "superjson" -import { buildApiUrl } from "#shared/utils.ts" - -export const trpcClient = createTRPCClient({ - links: [ - loggerLink({ - colorMode: "ansi", - enabled: () => import.meta.env.DEV, - }), - httpBatchStreamLink({ - fetch: (requestUrl, options) => - fetch(requestUrl, { - ...options, - credentials: "include", - }), - transformer: superjson, - url: buildApiUrl("/trpc"), - }), - ], -}) -``` - -Both variants also install the Hono-style API URL wiring from §4 for their app. - -### 6. Shared mobile auth UI templates - -Both mobile auth variants (Convex and Hono) install a minimal sign-in screen and a -guarded route group; the selected adapter supplies its own `#shared/auth.ts`. Auth -wiring must not install an unused client module — every generated file has a consumer. - -Route protection follows Expo Router's current recommendation: `Stack.Protected` with -`guard`, not manual `` components (the legacy pattern). Because the add-only -rule forbids rewriting `apps/mobile/src/app/_layout.tsx`, the guards live in a -generated route group layout instead of the root layout: - -The group naming mirrors `apps/app`'s routing convention (`_authenticated` / -`_unauthenticated` pathless layouts). Expo Router guards must be declared in a parent -stack, and the root layout is off-limits, so both groups live under one generated -wrapper group: - -```text -apps/mobile/src/app/(auth)/ - _layout.tsx # generated: owns both guards - (unauthenticated)/ - sign-in.tsx # only reachable while signed out - (authenticated)/ - ... # only reachable while signed in -``` - -```tsx -// apps/mobile/src/app/(auth)/_layout.tsx -import { ActivityIndicator } from "@init/native-ui/components/activity-indicator" -import { Stack } from "expo-router" -import { View } from "react-native" -import { useSession } from "#shared/auth.ts" - -export default function AuthLayout() { - const { data: session, isPending } = useSession() - - if (isPending) { - return ( - - - - ) - } - - return ( - - - - - - - - - ) -} -``` - -- `Stack.Protected` redirects automatically when the guard flips and clears protected - history entries on sign-out; the inverted guard hides the unauthenticated screens - from signed-in users. No separate session-gate component exists, so nothing can - become dead code. -- `(unauthenticated)/` holds `sign-in.tsx` initially and is the home for future - unauthenticated screens (sign-up, forgot-password), matching `apps/app`'s - `_unauthenticated` layout. -- Better Auth caches the session in SecureStore on native, so the `isPending` state is - rarely visible on reload. -- With `example` true, the example screen is generated **inside** `(authenticated)/` - so the demo exercises the full auth flow. With auth enabled and no example, - `(authenticated)/` ships containing a minimal generated index screen so the guarded - stack always has an available route. -- Existing default routes (such as `index.tsx`) remain public; generator output and - documentation state explicitly that users move screens into - `app/(auth)/(authenticated)/` to require a session. - -```tsx -// apps/mobile/src/app/(auth)/(unauthenticated)/sign-in.tsx -import { Button } from "@init/native-ui/components/button" -import { Text } from "@init/native-ui/components/text" -import { useState } from "react" -import { TextInput, View } from "react-native" -import { auth } from "#shared/auth.ts" - -export default function SignInScreen() { - const [email, setEmail] = useState("") - const [password, setPassword] = useState("") - const [error, setError] = useState() - - async function signIn() { - setError(undefined) - const result = await auth.signIn.email({ email, password }) - if (result.error) setError(result.error.message) - } - - return ( - - Sign in - - - {error ? ( - - {error} - - ) : null} - - - ) -} -``` - -### 7. Safety and repeatability - -- Preflight with `ensureWorkspaceExists` before any write; report the exact remedy and - abort. Do not silently add or deploy a missing backend workspace. -- Discover package names from `package.json` files; never hardcode `@init/`. -- Use `skipIfExists` and content-check skip-functions so reruns: - - do not duplicate dependencies, imports, providers, routes, or environment keys; - - report an already-connected target as a no-op; - - allow auth or example layers to be added later to an existing connection. -- Every skipped target is named in the generator output; a run never reports success - while quietly omitting part of its work. -- Honor the add-only rule: generators never rewrite existing code in default-scaffold - files; behavior changes happen only through shipped seams (env values, - `providers.tsx`) or the sanctioned additive merges (env schema, `.env.template`, - `shared/utils.ts` export appends). -- Install dependencies once after all package changes and format affected files with - the repository's managed formatter. - -### 8. Documentation - -- Add `connect-backend` examples to the root README and generator documentation. -- Update `packages/backend/README.md` to describe Convex consumption as opt-in through - the generator. -- Document the old `hono-client` / `trpc-client` command names as removed. -- Record the supported app/backend matrix and the files each adapter owns so a user can - review or remove generated wiring. -- Explain that generators operate on the exact template snapshot already present in the - user's project. Plan 07 expands the same machinery with copy-once template recipes; - neither plan introduces a remote catalog or updater. - -## Verification (manual) - -```sh -bun run format -bun run check -bun run analyze -bun run check:monorepo -bun test -``` - -Run generator smoke tests manually in disposable scaffold copies (no CI harness): - -- A default mobile scaffold contains no Convex dependency, URL, provider, feature, or - route. -- Each adapter's connection-only run produces production wiring, no demo screen, and - the project checks without external accounts or deployments. -- Convex mobile wiring connects `ConvexQueryClient` to the existing persisted - `QueryClient`; its example reads loading and error state from - `useQuery(convexQuery(...))`. -- Rerunning the same command is a no-op; adding auth or the example later only adds the - new layer. -- A renamed npm scope is discovered correctly and leaves no hardcoded `@init/` - references. -- A missing backend workspace fails before any write and prints a concrete remedy. -- The Hono and tRPC adapters preserve the behavior of their previous generators. -- Existing demo consumers (dashboard, sign-up form, local server functions) are never - modified. -- In a default `apps/app` scaffold, setting `PUBLIC_API_URL` routes auth and API calls - to `apps/api` and removing it falls back to the local handler — with no file changes - from the generator beyond the `.env.template` append. -- After mobile auth wiring, an unauthenticated user cannot reach an - `(auth)/(authenticated)` route (the guard falls back to `sign-in`), a signed-in user - cannot reach the `(unauthenticated)` screens, and signing out clears authenticated - history entries. -- Skipped targets are reported explicitly in generator output. - -## Acceptance criteria - -- Fresh projects remain backend-neutral until the generator is invoked. -- One `connect-backend` command is the public interface for every supported adapter; - Convex, Hono, and tRPC retain separate explicit implementations behind it. -- `shared/components/providers.tsx` exists in app, desktop, and mobile and is the only - provider-insertion point generators use. -- `apps/app` ships transport-agnostic auth through the `PUBLIC_API_URL`/`buildApiUrl` - seam; connecting it to `apps/api` requires no modification of default-scaffold files. -- Generators never rewrite existing code in default-scaffold files; only the - sanctioned additive merges touch them (add-only rule). -- Production wiring does not depend on including auth or example layers. -- Generated dependencies respect the project's renamed package scope and are installed - with `bun add --exact`. -- Supported invocations are idempotent via Plop `skipIfExists`; unsupported - combinations fail safely. -- Generated projects pass the repository verification commands without requiring an - external service account. - -## Out of scope - -- Deploying Convex or any other backend; creating external accounts or credentials. -- Automatically adding a backend workspace that is absent from the scaffold. -- Snapshot/drift detection, rollback, or any mutation machinery beyond Plop primitives. -- Modifying or replacing existing demo consumers in user projects. -- Supporting arbitrary repositories or frameworks outside maintained Init adapters. -- The copy-once recipe catalog (Plan 07). -- Updating previously scaffolded projects from newer versions of the template. diff --git a/.plans/15-files-sdk-integration.md b/.plans/15-files-sdk-integration.md deleted file mode 100644 index 2c0695dc..00000000 --- a/.plans/15-files-sdk-integration.md +++ /dev/null @@ -1,166 +0,0 @@ -# Plan 15 — Files SDK API integration and React client generator - -**Status:** Completed -**Size:** M -**Depends on:** 07 - -Add Files SDK as a built-in capability of `apps/api`, alongside its existing Hono and -tRPC routes. Only the React client remains an optional copy-once generator. - -## Scope - -- **Server:** directly implement the complete Files SDK Hono gateway in `apps/api`. - It is part of the API workspace whenever that workspace is retained. -- **Client:** add a `files-client` generator based on the official - [React integration](https://files-sdk.dev/docs/ui/client/react). - -Storage remains an application integration, not a shared storage workspace or thin -S3-helper package. - -## 1. Built-in API integration - -Install `files-sdk` exactly in `apps/api`. - -### Files composition - -Create `apps/api/src/shared/files.ts` as the server-only composition root. It owns: - -- the native `files-sdk/bun-s3` adapter; -- the configured `Files` instance; -- bucket configuration; -- upload type, size, and key policy; -- signed URL expiry and download-disposition policy. - -The default adapter uses Bun's native S3 client and local MinIO, so it requires no -optional provider peer dependencies. Projects that need richer S3 behavior can replace -the composition with `files-sdk/s3` and install its peers. - -Keep server and client imports separate so provider credentials and native SDKs cannot -enter frontend bundles. - -### Complete Hono file router - -Create `apps/api/src/routes/v1/files.ts` with `createFilesRouter` from `files-sdk/api` -and `createRouteHandler` from `files-sdk/hono`. - -Mount the handler with `app.all("/")`. The Files SDK handler dispatches its complete -contract internally: - -- `GET` serves downloads; -- `POST` serves JSON operations; -- `PUT` serves upload bytes. - -Mount the route from `apps/api/src/routes/v1/index.ts`: - -```ts -.route("/files", filesRoutes) -``` - -The public endpoint is `/v1/files`. Do not create parallel top-level `/files` or -`/api/files` routes, and do not hand-build per-operation handlers. - -### Authentication and policy - -Configure `authorize` to: - -- resolve the existing Init session from raw request headers; -- throw `FilesError("Unauthorized", ...)` without a valid session; -- scope every key, bulk key, and both sides of copy/move under - `users//`; -- force attachment disposition; -- constrain URL expiry and result counts. - -Require a validated `FILES_API_SECRET` and pass it explicitly to the gateway. Configure -conservative upload, list, and search limits. Add `PUT` to the API's CORS methods. - -Use the gateway's keyless upload protocol: presign or select the safe proxy path, upload, -then complete and verify the stored object. Content sniffing and validation may force -the proxy path when a direct provider upload would bypass server policy. - -### Environment - -Validate and document: - -- `FILES_API_SECRET`; -- `S3_ACCESS_KEY_ID`; -- `S3_BUCKET`; -- `S3_ENDPOINT`; -- `S3_REGION`; -- `S3_SECRET_ACCESS_KEY`. - -The API env template points these variables at the existing local MinIO service and its -`assets` bucket. - -## 2. `files-client` React generator - -Register one public generator: - -```sh -bun run generate files-client -bun run generate files-client --args app http://localhost:3000/v1/files -``` - -The generator requires the built-in `/v1/files` API contract before writing. It can -target any workspace under `apps/` and generates the React integration. Astro consumers -use that integration through Astro's React renderer. React Native remains future work. - -The generator: - -1. Installs `files-sdk` exactly in the selected app. -2. Creates `src/shared/files.ts`. -3. Configures `/v1/files` through the existing API URL helper. -4. Sends the existing session credentials through JSON requests and gateway-bound XHR - uploads without forwarding credentials to provider-signed URLs. -5. Preserves XHR upload progress. -6. Exposes an app-local `useFiles` hook plus `useFile`, `useList`, and `useSearch`. -7. Keeps every provider import and credential out of the client workspace. - -The generator creates infrastructure, not feature UI. Documentation includes concise -upload, progress, download-versus-URL, error, cancellation, and reactive-read examples. - -## Verification - -- Type-check the built-in API integration. -- Assert `/v1/files` is mounted and unauthenticated operations are denied. -- Exercise the route against local MinIO. -- Generate `files-client` into a disposable scaffold and type-check the app. -- Verify JSON calls, explicit uploads, proxy uploads, and upload - authorization/completion send the required credentials. -- Rerun the client generator and verify a reported no-op. -- Verify a missing API contract or unsupported target fails before any write. -- Assert server-only provider modules are absent from client builds. -- Run: - -```sh -bun run format -bun run check -bun run analyze -bun run check:monorepo -bun test -``` - -## Acceptance criteria - -- `apps/api/src/shared/files.ts` owns the built-in Files instance. -- `apps/api/src/routes/v1/files.ts` exposes the official complete Hono gateway. -- The route is mounted exactly at `/v1/files`. -- The gateway requires an authenticated session and scopes every operation to that user. -- Stable secret, origin, upload, result, expiry, type, key, and disposition policies are - enforced. -- The checked-in API environment template works with local MinIO. -- `bun run generate files-client --args app` creates a type-checking authenticated React - client for `/v1/files`. -- `bun run generate files-client --args web` creates a type-checking React client for - use through Astro's React renderer. -- The client retains upload progress and never imports server-only modules. -- Rerunning the generator is a reported no-op. -- Missing or unsupported requirements fail during preflight without partial writes. - -## Out of scope - -- A server generator. -- Generated file-browser or upload UI. -- React Native/Expo client integration. -- A Convex files backend adapter. -- Other provider compositions in the base template. -- Deploying storage services or creating external credentials. diff --git a/.plans/TRACKER.md b/.plans/TRACKER.md deleted file mode 100644 index 00ae74c6..00000000 --- a/.plans/TRACKER.md +++ /dev/null @@ -1,53 +0,0 @@ -# Template Improvement Plans - -Plans for evolving the `init` template monorepo. Each plan is self-contained and can be handed to an agent independently, but respect the ordering constraints below. - -## Active plans - -None. - -## Completed plans - -| # | Plan | Notes | -| --- | ----------------------------------------------------------------------------------- | --------------------------------------------------- | -| 01 | [CLI correctness fixes](01-cli-correctness-fixes.md) | CLI is deleted by plan 13; kept as history | -| 02 | [Package consolidation & dead-code sweep](02-package-consolidation.md) | | -| 03 | [App hygiene](03-app-hygiene.md) | | -| 04 | [CLI manifest & setup rework](04-cli-manifest-and-setup.md) | CLI is deleted by plan 13; kept as history | -| 05 | [Convex backend example & conventions](05-convex-backend-example.md) | Durable conventions retained; demo superseded by 14 | -| 07 | [Local template recipes](07-template-recipes.md) | Copy-once recipe catalog and scaffold generators | -| 09 | [CLI: Effect v4, adamantite, CI](09-cli-effect-v4-and-tooling.md) | CLI is deleted by plan 13; kept as history | -| 10 | [init.now marketing site](10-marketing-site.md) | Deployed to Vercel at `init.now` | -| 12 | [AGENTS.md + CONTEXT.md + docs refactor](12-agents-context-docs-refactor.md) | Layered application and template documentation | -| 13 | [Descope: delete the CLI, return to template scripts](13-descope-cli-to-scripts.md) | CLI removed; template scripts restored | -| 14 | [Generic backend connection generator](14-connect-backend-generator.md) | Unified Convex, Hono, and tRPC adapter workflow | -| 15 | [Files SDK API + React client generator](15-files-sdk-integration.md) | Built-in API gateway and optional React client | - -## Deleted plans - -Plans 06 (update command rework), 08 (CLI release automation), and 11 (`bun create init-now` support) were deleted — they existed only because `init-now` was a published npm package, which plan 13 removes. Their durable ideas (rename idempotency, CI scaffold smoke test, agent-update diffing gotchas) were folded into plan 13. Full text remains in git history. - -## Context (shared by all plans) - -- This repo is a template monorepo. Users scaffold projects with `bun create metaideas/init`, then manage them with plain scripts inside the template (`bun template setup`, `bun template rename`, `bun template add app|package`). See plan 13. -- **CLI removal (decided, plan 13)**: the `init-now` npm package is deprecated and `cli/` deleted. No published tooling, no release-please, no template versioning. Projects stamp the template commit sha in `.template.json` at setup time so agents can diff against upstream for updates. -- Guiding principle: **the template ships a wired, consumed core with zero required external services**. Optional capability lives in selectable packages (chosen during `bun template setup`) or snapshot-matched local Turbo recipes (plan 07). Local dev must work with `docker compose` alone — no accounts, no API keys. -- Packages vs template-recipe criterion (decided): - - **Package** = ongoing dependency with its own third-party deps and lifecycle (payments, ai, analytics, kv, email client). These stay, even with zero in-template consumers, because setup lets users select them. - - **Template recipe** = copy-once code the user owns after generation (email templates, one-off utils, extra UI components, auth integration snippets, env presets). -- Recipe definitions ship inside `turbo/generators/` and match the scaffold's recorded - template commit. There is no hosted registry or independent updater; existing projects - receive newer recipes through the documented agent-assisted upstream diff workflow. -- `apps/app` is independently full-stack through TanStack Start server routes/functions. `apps/api` (Hono) and `packages/backend` (Convex) are optional backend choices; Plan 14 connects them to clients through local generator adapters. Convex stays in `packages/` because it is consumed like a library (client + generated types) and deploys to Convex cloud. - -## Verification (run after any code change) - -```sh -bun run format # adamantite format -bun run check # lint + typecheck -bun run analyze # knip dead-code/dep analysis -bun run check:monorepo -bun test # root workspaces -``` - -Use conventional commits (`feat:`, `fix:`, `chore:`, `refactor:`, ...). diff --git a/README.md b/README.md index 5931b505..0bf7a4e8 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

-Modern monorepo template for shipping TypeScript apps everywhere. +A modern monorepo template for whatever you build next. See the template in action at [init.now](https://init.now). The public site is the same polished Astro marketing app included when a scaffold keeps the `web` workspace. @@ -22,6 +22,7 @@ generators. The site is not a hosted recipe catalog or project updater. - [Getting Started](./docs/getting-started.md) - [Development](./docs/development.md) +- [Internationalization](./docs/internationalization.md) - [Project Structure](./docs/architecture/project-structure.md) - [Package Guidance](./docs/packages.md) - [Template Commands](./docs/template-commands.md) diff --git a/apps/api/package.json b/apps/api/package.json index 822e9af6..531389f2 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -39,7 +39,7 @@ "superjson": "2.2.6" }, "devDependencies": { - "@inlang/paraglide-js": "2.8.0", + "@inlang/paraglide-js": "2.23.0", "@tooling/tsconfig": "workspace:*", "@types/bun": "1.3.14", "typescript": "7.0.2" diff --git a/apps/api/src/routes/v1/index.ts b/apps/api/src/routes/v1/index.ts index aa54e363..2f2c1226 100644 --- a/apps/api/src/routes/v1/index.ts +++ b/apps/api/src/routes/v1/index.ts @@ -26,7 +26,9 @@ export default factory validator("query", z.object({ name: z.string().optional() })), (c) => { const query = c.req.valid("query") - return c.text(m.api_greeting({ name: query.name ?? "Hono" }, { locale: c.var.language })) + return c.text( + m.api_hello_greeting({ name: query.name ?? "Hono" }, { locale: c.var.language }) + ) } ) .get("/me", requireSession, (c) => c.json(c.var.session.user)) diff --git a/apps/app/package.json b/apps/app/package.json index 51dd79aa..09be99d1 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -35,7 +35,7 @@ }, "devDependencies": { "@babel/core": "7.29.7", - "@inlang/paraglide-js": "2.8.0", + "@inlang/paraglide-js": "2.23.0", "@rolldown/plugin-babel": "0.2.3", "@tailwindcss/vite": "4.3.3", "@tanstack/devtools-vite": "0.8.3", diff --git a/apps/app/src/shared/components/locale-toggle.tsx b/apps/app/src/shared/components/locale-toggle.tsx index 56ceb840..6afada9f 100644 --- a/apps/app/src/shared/components/locale-toggle.tsx +++ b/apps/app/src/shared/components/locale-toggle.tsx @@ -15,7 +15,7 @@ export function LocaleToggle() { }> - {m.switch_locale()} + {m.shared_locale_switch()} - 🇪🇸 {m.spanish()} + 🇪🇸 {m.shared_locale_spanish()} { void setLocale("en") }} > - 🇺🇸 {m.english()} + 🇺🇸 {m.shared_locale_english()} diff --git a/apps/desktop/package.json b/apps/desktop/package.json index e5b71a41..b354ac85 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -31,7 +31,7 @@ }, "devDependencies": { "@babel/core": "7.29.7", - "@inlang/paraglide-js": "2.8.0", + "@inlang/paraglide-js": "2.23.0", "@rolldown/plugin-babel": "0.2.3", "@tailwindcss/vite": "4.3.3", "@tanstack/devtools-vite": "0.8.3", diff --git a/apps/desktop/src/features/local-files/components/file-editor.tsx b/apps/desktop/src/features/local-files/components/file-editor.tsx index d3700a9e..8a536182 100644 --- a/apps/desktop/src/features/local-files/components/file-editor.tsx +++ b/apps/desktop/src/features/local-files/components/file-editor.tsx @@ -37,8 +37,8 @@ export default function FileEditor() { return ( - {m.desktop_file_title()} - {m.desktop_file_description()} + {m.desktop_local_files_title()} + {m.desktop_local_files_description()}
@@ -49,17 +49,17 @@ export default function FileEditor() { }} type="button" > - {m.choose_file()} + {m.desktop_local_files_choose()}

- {path ?? m.no_file_selected()} + {path ?? m.desktop_local_files_empty_state()}

{path ? ( <>