-
Notifications
You must be signed in to change notification settings - Fork 1
fmdapi template #72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fmdapi template #72
Conversation
…_REGISTRY_URL to provide a safe fallback when running from source. Improve settings management during stealth initialization and refine env file handling. Add preflight checks in the add command to ensure proper project setup. Update template metadata for fmdapi and t3-env. Refactor various utility functions for better clarity and error resilience.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
@proofkit/better-auth
@proofkit/cli
create-proofkit
@proofkit/fmdapi
@proofkit/typegen
@proofkit/webviewer
commit: |
WalkthroughAdds a registry-driven CLI add flow, a new registry template (fmdapi) with env post-install steps, registry index/type changes, expanded env discovery and env-management helpers, safer DEFAULT_REGISTRY_URL handling, stealth-init/settings refactors, and minor template/env validation adjustments. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Dev as Developer
participant CLI as CLI (add)
participant Reg as Registry
participant Inst as Installer
participant Post as PostInstall
participant Env as addToEnv
Dev->>CLI: run `proofkit add`
CLI->>Reg: listItems()
Reg-->>CLI: items
CLI->>Dev: prompt category/item
Dev-->>CLI: selection
CLI->>Inst: install(selected)
alt dependency workaround required
Inst->>Inst: install `utils/t3-env` first
end
Inst-->>CLI: install result
loop post-install steps
CLI->>Post: process(step)
alt step.action == "env"
Post->>Env: addToEnv({ envs })
Env-->>Post: .env/schema updated
else
Post-->>CLI: other step handled
end
end
CLI-->>Dev: finished / error
sequenceDiagram
autonumber
participant Stealth as stealthInit
participant Set as setSettings
participant Val as validateAndSetEnvFile
Stealth->>Stealth: if `proofkit.json` exists -> return
Stealth->>Set: set default settings
Stealth->>Val: validateAndSetEnvFile(".env")
Val-->>Stealth: updates settings / returns undefined
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/cli/src/utils/parseSettings.ts (1)
3-3: Import consistency: use “zod/v4” across the codebaseYour change in
packages/cli/src/utils/parseSettings.tsis correct—this file already imports from"zod/v4", which matches our project convention. However, the repository still contains mixed import styles (plain"zod"and even"zod/v3"), which will break at runtime if there’s no alias for those paths.Please standardize all imports to
"zod/v4". For example:
- packages/registry/lib/types.ts:
// ← needs updating import { z } from "zod/v3";- apps/docs/content/docs/fmdapi/CustomersLayout.ts:
// ← needs updating import { z } from "zod";To locate all remaining non-v4 imports, run:
rg -nP --type=ts "from\\s+['\"]zod(?!/v4)['\"]"After you’ve updated those imports to
"zod/v4", regenerate any affected snapshots or tests.
🧹 Nitpick comments (32)
packages/registry/templates/better-auth/auth.hbs (1)
45-50: Avoid logging PII and reset links in productionThe console warnings/logs include the user's email and a password-reset URL. Guard these behind a non-production check or remove them to prevent accidental leakage in server logs.
Apply this diff to gate the logs:
- console.warn( - "TODO: Customize this function to actually send to your users", - ); - console.log( - `To ${user.email}: Click the link to reset your password: ${url}`, - ); + if (env.NODE_ENV !== "production") { + console.warn( + "TODO: Customize this function to actually send to your users", + ); + console.log( + `To ${user.email}: Click the link to reset your password: ${url}`, + ); + }packages/cli/template/nextjs-shadcn/src/lib/env.ts (1)
6-9: Refactor NODE_ENV fallback to use.optional().default()instead of.catch()Using
.catch("development")on your Zod schema will silently convert any invalidNODE_ENVvalues (e.g., typos like"prod") to"development". To ensure invalid values still cause validation errors while only defaulting when the variable is truly undefined, replace.catch("development")with.optional().default("development")in every occurrence:• packages/cli/template/nextjs-shadcn/src/lib/env.ts (around lines 6–9)
- NODE_ENV: z - .enum(["development", "test", "production"]) - .catch("development"), + NODE_ENV: z + .enum(["development", "test", "production"]) + .optional() + .default("development"),• packages/registry/templates/utils/t3-env/env.ts (line 6)
- NODE_ENV: z.enum(["development", "production"]).catch("development"), + NODE_ENV: z + .enum(["development", "production"]) + .optional() + .default("development"),Please apply this refactor consistently to avoid silent misconfiguration.
packages/cli/src/utils/formatting.ts (1)
11-11: Pre-save outside try can fail unhandled and doubles disk I/O
project.saveSync()before the try will crash the process on I/O errors and writes unformatted files, followed by another save in finally. Rely on the finalawait project.save()(already present) or move the sync save inside the try/catch if you truly need it.Apply this diff to remove the redundant/unhandled pre-save:
-export async function formatAndSaveSourceFiles(project: Project) { - project.saveSync(); // save here in case formatting fails +export async function formatAndSaveSourceFiles(project: Project) {If you want a defensive sync save, put it under error handling:
} catch (error) { + try { + // last-resort attempt to persist any edits done so far + project.saveSync(); + } catch {} if (state.debug) { console.log("Error formatting files"); console.error(error); } } finally { await project.save(); }packages/cli/src/cli/init.ts (2)
272-272: Remove unused variable from setSettings result
registryUrlis assigned but not used; drop the destructuring to avoid unused-local warnings.Apply this diff:
- const { registryUrl } = setSettings(initialSettings); + setSettings(initialSettings);
248-271: Reduce duplication and drift risk ininitialSettingsExtract the shared fields into a common
baseobject and spread it into each branch so that only the UI-specific keys live in their respective blocks. This ensures alignment with theSettingsschema and prevents future drift.• File:
packages/cli/src/cli/init.ts(around lines 248–271)- const initialSettings: Settings = - state.ui === "mantine" - ? { - appType: state.appType ?? "browser", - ui: "mantine", - auth: { type: "none" }, - envFile: ".env", - dataSources: [], - tanstackQuery: false, - replacedMainPage: false, - appliedUpgrades: ["cursorRules"], - reactEmail: false, - reactEmailServer: false, - registryTemplates: [], - } - : { - appType: state.appType ?? "browser", - ui: "shadcn", - envFile: ".env", - dataSources: [], - replacedMainPage: false, - registryTemplates: [], - }; + const base: Settings = { + appType: state.appType ?? "browser", + ui: state.ui, + envFile: ".env", + dataSources: [], + replacedMainPage: false, + registryTemplates: [], + }; + + const initialSettings: Settings = + state.ui === "mantine" + ? { + ...base, + ui: "mantine", + auth: { type: "none" }, + tanstackQuery: false, + appliedUpgrades: ["cursorRules"], + reactEmail: false, + reactEmailServer: false, + } + : { ...base, ui: "shadcn" };• Centralizes the six common keys (
appType,ui,envFile,dataSources,replacedMainPage,registryTemplates).
• Limits each branch to its unique properties (Mantine:auth,tanstackQuery,appliedUpgrades,reactEmail,reactEmailServer).
• Verified all referenced properties exist insettingsSchema(seeparseSettings.ts).packages/cli/src/consts.ts (1)
33-41: Support a developer override via environment variable.Consider letting developers override the registry URL with
PROOFKIT_REGISTRY_URLfor local testing and CI. This keeps your current behavior as a fallback.Apply this diff:
-export const DEFAULT_REGISTRY_URL = - // typeof check avoids ReferenceError if not defined at runtime - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - __REGISTRY_URL__ is injected at build time - typeof __REGISTRY_URL__ !== "undefined" && __REGISTRY_URL__ - ? // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - __REGISTRY_URL__ is injected at build time - __REGISTRY_URL__ - : "https://proofkit.dev"; +const envOverride = process.env.PROOFKIT_REGISTRY_URL; +export const DEFAULT_REGISTRY_URL = + envOverride ?? + // typeof check avoids ReferenceError if not defined at runtime + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - __REGISTRY_URL__ is injected at build time + (typeof __REGISTRY_URL__ !== "undefined" && __REGISTRY_URL__ ? __REGISTRY_URL__ : "https://proofkit.dev");packages/cli/src/utils/parseSettings.ts (1)
139-156: Broaden env file discovery (optional) and avoid re-parsing settings repeatedly.Nice helper. Two small improvements:
- Accept a list of candidate env files (e.g., ".env.local", ".env.development") before falling back to clearing
envFile.- Cache the parsed settings locally to avoid calling
getSettings()again in nested flows.Apply this diff:
-export function validateAndSetEnvFile(envFileName = ".env") { - const settings = getSettings(); - const envFilePath = path.join(state.projectDir, envFileName); +export function validateAndSetEnvFile(envFileName = ".env", candidates?: string[]) { + const current = getSettings(); + const names = candidates && candidates.length ? candidates : [envFileName]; + const found = names.find((n) => fs.existsSync(path.join(state.projectDir, n))); + const envFilePath = found ? path.join(state.projectDir, found) : path.join(state.projectDir, envFileName); - if (fs.existsSync(envFilePath)) { - const updatedSettings = { ...settings, envFile: envFileName }; + if (fs.existsSync(envFilePath)) { + const updatedSettings = { ...current, envFile: path.basename(envFilePath) }; setSettings(updatedSettings); - return envFileName; + return updatedSettings.envFile; } // If no env file exists, ensure envFile is undefined in settings - if (settings.envFile) { - const { envFile, ...settingsWithoutEnvFile } = settings; + if (current.envFile) { + const { envFile, ...settingsWithoutEnvFile } = current; setSettings(settingsWithoutEnvFile as Settings); } return undefined; }packages/cli/src/installers/envVars.ts (1)
13-16: Avoid writing absolute paths into .env and improve the fallback.
- When
findT3EnvFile(false)returns a full path, embedding it directly into the.envcomment can leak absolute paths; use a repo-relative path for readability.- The fallback uses
./src/config/env.ts; for consistency with the fix above, usesrc/config/env.tsand then render a relative path in the comment.Apply this diff:
- const envFilePath = findT3EnvFile(false) ?? `./src/config/env.ts`; + const envFilePath = findT3EnvFile(false) ?? `src/config/env.ts`; + const relEnvFilePath = path.isAbsolute(envFilePath) + ? path.relative(state.projectDir, envFilePath) + : envFilePath; const envContent = ` -# When adding additional environment variables, the schema in "${envFilePath}" +# When adding additional environment variables, update the schema in "${relEnvFilePath}" # should be updated accordingly. `Also consider skipping creation if
.envalready exists to avoid clobbering user content:if (!fs.existsSync(envDest)) fs.writeFileSync(envDest, envContent, "utf-8");.changeset/brown-keys-float.md (1)
5-6: Polish the changeset message and remove stray character.
- Grammar: Prefer “Add utils/fmdapi to the registry.” (adds the article).
- There’s an unexpected literal “6” on Line 6 that should be removed.
Apply:
--- "@proofkit/cli": patch --- -Add utils/fmdapi to registry -6 +Add utils/fmdapi to the registry.packages/cli/src/cli/add/registry/postInstall/index.ts (2)
15-18: Consider passing optional env file description when available.If the registry’s env step supports a description (e.g., step.data.envFileDescription), propagate it so .env annotations are clearer. Safe to gate by presence to preserve typing.
- await addToEnv({ - envs: step.data.envs, - }); + await addToEnv({ + envs: step.data.envs, + ...(step.data.envFileDescription + ? { envFileDescription: step.data.envFileDescription } + : {}), + });
19-21: Improve unknown-step logging for readability.
${step}will log “[object Object]”. Log the action and a compact JSON payload for clarity.- } else { - logger.error(`Unknown post-install step: ${step}`); + } else { + logger.error( + `Unknown post-install step: ${String((step as any)?.action)} — ${JSON.stringify(step)}` + ); }packages/cli/src/cli/add/registry/getOptions.ts (1)
9-22: Remove redundant try/catch or add context; tighten error message.Catching and rethrowing without context is redundant. Either drop the try/catch or enrich the error with the template name for better diagnostics.
export async function getMetaFromRegistry(name: string) { - try { - const result = await registryFetch("@get/meta/:name", { - params: { name }, - }); - - if (result.error) { - if (result.error.status === 404) return null; - throw new Error(result.error.message); - } - - return result.data; - } catch (error) { - throw error; - } + const result = await registryFetch("@get/meta/:name", { params: { name } }); + if (result.error) { + if (result.error.status === 404) return null; + throw new Error( + `Failed to fetch registry meta for "${name}": ${result.error.message}` + ); + } + return result.data; }packages/cli/src/cli/add/registry/listItems.ts (1)
3-9: Add explicit return type and align error handling with getMetaFromRegistry.
- Give listItems a concrete return type to improve DX and catch schema drift at compile time.
- Wrap fetch in a try/catch only if createFetch may throw; otherwise, mirror the explicit error check you already have.
-import { registryFetch } from "./http.js"; +import { registryFetch } from "./http.js"; +import type { RegistryIndex } from "@proofkit/registry"; -export async function listItems() { - const { data: items, error } = await registryFetch("@get/"); - if (error) { - throw new Error(`Failed to fetch items from registry: ${error.message}`); - } - return items; -} +export async function listItems(): Promise<RegistryIndex[]> { + const { data, error } = await registryFetch("@get/"); + if (error) { + throw new Error( + `Failed to fetch items from registry: ${error.message ?? String(error)}` + ); + } + return data; +}packages/registry/templates/fmdapi/proofkit-typegen.config.jsonc (2)
5-10: Remove trailing commas to maximize parser compatibility.Even though this is .jsonc, some consumers/config loaders are strict and choke on trailing commas. Safer to remove them.
"layouts": [], "path": "./src/lib/schemas/filemaker", "clearOldFiles": true, - "clientSuffix": "Layout", - }, - ], + "clientSuffix": "Layout" + } + ]
5-6: Confirm that an empty "layouts" array is accepted by the TypeGen.If the generator requires at least one layout, this config will be a no-op or fail. If “all layouts” is intended, the tool might need a wildcard instead of [].
Happy to adjust the template based on the tool’s expected semantics (e.g., omit the key, or use "*").
packages/registry/templates/fmdapi/_meta.ts (2)
3-8: Add a short description for better registry listing UX.The registry index now surfaces title, category, and description. Without a description, the CLI list will have a blank summary.
export const meta: TemplateMetadata = { title: "@proofkit/fmdapi", + description: "FileMaker Data API utilities and TypeGen config.", category: "utility", registryType: "registry:lib", dependencies: ["@proofkit/fmdapi"],
20-36: Consider a slightly stronger/clearer validation for envs and double-check the OTTO key prefix.
- FM_SERVER: using url().startsWith('https://') gives both a URL shape and https-only.
- FM_DATABASE: endsWith('.fmp12') is fine.
- OTTO_API_KEY: please confirm the dk_ prefix is correct for your provider; adjust if specs have changed.
{ name: "FM_SERVER", type: "server", - zodValue: "z.string().startsWith('https://')", + zodValue: "z.string().url().startsWith('https://')", }, { name: "FM_DATABASE", type: "server", zodValue: "z.string().endsWith('.fmp12')", }, { name: "OTTO_API_KEY", type: "server", zodValue: "z.string().startsWith('dk_')", },packages/cli/src/utils/addToEnvs.ts (3)
117-122: Avoid trailing indentation/newlines and ensure a clean, idempotent append block.The current template literal may add stray spaces due to indentation. Construct the content via joins for predictability.
- const newContent = `${currentFile} -${envFileDescription ? `# ${envFileDescription}\n${envsString}` : envsString} - `; - - fs.writeFileSync(dotEnvFile, newContent); + const block = envFileDescription + ? `# ${envFileDescription}\n${envsString}` + : envsString; + const parts = [currentFile]; + if (block.trim().length) parts.push("", block, ""); // blank line before/after + fs.writeFileSync(dotEnvFile, parts.join("\n"));
54-68: Fail fast (or create) when the target objects are missing in createEnv options.If server/client/experimental__runtimeEnv are absent, the additions are silently dropped. Either throw with a clear message or create the objects.
Example approach:
if (serverEnvs.length && !serverProperty) { throw new Error('createEnv options missing "server" object'); } if (clientEnvs.length) { if (!clientProperty) { throw new Error('createEnv options missing "client" object'); } if (!runtimeEnvProperty) { // create experimental__runtimeEnv = {} const newProp = opts.addPropertyAssignment({ name: "experimental__runtimeEnv", initializer: "{}", }); // refresh the reference // runtimeEnvProperty = newProp.getFirstDescendantByKind(SyntaxKind.ObjectLiteralExpression); } }
19-26: Default projectDir defensively.If state.projectDir is unset, path.join will throw. Consider defaulting to process.cwd() or requiring the caller to pass projectDir.
-export async function addToEnv({ - projectDir = state.projectDir, +export async function addToEnv({ + projectDir = state.projectDir ?? process.cwd(),packages/registry/lib/types.ts (1)
72-93: Expose an optional envFileDescription to support annotated .env blocks.addToEnv already accepts envFileDescription, but the schema cannot carry it yet. Adding it enables nicer .env grouping and clearer provenance.
buildPostInstallStepsSchema( "env", z.object({ + envFileDescription: z.string().optional(), envs: z .object({ name: z.string(), zodValue: z.string(), defaultValue: z .string() .optional() .describe( "This value will be added to the .env file, unless `addToRuntimeEnv` is set to `false`.", ), type: z.enum(["server", "client"]), addToRuntimeEnv: z .boolean() .optional() .describe("Whether to add the env to the runtime env."), }) .array(), }), ),packages/registry/lib/utils.ts (2)
73-87: Consider returning a deterministically sorted index.Sorting by category then title improves reproducibility and UX for long lists.
- const index = templateDirs.map((templatePath) => { + const index = templateDirs.map((templatePath) => { const meta = loadTemplateMeta(templatePath, templatesPath); const item: RegistryIndex[number] = { name: templatePath, // Use the path as the name category: meta.category, title: meta.title, description: meta.description, }; return item; }); - return index; + return index.sort((a, b) => + a.category === b.category + ? a.title.localeCompare(b.title) + : a.category.localeCompare(b.category), + );
180-199: Minor: destinationPath replacement could be slightly clearer.Ternary with nested optional chaining is correct but terse. Consider readability refactor later; not blocking.
packages/cli/src/cli/add/registry/install.ts (7)
243-246: Treat cancellations distinctly and surface the real error messageRight now any error (including user cancellation) is reported as “Failed to fetch template metadata.” Consider handling UserAbortedError explicitly and otherwise failing the spinner with the actual error message. This prevents noisy logs on deliberate cancellations and improves UX.
- import { abortIfCancel } from "~/cli/utils.js"; + import { abortIfCancel, UserAbortedError } from "~/cli/utils.js"; @@ -} catch (error) { - spinner.fail("Failed to fetch template metadata."); - logger.error(error); -} +} catch (error) { + if (error instanceof UserAbortedError) { + spinner.stop(); + p.cancel("Cancelled."); + return; + } + const message = + error instanceof Error ? error.message : "Failed to add template."; + spinner.fail(message); + logger.error(error); +}Also applies to: 7-8
45-49: Unify “no schemas” path with cancellation semanticsWhen a data source has no schemas, you call p.cancel and return undefined. The caller then reports “Schema selection was cancelled,” which is misleading. Signal cancellation here consistently by throwing UserAbortedError, so the outer catch can handle it like any other cancellation.
- if (schemas.length === 0) { - p.cancel("This data source doesn't have any schemas to load data from"); - return undefined; - } + if (schemas.length === 0) { + p.cancel("This data source doesn't have any schemas to load data from"); + throw new UserAbortedError(); + }
139-157: Harden route input and generate a better pageName
- Validate trimmed input to avoid accepting whitespace-only strings.
- Normalize the route (strip leading “/”).
- Derive a human-friendly pageName from the last segment, converting “foo-bar_baz” to “Foo bar baz”.
- routeName = abortIfCancel( - await p.text({ - message: `Enter the URL PATH for your new page`, - placeholder: "/my-page", - validate: (value) => { - if (value.length === 0) { - return "URL path is required"; - } - return; - }, - }) - ); - - if (routeName.startsWith("/")) { - routeName = routeName.slice(1); - } - - pageName = capitalize(routeName.replace("/", "").trim()); + routeName = abortIfCancel( + await p.text({ + message: `Enter the URL PATH for your new page`, + placeholder: "/my-page", + validate: (value) => { + const v = value.trim(); + if (v.length === 0) return "URL path is required"; + if (v.includes(" ")) return "Use '-' or '_' instead of spaces"; + return; + }, + }) + ); + + // normalize + routeName = routeName.trim(); + if (routeName.startsWith("/")) routeName = routeName.slice(1); + + // label from last segment + const last = routeName.split("/").filter(Boolean).at(-1) ?? routeName; + pageName = capitalize(last.replace(/[-_]+/g, " "));
21-24: Remove unused import getFilePathgetFilePath isn’t used in this module. Drop it to keep imports tidy and avoid linter warnings.
-import { - buildHandlebarsData, - getFilePath, - randerHandlebarsToFile, -} from "./postInstall/handlebars.js"; +import { buildHandlebarsData, randerHandlebarsToFile } from "./postInstall/handlebars.js";
73-82: Semver check: prefer a clearer condition and validate inputsUse semver.lt(getVersion(), required) which reads closer to the intent. Optionally guard with semver.valid to avoid throwing on malformed versions from the registry.
- if ( - meta.minimumProofKitVersion && - semver.gt(meta.minimumProofKitVersion, getVersion()) - ) { + if ( + meta.minimumProofKitVersion && + semver.valid(meta.minimumProofKitVersion) && + semver.valid(getVersion()) && + semver.lt(getVersion(), meta.minimumProofKitVersion) + ) {
55-56: Tiny cleanup: avoid redundant nullish-coalescingschemas is already string[]. No need for o ?? "".
- options: schemas.map((o) => ({ label: o, value: o ?? "" })), + options: schemas.map((o) => ({ label: o, value: o })),
164-175: Workaround looks safe; fix a typo in the commentThe recursive call to installFromRegistry("utils/t3-env") won’t re-enter this branch because name differs, so recursion is bounded. Minor nit: there’s a “workaroudn” typo.
- // this last guard will allow this workaroudn to be bypassed if the registry server updates to start serving the dependency again + // this last guard will allow this workaround to be bypassed if the registry server updates to start serving the dependency againpackages/cli/src/cli/add/index.ts (2)
24-107: Gracefully handle user cancellations and guard empty category lists
- abortIfCancel throws; wrap the interactive section so a UserAbortedError prints a clean “Cancelled.” instead of an unhandled stack.
- itemsFromCategory may be undefined; default to an empty array to avoid mapping over undefined.
-const runAddFromRegistry = async (options?: { noInstall?: boolean }) => { +const runAddFromRegistry = async (options?: { noInstall?: boolean }) => { const settings = getSettings(); @@ - spinner.succeed(); - - const addType = abortIfCancel( - await p.select({ - message: "What do you want to add to your project?", - options: [ + spinner.succeed(); + + try { + const addType = abortIfCancel( + await p.select({ + message: "What do you want to add to your project?", + options: [ // if there are pages available to install, show them first ...(categories.includes("page") ? [{ label: "Page", value: "page" }] : []), { label: "Schema", value: "schema", hint: "load data from a new table or layout from an existing data source", }, ...(settings.appType === "browser" ? [ { label: "Data Source", value: "data", hint: "to connect to a new database or FileMaker file", }, ] : []), // show the rest of the categories ...categories .filter((category) => category !== "page") .map((category) => ({ label: capitalize(category), value: category, })), - ], - }) - ); + ], + }) + ); - if (addType === "schema") { + if (addType === "schema") { await runAddSchemaAction(); - } else if (addType === "data") { + } else if (addType === "data") { await runAddDataSourceCommand(); - } else if (categories.includes(addType as any)) { + } else if (categories.includes(addType as any)) { // one of the categories - const itemsFromCategory = - groupedByCategory[addType as keyof typeof groupedByCategory]; + const itemsFromCategory = + groupedByCategory[addType as keyof typeof groupedByCategory] ?? []; - const itemName = abortIfCancel( - await p.select({ - message: `Select a ${addType} to add to your project`, - options: itemsFromCategory.map((item) => ({ - label: item.title, - hint: item.description, - value: item.name, - })), - }) - ); + if (itemsFromCategory.length === 0) { + p.cancel(`No ${addType}s are available to install.`); + return; + } + + const itemName = abortIfCancel( + await p.select({ + message: `Select a ${addType} to add to your project`, + options: itemsFromCategory.map((item) => ({ + label: item.title, + hint: item.description, + value: item.name, + })), + }) + ); - await installFromRegistry(itemName); - } else { + await installFromRegistry(itemName); + } else { logger.error( `Could not find any available components in the category "${addType}"` ); - } + } + } catch (err) { + if ((err as any)?.name === "UserAbortedError") { + p.cancel("Cancelled."); + return; + } + throw err; + }
24-24: Minor: options.noInstall is unused in the registry flowThe noInstall option is threaded through but not acted upon in runAddFromRegistry/installFromRegistry. Either plumb it into installFromRegistry (if meaningful) or drop it from the function signature to avoid confusion.
Also applies to: 109-112
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (22)
.changeset/brown-keys-float.md(1 hunks)packages/cli/src/cli/add/index.ts(5 hunks)packages/cli/src/cli/add/registry/getOptions.ts(1 hunks)packages/cli/src/cli/add/registry/install.ts(5 hunks)packages/cli/src/cli/add/registry/listItems.ts(1 hunks)packages/cli/src/cli/add/registry/postInstall/index.ts(2 hunks)packages/cli/src/cli/init.ts(1 hunks)packages/cli/src/consts.ts(1 hunks)packages/cli/src/helpers/stealth-init.ts(2 hunks)packages/cli/src/installers/envVars.ts(2 hunks)packages/cli/src/utils/addToEnvs.ts(4 hunks)packages/cli/src/utils/formatting.ts(1 hunks)packages/cli/src/utils/parseSettings.ts(5 hunks)packages/cli/template/nextjs-shadcn/proofkit.json(1 hunks)packages/cli/template/nextjs-shadcn/src/lib/env.ts(1 hunks)packages/registry/lib/types.ts(4 hunks)packages/registry/lib/utils.ts(3 hunks)packages/registry/templates/better-auth/auth.hbs(1 hunks)packages/registry/templates/fmdapi/_meta.ts(1 hunks)packages/registry/templates/fmdapi/proofkit-typegen.config.jsonc(1 hunks)packages/registry/templates/utils/t3-env/_meta.ts(1 hunks)packages/registry/templates/utils/t3-env/env.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (12)
packages/cli/src/cli/add/registry/listItems.ts (1)
packages/cli/src/cli/add/registry/http.ts (1)
registryFetch(19-22)
packages/cli/src/cli/add/registry/getOptions.ts (1)
packages/cli/src/cli/add/registry/http.ts (1)
registryFetch(19-22)
packages/cli/src/cli/add/registry/postInstall/index.ts (1)
packages/cli/src/utils/addToEnvs.ts (1)
addToEnv(19-129)
packages/registry/templates/fmdapi/_meta.ts (2)
packages/registry/templates/utils/t3-env/_meta.ts (1)
meta(3-24)packages/registry/lib/types.ts (1)
TemplateMetadata(179-179)
packages/cli/src/helpers/stealth-init.ts (1)
packages/cli/src/utils/parseSettings.ts (3)
setSettings(127-133)defaultSettings(82-89)validateAndSetEnvFile(139-156)
packages/cli/src/cli/init.ts (2)
packages/cli/src/utils/parseSettings.ts (1)
Settings(118-118)packages/cli/src/state.ts (1)
state(22-22)
packages/cli/src/utils/addToEnvs.ts (2)
packages/cli/src/state.ts (1)
state(22-22)packages/cli/src/installers/envVars.ts (1)
findT3EnvFile(31-56)
packages/cli/src/cli/add/index.ts (6)
packages/cli/src/utils/parseSettings.ts (2)
getSettings(92-116)Settings(118-118)packages/cli/src/cli/utils.ts (1)
abortIfCancel(50-58)packages/cli/src/cli/add/fmschema.ts (1)
runAddSchemaAction(16-192)packages/cli/src/cli/add/data-source/index.ts (1)
runAddDataSourceCommand(9-27)packages/cli/src/cli/add/registry/install.ts (1)
installFromRegistry(62-247)packages/cli/src/cli/add/registry/preflight.ts (1)
preflightAddCommand(7-21)
packages/registry/lib/utils.ts (9)
packages/registry/lib/types.ts (1)
RegistryIndex(188-188)packages/registry/templates/fmdapi/_meta.ts (1)
meta(3-40)packages/registry/templates/utils/t3-env/_meta.ts (1)
meta(3-24)packages/registry/templates/components/mode-toggle/_meta.ts (1)
meta(3-19)packages/registry/templates/email/auth-code/_meta.ts (1)
meta(3-20)packages/registry/templates/email/generic/_meta.ts (1)
meta(3-20)packages/registry/templates/react-email/_meta.ts (1)
meta(3-21)packages/registry/templates/table/basic/_meta.ts (1)
meta(3-36)packages/registry/templates/utils/nuqs/_meta.ts (1)
meta(3-27)
packages/cli/src/utils/parseSettings.ts (1)
packages/cli/src/state.ts (1)
state(22-22)
packages/cli/src/cli/add/registry/install.ts (5)
packages/cli/src/cli/add/registry/preflight.ts (1)
preflightAddCommand(7-21)packages/cli/src/utils/parseSettings.ts (2)
getSettings(92-116)DataSource(47-47)packages/cli/src/cli/utils.ts (1)
abortIfCancel(50-58)packages/cli/src/helpers/shadcn-cli.ts (1)
shadcnInstall(11-28)packages/cli/src/cli/add/registry/postInstall/handlebars.ts (1)
buildHandlebarsData(79-94)
packages/cli/src/installers/envVars.ts (1)
packages/cli/src/state.ts (1)
state(22-22)
🪛 LanguageTool
.changeset/brown-keys-float.md
[grammar] ~5-~5: There might be a mistake here.
Context: ...ofkit/cli": patch --- Add utils/fmdapi to registry
(QB_NEW_EN)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build
- GitHub Check: build
🔇 Additional comments (18)
packages/registry/templates/better-auth/auth.hbs (1)
11-11: Good hardening: disable adapter debug logs by defaultSetting
debugLogs: falseis a safer default and helps avoid leaking sensitive details in production logs.packages/registry/templates/utils/t3-env/_meta.ts (1)
19-20: Formatting-only change looks goodSplitting the long string over multiple lines improves readability without changing behavior.
packages/cli/src/consts.ts (1)
33-41: Good defensive guard against an undefined injected constant.The
typeofguard avoids a runtime ReferenceError when running from source. The fallback to a sane default URL makes the CLI resilient in non-bundled scenarios.packages/cli/src/utils/parseSettings.ts (3)
82-89: Defaults look sane and align with the new flow.The expanded
defaultSettings(appType, dataSources, replacedMainPage, registryTemplates) matches how stealth init now seeds settings.
102-110: Lightweight migration for missing "ui".Injecting
ui: "mantine"when absent is reasonable for older configs. Just confirm this matches your historical default; the currentdefaultSettingsuses"shadcn", so the migration path and new defaults intentionally differ.Would you like the migration to align with
"shadcn"instead for consistency with new projects?
57-60: Optional envFile usage verifiedA search for unguarded
settings.envFileusages shows that every consumer either checks for its presence or gracefully handles a missing file (viafs.existsSync), including in:
proofkit-auth.ts,proofkit-webviewer.ts,fmschema.ts(all wrapped inif (settings.envFile) { … })deploy/index.ts(constructs the path and early-returns when the file doesn’t exist)- Stealth init helpers and default settings in
parseSettings.tsandinit.tsNo callers assume
envFileis always a string, so making it optional is safe. Great work!packages/cli/src/helpers/stealth-init.ts (2)
3-7: Switching to the settings API is a solid refactor.Using
setSettingsandvalidateAndSetEnvFilecentralizes persistence and keepsproofkit.jsonshape consistent across codepaths.
19-24: No fallback needed forstate.projectDir
Thestateobject’s schema inpackages/cli/src/state.tsalready defaultsprojectDirtoprocess.cwd()upon initialization, sostate.projectDircan never beundefinedat runtime. Consequently:
- There is no risk of writing to an unintended location when
setSettingsis called instealthInit().- Adding a manual fallback or importing
stateinstealth-init.tsis unnecessary, asparseSettings.ts—which definessetSettings—already imports and uses the sharedstate.Relevant location:
• packages/cli/src/state.ts —projectDir: z.string().default(process.cwd()),ensures a valid path at startup.Likely an incorrect or invalid review comment.
.changeset/brown-keys-float.md (1)
1-5: Confirm that all affected packages have changesets.This PR also adds/updates items in the registry package (templates/types/utils). Ensure those packages have accompanying changesets if their public surface changes (e.g., new template, new exported types).
Would you like me to generate a script to detect touched packages without corresponding changesets?
packages/cli/template/nextjs-shadcn/proofkit.json (1)
5-5: Review Update: Verify template metadata and CLI compatibility
- Confirmed the template directory exists at
packages/registry/templates/components/mode-toggleand contains a valid_meta.ts.- The
_meta.tsexportstitle: "Mode Toggle", along withdescription,category,registryType,dependencies, andregistryDependencies, but does not includeuiorappTypefields.- If your proofkit CLI relies on explicit
ui: "shadcn"andappType: "browser"in the template metadata, please add these properties to themetaobject in_meta.ts.- Otherwise, ensure that the
nextjs-shadcnproofkit logic correctly selects this component based on its existing metadata (e.g.registryTypeor other filters).packages/registry/lib/types.ts (2)
72-93: Env post-install step schema looks good.The structure aligns with the CLI’s addToEnv handler. No blocking issues spotted.
181-189: Good addition of RegistryIndex with name/title/description.This aligns the index shape to what the CLI needs to display templates meaningfully.
packages/registry/lib/utils.ts (1)
72-84: Registry index shape refactor is clean and clear.Switching to RegistryIndex and surfacing title/description improves UX. Using RegistryIndex[number] tightens types.
packages/cli/src/cli/add/registry/install.ts (3)
160-163: LGTM: consistent routeName propagation into registry URLPassing routeName as “/(main)/…” ensures page templates render files into the right segment group. This matches the downstream PATH replacement.
200-208: LGTM: explicit PATH replacement before renderingResolving PATH prior to rendering keeps file emissions deterministic and decoupled from handlebars context. Good call.
10-10: getRegistryUrl import validatedConfirmed that
getRegistryUrlis exported frompackages/cli/src/helpers/shadcn-cli.ts(exported on line 30) and is correctly imported ininstall.ts. No further changes needed.packages/cli/src/cli/add/index.ts (2)
121-126: Solid fallback path when settings are missingCatching getSettings(), running preflightAddCommand(), and pivoting to the registry flow is a sensible default for fresh projects or when running from source.
184-186: Commander action modernization LGTMSwitching to an async arrow maintains error propagation and avoids any type assertions. Good cleanup.
…d readability. Update environment variable handling in the nextjs-shadcn template to use .catch for default values.
6ef512f to
c71b0d4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/cli/src/installers/envVars.ts (1)
23-26: Don’t overwrite existing .env; make init idempotent
writeFileSyncwill clobber users’ environment variables. Bail out (or append guardedly) if.envalready exists.- fs.writeFileSync(envDest, envContent, "utf-8"); + if (fs.existsSync(envDest)) { + logger.info(`.env already exists; leaving it unchanged.`); + return; + } + fs.writeFileSync(envDest, envContent, "utf-8");
♻️ Duplicate comments (3)
packages/registry/templates/utils/t3-env/env.ts (1)
6-6: Include "test" in NODE_ENV to avoid breaking tests.
z.enum(["development","production"])rejects NODE_ENV="test" (common in Jest/Vitest). Keep.default("development")but add "test".- NODE_ENV: z.enum(["development", "production"]).default("development"), + NODE_ENV: z.enum(["development", "production", "test"]).default("development"),packages/cli/src/installers/envVars.ts (2)
33-39: Path-join bug fixed (relative candidates)Switching to project-relative candidates resolves the earlier issue where leading slashes caused
path.jointo ignorestate.projectDir. LGTM.
52-56: User-facing warning fixedCopy edit from the prior review is applied; message is clear. Consider including the searched paths in the error for faster debugging.
- throw new Error("T3 env file not found"); + throw new Error( + `T3 env file not found. Searched: ${[ + "src/config/env.ts", + "src/lib/env.ts", + "src/env.ts", + "lib/env.ts", + "env.ts", + "config/env.ts", + ].join(", ")}` + );
🧹 Nitpick comments (2)
.changeset/brown-keys-float.md (1)
5-5: Nit: improve release note grammarUse the article for readability.
-Add utils/fmdapi to registry +Add utils/fmdapi to the registrypackages/cli/src/cli/init.ts (1)
248-271: Prefer an explicit if/else over a long ternary for maintainability.Given the branch-specific shapes, an if/else improves readability and reduces merge pain as these objects evolve.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (6)
.changeset/brown-keys-float.md(1 hunks)packages/cli/src/cli/add/registry/postInstall/index.ts(2 hunks)packages/cli/src/cli/init.ts(1 hunks)packages/cli/src/installers/envVars.ts(2 hunks)packages/cli/template/nextjs-shadcn/src/lib/env.ts(1 hunks)packages/registry/templates/utils/t3-env/env.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/cli/template/nextjs-shadcn/src/lib/env.ts
- packages/cli/src/cli/add/registry/postInstall/index.ts
🧰 Additional context used
🧬 Code graph analysis (2)
packages/cli/src/cli/init.ts (2)
packages/cli/src/utils/parseSettings.ts (1)
Settings(118-118)packages/cli/src/state.ts (1)
state(22-22)
packages/cli/src/installers/envVars.ts (1)
packages/cli/src/state.ts (1)
state(22-22)
🪛 LanguageTool
.changeset/brown-keys-float.md
[grammar] ~5-~5: There might be a mistake here.
Context: ...ofkit/cli": patch --- Add utils/fmdapi to registry
(QB_NEW_EN)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build
- GitHub Check: build
🔇 Additional comments (3)
packages/registry/templates/utils/t3-env/env.ts (1)
6-6: Good switch to .default over .catch.This avoids swallowing invalid values and only defaults when absent.
packages/cli/src/installers/envVars.ts (1)
28-31: Typed overloads look correctOverload contracts match runtime behavior (throws by default; null only when passed
false). Good.packages/cli/src/cli/init.ts (1)
254-255: Verified Mantine-only settings in schema
All referenced fields (auth, envFile, tanstackQuery, reactEmail, reactEmailServer, appliedUpgrades) exist in settingsSchema, and validateAndSetEnvFile is exported—no further changes needed.
| --- | ||
| "@proofkit/cli": patch | ||
| --- | ||
|
|
||
| Add utils/fmdapi to registry |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Ensure all impacted packages have a changeset entry
This PR also modifies registry code; only @proofkit/cli is bumped here. Verify @proofkit/registry (and any other changed packages) have corresponding changesets so they get published.
Run to confirm:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Packages referenced in changesets:"
rg -nP '^\s*"[.@/\w-]+"\s*:\s*(patch|minor|major)' .changeset/*.md || true
echo -e "\nCheck for @proofkit/registry entry:"
rg -n '@proofkit/registry' .changeset/*.md || echo "Missing @proofkit/registry changeset?"Length of output: 873
Add a changeset entry for @proofkit/registry – registry code was updated but no corresponding .changeset bump exists; add one to ensure it’s published.
🧰 Tools
🪛 LanguageTool
[grammar] ~5-~5: There might be a mistake here.
Context: ...ofkit/cli": patch --- Add utils/fmdapi to registry
(QB_NEW_EN)
🤖 Prompt for AI Agents
In .changeset/brown-keys-float.md around lines 1 to 5, the changeset only lists
"@proofkit/cli" but the registry package was changed; add an entry for
"@proofkit/registry" (likely a patch bump) to this changeset so the registry
changes are published. Update the YAML frontmatter to include
"@proofkit/registry": patch (or the appropriate release type) and add a short
description line referencing the registry changes, then save the changeset so
the release tooling picks it up.
| appType: state.appType ?? "browser", | ||
| ui: "mantine", | ||
| auth: { type: "none" }, | ||
| envFile: ".env", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Don’t hardcode envFile; let centralized env detection decide.
Hardcoding ".env" may override stealth-init/validateAndSetEnvFile behavior and miss .env.local or other variants. Remove envFile here and delegate to the centralized flow.
Apply this diff to drop the hardcoded default:
- envFile: ".env",Repeat the same removal for the shadcn branch:
- envFile: ".env",If setSettings doesn’t already invoke validateAndSetEnvFile, call it right after setSettings to pick the best existing env file. I can draft that change if desired.
Also applies to: 267-267
🏁 Script executed:
#!/bin/bash
rg -n "envFile" -C2 packages/cli/src/cli/init.ts
rg -n "validateAndSetEnvFile" -C2 packages/cli/src
rg -n "setSettings" -C2 packages/cli/src/cli/init.tsLength of output: 1816
🏁 Script executed:
#!/bin/bash
# Locate parseSettings.ts and inspect setSettings implementation
rg -nP 'export function setSettings' -C20 packages/cli/src/utils/parseSettings.ts
# Check if setSettings calls validateAndSetEnvFile internally
rg -nP 'validateAndSetEnvFile' -C5 packages/cli/src/utils/parseSettings.tsLength of output: 1951
🏁 Script executed:
#!/bin/bash
rg -n "defaultSettings" -C5 packages/cli/src/utils/parseSettings.tsLength of output: 368
Drop hardcoded envFile and delegate to centralized detection
- Remove the
envFile: ".env"lines in both the mantine (line 255) and shadcn (line 267) branches ofpackages/cli/src/cli/init.ts. - After
import and call
const { registryUrl } = setSettings(initialSettings);
so the existingimport { validateAndSetEnvFile } from "~/utils/parseSettings.js"; validateAndSetEnvFile();
.env*variants are picked up by the central flow.
🤖 Prompt for AI Agents
In packages/cli/src/cli/init.ts around lines 255 and 267, remove the hardcoded
envFile: ".env" entries in both the mantine and shadcn branches so the CLI no
longer forces a specific .env; then after the line that calls
setSettings(initialSettings) (the one that destructures registryUrl), add an
import for validateAndSetEnvFile from ~/utils/parseSettings.js and call
validateAndSetEnvFile() so the centralized detection picks up existing .env*
variants instead of the previous hardcoded value.
| const envFilePath = findT3EnvFile(false) ?? `./src/config/env.ts`; | ||
|
|
||
| const envContent = ` | ||
| # When adding additional environment variables, the schema in "${envFilePath}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid embedding absolute paths into generated .env comments
findT3EnvFile(false) returns an absolute path; baking that into .env makes the comment machine-specific. Use a project-relative path (and drop the leading ./ in the fallback).
-export const initEnvFile: Installer = () => {
- const envFilePath = findT3EnvFile(false) ?? `./src/config/env.ts`;
+export const initEnvFile: Installer = () => {
+ const foundEnvFilePath = findT3EnvFile(false);
+ const envFilePath =
+ foundEnvFilePath
+ ? path.relative(state.projectDir, foundEnvFilePath)
+ : "src/config/env.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.
| const envFilePath = findT3EnvFile(false) ?? `./src/config/env.ts`; | |
| const envContent = ` | |
| # When adding additional environment variables, the schema in "${envFilePath}" | |
| export const initEnvFile: Installer = () => { | |
| const foundEnvFilePath = findT3EnvFile(false); | |
| const envFilePath = | |
| foundEnvFilePath | |
| ? path.relative(state.projectDir, foundEnvFilePath) | |
| : "src/config/env.ts"; | |
| const envContent = ` | |
| # When adding additional environment variables, the schema in "${envFilePath}" |
🤖 Prompt for AI Agents
In packages/cli/src/installers/envVars.ts around lines 13–16, the template
writes an absolute path from findT3EnvFile(false) into the generated .env
comment and the fallback uses a leading "./"; change it to use a
project-relative path and remove the leading "./" in the fallback. Concretely,
compute a relative path (e.g. path.relative(process.cwd(), envFilePath) or
equivalent) and use that string in the template instead of the raw envFilePath,
and change the fallback to "src/config/env.ts" (no "./") so the comment is not
machine-specific.

fmdapi template
Enhance error handling and fallback mechanisms in CLI. Update DEFAULT_REGISTRY_URL to provide a safe fallback when running from source. Improve settings management during stealth initialization and refine env file handling. Add preflight checks in the add command to ensure proper project setup. Update template metadata for fmdapi and t3-env. Refactor various utility functions for better clarity and error resilience.
Refactor initial settings structure in CLI initialization for improved readability. Update environment variable handling in the nextjs-shadcn template to use .catch for default values.
Summary by CodeRabbit
New Features
Bug Fixes / Improvements