Skip to content

feat(cli): polish CLI output, dev checks, and update notifications#116

Merged
maxktz merged 16 commits intomainfrom
feat/cli-updates
Apr 12, 2026
Merged

feat(cli): polish CLI output, dev checks, and update notifications#116
maxktz merged 16 commits intomainfrom
feat/cli-updates

Conversation

@maxktz
Copy link
Copy Markdown
Contributor

@maxktz maxktz commented Apr 12, 2026

Summary

  • Unify push command output style to match status command (consistent sections, single spinner)
  • Simplify telemetry to env-var-only gating (PAYKIT_TELEMETRY_DISABLED=1 / DO_NOT_TRACK=1), remove ~/.paykit/ config dir and telemetry subcommand
  • Add version consistency check across @paykitjs/* packages at dev startup
  • Add update notification to push and status commands (non-blocking npm registry check)
  • Show time-only dimmed timestamps in dev logger

Test plan

  • pnpm paykitjs status shows consistent output with sections
  • pnpm paykitjs push shows consistent output, confirmation prompt, and sync result in outro
  • Version mismatch detected when @paykitjs/* packages are on different versions
  • Update notification appears after outro when a newer version is available on npm
  • pnpm typecheck passes
  • pnpm lint passes (no new warnings)

Summary by CodeRabbit

  • New Features

    • Added update-notification checks and CLI install hint
    • Added DB studio script, product-diff preview, and push confirmation prompt
    • Suspense fallbacks for login and checkout loading states
  • Changes

    • Renamed CLI command from "check" to "status" and added --throw
    • Telemetry commands removed; telemetry now respects environment variables
    • CLI refactored for dynamic subcommand loading and shared utilities
    • Package versions bumped to 0.0.2
  • Chores

    • Next/React and dev deps updated; dev start script simplified

maxktz added 11 commits April 10, 2026 21:42
Export schema from paykitjs/database so consumers can reference it
without reaching into internal package paths. Add drizzle config and
db:studio script to demo app.
…shared utils

BREAKING: `paykitjs check` is removed. Use `paykitjs status` instead.
Pass `--throw` to exit with code 1 on issues (for CI).

- Add loading spinner during async work
- Remove command name echo (p.intro)
- Parallelize DB and Stripe checks in status
- Lazy-load command modules and heavy deps for instant spinner
- Skip runtime dev sync check during CLI via PAYKIT_CLI env var
- Fire telemetry early, don't await flush on success (fixes hang)
- Extract shared CLI utils (loadCliDeps, checkDatabase, checkStripe, etc.)
- Show full package manager command (e.g. pnpm dlx paykitjs push)
- Context-aware outro messages (migrations vs products vs both)
- Surface Stripe and DB errors with clear messages
- Add getRunCommand helper to detect.ts
- Rename check command to status, add --throw flag
- Fix e2e test fixture Pool reference and suppress runtime sync check
…migrations

On dev startup, PayKit now checks database connectivity, pending
migrations, and product sync status. Errors surface as clear log
messages instead of cryptic query failures.
Single spinner for all loading, consistent section headers with p.log.info,
confirmation prompt at the end showing full context, and sync result in outro.
Remove ~/.paykit/ config directory, telemetry subcommand, and first-run
notice. Telemetry is now disabled via PAYKIT_TELEMETRY_DISABLED=1 or
DO_NOT_TRACK=1 environment variables.
Checks that all @paykitjs/* packages are on the same version at dev
startup. Also simplifies dev check logging to use console.warn with
picocolors styling, removes separate DB connectivity check, and uses
globalThis to deduplicate checks across module re-evaluations.
Checks npm registry for latest version in parallel with command
execution and shows a notification after the outro if an update is
available. Uses detected package manager for the install command.
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
paykit Ready Ready Preview, Comment Apr 12, 2026 10:04am

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 12, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors CLI to dynamic command loading and shared DI utilities; replaces plan-based syncs with product-based flows (status/push); removes telemetry subcommands in favor of env-driven control; adds update-check and dependency tools; exposes database schema; adds Drizzle config and Suspense fallbacks; introduces multiple utilities, dev checks, and test config tweaks.

Changes

Cohort / File(s) Summary
Demo app & Drizzle
apps/demo/drizzle.config.ts, apps/demo/package.json
Add Drizzle Kit config referencing shared schema and DATABASE_URL; bump Next/React versions, change dev script to next dev, add db:studio, and add drizzle-kit/drizzle-orm deps.
React Suspense fallbacks
apps/demo/src/app/login/page.tsx, apps/demo/src/app/page.tsx
Wrap page contents in React Suspense with pulsing fallback components.
Type-only import
apps/demo/src/trpc/react.tsx
Change AppRouter import to a pure type import (import type).
E2E/tests & docs updates
e2e/cli/setup.ts, e2e/cli/status.test.ts, e2e/cli/vitest.config.ts, e2e/smoke/vitest.shared.ts, landing/content/docs/concepts/cli.mdx, docs/superpowers/plans/2026-04-09-docs-content.md
Set PAYKIT_CLI in e2e setup; adjust generated fixture pg Pool instantiation; rename CLI docs/tests from checkstatus, document --throw; add NODE_ENV=production to vitest/test shared config; remove long docs plan file.
Package metadata & exports
packages/dash/package.json, packages/paykit/package.json, packages/stripe/package.json
Bump package versions to 0.0.2; add ./database export and publishConfig mapping in packages/paykit to expose src/database/schema.ts.
CLI: removed commands
packages/paykit/src/cli/commands/check.ts, packages/paykit/src/cli/commands/telemetry.ts
Remove check and telemetry command implementations; telemetry now controlled by env vars.
CLI: refactored commands
packages/paykit/src/cli/commands/status.ts, packages/paykit/src/cli/commands/push.ts
Refactor to use loadCliDeps DI, createPool, concurrent DB+Stripe checks, product-based diffs, spinner/update-check flow; status gains --throw; push switches to product sync flow and uses shared deps.
CLI entry & runtime
packages/paykit/src/cli/index.ts
Set process.env.PAYKIT_CLI = "1" at module load; switch to dynamic per-command imports based on argv; defer telemetry flush to error path and import capture/flush lazily.
Shared CLI utilities
packages/paykit/src/cli/utils/shared.ts, packages/paykit/src/cli/utils/detect.ts, packages/paykit/src/cli/utils/update-check.ts
Add CliDeps loader and helpers (createPool, checkDatabase, checkStripe, loadProductDiffs, formatProductDiffs), getRunCommand mapping for package managers, and update-check fetch/compare/notification utilities.
Telemetry utilities
packages/paykit/src/cli/utils/telemetry.ts
Remove filesystem persistence and setEnabled; telemetry enablement now read-only from env vars (PAYKIT_TELEMETRY_DISABLED/DO_NOT_TRACK).
Dependency utilities
packages/paykit/src/utilities/dependencies/get-dependencies.ts, .../check-dependencies.ts, .../index.ts, .../paykit-package-list.ts
Add package resolution API, dependency version grouping/mismatch detection with suggested install command, checkPayKitDependencies() helper and PAYKIT_PACKAGE_LIST.
Core runtime & logging
packages/paykit/src/core/create-paykit.ts, packages/paykit/src/core/logger.ts, packages/paykit/src/version.ts
create-paykit now constructs/uses pg.Pool and runs guarded dev checks; logger timestamp reduced to time-only and dimmed; add version re-export.
Misc CLI/core/test updates
various files (e2e, tests, small helpers)
Minor import/guard adjustments, DB pool instantiation change in fixtures, and test config tweaks to integrate new utilities and behaviors.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(240,248,255,0.5)
    participant CLI as CLI Entry
    participant Loader as loadCliDeps
    participant DB as PostgreSQL
    participant Stripe as Stripe API
    participant Sync as Product Sync
    participant User as Console
    end

    CLI->>CLI: determine command (status / push / init)
    CLI->>Loader: loadCliDeps()
    Loader-->>CLI: CliDeps (Pool, StripeSdk, ctx creators, helpers)

    alt status flow
        CLI->>Loader: createPool(connection)
        Loader->>DB: open pool / ping
        par concurrent checks
            Loader->>DB: query pending migrations
            Loader->>Stripe: fetch account & webhooks
        end
        CLI->>Loader: loadProductDiffs(config)
        Loader->>Sync: dryRunSyncProducts(ctx)
        Sync-->>Loader: diffs
        Loader-->>CLI: formatted status blocks
        CLI->>User: print status output
        opt --throw
            CLI->>CLI: exit(1) if issues
        end
    else push flow
        CLI->>Loader: createPool & checkStripe
        CLI->>User: prompt confirmation
        User-->>CLI: confirm
        CLI->>Loader: migrateDatabase()
        Loader->>DB: run migrations
        CLI->>Loader: syncProducts(ctx)
        Sync-->>Loader: complete
        CLI->>User: print "products synced"
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 I hopped through code with a curious twitch,

status checks replaced the old command switch.
Products, schemas, migrations hum in line,
Suspense keeps pages cozy while they bide time,
A tiny hop — the repo’s feeling fine.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: CLI output polishing, dev checks, and update notifications for the CLI tool.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cli-updates

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
landing/content/docs/concepts/cli.mdx (1)

43-57: ⚠️ Potential issue | 🟠 Major

Remove stale paykitjs telemetry docs after command removal.

Line 47 onward still documents paykitjs telemetry, but this PR removes telemetry subcommands. This will direct users to a non-existent command.

📘 Suggested docs fix
-## `paykitjs telemetry`
-
-<PackageRun command="paykitjs telemetry status" />
-
-Manages anonymous usage telemetry. PayKit collects no personally identifiable information. Subcommands:
-
-- `enable` - opt in to telemetry
-- `disable` - opt out
-- `status` - show the current setting
-
-You can also disable telemetry permanently by setting the `PAYKIT_TELEMETRY_DISABLED` or `DO_NOT_TRACK` environment variables. Either one takes precedence over the local setting.
+## Telemetry
+
+Telemetry can be disabled via environment variables:
+
+- `PAYKIT_TELEMETRY_DISABLED=1`
+- `DO_NOT_TRACK=1`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@landing/content/docs/concepts/cli.mdx` around lines 43 - 57, Remove the
entire `paykitjs telemetry` subsection (including the `<PackageRun
command="paykitjs telemetry status" />` example), the bullet list of telemetry
subcommands (`enable`, `disable`, `status`), and the paragraph about
`PAYKIT_TELEMETRY_DISABLED` / `DO_NOT_TRACK` from the CLI docs so no references
to the removed `paykitjs telemetry` command remain; if you want to keep users
informed, replace that block with a single-line note stating telemetry
subcommands were removed and point to the relevant changelog or migration doc.
🧹 Nitpick comments (3)
e2e/cli/setup.ts (1)

9-10: Scope PAYKIT_CLI env mutation to test lifecycle.

Setting this at module load can leak across test suites. Prefer setting/restoring it in fixture setup/cleanup for better isolation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/cli/setup.ts` around lines 9 - 10, The file currently sets
process.env.PAYKIT_CLI = "1" at module load which can leak across tests; change
this to set and restore the environment variable inside your test lifecycle
(e.g., in your test fixture or beforeEach/afterEach hooks): capture the original
value of process.env.PAYKIT_CLI, assign "1" at setup, and restore the original
value in cleanup so the mutation is scoped to the test. Update any related
fixture file or setup/teardown logic that uses process.env.PAYKIT_CLI to perform
this save/restore rather than assigning at module top-level.
packages/paykit/src/utilities/dependencies/paykit-package-list.ts (1)

1-1: Make the package list immutable to reduce drift risk.

Consider exporting this as as const (readonly literals) and reusing it in install/check flows to avoid divergence between multiple hardcoded package arrays.

♻️ Suggested refactor
-export const PAYKIT_PACKAGE_LIST = ["paykitjs", "@paykitjs/stripe", "@paykitjs/dash"];
+export const PAYKIT_PACKAGE_LIST = ["paykitjs", "@paykitjs/stripe", "@paykitjs/dash"] as const;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/paykit/src/utilities/dependencies/paykit-package-list.ts` at line 1,
The package array PAYKIT_PACKAGE_LIST should be made immutable to prevent drift;
change its export to use readonly literal tuples (e.g., export const
PAYKIT_PACKAGE_LIST = [...] as const) and update any install/check code to
consume that constant (or derive a string[] via Array.from or typeof
PAYKIT_PACKAGE_LIST[number] where needed) so all flows reuse the single
immutable source of truth.
packages/paykit/src/cli/utils/shared.ts (1)

91-94: Precompute plan lookup to avoid repeated linear scans

plans.find(...) inside diffs.map(...) does O(n*m) work. Build a map once for better scalability when there are many plans/diffs.

Proposed refactor
 export function formatProductDiffs(
   diffs: ProductDiff[],
   plans: readonly NormalizedPlan[],
   deps: Pick<CliDeps, "formatPlanLine" | "formatPrice">,
 ): string[] {
+  const plansById = new Map(plans.map((pl) => [pl.id, pl]));
   return diffs.map((diff) => {
-    const plan = plans.find((pl) => pl.id === diff.id);
+    const plan = plansById.get(diff.id);
     const price = plan ? deps.formatPrice(plan.priceAmount ?? 0, plan.priceInterval) : "$0";
     return deps.formatPlanLine(diff.action, diff.id, price);
   });
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/paykit/src/cli/utils/shared.ts` around lines 91 - 94, The current
diffs.map callback does a linear search with plans.find for each diff causing
O(n*m) complexity; instead, precompute a lookup (e.g., const plansById = new
Map(plans.map(p => [p.id, p])) ) before the map and then inside the mapping use
plansById.get(diff.id) to get the plan; update the price calculation to use the
found plan (plan?.priceAmount ?? 0 and plan?.priceInterval) and keep the
deps.formatPlanLine(diff.action, diff.id, price) call unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/demo/drizzle.config.ts`:
- Around line 7-9: Replace the non-null assertion on process.env.DATABASE_URL in
dbCredentials with an explicit runtime check that throws a clear, descriptive
error if DATABASE_URL is missing; update the drizzle config's dbCredentials
block to read the env var into a local constant (e.g., databaseUrl) and validate
it, then use that variable instead of process.env.DATABASE_URL!, so any missing
configuration surfaces as a helpful error at startup.

In `@docs/superpowers/plans/2026-04-09-docs-content.md`:
- Around line 564-565: Remove the stale "paykitjs telemetry" CLI documentation
entry: delete or replace the bullet that documents the `paykitjs telemetry`
command (and its subcommands `enable`, `disable`, `status`) so docs no longer
reference the removed telemetry command; ensure only the `paykitjs status` entry
(and any other current commands) remain and that references to env vars
`PAYKIT_TELEMETRY_DISABLED` and `DO_NOT_TRACK` are removed or moved only if
still relevant elsewhere.

In `@packages/paykit/package.json`:
- Around line 34-35: The package exports include an entry named "./database"
that points to built artifacts that are never emitted because tsdown.config.ts
lacks an entry for src/database/schema.ts; add a build entry in tsdown.config.ts
for src/database/schema.ts so the dist/database/schema.js and
dist/database/schema.d.ts are generated during the build and satisfy the
"./database" export (ensure the entry uses the same source path and will emit to
dist/database/schema.js/.d.ts).

In `@packages/paykit/src/cli/commands/push.ts`:
- Around line 33-49: Before treating a failure as "pending migrations", perform
the explicit DB connectivity check used in the status flow and bail early on
connection errors: call the same DB-check helper from the status command (the
explicit connectivity check) before invoking
deps.getPendingMigrationCount(database); if the check reports a
connectivity/error condition, stop the spinner, log the DB error and exit
instead of proceeding to deps.migrateDatabase(database). Ensure you keep the
existing references to deps.getPendingMigrationCount and deps.migrateDatabase
but gate them behind the status-style DB connectivity check.
- Around line 27-35: The code dereferences config.options.provider.secretKey
without checking provider exists, causing a TypeError for configs missing
provider; update the push command flow in push.ts to validate that
config.options.provider is present before calling checkStripe (or reading
secretKey) — mirror the same provider-existence check used in the status path
and, if absent, surface the same CLI-facing “No provider configured” error
instead of proceeding to checkStripe; adjust the call site where
checkStripe(deps, config.options.provider.secretKey) is invoked so it only runs
after the provider validation.

In `@packages/paykit/src/cli/index.ts`:
- Around line 11-37: The lazy imports and program.addCommand calls
(statusCommand, initCommand, pushCommand, and Promise.all block) currently run
before the try block so import failures bypass the CLI's friendly
error/telemetry/exit handling; move the entire command loading and registration
logic (the switch on commandName and the default Promise.all import +
program.addCommand calls) inside the existing try or into an awaited helper
invoked from the try so any import errors are caught and handled by the existing
error/telemetry/exit path.

In `@packages/paykit/src/cli/utils/detect.ts`:
- Around line 34-39: getRunCommand currently returns `yarn dlx` for
PackageManager "yarn", which breaks for Yarn Classic (v1); update getRunCommand
to detect Yarn version and fall back to `npx ${script}` for v1 users (or
implement a helper like isYarnBerry() that runs `yarn --version` and checks for
v2+), then return `yarn dlx ${script}` only when the helper reports Yarn Berry;
keep the same behavior for "npm", "bun", and "pnpm" branches and ensure the
detection helper is used by getRunCommand.

In `@packages/paykit/src/cli/utils/shared.ts`:
- Around line 1-2: The type-only imports `Pool` and `StripeSdk` are being
referenced with `typeof Pool` and `typeof StripeSdk`, which is invalid for
type-only imports; update those references to use import-style typeof
expressions: replace `typeof Pool` with `typeof import("pg").Pool` and replace
`typeof StripeSdk` with `typeof import("stripe").default` wherever they appear
(e.g., the variables/types that reference `Pool` and `StripeSdk` in this module)
so the runtime types are correctly resolved.

In `@packages/paykit/src/cli/utils/update-check.ts`:
- Around line 7-22: The isNewer function currently only treats stable vs
prerelease and doesn't order two prerelease versions; update isNewer to, after
main version parity, handle prerelease comparison by splitting latestPre and
currentPre on "." into identifiers and comparing each identifier: treat numeric
identifiers as numbers and compare numerically, otherwise compare as ASCII
strings, and if all compared identifiers are equal the longer identifier list is
newer; keep the existing rule that a stable release (no prerelease) is newer
than any prerelease. This change should be implemented in the isNewer function
using the latest/current and latestPre/currentPre variables so cases like
"0.0.1-alpha.2" > "0.0.1-alpha.1" are correctly detected.

In `@packages/paykit/src/core/create-paykit.ts`:
- Around line 31-47: The current Promise.allSettled() call swallows rejections
from dryRunSyncProducts and relies on getPendingMigrationCount treating DB
errors as "all pending"; update the Promise.allSettled(...) block to inspect
each result, surface and log (or rethrow) any rejected promises instead of
ignoring them, and treat an error from getPendingMigrationCount(pool) as a real
error (log a clear DB/connectivity error or throw) rather than interpreting it
as a positive migration count; specifically modify the Promise.allSettled usage
around getPendingMigrationCount and dryRunSyncProducts to check
settled[i].status === "rejected" and handle its reason, and adjust
getPendingMigrationCount to propagate/throw errors instead of returning a
misleading count on failure.
- Around line 51-55: When creating the DB Pool from a connection string (the
const pool assigned when typeof options.database === "string") ensure the code
cleans up the owned Pool if createContext(...) rejects: wrap the await
createContext({ ...options, database: pool }) call in a try/catch, detect that
you own the Pool (i.e., it was created from options.database string), call
pool.end() (await it) in the catch to close sockets, then rethrow the original
error so startup still fails cleanly; reference the variables pool,
options.database and the createContext function when making this change.

In `@packages/paykit/src/utilities/dependencies/check-dependencies.ts`:
- Around line 11-17: The current logic in check-dependencies.ts uses
foundVersions to map a version to a single package name, causing subsequent
packages with the same version to be dropped and only one mismatch reported;
update the logic that iterates over resolved (the for (const [pkg, { version }]
of resolved) loop) so that foundVersions maps each version to an array of
package names (or otherwise accumulates duplicates) and then build the warning
and suggested install command using all package names for each mismatched
version (the same change should be applied to the later block around the 24-34
region), ensuring every package that shares a wrong version is included in the
reported message and fix command.

In `@packages/paykit/src/version.ts`:
- Line 1: Replace the unsupported named re-export of the JSON module by
importing the package.json default and then re-exporting the version property:
change the current `export { version } from "../package.json"` pattern to a
default import of `packageJson` (or similarly named identifier) from
`"../package.json"` and then export `version` as a named export (e.g., `export
const version = packageJson.version`) so the `version` symbol is provided at
runtime.

---

Outside diff comments:
In `@landing/content/docs/concepts/cli.mdx`:
- Around line 43-57: Remove the entire `paykitjs telemetry` subsection
(including the `<PackageRun command="paykitjs telemetry status" />` example),
the bullet list of telemetry subcommands (`enable`, `disable`, `status`), and
the paragraph about `PAYKIT_TELEMETRY_DISABLED` / `DO_NOT_TRACK` from the CLI
docs so no references to the removed `paykitjs telemetry` command remain; if you
want to keep users informed, replace that block with a single-line note stating
telemetry subcommands were removed and point to the relevant changelog or
migration doc.

---

Nitpick comments:
In `@e2e/cli/setup.ts`:
- Around line 9-10: The file currently sets process.env.PAYKIT_CLI = "1" at
module load which can leak across tests; change this to set and restore the
environment variable inside your test lifecycle (e.g., in your test fixture or
beforeEach/afterEach hooks): capture the original value of
process.env.PAYKIT_CLI, assign "1" at setup, and restore the original value in
cleanup so the mutation is scoped to the test. Update any related fixture file
or setup/teardown logic that uses process.env.PAYKIT_CLI to perform this
save/restore rather than assigning at module top-level.

In `@packages/paykit/src/cli/utils/shared.ts`:
- Around line 91-94: The current diffs.map callback does a linear search with
plans.find for each diff causing O(n*m) complexity; instead, precompute a lookup
(e.g., const plansById = new Map(plans.map(p => [p.id, p])) ) before the map and
then inside the mapping use plansById.get(diff.id) to get the plan; update the
price calculation to use the found plan (plan?.priceAmount ?? 0 and
plan?.priceInterval) and keep the deps.formatPlanLine(diff.action, diff.id,
price) call unchanged.

In `@packages/paykit/src/utilities/dependencies/paykit-package-list.ts`:
- Line 1: The package array PAYKIT_PACKAGE_LIST should be made immutable to
prevent drift; change its export to use readonly literal tuples (e.g., export
const PAYKIT_PACKAGE_LIST = [...] as const) and update any install/check code to
consume that constant (or derive a string[] via Array.from or typeof
PAYKIT_PACKAGE_LIST[number] where needed) so all flows reuse the single
immutable source of truth.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 06149e67-900c-4a69-89c9-2410ecb772eb

📥 Commits

Reviewing files that changed from the base of the PR and between 860d930 and a53f2a2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (28)
  • apps/demo/drizzle.config.ts
  • apps/demo/package.json
  • apps/demo/src/app/login/page.tsx
  • apps/demo/src/app/page.tsx
  • apps/demo/src/trpc/react.tsx
  • docs/superpowers/plans/2026-04-09-docs-content.md
  • e2e/cli/setup.ts
  • e2e/cli/status.test.ts
  • landing/content/docs/concepts/cli.mdx
  • packages/dash/package.json
  • packages/paykit/package.json
  • packages/paykit/src/cli/commands/check.ts
  • packages/paykit/src/cli/commands/push.ts
  • packages/paykit/src/cli/commands/status.ts
  • packages/paykit/src/cli/commands/telemetry.ts
  • packages/paykit/src/cli/index.ts
  • packages/paykit/src/cli/utils/detect.ts
  • packages/paykit/src/cli/utils/shared.ts
  • packages/paykit/src/cli/utils/telemetry.ts
  • packages/paykit/src/cli/utils/update-check.ts
  • packages/paykit/src/core/create-paykit.ts
  • packages/paykit/src/core/logger.ts
  • packages/paykit/src/utilities/dependencies/check-dependencies.ts
  • packages/paykit/src/utilities/dependencies/get-dependencies.ts
  • packages/paykit/src/utilities/dependencies/index.ts
  • packages/paykit/src/utilities/dependencies/paykit-package-list.ts
  • packages/paykit/src/version.ts
  • packages/stripe/package.json
💤 Files with no reviewable changes (2)
  • packages/paykit/src/cli/commands/telemetry.ts
  • packages/paykit/src/cli/commands/check.ts

Comment on lines +7 to +9
dbCredentials: {
url: process.env.DATABASE_URL!,
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid non-null assertion for DATABASE_URL in config.

Line 8 uses process.env.DATABASE_URL!; when unset, failure is less clear than an explicit config error.

🛠️ Suggested improvement
 import { defineConfig } from "drizzle-kit";
 import "dotenv/config";

+const databaseUrl = process.env.DATABASE_URL;
+if (!databaseUrl) {
+  throw new Error("Missing DATABASE_URL for Drizzle config");
+}
+
 export default defineConfig({
   dialect: "postgresql",
   schema: "../../packages/paykit/src/database/schema.ts",
   dbCredentials: {
-    url: process.env.DATABASE_URL!,
+    url: databaseUrl,
   },
   migrations: {
     schema: "public",
     table: "paykit_migrations",
   },
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
dbCredentials: {
url: process.env.DATABASE_URL!,
},
import { defineConfig } from "drizzle-kit";
import "dotenv/config";
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error("Missing DATABASE_URL for Drizzle config");
}
export default defineConfig({
dialect: "postgresql",
schema: "../../packages/paykit/src/database/schema.ts",
dbCredentials: {
url: databaseUrl,
},
migrations: {
schema: "public",
table: "paykit_migrations",
},
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/demo/drizzle.config.ts` around lines 7 - 9, Replace the non-null
assertion on process.env.DATABASE_URL in dbCredentials with an explicit runtime
check that throws a clear, descriptive error if DATABASE_URL is missing; update
the drizzle config's dbCredentials block to read the env var into a local
constant (e.g., databaseUrl) and validate it, then use that variable instead of
process.env.DATABASE_URL!, so any missing configuration surfaces as a helpful
error at startup.

Comment on lines +34 to 35
"./database": "./src/database/schema.ts"
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package exports for ./database =="
jq -r '.exports["./database"], .publishConfig.exports["./database"].default, .publishConfig.exports["./database"].types' packages/paykit/package.json

echo
echo "== tsdown entry points =="
sed -n '1,120p' packages/paykit/tsdown.config.ts

echo
echo "== quick check for database schema entry presence =="
rg -n '"database|schema\.ts"' packages/paykit/tsdown.config.ts

Repository: getpaykit/paykit

Length of output: 814


./database export points to unbuilt dist files.

The ./database export at lines 34 and 50 targets dist/database/schema.js and dist/database/schema.d.ts, but packages/paykit/tsdown.config.ts has no entry for src/database/schema.ts. This means the dist files will never be emitted during the build, breaking the paykitjs/database import in published packages.

Add the missing entry to tsdown.config.ts:

export default defineConfig({
  ...
  entry: {
    index: "src/index.ts",
    "cli/index": "src/cli/index.ts",
    "handlers/next": "src/handlers/next.ts",
    "client/index": "src/client/index.ts",
+   "database/schema": "src/database/schema.ts",
  },
  ...
});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"./database": "./src/database/schema.ts"
},
export default defineConfig({
entry: {
index: "src/index.ts",
"cli/index": "src/cli/index.ts",
"handlers/next": "src/handlers/next.ts",
"client/index": "src/client/index.ts",
"database/schema": "src/database/schema.ts",
},
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/paykit/package.json` around lines 34 - 35, The package exports
include an entry named "./database" that points to built artifacts that are
never emitted because tsdown.config.ts lacks an entry for
src/database/schema.ts; add a build entry in tsdown.config.ts for
src/database/schema.ts so the dist/database/schema.js and
dist/database/schema.d.ts are generated during the build and satisfy the
"./database" export (ensure the entry uses the same source path and will emit to
dist/database/schema.js/.d.ts).

Comment on lines +33 to 49
const [stripeResult, pendingMigrations] = await Promise.all([
checkStripe(deps, config.options.provider.secretKey),
deps.getPendingMigrationCount(database),
]);

if (!stripeResult.account.ok) {
s.stop("");
p.log.error(`Stripe\n ${picocolors.red("✖")} ${stripeResult.account.message}`);
p.cancel("Push failed");
process.exit(1);
}

// Apply pending migrations before checking products
if (pendingMigrations > 0) {
await migrateDatabase(database);
p.log.success(`Schema ${picocolors.dim("·")} migrated`);
} else {
p.log.step(`Schema ${picocolors.dim("·")} up to date`);
s.message("Applying migrations");
await deps.migrateDatabase(database);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Probe DB connectivity before treating migrations as pending.

getPendingMigrationCount() collapses query failures into “all migrations are pending”, so a broken database connection falls into the migration path here and only fails later in migrateDatabase(). That turns a clear connectivity error into a misleading migration flow. Reuse the explicit DB check from status before deciding whether to migrate.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/paykit/src/cli/commands/push.ts` around lines 33 - 49, Before
treating a failure as "pending migrations", perform the explicit DB connectivity
check used in the status flow and bail early on connection errors: call the same
DB-check helper from the status command (the explicit connectivity check) before
invoking deps.getPendingMigrationCount(database); if the check reports a
connectivity/error condition, stop the spinner, log the DB error and exit
instead of proceeding to deps.migrateDatabase(database). Ensure you keep the
existing references to deps.getPendingMigrationCount and deps.migrateDatabase
but gate them behind the status-style DB connectivity check.

Comment on lines +31 to +47
await Promise.allSettled([
getPendingMigrationCount(pool).then((count) => {
if (count > 0) {
console.warn(
`${picocolors.yellow("[paykit]")} ${count} pending migration${count === 1 ? "" : "s"}. Run ${picocolors.bold("paykitjs push")} to apply.`,
);
}
}),
dryRunSyncProducts(ctx).then((results) => {
const outOfSync = results.filter((r) => r.action !== "unchanged");
if (outOfSync.length > 0) {
ctx.logger.error(
`${outOfSync.length} plan(s) out of sync: ${outOfSync.map((r) => r.id).join(", ")}. Run \`paykitjs push\` to update.`,
console.warn(
`${picocolors.yellow("[paykit]")} ${outOfSync.length} product${outOfSync.length === 1 ? "" : "s"} out of sync: ${outOfSync.map((r) => r.id).join(", ")}. Run ${picocolors.bold("paykitjs push")} to update.`,
);
}
} catch {
ctx.logger.debug("Skipped plan sync check (database may not be initialized yet)");
}
}),
]);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't silently hide failed dev checks.

This path never inspects the Promise.allSettled() results, so a rejected dryRunSyncProducts(ctx) disappears completely. On top of that, getPendingMigrationCount(pool) treats query failures as “all migrations pending”, so a bad DB connection is reported as a migration issue instead of a connectivity failure. That means the new dev checks can mask the real problem instead of surfacing it clearly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/paykit/src/core/create-paykit.ts` around lines 31 - 47, The current
Promise.allSettled() call swallows rejections from dryRunSyncProducts and relies
on getPendingMigrationCount treating DB errors as "all pending"; update the
Promise.allSettled(...) block to inspect each result, surface and log (or
rethrow) any rejected promises instead of ignoring them, and treat an error from
getPendingMigrationCount(pool) as a real error (log a clear DB/connectivity
error or throw) rather than interpreting it as a positive migration count;
specifically modify the Promise.allSettled usage around getPendingMigrationCount
and dryRunSyncProducts to check settled[i].status === "rejected" and handle its
reason, and adjust getPendingMigrationCount to propagate/throw errors instead of
returning a misleading count on failure.

Comment on lines +51 to +55
const pool =
typeof options.database === "string"
? new Pool({ connectionString: options.database })
: options.database;
const ctx = await createContext({ ...options, database: pool });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Dispose the owned Pool when context creation fails.

When options.database is a connection string, this allocates a new pool before createContext(). If createContext() rejects, nothing closes that pool, so startup failures leak sockets until process exit.

♻️ Suggested fix
 async function initContext(options: PayKitOptions): Promise<PayKitContext> {
-  const pool =
-    typeof options.database === "string"
-      ? new Pool({ connectionString: options.database })
-      : options.database;
-  const ctx = await createContext({ ...options, database: pool });
+  const ownsPool = typeof options.database === "string";
+  const pool = ownsPool
+    ? new Pool({ connectionString: options.database })
+    : options.database;
+
+  let ctx: PayKitContext;
+  try {
+    ctx = await createContext({ ...options, database: pool });
+  } catch (error) {
+    if (ownsPool) {
+      await pool.end();
+    }
+    throw error;
+  }
 
   if (process.env.NODE_ENV !== "production" && !process.env.PAYKIT_CLI) {
     runDevChecks(ctx, pool).catch(() => {});
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const pool =
typeof options.database === "string"
? new Pool({ connectionString: options.database })
: options.database;
const ctx = await createContext({ ...options, database: pool });
const ownsPool = typeof options.database === "string";
const pool = ownsPool
? new Pool({ connectionString: options.database })
: options.database;
let ctx: PayKitContext;
try {
ctx = await createContext({ ...options, database: pool });
} catch (error) {
if (ownsPool) {
await pool.end();
}
throw error;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/paykit/src/core/create-paykit.ts` around lines 51 - 55, When
creating the DB Pool from a connection string (the const pool assigned when
typeof options.database === "string") ensure the code cleans up the owned Pool
if createContext(...) rejects: wrap the await createContext({ ...options,
database: pool }) call in a try/catch, detect that you own the Pool (i.e., it
was created from options.database string), call pool.end() (await it) in the
catch to close sockets, then rethrow the original error so startup still fails
cleanly; reference the variables pool, options.database and the createContext
function when making this change.

@@ -0,0 +1 @@
export { version } from "../package.json";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In TypeScript 5.9 + Node ESM, is export { version } from "../package.json" reliably supported, or should JSON be default-imported and then re-exported as a const?

💡 Result:

No, export { version } from "../package.json" is not reliably supported in TypeScript 5.9 with Node.js ESM. JSON modules in Node.js only provide a default export, not named exports like version. Attempting named imports or re-exports from JSON files results in runtime errors such as "The requested module does not provide an export named 'version'". TypeScript's nodenext/node20 modes enforce this by erroring on named imports from JSON (TS1544) and requiring with { type: "json" } for imports. The reliable approach is to default-import the JSON and re-export as a named const: import pkg from "../package.json" with { type: "json" }; export const version = pkg.version; This works at runtime in Node.js (v18.20+ stable JSON modules) and compiles correctly in TS 5.9 under module: "nodenext" or "node20", providing a named export version with proper typing.

Citations:


🏁 Script executed:

# Check for tsconfig.json and its module settings
find . -name "tsconfig.json" -type f | head -5

Repository: getpaykit/paykit

Length of output: 191


🏁 Script executed:

# Search for existing JSON imports in the codebase
rg "import.*from.*\.json" -t ts -t tsx --max-count 20

Repository: getpaykit/paykit

Length of output: 87


🏁 Script executed:

# Check package.json for any JSON module type or import assertions
cat package.json | grep -A 5 -B 5 "json\|module\|type"

Repository: getpaykit/paykit

Length of output: 1581


🏁 Script executed:

# Check the paykit tsconfig.json for module configuration
cat ./packages/paykit/tsconfig.json

Repository: getpaykit/paykit

Length of output: 174


🏁 Script executed:

# Search for existing JSON imports in the codebase (corrected syntax)
rg "import.*from.*\.json" -t ts --max-count 20

Repository: getpaykit/paykit

Length of output: 153


🏁 Script executed:

# Also search for any JSON import assertions
rg "import.*\.json.*with" -t ts --max-count 20

Repository: getpaykit/paykit

Length of output: 42


🏁 Script executed:

# Check the base tsconfig.json
cat ./tsconfig.base.json

Repository: getpaykit/paykit

Length of output: 867


🏁 Script executed:

# Check the landing/src/lib/community-stats.ts file to see the JSON import pattern
cat -n ./landing/src/lib/community-stats.ts | head -20

Repository: getpaykit/paykit

Length of output: 643


Named re-export from JSON is not supported and will fail at runtime.

Line 1 attempts export { version } from "../package.json", but JSON modules in Node.js only provide a default export. This will cause a runtime error: "The requested module does not provide an export named 'version'". The codebase already uses the correct pattern (see landing/src/lib/community-stats.ts).

Use a default import and explicit named export instead:

Fix
-export { version } from "../package.json";
+import pkg from "../package.json";
+
+export const version = pkg.version;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export { version } from "../package.json";
import pkg from "../package.json";
export const version = pkg.version;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/paykit/src/version.ts` at line 1, Replace the unsupported named
re-export of the JSON module by importing the package.json default and then
re-exporting the version property: change the current `export { version } from
"../package.json"` pattern to a default import of `packageJson` (or similarly
named identifier) from `"../package.json"` and then export `version` as a named
export (e.g., `export const version = packageJson.version`) so the `version`
symbol is provided at runtime.

- Fix check-dependencies to track all packages per version, not just first
- Improve isNewer to compare pre-release identifiers properly
- Add provider existence check in push command before calling Stripe
- Precompute plan lookup Map in formatProductDiffs
- Make PAYKIT_PACKAGE_LIST immutable with as const
- Update CLI docs to remove stale telemetry command section
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
packages/paykit/src/cli/utils/shared.ts (1)

19-21: ⚠️ Potential issue | 🔴 Critical

Fix invalid typeof on type-only imports.

Line 20 and Line 21 use typeof on symbols imported with import type, which is invalid in TypeScript and will fail type-checking.

Proposed fix
 export interface CliDeps {
-  Pool: typeof Pool;
-  StripeSdk: typeof StripeSdk;
+  Pool: typeof import("pg").Pool;
+  StripeSdk: typeof import("stripe").default;
   createContext: typeof createContext;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/paykit/src/cli/utils/shared.ts` around lines 19 - 21, The CliDeps
interface incorrectly uses "typeof" on type-only imports (Pool and StripeSdk);
remove the invalid "typeof" and reference the types directly in the CliDeps
interface (i.e., change "Pool: typeof Pool" and "StripeSdk: typeof StripeSdk" to
"Pool: Pool" and "StripeSdk: StripeSdk"), or alternatively convert those imports
from "import type" to regular value imports if you actually need the runtime
constructors — update the declarations in the CliDeps interface accordingly
(referencing the CliDeps interface, Pool and StripeSdk symbols).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/paykit/src/cli/utils/shared.ts`:
- Around line 19-21: The CliDeps interface incorrectly uses "typeof" on
type-only imports (Pool and StripeSdk); remove the invalid "typeof" and
reference the types directly in the CliDeps interface (i.e., change "Pool:
typeof Pool" and "StripeSdk: typeof StripeSdk" to "Pool: Pool" and "StripeSdk:
StripeSdk"), or alternatively convert those imports from "import type" to
regular value imports if you actually need the runtime constructors — update the
declarations in the CliDeps interface accordingly (referencing the CliDeps
interface, Pool and StripeSdk symbols).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 983ef385-dcc7-4826-a577-560e98ae4c2d

📥 Commits

Reviewing files that changed from the base of the PR and between a53f2a2 and 6fd866f.

📒 Files selected for processing (7)
  • docs/superpowers/plans/2026-04-09-docs-content.md
  • landing/content/docs/concepts/cli.mdx
  • packages/paykit/src/cli/commands/push.ts
  • packages/paykit/src/cli/utils/shared.ts
  • packages/paykit/src/cli/utils/update-check.ts
  • packages/paykit/src/utilities/dependencies/check-dependencies.ts
  • packages/paykit/src/utilities/dependencies/paykit-package-list.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/paykit/src/utilities/dependencies/paykit-package-list.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • docs/superpowers/plans/2026-04-09-docs-content.md
  • landing/content/docs/concepts/cli.mdx
  • packages/paykit/src/utilities/dependencies/check-dependencies.ts
  • packages/paykit/src/cli/utils/update-check.ts
  • packages/paykit/src/cli/commands/push.ts

@maxktz maxktz merged commit 95d7147 into main Apr 12, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant