From cc03ae55f12813fc8fa4704a08d91c4577206c8c Mon Sep 17 00:00:00 2001 From: Tyler Benfield Date: Fri, 12 Jun 2026 06:56:17 -0400 Subject: [PATCH 1/8] chore: update workflow automation --- .agents/diary/pr-quality-workflow.md | 2 + .github/workflows/AGENTS.md | 3 + .github/workflows/pr-quality.yml | 75 ++++++ .github/workflows/preview-cli-package.yml | 24 +- .github/workflows/publish-cli.yml | 71 ++---- .github/workflows/publish-compute.yml | 268 ++++++++++++++++++++++ AGENTS.md | 110 +-------- package.json | 4 + packages/cli/AGENTS.md | 109 +++++++++ scripts/prepare-compute-publish.mjs | 133 +++++++++++ scripts/resolve-cli-version.mjs | 118 ---------- scripts/resolve-package-version.mjs | 177 ++++++++++++++ 12 files changed, 801 insertions(+), 293 deletions(-) create mode 100644 .agents/diary/pr-quality-workflow.md create mode 100644 .github/workflows/AGENTS.md create mode 100644 .github/workflows/pr-quality.yml create mode 100644 .github/workflows/publish-compute.yml create mode 100644 packages/cli/AGENTS.md create mode 100644 scripts/prepare-compute-publish.mjs delete mode 100644 scripts/resolve-cli-version.mjs create mode 100644 scripts/resolve-package-version.mjs diff --git a/.agents/diary/pr-quality-workflow.md b/.agents/diary/pr-quality-workflow.md new file mode 100644 index 0000000..775525f --- /dev/null +++ b/.agents/diary/pr-quality-workflow.md @@ -0,0 +1,2 @@ +Unrelated bug or code smell: The new recursive typecheck command exposes existing `packages/cli` TypeScript errors in `src/lib/app/branch-database.ts`, `tests/helpers.ts`, several `project-real-mode` mocks, and imports from scripts without declarations. The workflow change intentionally does not fix these code issues. +Unrelated bug or code smell: The new recursive test command currently fails in `packages/cli` because `tests/resolve-cli-version.test.ts` imports the deleted `scripts/resolve-cli-version.mjs`, and several other tests time out. This is outside the PR Quality workflow scope. diff --git a/.github/workflows/AGENTS.md b/.github/workflows/AGENTS.md new file mode 100644 index 0000000..8f26217 --- /dev/null +++ b/.github/workflows/AGENTS.md @@ -0,0 +1,3 @@ +# GitHub Workflows + +- Pin all GitHub Actions to their commit SHA, not a tag. Append the tag as a comment for readability (e.g., `uses: actions/checkout@abc123... # v4.3.2`). diff --git a/.github/workflows/pr-quality.yml b/.github/workflows/pr-quality.yml new file mode 100644 index 0000000..9d40d8e --- /dev/null +++ b/.github/workflows/pr-quality.yml @@ -0,0 +1,75 @@ +name: PR Quality + +# This workflow owns required PR status checks. Add only jobs that should block +# merging when they fail. + +on: + pull_request: + +concurrency: + group: pr-quality-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + typecheck: + name: Type Check + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Set up pnpm + uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + + - name: Set up Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version-file: package.json + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Type check + run: pnpm --recursive exec tsc --noEmit + + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Set up Biome + uses: biomejs/setup-biome@4c91541eaada48f67d7dbd7833600ce162b68f51 # v2.7.1 + + - name: Lint + run: biome ci . + + test: + name: Test + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + - name: Set up pnpm + uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + + - name: Set up Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version-file: package.json + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Test + run: pnpm --recursive test diff --git a/.github/workflows/preview-cli-package.yml b/.github/workflows/preview-cli-package.yml index 343834f..185584d 100644 --- a/.github/workflows/preview-cli-package.yml +++ b/.github/workflows/preview-cli-package.yml @@ -20,29 +20,18 @@ jobs: pull-requests: write steps: - - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e + - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 with: - node-version: 24 + cache: true - - name: Enable pnpm - run: | - corepack enable - PNPM_VERSION="$(node -p 'const pm = require("./package.json").packageManager; const match = pm && pm.match(/^pnpm@(.+)$/); if (!match) throw new Error("packageManager must be pnpm@"); match[1]')" - corepack prepare "pnpm@${PNPM_VERSION}" --activate - echo "PNPM_STORE_PATH=$(pnpm store path)" >> "$GITHUB_ENV" - - - name: Cache pnpm store - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: - path: ${{ env.PNPM_STORE_PATH }} - key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - pnpm-store-${{ runner.os }}- + node-version-file: package.json - name: Install dependencies run: pnpm install --frozen-lockfile @@ -50,7 +39,8 @@ jobs: - name: Resolve preview version id: cli_version run: | - node scripts/resolve-cli-version.mjs pr \ + node scripts/resolve-package-version.mjs pr \ + --package-dir packages/cli \ --sha '${{ github.event.pull_request.head.sha }}' \ --pr-number '${{ github.event.pull_request.number }}' >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/publish-cli.yml b/.github/workflows/publish-cli.yml index 9f630fa..80e05cc 100644 --- a/.github/workflows/publish-cli.yml +++ b/.github/workflows/publish-cli.yml @@ -25,29 +25,18 @@ jobs: id-token: write steps: - - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e + - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 with: - node-version: 24 - registry-url: https://registry.npmjs.org - - - name: Enable pnpm - run: | - corepack enable - PNPM_VERSION="$(node -p 'const pm = require("./package.json").packageManager; const match = pm && pm.match(/^pnpm@(.+)$/); if (!match) throw new Error("packageManager must be pnpm@"); match[1]')" - corepack prepare "pnpm@${PNPM_VERSION}" --activate - echo "PNPM_STORE_PATH=$(pnpm store path)" >> "$GITHUB_ENV" + cache: true - - name: Cache pnpm store - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: - path: ${{ env.PNPM_STORE_PATH }} - key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - pnpm-store-${{ runner.os }}- + node-version-file: package.json + registry-url: https://registry.npmjs.org - name: Install dependencies run: pnpm install --frozen-lockfile @@ -55,19 +44,11 @@ jobs: - name: Resolve dev version id: cli_version run: | - node scripts/resolve-cli-version.mjs dev \ + node scripts/resolve-package-version.mjs dev \ + --package-dir packages/cli \ --run-number "${GITHUB_RUN_NUMBER}" \ --run-attempt "${GITHUB_RUN_ATTEMPT}" >> "$GITHUB_OUTPUT" - - name: Fail if version already exists on npm - run: | - PACKAGE='@prisma/cli' - VERSION='${{ steps.cli_version.outputs.version }}' - if npm view "${PACKAGE}@${VERSION}" version >/dev/null 2>&1; then - echo "${PACKAGE}@${VERSION} already exists on npm." - exit 1 - fi - - name: Run focused CLI tests run: pnpm --filter @prisma/cli test @@ -150,30 +131,19 @@ jobs: exit 1 fi - - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 persist-credentials: false - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e + - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 with: - node-version: 24 - registry-url: https://registry.npmjs.org - - - name: Enable pnpm - run: | - corepack enable - PNPM_VERSION="$(node -p 'const pm = require("./package.json").packageManager; const match = pm && pm.match(/^pnpm@(.+)$/); if (!match) throw new Error("packageManager must be pnpm@"); match[1]')" - corepack prepare "pnpm@${PNPM_VERSION}" --activate - echo "PNPM_STORE_PATH=$(pnpm store path)" >> "$GITHUB_ENV" + cache: true - - name: Cache pnpm store - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: - path: ${{ env.PNPM_STORE_PATH }} - key: pnpm-store-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - pnpm-store-${{ runner.os }}- + node-version-file: package.json + registry-url: https://registry.npmjs.org - name: Install dependencies run: pnpm install --frozen-lockfile @@ -183,16 +153,9 @@ jobs: run: | PACKAGE='@prisma/cli' LATEST="$(npm view "${PACKAGE}" dist-tags.latest --silent 2>/dev/null || true)" - node scripts/resolve-cli-version.mjs next-beta --latest "${LATEST}" >> "$GITHUB_OUTPUT" - - - name: Fail if version already exists on npm - run: | - PACKAGE='@prisma/cli' - VERSION='${{ steps.cli_version.outputs.version }}' - if npm view "${PACKAGE}@${VERSION}" version >/dev/null 2>&1; then - echo "${PACKAGE}@${VERSION} already exists on npm." - exit 1 - fi + node scripts/resolve-package-version.mjs next-beta \ + --package-dir packages/cli \ + --latest "${LATEST}" >> "$GITHUB_OUTPUT" - name: Fail if release tag already exists run: | diff --git a/.github/workflows/publish-compute.yml b/.github/workflows/publish-compute.yml new file mode 100644 index 0000000..256d1db --- /dev/null +++ b/.github/workflows/publish-compute.yml @@ -0,0 +1,268 @@ +name: Publish Compute + +on: + push: + branches: + - main + workflow_dispatch: + inputs: + dry_run: + description: Validate the next official beta release without publishing or tagging + required: false + type: boolean + default: false + +concurrency: + group: publish-compute-${{ github.event_name }} + cancel-in-progress: false + +jobs: + publish-dev: + if: ${{ github.event_name == 'push' }} + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + + - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + with: + cache: true + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version-file: package.json + registry-url: https://registry.npmjs.org + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Resolve dev version + id: compute_version + run: | + node scripts/resolve-package-version.mjs dev \ + --package-dir packages/compute \ + --run-number "${GITHUB_RUN_NUMBER}" \ + --run-attempt "${GITHUB_RUN_ATTEMPT}" >> "$GITHUB_OUTPUT" + + - name: Run focused compute tests + run: pnpm --filter @prisma/compute test + + - name: Build compute package + run: pnpm --filter @prisma/compute build + + - name: Prepare staged publish package + run: node scripts/prepare-compute-publish.mjs .publish/compute --version '${{ steps.compute_version.outputs.version }}' + + - name: Audit staged package contents + working-directory: .publish/compute + run: | + PACK_JSON="$(npm pack --dry-run --json)" + PACK_JSON="${PACK_JSON}" node -e " + const pack = JSON.parse(process.env.PACK_JSON)[0] + const files = pack.files.map((file) => file.path).sort() + const forbidden = files.filter((file) => + file.startsWith('src/') || + file.startsWith('tests/') || + file.startsWith('fixtures/') || + file.startsWith('docs/') || + file.startsWith('.prisma/') || + file.startsWith('.publish/') + ) + if (forbidden.length) { + console.error('Forbidden files in npm package:', forbidden.join(', ')) + process.exit(1) + } + for (const required of ['dist/index.js', 'dist/index.d.ts', 'README.md', 'LICENSE', 'package.json']) { + if (!files.includes(required)) { + console.error('Missing required package file:', required) + process.exit(1) + } + } + " + + - name: Smoke test staged tarball install + run: | + TARBALL="$(cd .publish/compute && npm pack --silent)" + TMPDIR="$(mktemp -d)" + cat > "${TMPDIR}/package.json" <<'EOF' + { + "name": "compute-publish-smoke", + "private": true, + "type": "module" + } + EOF + pnpm add -D "${PWD}/.publish/compute/${TARBALL}" --dir "${TMPDIR}" + ( + cd "${TMPDIR}" + node -e "await import('@prisma/compute')" + ) + + - name: Publish dev package to npm + working-directory: .publish/compute + run: npm publish --access public --tag dev --provenance + + - name: Summarize dev publish + run: | + VERSION='${{ steps.compute_version.outputs.version }}' + { + echo "## Publish Compute Dev" + echo + echo "- npm package: \`@prisma/compute@${VERSION}\`" + echo "- npm dist-tag: \`dev\`" + } >> "$GITHUB_STEP_SUMMARY" + + publish-official: + if: ${{ github.event_name == 'workflow_dispatch' }} + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + + steps: + - name: Ensure workflow runs from main + run: | + if [ "${GITHUB_REF}" != "refs/heads/main" ]; then + echo "This workflow only publishes official releases from main." + exit 1 + fi + + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + fetch-depth: 0 + persist-credentials: false + + - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + with: + cache: true + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version-file: package.json + registry-url: https://registry.npmjs.org + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Resolve next beta version + id: compute_version + run: | + PACKAGE='@prisma/compute' + LATEST="$(npm view "${PACKAGE}" dist-tags.latest --silent 2>/dev/null || true)" + node scripts/resolve-package-version.mjs next-beta \ + --package-dir packages/compute \ + --latest "${LATEST}" >> "$GITHUB_OUTPUT" + + - name: Fail if release tag already exists + run: | + VERSION='${{ steps.compute_version.outputs.version }}' + TAG="compute-v${VERSION}" + if git ls-remote --exit-code --tags origin "refs/tags/${TAG}" >/dev/null 2>&1; then + echo "Release tag ${TAG} already exists." + exit 1 + fi + + - name: Run focused compute tests + run: pnpm --filter @prisma/compute test + + - name: Build compute package + run: pnpm --filter @prisma/compute build + + - name: Prepare staged publish package + run: node scripts/prepare-compute-publish.mjs .publish/compute --version '${{ steps.compute_version.outputs.version }}' + + - name: Audit staged package contents + working-directory: .publish/compute + run: | + PACK_JSON="$(npm pack --dry-run --json)" + PACK_JSON="${PACK_JSON}" node -e " + const pack = JSON.parse(process.env.PACK_JSON)[0] + const files = pack.files.map((file) => file.path).sort() + const forbidden = files.filter((file) => + file.startsWith('src/') || + file.startsWith('tests/') || + file.startsWith('fixtures/') || + file.startsWith('docs/') || + file.startsWith('.prisma/') || + file.startsWith('.publish/') + ) + if (forbidden.length) { + console.error('Forbidden files in npm package:', forbidden.join(', ')) + process.exit(1) + } + for (const required of ['dist/index.js', 'dist/index.d.ts', 'README.md', 'LICENSE', 'package.json']) { + if (!files.includes(required)) { + console.error('Missing required package file:', required) + process.exit(1) + } + } + " + + - name: Smoke test staged tarball install + run: | + TARBALL="$(cd .publish/compute && npm pack --silent)" + TMPDIR="$(mktemp -d)" + cat > "${TMPDIR}/package.json" <<'EOF' + { + "name": "compute-publish-smoke", + "private": true, + "type": "module" + } + EOF + pnpm add -D "${PWD}/.publish/compute/${TARBALL}" --dir "${TMPDIR}" + ( + cd "${TMPDIR}" + node -e "await import('@prisma/compute')" + ) + + - name: Ensure release still targets the latest main + if: ${{ !inputs.dry_run }} + run: | + git fetch origin main + if [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/main)" ]; then + echo "main moved while the release was running. Rerun the workflow from the latest main." + exit 1 + fi + + - name: Publish official package to npm + if: ${{ !inputs.dry_run }} + working-directory: .publish/compute + run: npm publish --access public --tag latest --provenance + + - name: Create release tag + if: ${{ !inputs.dry_run }} + env: + GH_TOKEN: ${{ github.token }} + run: | + VERSION='${{ steps.compute_version.outputs.version }}' + TAG="compute-v${VERSION}" + SHA="$(git rev-parse HEAD)" + gh api \ + --method POST \ + "repos/${GITHUB_REPOSITORY}/git/refs" \ + -f ref="refs/tags/${TAG}" \ + -f sha="${SHA}" + + - name: Summarize release + run: | + VERSION='${{ steps.compute_version.outputs.version }}' + LATEST='${{ steps.compute_version.outputs.latest }}' + { + echo "## Publish Compute Official" + echo + echo "- Previous npm latest: \`${LATEST:-none}\`" + echo "- Version: \`${VERSION}\`" + echo "- Dry run: \`${{ inputs.dry_run }}\`" + if [ '${{ inputs.dry_run }}' = 'true' ]; then + echo "- Publish: skipped" + echo "- Tag creation: skipped" + else + echo "- npm package: \`@prisma/compute@${VERSION}\`" + echo "- npm dist-tag: \`latest\`" + echo "- git tag: \`compute-v${VERSION}\`" + fi + } >> "$GITHUB_STEP_SUMMARY" diff --git a/AGENTS.md b/AGENTS.md index f305150..5bc015a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,109 +2,11 @@ ## Purpose -This repo uses document-driven development for the new Prisma CLI. +Use this file for repo-wide instructions. Use package-local `AGENTS.md` files for package-specific rules. -The docs in `docs/product` are the source of truth. Do not invent product behavior in code that is not already grounded in those docs. If behavior is unclear or missing, update docs first. +## Development Setup -## What This CLI Is - -- This is the future unified Prisma CLI. -- The first implementation slice is app deployment workflows, but the command model must preserve the long-term CLI for ORM, Postgres, and app workflows. - -## Read These First - -Start with `docs/README.md` for the public docs index. - -1. `docs/product/resource-model.md` -2. `docs/product/command-principles.md` -3. `docs/product/command-spec.md` -4. `docs/product/cli-style-guide.md` -5. `docs/product/output-conventions.md` -6. `docs/product/error-conventions.md` - -Architecture and contributor workflow references: - -- `ARCHITECTURE.md` -- `docs/architecture/overview.md` -- `docs/onboarding/getting-started.md` -- `docs/onboarding/common-tasks.md` -- `docs/onboarding/testing.md` - -## Non-Negotiable Product Rules - -- Group commands by developer workflow, not product ownership. -- No `orm`, `postgres`, or `compute` namespaces in the command surface. -- Canonical command shape is `prisma `. -- The current preview uses only `auth`, `project`, `branch`, and `app`. -- Preserve the long-term resource model: - - `workspace -> project -> branch -> { app, database }` - -For exact definitions and resolution rules, see `resource-model.md` and `command-spec.md`. - -## Branch Model - -Do not redefine this casually: - -- everything under a project happens in a branch -- `local` is local CLI context only, not a branch or deploy target -- `production` is a protected durable branch -- every other named branch is preview by default -- preview branches are disposable by default -- non-production branches can become durable later -- first remote deploy defaults to preview -- production is reached by `app promote` or explicit user targeting - -See: - -- `docs/product/resource-model.md` -- `docs/product/command-spec.md` - -## Output and Error Behavior - -Before changing CLI UX, read: - -- `docs/product/cli-style-guide.md` -- `docs/product/output-conventions.md` -- `docs/product/error-conventions.md` - -Important themes: - -- stdout is for machine-readable data -- stderr is for human-oriented status and decoration -- `--json` is explicit -- non-TTY and non-interactive behavior must stay automation-friendly -- structured error codes are the branching surface for agents and CI - -## Error Handling - -- Use `better-result` for owned application code that can fail. -- Model expected failures as `TaggedError` types from `better-result`. -- Put tagged error message construction in the constructor. Instantiate tagged errors directly; delete factory functions that only wrap constructors. Use static constructors only when they encode distinct domain variants. -- Represent unexpected failures as `UnhandledException` from `better-result`. -- Return only errors produced by the function plus errors propagated from callees. Do not create app-wide error unions. -- Wrap throwing or rejecting boundaries with `Result.try` or `Result.tryPromise`. This includes SDK calls, I/O, parsing, and async framework calls. -- Wrap expected throwing failures at the lowest throwing expression and map them to the local tagged error type. -- Once a function returns `Result`, do not throw inside its body for modeled boundaries. Return expected errors, abort errors, and propagated `UnhandledException` values in the error union; throw only at temporary or final CLI-facing boundaries. -- When mapping abortable boundary failures, prefer `signal.aborted` over matching error names or messages to detect cancellation. -- Do not wrap a boundary only to match `UnhandledException` and rethrow it. Let unexpected failures throw directly when no expected error is modeled or propagated. -- Use `Result.gen` to compose multiple results. Do not manually chain `isErr` propagation when `yield*` can express the flow. -- Propagate typed results through lower layers. Do not convert results to plain values, `null`, booleans, or thrown exceptions below the boundary. -- Convert results only at CLI-facing boundaries: command runners, controllers that produce command output, auth providers, API/client adapters, storage adapters, and startup assembly. -- Match errors exhaustively with `matchError`. Do not use catch-all handlers or partial matches. -- Throw tagged errors directly when their message and context are already correct. Do not wrap them in generic `Error` instances. -- Do not refactor generated code, third-party SDK internals, or framework internals for result handling. - -## When Making Changes - -- Prefer tightening existing docs over adding new surface area. -- Keep nouns and verbs stable across docs, help, output, and code. -- Do not add shortcuts or aliases as canonical forms. -- Do not let the current app preview introduce abstractions that will block later ORM/Postgres integration. -- If docs conflict, resolve the docs rather than guessing in implementation. - -## Default Agent Workflow - -1. Read the product docs above. -2. Identify the relevant source-of-truth doc for the task. -3. If implementation requires undefined behavior, update docs first. -4. Keep changes aligned with the unified CLI direction, not just the current app slice. +1. Run `pnpm install`. +2. Use `pnpm --filter