Skip to content

Conversation

@therealsamyak
Copy link
Contributor

@therealsamyak therealsamyak commented Dec 7, 2025

Add lefthook to addons section.

Areas Updates

  • *.md files across GitHub repo
  • Documentation (CLI-Commands, CLI-Programmatic-API, CLI-Options Reference, bts.jsonc)
  • CLI
    • added prepare script with lefthook install to package.json in template.
  • Stack Builder

Notes

  • You can only select one of { lefthook, husky }
  • "Fast and powerful Git hooks manager" is taken directly from Lefthook's GitHub description https://github.com/evilmartians/lefthook/.
  • If Ultracite is configured, Ultracite handles Git hooks setup instead.
    • There is a "bug" / missing feature in Ultracite where a prepare script in the package.json is not generated. I filed a feature request within the Ultracite repo (Prepare script when using Git hooks manager (ex. Lefthook) haydenbleasel/ultracite#427) so hopefully this is resolved soon. Ultracite already runs 'lefthook install' (or similar hook scripts) upon finishing its setup, so not a huge huge problem if the Git hooks don't change. this is fixed
  • If not Ultracite, and Biome or Oxlint is configured, the pre-commit hooks are populated with lint-staged (husky), or the specific lint commands biome check --no-errors-on-unmatched --files-ignore-unknown=true --colors=off {staged_files} and/or oxlint --fix . (lefthook) respectively. Lefthook doesn't need lint-staged (just use stage_fixed: true), so why add another dependency when it's not necessary.
  • The logo for lefthook can be found here https://github.com/evilmartians/lefthook/blob/master/logo.svg. Not sure how to add it xd.
  • Currently you are allowed to select Husky / Lefthook without requiring an init of a git repo, even though git hooks don't work unless you init a git repo. I maintained that behavior here, but it is something to consider changing later down the line.

Disclaimer that majority of the code is AI-generated, but I did test it throughly, reviewed all the code generated manually with my own 2 eyes, and tested it locally to verify it worked before opening this PR.

Screenshots

image image image image image

Summary by CodeRabbit

  • New Features

    • Added Lefthook as an available Git-hooks addon alongside Husky.
    • Ultracite integrations expanded with additional AI agents and Claude hook support.
  • Documentation

    • Updated docs, guides, flags cheat sheet, and examples to include Lefthook and adjusted addon ordering.
    • Post-install output now includes Lefthook setup/instructions.
  • Tests

    • Added tests for Lefthook and combined Lefthook+Husky scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.


Note

Introduces lefthook as a git-hooks addon and wires it through CLI, templates, tests, and docs, while enhancing Ultracite integrations.

  • Add lefthook addon: CLI constants, prompts, setup (setupLefthook), post-install hints, schema/types, and Stack Builder UI/command; template adds lefthook.yml; version map includes lefthook
  • Ultracite updates: run when selected; accepts multiple git-hook integrations (husky, lefthook) and adds lint-staged when using husky; new agents (cursor-cli, mistral-vibe, vercel) and hook claude
  • Documentation: READMEs and docs list lefthook in --addons and compatibility notes
  • Tests: expand universal addons to include lefthook; add scenario for lefthook + husky together

Written by Cursor Bugbot for commit b22990e. This will update automatically on new commits. Configure here.

@vercel
Copy link

vercel bot commented Dec 7, 2025

@therealsamyak is attempting to deploy a commit to the Better T Stack Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 7, 2025

Walkthrough

Adds Lefthook as a new git-hooks addon across CLI, templates, types, web UI, and tests; integrates Lefthook into addon detection/setup flows, Ultracite integration, post-install instructions, templates, dependency mappings, and docs.

Changes

Cohort / File(s) Summary
CLI docs
apps/cli/README.md, README.md
Inserted Lefthook into Addons lists and example help text; adjusted ordering (Lefthook before Husky) and minor formatting.
CLI addon wiring
apps/cli/src/constants.ts, apps/cli/src/helpers/addons/addons-setup.ts, apps/cli/src/helpers/addons/ultracite-setup.ts
Added lefthook to ADDON_COMPATIBILITY; detect hasLefthook; added/exported setupLefthook(projectDir); changed setupUltracite signature to accept gitHooks: string[] and build integrations from that array.
Post-install & prompts
apps/cli/src/helpers/core/post-installation.ts, apps/cli/src/prompts/addons.ts
Added lefthook instructions and hasGitHooksOrLinting usage; added display entry for Lefthook and included it in addon groups.
CLI tests
apps/cli/test/addons.test.ts
Added lefthook to universalAddons and new tests covering Lefthook+Husky combinations.
Web UI & stack utils
apps/web/src/lib/constant.ts, apps/web/src/lib/stack-utils.ts, apps/web/content/docs/cli/options.mdx, apps/web/content/docs/index.mdx, apps/web/content/docs/project-structure.mdx
Added Lefthook to TECH_OPTIONS/addons and stack-command whitelist; updated docs and project-structure examples to include Lefthook; reordered some addon entries (fumadocs/biome/turborepo/ultracite adjustments).
Types & templates
packages/types/src/schemas.ts, packages/template-generator/src/utils/add-deps.ts, packages/template-generator/templates/addons/...
Added lefthook to AddonsSchema; added lefthook dependency mapping ("^2.0.13"); added lefthook.yml.hbs template; updated ruler template to reference Husky/Lefthook; removed Ultracite biome JSON template.

Possibly related PRs

  • #674: Alters ultracite-setup.ts by extending Ultracite agents/hooks and changing how ultracite args/integrations are constructed — directly related to the Ultracite signature and integrations changes.
  • #794: Modifies addon wiring in addons-setup.ts (setupX functions and flow) similar to the Lefthook hookup and orchestration changes.
  • #481: Updates ADDON_COMPATIBILITY and related addon constants in constants.ts, which is directly related to adding lefthook to compatibility mappings.
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.03% 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 pull request title 'feat: Add lefthook to addons' accurately summarizes the main change: adding Lefthook as a new addon across the codebase.

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


📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 700ad84 and b22990e.

📒 Files selected for processing (1)
  • apps/web/src/lib/constant.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

Define functions using the standard function declaration syntax, not arrow functions

Files:

  • apps/web/src/lib/constant.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.{ts,tsx}: Use TypeScript type aliases instead of interface declarations
Do not use explicit return types

Files:

  • apps/web/src/lib/constant.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file> for running TypeScript/JavaScript files
Bun automatically loads .env files, so don't use the dotenv package
Use Bun.serve() which supports WebSockets, HTTPS, and routes instead of express
Use bun:sqlite module for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of the ws package
Prefer Bun.file over node:fs readFile/writeFile methods
Use Bun.$ template literal syntax instead of execa for shell command execution
Import .css files directly in TypeScript/JavaScript files; Bun's CSS bundler will handle bundling
Run server with bun --hot <file> to enable hot reloading during development

Files:

  • apps/web/src/lib/constant.ts
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file> instead of webpack or esbuild for bundling TypeScript, JavaScript, and CSS files

Files:

  • apps/web/src/lib/constant.ts
**/*.{html,tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use HTML imports with Bun.serve() for frontend instead of Vite

Files:

  • apps/web/src/lib/constant.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-18T19:15:55.011Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, the Ultracite CLI's `--integrations` flag requires git hooks to be passed as a single space-separated string (e.g., `gitHooks.join(" ")`) rather than using the spread operator like other Ultracite flags. This has been tested and confirmed to work.
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-25T00:49:57.385Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, when calling Ultracite CLI with the `--integrations` flag, `lint-staged` should be passed as a separate positional argument after the integrations string (e.g., `ultraciteArgs.push("--integrations", gitHooks.join(" "))` followed by `ultraciteArgs.push("lint-staged")`), not included within the integrations string itself. This has been tested and confirmed to work.
⏰ 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: Test Suite
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (2)
apps/web/src/lib/constant.ts (2)

504-510: Lefthook addon added successfully.

The new Lefthook addon is correctly structured and consistent with the existing Husky addon (both have empty icons).

Note: Per the PR objectives, mutual exclusivity between Husky and Lefthook is not yet enforced—users can currently select both simultaneously. The author has acknowledged this and plans to address it in a follow-up PR along with enforcing git-init requirements for git hooks managers.


535-542: No action needed. Turborepo's exclusion from the Uniwind preset is intentional. The preset is explicitly designed as a minimal native-only app ("Expo + Uniwind native app with no backend services") with no backend, database, authentication, or API layer. The default: true flag does not auto-include turborepo in all presets; each preset explicitly selects its own addons. Since Uniwind is a single native app, turborepo (a monorepo build system) is not applicable.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

@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: 3

🧹 Nitpick comments (3)
apps/web/src/lib/stack-url-keys.ts (1)

17-17: Consider abbreviating the URL parameter for consistency.

While functionally correct, the mapping gitHooks: "gitHooks" uses the full property name as the URL parameter. Most other mappings use abbreviations (e.g., webFrontend: "fe-w", runtime: "rt", backend: "be"). Consider using an abbreviated form like gitHooks: "gh" to maintain consistency and reduce URL length.

apps/web/src/lib/stack-utils.ts (1)

18-18: Verify the position of gitHooks in CATEGORY_ORDER.

The gitHooks category is placed after packageManager and before addons. While this ordering seems reasonable, consider whether it should be grouped closer to the "git" option (line 21) since they're conceptually related.

Consider this alternative ordering if you want to group Git-related options together:

  "packageManager",
+  "git",
+  "gitHooks",
-  "gitHooks",
  "addons",
  "examples",
-  "git",
  "install",
apps/cli/src/helpers/git-hooks/git-hooks-setup.ts (1)

87-97: Consider adding error handling for template operations.

If the Handlebars template file is missing or unreadable, this will throw an unhandled error with a potentially cryptic message. Consider wrapping in try-catch with a descriptive error, consistent with the pattern in createReadme (line 24 in create-readme.ts).

+  try {
    // Read and compile Handlebars template
    const templatePath = path.join(PKG_ROOT, "templates/git-hooks/lefthook/lefthook.yml.hbs");
    const templateContent = await fs.readFile(templatePath, "utf-8");
    const template = Handlebars.compile(templateContent);

    // Generate lefthook.yml content using template
    const lefthookYmlContent = template({ biome, oxlint });

    const lefthookYmlPath = path.join(projectDir, "lefthook.yml");
    await fs.writeFile(lefthookYmlPath, lefthookYmlContent);
+  } catch (error) {
+    throw new Error(`Failed to setup lefthook configuration: ${error}`);
+  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89d4a98 and c74d8fe.

📒 Files selected for processing (41)
  • README.md (1 hunks)
  • apps/cli/README.md (3 hunks)
  • apps/cli/src/constants.ts (2 hunks)
  • apps/cli/src/helpers/addons/addons-setup.ts (1 hunks)
  • apps/cli/src/helpers/addons/ultracite-setup.ts (3 hunks)
  • apps/cli/src/helpers/core/add-addons.ts (1 hunks)
  • apps/cli/src/helpers/core/command-handlers.ts (1 hunks)
  • apps/cli/src/helpers/core/create-project.ts (4 hunks)
  • apps/cli/src/helpers/core/create-readme.ts (5 hunks)
  • apps/cli/src/helpers/core/detect-project-config.ts (1 hunks)
  • apps/cli/src/helpers/core/post-installation.ts (2 hunks)
  • apps/cli/src/helpers/core/template-manager.ts (1 hunks)
  • apps/cli/src/helpers/git-hooks/git-hooks-setup.ts (1 hunks)
  • apps/cli/src/index.ts (3 hunks)
  • apps/cli/src/prompts/addons.ts (2 hunks)
  • apps/cli/src/prompts/config-prompts.ts (5 hunks)
  • apps/cli/src/prompts/git-hooks.ts (1 hunks)
  • apps/cli/src/types.ts (5 hunks)
  • apps/cli/src/utils/bts-config.ts (2 hunks)
  • apps/cli/src/utils/config-processing.ts (2 hunks)
  • apps/cli/src/utils/display-config.ts (1 hunks)
  • apps/cli/src/validation.ts (1 hunks)
  • apps/cli/templates/addons/ruler/.ruler/bts.md.hbs (1 hunks)
  • apps/cli/templates/git-hooks/lefthook/lefthook.yml.hbs (1 hunks)
  • apps/cli/test/addons.test.ts (2 hunks)
  • apps/cli/test/backend-runtime.test.ts (1 hunks)
  • apps/cli/test/benchmark.test.ts (1 hunks)
  • apps/cli/test/git-hooks.test.ts (1 hunks)
  • apps/cli/test/integration.test.ts (4 hunks)
  • apps/cli/test/test-utils.ts (2 hunks)
  • apps/web/content/docs/cli/index.mdx (1 hunks)
  • apps/web/content/docs/cli/options.mdx (2 hunks)
  • apps/web/content/docs/cli/programmatic-api.mdx (1 hunks)
  • apps/web/content/docs/index.mdx (1 hunks)
  • apps/web/content/docs/project-structure.mdx (1 hunks)
  • apps/web/src/lib/constant.ts (9 hunks)
  • apps/web/src/lib/stack-url-keys.ts (1 hunks)
  • apps/web/src/lib/stack-url-state.client.ts (1 hunks)
  • apps/web/src/lib/stack-url-state.ts (1 hunks)
  • apps/web/src/lib/stack-utils.ts (2 hunks)
  • apps/web/src/lib/types.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

Define functions using the standard function declaration syntax, not arrow functions

Files:

  • apps/web/src/lib/types.ts
  • apps/cli/src/prompts/addons.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/src/validation.ts
  • apps/cli/src/helpers/core/add-addons.ts
  • apps/cli/src/index.ts
  • apps/cli/test/backend-runtime.test.ts
  • apps/cli/src/utils/display-config.ts
  • apps/cli/test/benchmark.test.ts
  • apps/cli/src/prompts/config-prompts.ts
  • apps/cli/test/test-utils.ts
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/cli/src/prompts/git-hooks.ts
  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/git-hooks/git-hooks-setup.ts
  • apps/cli/src/constants.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/src/types.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/stack-utils.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/web/src/lib/stack-url-state.ts
  • apps/cli/src/helpers/core/detect-project-config.ts
  • apps/cli/src/helpers/core/command-handlers.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/cli/src/utils/bts-config.ts
  • apps/cli/src/helpers/core/create-project.ts
  • apps/cli/src/utils/config-processing.ts
  • apps/web/src/lib/constant.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.{ts,tsx}: Use TypeScript type aliases instead of interface declarations
Do not use explicit return types

Files:

  • apps/web/src/lib/types.ts
  • apps/cli/src/prompts/addons.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/src/validation.ts
  • apps/cli/src/helpers/core/add-addons.ts
  • apps/cli/src/index.ts
  • apps/cli/test/backend-runtime.test.ts
  • apps/cli/src/utils/display-config.ts
  • apps/cli/test/benchmark.test.ts
  • apps/cli/src/prompts/config-prompts.ts
  • apps/cli/test/test-utils.ts
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/cli/src/prompts/git-hooks.ts
  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/git-hooks/git-hooks-setup.ts
  • apps/cli/src/constants.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/src/types.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/stack-utils.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/web/src/lib/stack-url-state.ts
  • apps/cli/src/helpers/core/detect-project-config.ts
  • apps/cli/src/helpers/core/command-handlers.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/cli/src/utils/bts-config.ts
  • apps/cli/src/helpers/core/create-project.ts
  • apps/cli/src/utils/config-processing.ts
  • apps/web/src/lib/constant.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file> for running TypeScript/JavaScript files
Bun automatically loads .env files, so don't use the dotenv package
Use Bun.serve() which supports WebSockets, HTTPS, and routes instead of express
Use bun:sqlite module for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of the ws package
Prefer Bun.file over node:fs readFile/writeFile methods
Use Bun.$ template literal syntax instead of execa for shell command execution
Import .css files directly in TypeScript/JavaScript files; Bun's CSS bundler will handle bundling
Run server with bun --hot <file> to enable hot reloading during development

Files:

  • apps/web/src/lib/types.ts
  • apps/cli/src/prompts/addons.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/src/validation.ts
  • apps/cli/src/helpers/core/add-addons.ts
  • apps/cli/src/index.ts
  • apps/cli/test/backend-runtime.test.ts
  • apps/cli/src/utils/display-config.ts
  • apps/cli/test/benchmark.test.ts
  • apps/cli/src/prompts/config-prompts.ts
  • apps/cli/test/test-utils.ts
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/cli/src/prompts/git-hooks.ts
  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/git-hooks/git-hooks-setup.ts
  • apps/cli/src/constants.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/src/types.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/stack-utils.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/web/src/lib/stack-url-state.ts
  • apps/cli/src/helpers/core/detect-project-config.ts
  • apps/cli/src/helpers/core/command-handlers.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/cli/src/utils/bts-config.ts
  • apps/cli/src/helpers/core/create-project.ts
  • apps/cli/src/utils/config-processing.ts
  • apps/web/src/lib/constant.ts
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file> instead of webpack or esbuild for bundling TypeScript, JavaScript, and CSS files

Files:

  • apps/web/src/lib/types.ts
  • apps/cli/src/prompts/addons.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/src/validation.ts
  • apps/cli/src/helpers/core/add-addons.ts
  • apps/cli/src/index.ts
  • apps/cli/test/backend-runtime.test.ts
  • apps/cli/src/utils/display-config.ts
  • apps/cli/test/benchmark.test.ts
  • apps/cli/src/prompts/config-prompts.ts
  • apps/cli/test/test-utils.ts
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/cli/src/prompts/git-hooks.ts
  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/git-hooks/git-hooks-setup.ts
  • apps/cli/src/constants.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/src/types.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/stack-utils.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/web/src/lib/stack-url-state.ts
  • apps/cli/src/helpers/core/detect-project-config.ts
  • apps/cli/src/helpers/core/command-handlers.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/cli/src/utils/bts-config.ts
  • apps/cli/src/helpers/core/create-project.ts
  • apps/cli/src/utils/config-processing.ts
  • apps/web/src/lib/constant.ts
**/*.{html,tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use HTML imports with Bun.serve() for frontend instead of Vite

Files:

  • apps/web/src/lib/types.ts
  • apps/cli/src/prompts/addons.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/src/validation.ts
  • apps/cli/src/helpers/core/add-addons.ts
  • apps/cli/src/index.ts
  • apps/cli/test/backend-runtime.test.ts
  • apps/cli/src/utils/display-config.ts
  • apps/cli/test/benchmark.test.ts
  • apps/cli/src/prompts/config-prompts.ts
  • apps/cli/test/test-utils.ts
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/cli/src/prompts/git-hooks.ts
  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/git-hooks/git-hooks-setup.ts
  • apps/cli/src/constants.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/src/types.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/stack-utils.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/web/src/lib/stack-url-state.ts
  • apps/cli/src/helpers/core/detect-project-config.ts
  • apps/cli/src/helpers/core/command-handlers.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/cli/src/utils/bts-config.ts
  • apps/cli/src/helpers/core/create-project.ts
  • apps/cli/src/utils/config-processing.ts
  • apps/web/src/lib/constant.ts
**/*.hbs

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.hbs: In Handlebars templates, avoid generic if/else blocks. Write explicit conditions, such as: use if (eq orm "prisma") for Prisma, and else if (eq orm "drizzle") for Drizzle
Escape the '{{' in Handlebars templates like '{{'

Files:

  • apps/cli/templates/git-hooks/lefthook/lefthook.yml.hbs
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.test.{ts,tsx,js,jsx}: Use bun test instead of jest or vitest for running tests
Use bun:test module with test and expect functions for writing tests

Files:

  • apps/cli/test/backend-runtime.test.ts
  • apps/cli/test/benchmark.test.ts
  • apps/cli/test/addons.test.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/test/integration.test.ts
🧠 Learnings (4)
📚 Learning: 2025-12-03T07:48:26.410Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.410Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use `bun:test` module with `test` and `expect` functions for writing tests

Applied to files:

  • apps/cli/test/test-utils.ts
  • apps/cli/test/git-hooks.test.ts
📚 Learning: 2025-12-03T07:48:26.410Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.410Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use `bun test` instead of `jest` or `vitest` for running tests

Applied to files:

  • apps/cli/test/git-hooks.test.ts
📚 Learning: 2025-12-03T07:48:14.703Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-03T07:48:14.703Z
Learning: Applies to convex/**/*.ts : Use the api object from convex/_generated/api.ts to call public functions registered with query, mutation, or action

Applied to files:

  • apps/cli/src/helpers/core/create-readme.ts
📚 Learning: 2025-12-03T07:48:26.410Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.410Z
Learning: Applies to {package.json,bun.lockb} : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install`

Applied to files:

  • apps/web/content/docs/cli/index.mdx
🧬 Code graph analysis (10)
apps/web/src/lib/stack-url-state.client.ts (1)
apps/web/src/lib/constant.ts (2)
  • StackState (740-761)
  • DEFAULT_STACK (763-784)
apps/cli/src/index.ts (1)
apps/cli/src/types.ts (1)
  • GitHooksSchema (88-88)
apps/cli/src/prompts/git-hooks.ts (3)
apps/cli/src/types.ts (1)
  • GitHooks (89-89)
apps/cli/src/constants.ts (1)
  • DEFAULT_CONFIG (41-41)
apps/cli/src/utils/errors.ts (1)
  • exitCancelled (17-23)
apps/cli/src/helpers/addons/ultracite-setup.ts (2)
apps/cli/src/types.ts (1)
  • ProjectConfig (167-188)
apps/cli/src/utils/add-package-deps.ts (1)
  • addPackageDependency (6-57)
apps/cli/src/types.ts (1)
apps/cli/src/index.ts (1)
  • GitHooks (256-256)
apps/cli/test/git-hooks.test.ts (1)
apps/cli/src/types.ts (1)
  • GitHooks (89-89)
apps/cli/src/helpers/core/create-readme.ts (2)
apps/cli/src/index.ts (1)
  • GitHooks (256-256)
apps/cli/src/types.ts (1)
  • GitHooks (89-89)
apps/web/src/lib/stack-url-state.ts (1)
apps/web/src/lib/constant.ts (2)
  • StackState (740-761)
  • DEFAULT_STACK (763-784)
apps/cli/src/helpers/core/template-manager.ts (2)
apps/cli/src/types.ts (1)
  • ProjectConfig (167-188)
apps/cli/src/constants.ts (1)
  • PKG_ROOT (7-7)
apps/cli/src/helpers/core/create-project.ts (2)
apps/cli/src/helpers/core/template-manager.ts (1)
  • setupGitHooksTemplate (599-610)
apps/cli/src/helpers/git-hooks/git-hooks-setup.ts (1)
  • setupGitHooks (8-34)
🔇 Additional comments (58)
apps/cli/templates/addons/ruler/.ruler/bts.md.hbs (1)

119-119: LGTM!

The removal of Husky from the addons list aligns with the PR's objective to move Git hooks to a dedicated section. The documentation now accurately reflects the new structure.

README.md (1)

47-48: LGTM!

The updated documentation clearly reflects the architectural change, separating Git hooks management from addons. The addition of Lefthook as an option alongside Husky provides users with flexibility.

apps/cli/src/helpers/core/post-installation.ts (3)

22-22: LGTM!

The addition of gitHooks to the destructured config properly integrates the new Git hooks feature into the post-installation flow.


30-30: LGTM!

The renamed condition hasHooksOrBiome correctly handles the new gitHooks configuration. The logic properly evaluates to true when gitHooks is set to either "husky" or "lefthook", and false when it's "none", while maintaining the fallback check for Biome.


46-46: LGTM!

The updated reference to hasHooksOrBiome ensures linting instructions are displayed when either Git hooks or Biome is configured.

apps/cli/src/helpers/core/command-handlers.ts (1)

105-105: LGTM!

The addition of gitHooks: "none" to the error path configuration properly satisfies the updated ProjectConfig type. Using "none" as the default is appropriate for error scenarios.

apps/cli/src/constants.ts (1)

27-27: LGTM!

Setting gitHooks: "none" as the default is appropriate, making Git hooks management opt-in rather than forced upon users.

apps/cli/src/prompts/git-hooks.ts (1)

1-34: LGTM!

The implementation follows best practices:

  • Uses standard function declaration syntax as per coding guidelines
  • Implements efficient early return when gitHooks is already defined
  • Provides clear, descriptive labels and hints for each option
  • Properly handles user cancellation
  • Uses the appropriate default from DEFAULT_CONFIG.gitHooks
apps/cli/src/validation.ts (1)

21-21: Verify flag name consistency.

The addition of "hooks" to CORE_STACK_FLAGS should match the actual CLI flag name. Other entries in this set use the full config property names (e.g., "database", "backend", "gitHooks"). Please verify whether the CLI flag is defined as --hooks or --git-hooks and ensure consistency.

apps/cli/src/prompts/addons.ts (1)

66-66: LGTM! Clean removal of Husky from addons.

The removal of "husky" from the Other addons group is consistent with the PR's refactoring of Git hooks into a dedicated feature. The change aligns with the new --git-hooks option.

apps/web/content/docs/cli/programmatic-api.mdx (1)

163-163: LGTM! Clear documentation of the new gitHooks option.

The documentation clearly describes the new gitHooks field with its allowed values, following the established pattern for other CreateInput fields.

apps/cli/test/addons.test.ts (1)

7-7: LGTM! Test updates align with the Git hooks refactoring.

The removal of "husky" from the universalAddons and multiple addons test cases correctly reflects the architectural change where Husky is now part of the dedicated gitHooks feature rather than an addon.

Also applies to: 178-178

apps/cli/src/utils/display-config.ts (1)

46-48: LGTM! Consistent implementation for displaying gitHooks.

The implementation follows the established pattern used for other configuration options, with proper undefined checking and String() conversion.

apps/cli/src/index.ts (1)

28-29: LGTM! Clean integration of GitHooks into the public API.

The GitHooks type and schema are properly imported, integrated into the input schema as an optional field, and exported for programmatic usage. The implementation follows the established pattern used for other CLI options.

Also applies to: 90-90, 256-256

apps/cli/test/test-utils.ts (1)

7-7: LGTM! Appropriate default value for test utilities.

The addition of gitHooks: "none" to coreStackDefaults ensures tests have a sensible default value when gitHooks is not explicitly specified, preventing test failures while maintaining consistency with the new Git hooks feature.

Also applies to: 113-113

apps/cli/test/benchmark.test.ts (1)

430-431: LGTM! Correctly demonstrates the Git hooks refactoring.

The change from addons: ["turborepo", "biome", "husky"] to addons: ["turborepo", "biome"] with the addition of gitHooks: "husky" clearly demonstrates the architectural change where Husky is now configured through the dedicated gitHooks option rather than as an addon.

apps/web/src/lib/stack-utils.ts (2)

104-116: LGTM! Husky correctly removed from addons filter.

The removal of "husky" from the allowed addons filter list (it's no longer included) is consistent with the refactoring where Husky is now configured through the gitHooks option instead.


96-96: Verify that stack.gitHooks is always defined.

The template literal --git-hooks ${stack.gitHooks} assumes stack.gitHooks is always defined. If it can be undefined, this would generate --git-hooks undefined in the command string. Verify that StackState type always provides a default value for gitHooks or add a fallback value (e.g., ${stack.gitHooks ?? ''}).

apps/cli/test/backend-runtime.test.ts (1)

40-40: LGTM!

The addition of gitHooks: "none" to the test configuration is consistent with the new Git Hooks feature being introduced across the CLI.

apps/cli/src/helpers/core/add-addons.ts (1)

53-53: LGTM!

The gitHooks field assignment follows the established pattern with proper fallback precedence: input → detected config → default value.

apps/web/src/lib/stack-url-state.client.ts (1)

35-37: LGTM!

The gitHooks parser implementation is consistent with other string enum parsers in the file, properly using getValidIds and defaulting to DEFAULT_STACK.gitHooks.

apps/cli/src/helpers/core/template-manager.ts (1)

599-610: LGTM!

The setupGitHooksTemplate function follows the established pattern of other template setup functions in this file, including:

  • Early returns for "none" and ultracite scenarios
  • Path existence verification before copying
  • Standard async function declaration per coding guidelines
apps/cli/src/helpers/core/detect-project-config.ts (1)

26-26: LGTM!

The addition of gitHooks: btsConfig.gitHooks is consistent with the pattern used for all other config fields in the detected config object.

apps/web/content/docs/index.mdx (1)

313-314: LGTM!

The documentation correctly reflects the refactoring of Git Hooks into a dedicated configuration option. Moving Husky from addons to the --hooks flag aligns with the PR objectives and provides better separation of concerns.

apps/web/content/docs/cli/index.mdx (1)

30-30: LGTM!

The --git-hooks option documentation is clear, well-positioned after the Git initialization option, and consistent with the format used for other CLI flags.

apps/cli/templates/git-hooks/lefthook/lefthook.yml.hbs (1)

1-12: The Lefthook placeholder syntax {staged_files} is correct and matches official documentation. No changes needed. The template properly uses this syntax for both biome and oxlint commands.

Likely an incorrect or invalid review comment.

apps/cli/test/integration.test.ts (2)

38-39: LGTM! Test correctly uses new gitHooks field.

The migration of Husky from addons to the dedicated gitHooks field aligns with the PR's architectural change.

Consider adding a test case with gitHooks: "lefthook" to ensure coverage of the newly introduced option.


186-187: Consistent gitHooks migration across test cases.

All test configurations correctly separate Husky into the gitHooks field while keeping other addons in addons array.

Also applies to: 273-274, 472-473

apps/cli/src/prompts/config-prompts.ts (2)

113-113: LGTM! gitHooks prompt integration follows established patterns.

The gitHooks prompt is correctly wired into the config group without dependencies, which aligns with the PR behavior notes that git hooks selection is independent of other choices.


56-56: Type and return value properly extended.

The PromptGroupResults type and returned configuration object are correctly updated to include gitHooks.

Also applies to: 141-141

apps/cli/src/utils/config-processing.ts (1)

90-92: LGTM! Flag processing follows established pattern.

The gitHooks flag processing mirrors the existing approach for other single-value options like api, backend, and database.

apps/web/src/lib/types.ts (1)

15-15: LGTM! TechCategory type correctly extended.

The gitHooks category is appropriately added to the union type, enabling type-safe handling of git hooks in the web interface.

apps/web/src/lib/stack-url-state.ts (1)

49-51: LGTM! Server-side parser correctly implemented.

The gitHooks parser follows the established pattern used for packageManager and other enum fields, with proper validation via getValidIds and appropriate defaulting.

apps/cli/src/utils/bts-config.ts (1)

27-27: LGTM! BTS config correctly extended with gitHooks.

The gitHooks property is properly added to both the internal config object and the serialized output, ensuring the configuration is persisted in bts.jsonc.

Also applies to: 48-48

apps/web/content/docs/cli/options.mdx (2)

239-253: LGTM! Clear documentation for the new Git Hooks feature.

The new Git Hooks section provides clear documentation for the --git-hooks option with appropriate examples. The default value of none is clearly stated.


273-273: LGTM! Addons example correctly updated.

The example command correctly reflects that Husky has been moved from addons to the dedicated Git Hooks option.

apps/web/content/docs/project-structure.mdx (1)

257-258: LGTM! Configuration schema correctly updated.

The Better-T-Stack configuration schema accurately reflects the changes:

  • Husky removed from addons list
  • New hooks field added with appropriate values
apps/cli/src/helpers/core/create-project.ts (3)

15-15: LGTM! Imports correctly added.

The new Git Hooks setup imports are properly added and follow the existing pattern for other setup features.

Also applies to: 34-34


64-64: LGTM! Template setup correctly positioned.

The Git Hooks template setup is appropriately placed after addons template setup, maintaining consistency with the project scaffolding flow.


88-90: LGTM! Git Hooks setup logic is correct.

The conditional setup correctly checks for non-"none" gitHooks values before invoking the setup function, following the same pattern as auth and payments setup.

apps/cli/src/helpers/addons/addons-setup.ts (1)

63-63: LGTM! Ultracite setup call correctly simplified.

The setupUltracite call now passes only the config parameter, correctly reflecting the refactored function signature where Git Hooks logic is handled within the config itself rather than as a separate boolean flag.

apps/cli/test/git-hooks.test.ts (1)

5-212: LGTM! Comprehensive test coverage for Git Hooks.

The test suite provides excellent coverage:

  • Individual git hook options (lefthook, husky, none)
  • Linter integrations (biome, oxlint)
  • Multiple addon combinations
  • Default behavior validation

Once the import issue is fixed, these tests will properly validate the Git Hooks functionality.

apps/cli/src/helpers/addons/ultracite-setup.ts (3)

133-134: LGTM! Function signature correctly refactored.

The function signature has been properly simplified to remove the hasHusky boolean parameter in favor of extracting gitHooks directly from the config. This is a cleaner design that allows Ultracite to support both Lefthook and Husky.


200-202: LGTM! Integration logic correctly updated.

The logic now correctly checks gitHooks !== "none" to determine whether to enable Git Hooks integration, replacing the previous boolean flag approach. This allows Ultracite to support multiple Git Hooks managers.


218-223: LGTM! Dependency installation correctly updated.

The dependency installation logic correctly:

  • Guards installation with gitHooks !== "none"
  • Uses the gitHooks value directly as the package name (which is valid since both "husky" and "lefthook" are real package names)
  • Installs "lint-staged" alongside the selected Git Hooks manager
apps/cli/README.md (3)

43-45: LGTM! Features table correctly updated.

The features table has been properly updated:

  • New Git Hooks row added with Lefthook, Husky, and None options
  • Husky removed from the Addons row

This clearly communicates that Git Hooks are now a separate feature category.


63-63: LGTM! CLI usage documentation updated.

The --git-hooks option is properly documented in the usage section with clear parameter descriptions.


197-197: LGTM! Compatibility note correctly updated.

The compatibility note for addons "none" has been updated to reflect that Husky is no longer part of the addons list.

apps/cli/src/types.ts (5)

88-89: LGTM! Git Hooks schema properly defined.

The GitHooksSchema and GitHooks type are correctly defined using Zod's enum with appropriate values ("husky", "lefthook", "none") and a clear description.


150-150: LGTM! CreateInput type correctly extended.

The gitHooks field is properly added as an optional property to CreateInput, following the same pattern as other optional configuration fields.


160-160: LGTM! AddInput type correctly extended.

The gitHooks field is properly added as an optional property to AddInput, allowing Git Hooks to be configured during the add command.


187-187: LGTM! ProjectConfig interface correctly extended.

The gitHooks field is properly added as a required property to ProjectConfig, ensuring that all resolved project configurations include a Git Hooks value (even if it's "none").


207-207: LGTM! BetterTStackConfig interface correctly extended.

The gitHooks field is properly added to BetterTStackConfig, ensuring that the persisted configuration includes the Git Hooks setting.

apps/cli/src/helpers/core/create-readme.ts (1)

472-476: LGTM!

The git hooks feature integration is clean. The optional parameter handling and name mapping logic correctly handle both undefined and "none" cases, following the existing pattern used for other features.

apps/cli/src/helpers/git-hooks/git-hooks-setup.ts (1)

8-16: LGTM!

The skip logic correctly handles the ultracite case (where ultracite manages its own hooks) and the "none" case. The linter detection logic properly checks addons before delegating to the appropriate setup function.

apps/web/src/lib/constant.ts (3)

472-497: LGTM!

The gitHooks category is well-structured with appropriate options (lefthook, husky, none). The default is correctly set to none for the category, matching the conservative approach of not imposing git hooks by default.


752-752: LGTM!

The gitHooks field is properly added to the StackState type, maintaining consistency with the type alias pattern used throughout the file.


646-646: Verify: Preset templates all default to lefthook.

All preset templates now include gitHooks: "lefthook". This seems intentional as Lefthook is positioned as the preferred option, but it differs from DEFAULT_STACK which uses "none". Confirm this is the desired behavior for preset templates.

Also applies to: 673-673, 700-701, 727-727

@therealsamyak therealsamyak changed the title feat: add lefthook; refactor out Git Hooks to their own section feat: Add lefthook; refactor out Git hooks to their own section Dec 7, 2025
Copy link
Contributor

@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: 0

🧹 Nitpick comments (3)
apps/cli/test/backend-runtime.test.ts (1)

40-40: Consider adding gitHooks: "none" to other test configurations for consistency.

The explicit gitHooks: "none" is correctly added here. However, other test configurations in this file (e.g., invalid combinations, constraint tests) don't explicitly set gitHooks. While the test utilities likely default to "none", explicitly setting it throughout would improve consistency and make the test behavior more predictable.

apps/cli/test/git-hooks.test.ts (1)

160-200: Consider using describe blocks to group related tests.

The tests are well-structured, but using describe blocks (e.g., "Lefthook Linter Integration", "Husky Integration", "Hooks with Addons") would improve readability and match the organization style in other test files like integration.test.ts.

+import { test, describe } from "bun:test";
-import { test } from "bun:test";
 import type { GitHooks } from "../src/types";
 import { runTRPCTest, expectSuccess } from "./test-utils";

 const gitHookOptions = ["husky", "lefthook", "none"] as GitHooks[];

+describe("Git Hooks Configurations", () => {
   for (const gitHook of gitHookOptions) {
-    test(`Git Hooks Configurations - should work with ${gitHook} git hooks`, async () => {
+    test(`should work with ${gitHook} git hooks`, async () => {
       // ... test body
     });
   }
+});
apps/web/content/docs/project-structure.mdx (1)

245-266: Clarify that hooks maps to the Git hooks manager / --git-hooks option

The new "hooks": "<lefthook|husky|none>" field looks correct, but it might help readers if the surrounding text briefly calls out that this controls the Git hooks manager and corresponds to the --git-hooks CLI flag, to avoid confusion with other kinds of “hooks”.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f69ff1f and bb93a72.

📒 Files selected for processing (41)
  • README.md (1 hunks)
  • apps/cli/README.md (3 hunks)
  • apps/cli/src/constants.ts (2 hunks)
  • apps/cli/src/helpers/addons/addons-setup.ts (1 hunks)
  • apps/cli/src/helpers/addons/ultracite-setup.ts (3 hunks)
  • apps/cli/src/helpers/core/add-addons.ts (1 hunks)
  • apps/cli/src/helpers/core/command-handlers.ts (1 hunks)
  • apps/cli/src/helpers/core/create-project.ts (4 hunks)
  • apps/cli/src/helpers/core/create-readme.ts (5 hunks)
  • apps/cli/src/helpers/core/detect-project-config.ts (1 hunks)
  • apps/cli/src/helpers/core/post-installation.ts (2 hunks)
  • apps/cli/src/helpers/core/template-manager.ts (1 hunks)
  • apps/cli/src/helpers/git-hooks/git-hooks-setup.ts (1 hunks)
  • apps/cli/src/index.ts (3 hunks)
  • apps/cli/src/prompts/addons.ts (2 hunks)
  • apps/cli/src/prompts/config-prompts.ts (5 hunks)
  • apps/cli/src/prompts/git-hooks.ts (1 hunks)
  • apps/cli/src/types.ts (5 hunks)
  • apps/cli/src/utils/bts-config.ts (2 hunks)
  • apps/cli/src/utils/config-processing.ts (2 hunks)
  • apps/cli/src/utils/display-config.ts (1 hunks)
  • apps/cli/src/validation.ts (1 hunks)
  • apps/cli/templates/addons/ruler/.ruler/bts.md.hbs (1 hunks)
  • apps/cli/templates/git-hooks/lefthook/lefthook.yml.hbs (1 hunks)
  • apps/cli/test/addons.test.ts (2 hunks)
  • apps/cli/test/backend-runtime.test.ts (1 hunks)
  • apps/cli/test/benchmark.test.ts (1 hunks)
  • apps/cli/test/git-hooks.test.ts (1 hunks)
  • apps/cli/test/integration.test.ts (4 hunks)
  • apps/cli/test/test-utils.ts (2 hunks)
  • apps/web/content/docs/cli/index.mdx (1 hunks)
  • apps/web/content/docs/cli/options.mdx (2 hunks)
  • apps/web/content/docs/cli/programmatic-api.mdx (1 hunks)
  • apps/web/content/docs/index.mdx (1 hunks)
  • apps/web/content/docs/project-structure.mdx (1 hunks)
  • apps/web/src/lib/constant.ts (9 hunks)
  • apps/web/src/lib/stack-url-keys.ts (1 hunks)
  • apps/web/src/lib/stack-url-state.client.ts (1 hunks)
  • apps/web/src/lib/stack-url-state.ts (1 hunks)
  • apps/web/src/lib/stack-utils.ts (2 hunks)
  • apps/web/src/lib/types.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/cli/templates/addons/ruler/.ruler/bts.md.hbs
🚧 Files skipped from review as they are similar to previous changes (23)
  • apps/web/src/lib/stack-url-keys.ts
  • apps/cli/src/helpers/core/command-handlers.ts
  • apps/cli/src/prompts/config-prompts.ts
  • apps/cli/templates/git-hooks/lefthook/lefthook.yml.hbs
  • apps/cli/src/prompts/git-hooks.ts
  • apps/cli/src/utils/config-processing.ts
  • apps/cli/src/constants.ts
  • apps/web/content/docs/cli/index.mdx
  • apps/cli/README.md
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/web/content/docs/cli/programmatic-api.mdx
  • README.md
  • apps/cli/src/helpers/git-hooks/git-hooks-setup.ts
  • apps/cli/test/benchmark.test.ts
  • apps/cli/src/helpers/core/add-addons.ts
  • apps/web/src/lib/stack-url-state.ts
  • apps/cli/src/utils/display-config.ts
  • apps/cli/src/types.ts
  • apps/cli/test/test-utils.ts
  • apps/cli/src/validation.ts
  • apps/cli/src/index.ts
  • apps/web/src/lib/stack-utils.ts
  • apps/cli/src/helpers/core/create-project.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

Define functions using the standard function declaration syntax, not arrow functions

Files:

  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/test/backend-runtime.test.ts
  • apps/cli/src/helpers/core/detect-project-config.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/utils/bts-config.ts
  • apps/web/src/lib/types.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.{ts,tsx}: Use TypeScript type aliases instead of interface declarations
Do not use explicit return types

Files:

  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/test/backend-runtime.test.ts
  • apps/cli/src/helpers/core/detect-project-config.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/utils/bts-config.ts
  • apps/web/src/lib/types.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file> for running TypeScript/JavaScript files
Bun automatically loads .env files, so don't use the dotenv package
Use Bun.serve() which supports WebSockets, HTTPS, and routes instead of express
Use bun:sqlite module for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of the ws package
Prefer Bun.file over node:fs readFile/writeFile methods
Use Bun.$ template literal syntax instead of execa for shell command execution
Import .css files directly in TypeScript/JavaScript files; Bun's CSS bundler will handle bundling
Run server with bun --hot <file> to enable hot reloading during development

Files:

  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/test/backend-runtime.test.ts
  • apps/cli/src/helpers/core/detect-project-config.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/utils/bts-config.ts
  • apps/web/src/lib/types.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.test.{ts,tsx,js,jsx}: Use bun test instead of jest or vitest for running tests
Use bun:test module with test and expect functions for writing tests

Files:

  • apps/cli/test/addons.test.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/test/backend-runtime.test.ts
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file> instead of webpack or esbuild for bundling TypeScript, JavaScript, and CSS files

Files:

  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/test/backend-runtime.test.ts
  • apps/cli/src/helpers/core/detect-project-config.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/utils/bts-config.ts
  • apps/web/src/lib/types.ts
**/*.{html,tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use HTML imports with Bun.serve() for frontend instead of Vite

Files:

  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/test/backend-runtime.test.ts
  • apps/cli/src/helpers/core/detect-project-config.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/utils/bts-config.ts
  • apps/web/src/lib/types.ts
🧠 Learnings (4)
📚 Learning: 2025-12-03T07:48:26.410Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.410Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use `bun:test` module with `test` and `expect` functions for writing tests

Applied to files:

  • apps/cli/test/git-hooks.test.ts
📚 Learning: 2025-12-03T07:48:26.410Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.410Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use `bun test` instead of `jest` or `vitest` for running tests

Applied to files:

  • apps/cli/test/git-hooks.test.ts
📚 Learning: 2025-12-03T07:48:26.410Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.410Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `bun <file>` instead of `node <file>` or `ts-node <file>` for running TypeScript/JavaScript files

Applied to files:

  • apps/cli/test/git-hooks.test.ts
📚 Learning: 2025-12-03T07:48:14.703Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-03T07:48:14.703Z
Learning: Applies to convex/**/*.ts : Use the api object from convex/_generated/api.ts to call public functions registered with query, mutation, or action

Applied to files:

  • apps/cli/src/helpers/core/create-readme.ts
🧬 Code graph analysis (5)
apps/cli/src/helpers/core/template-manager.ts (2)
apps/cli/src/types.ts (1)
  • ProjectConfig (167-188)
apps/cli/src/constants.ts (1)
  • PKG_ROOT (7-7)
apps/cli/src/helpers/addons/ultracite-setup.ts (2)
apps/cli/src/types.ts (1)
  • ProjectConfig (167-188)
apps/cli/src/utils/add-package-deps.ts (1)
  • addPackageDependency (6-57)
apps/web/src/lib/stack-url-state.client.ts (1)
apps/web/src/lib/constant.ts (2)
  • StackState (740-761)
  • DEFAULT_STACK (763-784)
apps/cli/src/helpers/core/create-readme.ts (2)
apps/cli/src/index.ts (1)
  • GitHooks (256-256)
apps/cli/src/types.ts (1)
  • GitHooks (89-89)
apps/cli/src/helpers/addons/addons-setup.ts (1)
apps/cli/src/helpers/addons/ultracite-setup.ts (1)
  • setupUltracite (133-232)
🔇 Additional comments (25)
apps/cli/src/prompts/addons.ts (1)

46-66: LGTM! Clean migration of Husky out of addons.

The removal of Husky from the addons switch case and ADDON_GROUPS.Other is consistent with the PR's objective to move Git hooks into their own configuration surface.

apps/cli/src/helpers/core/detect-project-config.ts (1)

26-26: LGTM! Correct propagation of gitHooks field.

The gitHooks field is properly extracted from the BTS config and included in the returned project configuration, consistent with other config fields.

apps/cli/src/helpers/core/template-manager.ts (1)

599-610: LGTM! Well-structured Git hooks template setup.

The function correctly:

  • Guards against invalid states (none, missing gitHooks)
  • Skips when Ultracite is handling hooks (per PR behavior notes)
  • Follows the established template setup pattern
  • Uses standard function declaration syntax as per coding guidelines
apps/web/src/lib/types.ts (1)

15-15: LGTM! Correct union type extension.

The addition of "gitHooks" to TechCategory properly extends the type to support the new Git hooks configuration category.

apps/cli/src/utils/bts-config.ts (1)

27-48: LGTM! Proper gitHooks config persistence.

The gitHooks field is correctly included in both the runtime config object and the serialized baseContent, ensuring it persists in bts.jsonc and remains available for downstream operations.

apps/cli/src/helpers/core/create-readme.ts (3)

11-11: LGTM! Correct type import.

The GitHooks type import enables type-safe handling of the gitHooks parameter in README generation.


42-70: LGTM! Proper gitHooks propagation.

The gitHooks field is correctly destructured from options and passed through to generateFeaturesList, maintaining consistency with how other config fields are handled.


353-476: LGTM! Well-implemented Git hooks feature rendering.

The implementation correctly:

  • Uses an optional parameter since gitHooks may be "none"
  • Guards against undefined and "none" values before rendering
  • Maps gitHooks values to appropriate display names
  • Adds the feature to the list in a consistent format
apps/web/content/docs/index.mdx (1)

313-314: LGTM! Documentation correctly updated.

The docs properly reflect the migration of Husky from addons to the new --hooks flag, with accurate values (lefthook, husky, none) documented for users.

apps/cli/test/addons.test.ts (2)

7-7: LGTM! Test correctly updated for Husky migration.

Removing "husky" from the universalAddons array aligns with the PR's migration of Husky from addons to the gitHooks configuration.


178-178: LGTM! Multiple addons test correctly updated.

The test array properly excludes "husky" while still validating that multiple compatible addons can be used together.

apps/cli/test/integration.test.ts (4)

38-39: LGTM!

The migration of husky from the addons array to a dedicated gitHooks: "husky" field is correctly implemented. This aligns with the PR's objective to refactor Git hooks into their own section.


186-187: LGTM!

Consistent application of the gitHooks field for the MongoDB + Mongoose test configuration.


273-274: LGTM!

Frontend-only Nuxt test configuration correctly updated with gitHooks field.


472-473: LGTM!

Maximum complexity test configuration correctly updated to use gitHooks instead of husky in addons.

apps/web/content/docs/cli/options.mdx (2)

239-253: LGTM!

The new Git Hooks section is well-documented with clear descriptions for each option (lefthook, husky, none) and helpful usage examples. The placement after Authentication and before Addons is logical.


273-273: LGTM!

The addons example correctly excludes husky, which is now documented in the Git Hooks section.

apps/web/src/lib/stack-url-state.client.ts (1)

35-37: LGTM!

The gitHooks parser follows the established pattern used by other enum parsers in this file. It correctly uses parseAsStringEnum with valid IDs from TECH_OPTIONS["gitHooks"] and defaults to DEFAULT_STACK.gitHooks ("none").

apps/cli/test/git-hooks.test.ts (4)

1-3: LGTM!

Correct usage of bun:test per coding guidelines. The imports are appropriate for the test file.


5-28: LGTM!

Good use of a loop to test all gitHook options, reducing code duplication while ensuring coverage for husky, lefthook, and none configurations.


30-93: LGTM!

Comprehensive Lefthook integration tests covering biome, oxlint, and standalone scenarios. Test configurations are complete and follow established patterns.


95-158: LGTM!

Comprehensive Husky integration tests mirroring the Lefthook tests for consistency. Good coverage of biome, oxlint, and standalone scenarios.

apps/cli/src/helpers/addons/addons-setup.ts (1)

59-68: Ultracite setup now correctly forwards full ProjectConfig (including gitHooks)

Switching to setupUltracite(config) keeps the Biome/Ultracite branching intact and correctly passes gitHooks and other fields through the config object; the control flow for ultracite vs biome remains sound.

apps/cli/src/helpers/addons/ultracite-setup.ts (1)

133-135: gitHooks-based Ultracite integration and dependency wiring look consistent

Using config.gitHooks to gate --integrations <gitHooks> lint-staged and to install [gitHooks, "lint-staged"] only when gitHooks !== "none" matches the intended behavior and cleanly replaces the earlier hasHusky flag.

Also applies to: 200-202, 218-221

apps/web/src/lib/constant.ts (1)

472-497: gitHooks option and defaults are wired consistently across web constants and presets

The new gitHooks tech category, its default of "none", the preset stacks setting "lefthook" where desired, and the StackState/DEFAULT_STACK updates all align and should integrate cleanly with the rest of the stack/state logic.

Also applies to: 646-647, 673-674, 700-703, 727-728, 752-752, 775-775

@therealsamyak
Copy link
Contributor Author

@AmanVarshney01 this is ready to review when you are!

Copy link
Contributor

@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: 0

🧹 Nitpick comments (7)
apps/web/content/docs/cli/programmatic-api.mdx (1)

163-163: Improve inline comment to describe field purpose, not just values.

The comment lists allowed values but doesn't describe what the field does. Compare with the git field above (line 162: "Initialize git repo"), which is more informative. Consider updating the comment to explain the purpose.

- gitHooks?: GitHooks;             // "lefthook" | "husky" | "none"
+ gitHooks?: GitHooks;             // Git hooks manager: "lefthook" | "husky" | "none"

Alternatively, if you want to keep it even more concise to match the union-type pattern, just ensure consistency:

- gitHooks?: GitHooks;             // "lefthook" | "husky" | "none"
+ gitHooks?: GitHooks;             // Git hooks manager ("lefthook" | "husky" | "none")
apps/web/src/lib/stack-url-keys.ts (1)

17-17: Consider using an abbreviated URL key for consistency.

Most URL keys use abbreviated forms (e.g., pm, add, ex, wd). Consider using gh instead of gitHooks to keep URLs shorter and consistent with the abbreviated pattern:

-  gitHooks: "gitHooks",
+  gitHooks: "gh",

That said, keys like api, git, and yolo also use full names, so the current approach is acceptable.

apps/web/src/lib/constant.ts (1)

540-541: Minor: Remove trailing empty line.

This appears to be leftover formatting from removing Husky from addons. Consider removing the extra blank line for cleaner formatting.

apps/cli/test/addons.test.ts (1)

7-30: Addon tests correctly drop Husky; consider deriving lists from shared config

Updating universalAddons, the “multiple compatible addons” config, and testableAddons to exclude "husky" matches the new separation between addons and gitHooks. To reduce future drift, you could derive these arrays from AddonsSchema.options (filtering out prompt‑only addons) instead of maintaining the string lists by hand, but that’s optional.

Also applies to: 174-195, 283-323

apps/cli/test/integration.test.ts (1)

28-45: Integration tests now exercise Husky via gitHooks; add a Lefthook case for full coverage

Switching Husky from addons to gitHooks: "husky" in these scenarios keeps real‑world coverage aligned with the new API shape. To fully exercise the new --git-hooks surface, it would be worth adding at least one analogous integration test using gitHooks: "lefthook" (with Biome/Oxlint enabled) so both hook managers are covered end‑to‑end.

Also applies to: 176-193, 263-281, 461-479

apps/cli/test/git-hooks.test.ts (2)

1-28: Parameterized gitHooks tests are well-structured

The shared gitHookOptions array plus the looped test covers all three allowed git hooks values with a single, readable pattern, and each run uses a distinct projectName, so there’s no cross-test directory clash. Just keep in mind that if new git hooks modes are ever added to the config, this array will need updating to keep coverage aligned, but as written it’s correct and maintainable.


30-200: Comprehensive scenario coverage; minor duplication only

These tests thoroughly exercise git hooks with and without linters (Biome/Oxlint), both managers (lefthook/husky), multiple addons, and the implicit default case where gitHooks is omitted. The configurations and projectName values are consistent and should give good confidence that CLI scaffolding works across combinations. If this suite grows further, consider extracting a small helper to build the common config shape (frontend/backend/runtime/db/auth/api flags) to reduce repetition, but that’s optional.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb93a72 and fb1e620.

📒 Files selected for processing (41)
  • README.md (1 hunks)
  • apps/cli/README.md (3 hunks)
  • apps/cli/src/constants.ts (2 hunks)
  • apps/cli/src/helpers/addons/addons-setup.ts (1 hunks)
  • apps/cli/src/helpers/addons/ultracite-setup.ts (3 hunks)
  • apps/cli/src/helpers/core/add-addons.ts (1 hunks)
  • apps/cli/src/helpers/core/command-handlers.ts (1 hunks)
  • apps/cli/src/helpers/core/create-project.ts (4 hunks)
  • apps/cli/src/helpers/core/create-readme.ts (5 hunks)
  • apps/cli/src/helpers/core/detect-project-config.ts (1 hunks)
  • apps/cli/src/helpers/core/post-installation.ts (2 hunks)
  • apps/cli/src/helpers/core/template-manager.ts (1 hunks)
  • apps/cli/src/helpers/git-hooks/git-hooks-setup.ts (1 hunks)
  • apps/cli/src/index.ts (3 hunks)
  • apps/cli/src/prompts/addons.ts (2 hunks)
  • apps/cli/src/prompts/config-prompts.ts (5 hunks)
  • apps/cli/src/prompts/git-hooks.ts (1 hunks)
  • apps/cli/src/types.ts (5 hunks)
  • apps/cli/src/utils/bts-config.ts (2 hunks)
  • apps/cli/src/utils/config-processing.ts (2 hunks)
  • apps/cli/src/utils/display-config.ts (1 hunks)
  • apps/cli/src/validation.ts (1 hunks)
  • apps/cli/templates/addons/ruler/.ruler/bts.md.hbs (1 hunks)
  • apps/cli/templates/git-hooks/lefthook/lefthook.yml.hbs (1 hunks)
  • apps/cli/test/addons.test.ts (2 hunks)
  • apps/cli/test/backend-runtime.test.ts (1 hunks)
  • apps/cli/test/benchmark.test.ts (1 hunks)
  • apps/cli/test/git-hooks.test.ts (1 hunks)
  • apps/cli/test/integration.test.ts (4 hunks)
  • apps/cli/test/test-utils.ts (2 hunks)
  • apps/web/content/docs/cli/index.mdx (1 hunks)
  • apps/web/content/docs/cli/options.mdx (2 hunks)
  • apps/web/content/docs/cli/programmatic-api.mdx (1 hunks)
  • apps/web/content/docs/index.mdx (1 hunks)
  • apps/web/content/docs/project-structure.mdx (1 hunks)
  • apps/web/src/lib/constant.ts (9 hunks)
  • apps/web/src/lib/stack-url-keys.ts (1 hunks)
  • apps/web/src/lib/stack-url-state.client.ts (1 hunks)
  • apps/web/src/lib/stack-url-state.ts (1 hunks)
  • apps/web/src/lib/stack-utils.ts (2 hunks)
  • apps/web/src/lib/types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (30)
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/templates/addons/ruler/.ruler/bts.md.hbs
  • apps/cli/src/constants.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/cli/src/helpers/core/detect-project-config.ts
  • apps/cli/src/index.ts
  • apps/web/content/docs/project-structure.mdx
  • apps/cli/src/utils/display-config.ts
  • apps/cli/src/prompts/git-hooks.ts
  • apps/cli/test/backend-runtime.test.ts
  • apps/cli/src/prompts/config-prompts.ts
  • apps/cli/src/helpers/core/command-handlers.ts
  • apps/web/src/lib/stack-utils.ts
  • apps/web/content/docs/cli/options.mdx
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/src/validation.ts
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/web/src/lib/stack-url-state.ts
  • apps/cli/src/helpers/git-hooks/git-hooks-setup.ts
  • apps/cli/src/helpers/core/add-addons.ts
  • apps/cli/test/benchmark.test.ts
  • apps/cli/src/utils/bts-config.ts
  • apps/cli/templates/git-hooks/lefthook/lefthook.yml.hbs
  • apps/web/content/docs/cli/index.mdx
  • apps/cli/src/utils/config-processing.ts
  • apps/web/content/docs/index.mdx
  • apps/cli/src/types.ts
  • apps/cli/test/test-utils.ts
  • apps/cli/src/helpers/core/create-project.ts
  • apps/web/src/lib/types.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

Define functions using the standard function declaration syntax, not arrow functions

Files:

  • apps/cli/test/addons.test.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/web/src/lib/constant.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.{ts,tsx}: Use TypeScript type aliases instead of interface declarations
Do not use explicit return types

Files:

  • apps/cli/test/addons.test.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/web/src/lib/constant.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file> for running TypeScript/JavaScript files
Bun automatically loads .env files, so don't use the dotenv package
Use Bun.serve() which supports WebSockets, HTTPS, and routes instead of express
Use bun:sqlite module for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of the ws package
Prefer Bun.file over node:fs readFile/writeFile methods
Use Bun.$ template literal syntax instead of execa for shell command execution
Import .css files directly in TypeScript/JavaScript files; Bun's CSS bundler will handle bundling
Run server with bun --hot <file> to enable hot reloading during development

Files:

  • apps/cli/test/addons.test.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/web/src/lib/constant.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.test.{ts,tsx,js,jsx}: Use bun test instead of jest or vitest for running tests
Use bun:test module with test and expect functions for writing tests

Files:

  • apps/cli/test/addons.test.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/test/git-hooks.test.ts
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file> instead of webpack or esbuild for bundling TypeScript, JavaScript, and CSS files

Files:

  • apps/cli/test/addons.test.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/web/src/lib/constant.ts
**/*.{html,tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use HTML imports with Bun.serve() for frontend instead of Vite

Files:

  • apps/cli/test/addons.test.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/test/git-hooks.test.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/web/src/lib/constant.ts
🧠 Learnings (4)
📚 Learning: 2025-12-03T07:48:26.410Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.410Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use `bun:test` module with `test` and `expect` functions for writing tests

Applied to files:

  • apps/cli/test/git-hooks.test.ts
📚 Learning: 2025-12-03T07:48:26.410Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.410Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use `bun test` instead of `jest` or `vitest` for running tests

Applied to files:

  • apps/cli/test/git-hooks.test.ts
📚 Learning: 2025-12-03T07:48:26.410Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.410Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `bun <file>` instead of `node <file>` or `ts-node <file>` for running TypeScript/JavaScript files

Applied to files:

  • apps/cli/test/git-hooks.test.ts
📚 Learning: 2025-12-03T07:48:14.703Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-03T07:48:14.703Z
Learning: Applies to convex/**/*.ts : Use the api object from convex/_generated/api.ts to call public functions registered with query, mutation, or action

Applied to files:

  • apps/cli/src/helpers/core/create-readme.ts
🧬 Code graph analysis (3)
apps/cli/test/git-hooks.test.ts (2)
apps/cli/src/types.ts (1)
  • GitHooks (89-89)
apps/cli/test/test-utils.ts (2)
  • runTRPCTest (58-161)
  • expectSuccess (163-173)
apps/cli/src/helpers/addons/addons-setup.ts (1)
apps/cli/src/helpers/addons/ultracite-setup.ts (1)
  • setupUltracite (133-232)
apps/cli/src/helpers/core/create-readme.ts (2)
apps/cli/src/index.ts (1)
  • GitHooks (256-256)
apps/cli/src/types.ts (1)
  • GitHooks (89-89)
🔇 Additional comments (12)
README.md (1)

47-48: Documentation properly reflects Git Hooks as distinct feature.

The changes cleanly move Husky from Addons into the new Git Hooks section while maintaining the comprehensive addon list. Formatting and feature presentation are consistent with the rest of the README.

apps/cli/README.md (4)

43-43: Git Hooks table row properly integrated.

The new table entry follows the established format, lists correct options (Lefthook, Husky, None), and fits naturally within the Features documentation structure.


45-45: Addons section correctly updated.

Husky has been cleanly removed from the Addons row. The remaining addons are properly formatted and consistent with the PR's refactoring objective.


62-63: CLI options properly documented.

The new --git-hooks <type> option is correctly documented with appropriate values and formatting consistent with other CLI flags. The addons option no longer references Husky.


197-197: Compatibility notes updated consistently.

The Addons 'none' compatibility note has been properly updated to remove reference to Husky, maintaining internal consistency with the broader refactoring.

apps/web/src/lib/constant.ts (3)

472-497: LGTM!

The new gitHooks category is well-structured with appropriate options (lefthook, husky, none) and sensible defaults. The configuration follows the established pattern of other tech options.


646-647: LGTM!

The gitHooks: "lefthook" addition to preset templates is consistent across all presets (mern, pern, t3, uniwind), aligning with the PR's intent to promote Lefthook as the recommended Git hooks manager.


752-752: LGTM!

The gitHooks field is correctly added to StackState type with an appropriate default of "none" in DEFAULT_STACK, allowing users to opt-in to Git hooks management.

apps/cli/src/helpers/addons/addons-setup.ts (1)

63-63: LGTM: Clean refactoring that improves separation of concerns.

The simplified call to setupUltracite(config) correctly aligns with the new architecture where Git hooks configuration is managed through the config.gitHooks field rather than a separate boolean parameter. This refactoring moves Git hooks setup logic to a dedicated module, improving maintainability and reducing coupling in the addons setup flow.

apps/cli/src/prompts/addons.ts (1)

17-58: Husky removal from addon display/groups looks consistent with new Git Hooks model

The switch in getAddonDisplay and ADDON_GROUPS.Other now exclude "husky", so Husky will no longer appear as an addon option, which matches the move to a dedicated gitHooks field. As long as "husky" has also been removed from AddonsSchema.options, this prompt logic remains consistent and won’t ever hit the generic default branch for Husky.

Also applies to: 63-67

apps/cli/src/helpers/core/create-readme.ts (2)

28-43: gitHooks plumbing into README generation is consistent

Destructuring gitHooks from options and threading it into generateFeaturesList cleanly connects the new config field to README output without changing other behavior. This keeps README content in sync with the project’s git hooks choice while remaining backward compatible when gitHooks is absent.

Also applies to: 70-70


344-354: Git hooks feature entry in Features list behaves as expected

Extending generateFeaturesList with an optional gitHooks parameter and appending a single bullet when it’s set to something other than "none" is straightforward and matches the current allowed values (labeling "husky" vs "lefthook"). This keeps the Features section accurate without clutter when no hooks manager is selected.

Also applies to: 472-476

@AmanVarshney01
Copy link
Owner

AmanVarshney01 commented Dec 11, 2025

Thanks for the effort on this PR but i cant add another flag to the CLI it will add a lot more problems in other areas :(

--addons lefthook is fine i think 🤔

Copilot AI review requested due to automatic review settings December 13, 2025 10:58
@therealsamyak therealsamyak force-pushed the sk/add-lefthook branch 2 times, most recently from 04bc830 to 1c8db0c Compare December 13, 2025 11:04
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new --git-hooks CLI flag to manage Git hooks setup, refactoring the Git hooks functionality out of the addons section into its own dedicated category. The change supports three options: lefthook (a fast and powerful Git hooks manager), husky (the existing option), and none (the default). This aligns with BTS's own adoption of lefthook and provides better organization by separating Git hooks management from general addons.

Key Changes:

  • Added new --git-hooks flag with support for lefthook, husky, and none options
  • Moved husky from addons array to the dedicated gitHooks field across all configurations
  • Integrated Git hooks with linters (biome, oxlint) using different strategies: lint-staged for husky, native stage_fixed for lefthook

Reviewed changes

Copilot reviewed 35 out of 37 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
apps/web/src/lib/types.ts Added "gitHooks" to TechCategory type
apps/web/src/lib/stack-utils.ts Added gitHooks parameter to stack command generation, removed husky from addons filter
apps/web/src/lib/stack-url-state.ts Added gitHooks parser for server-side URL state management
apps/web/src/lib/stack-url-state.client.ts Added gitHooks parser for client-side URL state management
apps/web/src/lib/stack-url-keys.ts Added "gitHooks" URL key mapping
apps/web/src/lib/constant.ts Added gitHooks tech options, moved husky from addons to gitHooks, updated presets to use lefthook
apps/web/content/docs/project-structure.mdx Updated bts.jsonc schema documentation with hooks field
apps/web/content/docs/index.mdx Updated CLI reference to show git-hooks flag
apps/web/content/docs/cli/programmatic-api.mdx Added gitHooks to CreateInput interface documentation
apps/web/content/docs/cli/options.mdx Added Git Hooks section with --git-hooks flag documentation
apps/web/content/docs/cli/index.mdx Added --git-hooks to CLI command reference
apps/cli/test/test-utils.ts Added GitHooks type import and default gitHooks value in test configuration
apps/cli/test/integration.test.ts Updated integration tests to use gitHooks field instead of husky addon
apps/cli/test/git-hooks.test.ts New comprehensive test suite for git hooks configurations
apps/cli/test/benchmark.test.ts Updated benchmark test to use gitHooks field
apps/cli/test/backend-runtime.test.ts Added gitHooks default to test configuration
apps/cli/test/addons.test.ts Removed husky from addons tests and universal addons list
apps/cli/templates/git-hooks/lefthook/lefthook.yml.hbs New Handlebars template for lefthook configuration with biome/oxlint support
apps/cli/templates/git-hooks/husky/.husky/pre-commit New husky pre-commit hook template
apps/cli/templates/addons/ruler/.ruler/bts.md.hbs Removed Husky from addons list in ruler documentation
apps/cli/src/validation.ts Added "hooks" to CORE_STACK_FLAGS set
apps/cli/src/utils/display-config.ts Added gitHooks display in configuration output
apps/cli/src/utils/config-processing.ts Added gitHooks processing in processFlags function
apps/cli/src/utils/bts-config.ts Added gitHooks to BTS config file generation
apps/cli/src/types.ts Added GitHooksSchema type, removed husky from AddonsSchema, added gitHooks to interfaces
apps/cli/src/prompts/git-hooks.ts New prompt module for Git hooks selection
apps/cli/src/prompts/config-prompts.ts Integrated git hooks prompt into configuration gathering
apps/cli/src/prompts/addons.ts Removed husky from addon display and groups
apps/cli/src/index.ts Added GitHooksSchema and GitHooks export to API
apps/cli/src/helpers/git-hooks/git-hooks-setup.ts New module for setting up git hooks (lefthook and husky) with linter integration
apps/cli/src/helpers/core/template-manager.ts Added setupGitHooksTemplate function
apps/cli/src/helpers/core/post-installation.ts Updated to check gitHooks instead of husky addon
apps/cli/src/helpers/core/detect-project-config.ts Added gitHooks to config detection
apps/cli/src/helpers/core/create-readme.ts Added gitHooks parameter and moved git hooks info from addons to dedicated section
apps/cli/src/helpers/core/create-project.ts Added git hooks template and setup calls
apps/cli/src/helpers/core/command-handlers.ts Added gitHooks default to project config
apps/cli/src/helpers/core/add-addons.ts Added gitHooks to addons project config
apps/cli/src/helpers/addons/ultracite-setup.ts Updated to use gitHooks field for ultracite integration
apps/cli/src/helpers/addons/addons-setup.ts Removed setupHusky function (moved to git-hooks module)
apps/cli/src/constants.ts Added lefthook version, removed husky from addon compatibility, added gitHooks to default config
apps/cli/README.md Added Git Hooks feature row and --git-hooks flag documentation, removed husky from addons
README.md Updated feature list to separate Git Hooks from Addons

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@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: 2

Caution

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

⚠️ Outside diff range comments (2)
apps/cli/src/helpers/core/post-installation.ts (1)

8-47: Linting instructions gating likely misses oxlint-only setups
Right now you show linting instructions only when hooks are enabled or Biome addon is present. If oxlint is selected (and hooks are none), users may still want the “run check” hint—consider including addons?.includes("oxlint") (or whatever set defines lint tooling).

apps/cli/src/helpers/addons/ultracite-setup.ts (1)

133-205: Fix integration arguments format — must be comma-separated, not space-separated

At lines 200–205, the integration arguments are passed as separate array elements, which produces space-separated CLI args:

  • Current: ["--integrations", "husky", "lint-staged"]--integrations husky lint-staged (incorrect)
  • Should be: ["--integrations", "husky,lint-staged"]--integrations husky,lint-staged

Ultracite CLI requires comma-separated values for the --integrations flag. The lefthook case works by accident (single value), but the husky case will fail to parse correctly.

Additionally, per coding guidelines, replace execa with Bun.$ template literal syntax (lines 214–218).

🧹 Nitpick comments (6)
apps/web/src/lib/stack-url-keys.ts (1)

17-17: Consider using an abbreviated URL key for consistency.

Most URL keys in this mapping use abbreviations (e.g., "pm" for packageManager, "add" for addons, "ex" for examples). Using the full word "hooks" breaks this pattern. Consider shortening to "hk" or "h" to maintain consistency and keep URLs more compact.

-  hooks: "hooks",
+  hooks: "hk",
apps/cli/src/prompts/hooks.ts (1)

6-34: Optional: avoid unchecked cast on prompt result
If Hooks can be influenced by programmatic input or future option changes, consider validating response against the allowed set before casting.

apps/cli/src/helpers/addons/ultracite-setup.ts (1)

214-218: Avoid shell: true (and prefer Bun.$ over execa)
This is both against repo guidelines and needlessly risky; prefer direct exec with args (shell=false) or Bun.$ to avoid quoting/injection edge cases.

apps/cli/test/addons.test.ts (1)

1-3: Repo guideline mismatch: tests use Vitest instead of bun:test
If the project is standardizing on Bun’s test runner, this file is a candidate for later migration.

apps/cli/src/helpers/hooks/hooks-setup.ts (2)

27-58: Reconsider lint-staged setup when no linter is configured.

When linter is undefined (lines 51-53), the code still installs lint-staged as a devDependency and configures it with an empty string command. This creates several issues:

  1. Empty string commands may not work correctly with lint-staged
  2. Installing lint-staged without a valid use case is wasteful
  3. Users get a partial/broken configuration

Consider either:

  • Not installing lint-staged when no linter is configured
  • Omitting the lint-staged configuration from package.json when no linter is present
  • Setting up a sensible default command or documenting that users should configure it manually
   await addPackageDependency({
-    devDependencies: ["husky", "lint-staged"],
+    devDependencies: linter ? ["husky", "lint-staged"] : ["husky"],
     projectDir,
   });

   const packageJsonPath = path.join(projectDir, "package.json");
   if (await fs.pathExists(packageJsonPath)) {
     const packageJson = await fs.readJson(packageJsonPath);

     packageJson.scripts = {
       ...packageJson.scripts,
       prepare: "husky",
     };

-    if (linter === "oxlint") {
+    if (linter) {
+      if (linter === "oxlint") {
-      packageJson["lint-staged"] = {
-        "**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue,astro,svelte}": "oxlint",
-      };
+        packageJson["lint-staged"] = {
+          "**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue,astro,svelte}": "oxlint",
+        };
-    } else if (linter === "biome") {
+      } else if (linter === "biome") {
-      packageJson["lint-staged"] = {
-        "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": ["biome check --write ."],
-      };
-    } else {
-      packageJson["lint-staged"] = {
-        "**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue,astro,svelte}": "",
-      };
+        packageJson["lint-staged"] = {
+          "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": ["biome check --write ."],
+        };
+      }
     }

     await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
   }

60-106: Consider using lefthook's native features instead of lint-staged.

According to the PR objectives, "Lefthook can use stage_fixed: true and does not require lint-staged." However, the current implementation still installs and configures lint-staged for lefthook (lines 62, 75-87).

Additionally, when no linter is configured (lines 84-86), lint-staged is still installed with an empty string command, which may not work correctly.

Consider leveraging lefthook's native stage_fixed: true feature to avoid the lint-staged dependency, which would simplify the setup and align with the PR's stated design.

Potential approach:

  1. Use lefthook's native stage_fixed: true in the lefthook.yml template
  2. Only install lint-staged if there's a specific need for it
  3. Generate lefthook.yml regardless of linter presence, with appropriate default configuration
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb1e620 and 1c8db0c.

⛔ Files ignored due to path filters (1)
  • lefthook.svg is excluded by !**/*.svg
📒 Files selected for processing (35)
  • apps/cli/README.md (1 hunks)
  • apps/cli/src/constants.ts (2 hunks)
  • apps/cli/src/helpers/addons/addons-setup.ts (1 hunks)
  • apps/cli/src/helpers/addons/ultracite-setup.ts (3 hunks)
  • apps/cli/src/helpers/core/add-addons.ts (1 hunks)
  • apps/cli/src/helpers/core/command-handlers.ts (1 hunks)
  • apps/cli/src/helpers/core/create-project.ts (4 hunks)
  • apps/cli/src/helpers/core/create-readme.ts (5 hunks)
  • apps/cli/src/helpers/core/detect-project-config.ts (1 hunks)
  • apps/cli/src/helpers/core/post-installation.ts (2 hunks)
  • apps/cli/src/helpers/core/template-manager.ts (1 hunks)
  • apps/cli/src/helpers/hooks/hooks-setup.ts (1 hunks)
  • apps/cli/src/index.ts (2 hunks)
  • apps/cli/src/prompts/addons.ts (2 hunks)
  • apps/cli/src/prompts/config-prompts.ts (5 hunks)
  • apps/cli/src/prompts/hooks.ts (1 hunks)
  • apps/cli/src/types.ts (5 hunks)
  • apps/cli/src/utils/config-processing.ts (2 hunks)
  • apps/cli/src/validation.ts (1 hunks)
  • apps/cli/templates/hooks/lefthook/lefthook.yml.hbs (1 hunks)
  • apps/cli/test/addons.test.ts (2 hunks)
  • apps/cli/test/backend-runtime.test.ts (1 hunks)
  • apps/cli/test/benchmark.test.ts (1 hunks)
  • apps/cli/test/hooks.test.ts (1 hunks)
  • apps/cli/test/integration.test.ts (4 hunks)
  • apps/cli/test/test-utils.ts (2 hunks)
  • apps/web/content/docs/cli/options.mdx (2 hunks)
  • apps/web/content/docs/index.mdx (1 hunks)
  • apps/web/content/docs/project-structure.mdx (1 hunks)
  • apps/web/src/lib/constant.ts (9 hunks)
  • apps/web/src/lib/stack-url-keys.ts (1 hunks)
  • apps/web/src/lib/stack-url-state.client.ts (1 hunks)
  • apps/web/src/lib/stack-url-state.ts (1 hunks)
  • apps/web/src/lib/stack-utils.ts (2 hunks)
  • apps/web/src/lib/types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (17)
  • apps/cli/src/helpers/core/detect-project-config.ts
  • apps/cli/src/constants.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/cli/src/types.ts
  • apps/web/src/lib/stack-url-state.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/README.md
  • apps/cli/src/helpers/core/add-addons.ts
  • apps/cli/src/prompts/config-prompts.ts
  • apps/cli/test/integration.test.ts
  • apps/web/content/docs/project-structure.mdx
  • apps/cli/test/test-utils.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/test/backend-runtime.test.ts
  • apps/web/content/docs/cli/options.mdx
  • apps/web/src/lib/stack-utils.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

Define functions using the standard function declaration syntax, not arrow functions

Files:

  • apps/cli/src/prompts/hooks.ts
  • apps/cli/src/helpers/core/create-project.ts
  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/cli/src/validation.ts
  • apps/cli/test/benchmark.test.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/core/command-handlers.ts
  • apps/cli/src/helpers/hooks/hooks-setup.ts
  • apps/cli/test/hooks.test.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/utils/config-processing.ts
  • apps/cli/src/index.ts
  • apps/web/src/lib/types.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.{ts,tsx}: Use TypeScript type aliases instead of interface declarations
Do not use explicit return types

Files:

  • apps/cli/src/prompts/hooks.ts
  • apps/cli/src/helpers/core/create-project.ts
  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/cli/src/validation.ts
  • apps/cli/test/benchmark.test.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/core/command-handlers.ts
  • apps/cli/src/helpers/hooks/hooks-setup.ts
  • apps/cli/test/hooks.test.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/utils/config-processing.ts
  • apps/cli/src/index.ts
  • apps/web/src/lib/types.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file> for running TypeScript/JavaScript files
Bun automatically loads .env files, so don't use the dotenv package
Use Bun.serve() which supports WebSockets, HTTPS, and routes instead of express
Use bun:sqlite module for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of the ws package
Prefer Bun.file over node:fs readFile/writeFile methods
Use Bun.$ template literal syntax instead of execa for shell command execution
Import .css files directly in TypeScript/JavaScript files; Bun's CSS bundler will handle bundling
Run server with bun --hot <file> to enable hot reloading during development

Files:

  • apps/cli/src/prompts/hooks.ts
  • apps/cli/src/helpers/core/create-project.ts
  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/cli/src/validation.ts
  • apps/cli/test/benchmark.test.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/core/command-handlers.ts
  • apps/cli/src/helpers/hooks/hooks-setup.ts
  • apps/cli/test/hooks.test.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/utils/config-processing.ts
  • apps/cli/src/index.ts
  • apps/web/src/lib/types.ts
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file> instead of webpack or esbuild for bundling TypeScript, JavaScript, and CSS files

Files:

  • apps/cli/src/prompts/hooks.ts
  • apps/cli/src/helpers/core/create-project.ts
  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/cli/src/validation.ts
  • apps/cli/test/benchmark.test.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/core/command-handlers.ts
  • apps/cli/src/helpers/hooks/hooks-setup.ts
  • apps/cli/test/hooks.test.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/utils/config-processing.ts
  • apps/cli/src/index.ts
  • apps/web/src/lib/types.ts
**/*.{html,tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use HTML imports with Bun.serve() for frontend instead of Vite

Files:

  • apps/cli/src/prompts/hooks.ts
  • apps/cli/src/helpers/core/create-project.ts
  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/cli/src/validation.ts
  • apps/cli/test/benchmark.test.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/core/command-handlers.ts
  • apps/cli/src/helpers/hooks/hooks-setup.ts
  • apps/cli/test/hooks.test.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/utils/config-processing.ts
  • apps/cli/src/index.ts
  • apps/web/src/lib/types.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.test.{ts,tsx,js,jsx}: Use bun test instead of jest or vitest for running tests
Use bun:test module with test and expect functions for writing tests

Files:

  • apps/cli/test/addons.test.ts
  • apps/cli/test/benchmark.test.ts
  • apps/cli/test/hooks.test.ts
**/*.hbs

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.hbs: In Handlebars templates, avoid generic if/else blocks. Write explicit conditions, such as: use if (eq orm "prisma") for Prisma, and else if (eq orm "drizzle") for Drizzle
Escape the '{{' in Handlebars templates like '{{'

Files:

  • apps/cli/templates/hooks/lefthook/lefthook.yml.hbs
🧠 Learnings (2)
📚 Learning: 2025-12-03T07:48:26.419Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.419Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use `bun:test` module with `test` and `expect` functions for writing tests

Applied to files:

  • apps/cli/test/hooks.test.ts
📚 Learning: 2025-12-03T07:48:26.419Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.419Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use `bun test` instead of `jest` or `vitest` for running tests

Applied to files:

  • apps/cli/test/hooks.test.ts
🧬 Code graph analysis (6)
apps/cli/src/helpers/core/create-project.ts (2)
apps/cli/src/helpers/core/template-manager.ts (1)
  • setupHooksTemplate (599-607)
apps/cli/src/helpers/hooks/hooks-setup.ts (1)
  • setupHooks (7-25)
apps/cli/src/helpers/addons/ultracite-setup.ts (2)
apps/cli/src/types.ts (1)
  • ProjectConfig (167-188)
apps/cli/src/utils/add-package-deps.ts (1)
  • addPackageDependency (6-57)
apps/cli/src/helpers/hooks/hooks-setup.ts (2)
apps/cli/src/types.ts (1)
  • ProjectConfig (167-188)
apps/cli/src/utils/add-package-deps.ts (1)
  • addPackageDependency (6-57)
apps/cli/test/hooks.test.ts (2)
apps/cli/src/types.ts (1)
  • Hooks (89-89)
apps/cli/test/test-utils.ts (2)
  • runTRPCTest (58-161)
  • expectSuccess (163-173)
apps/cli/src/utils/config-processing.ts (1)
apps/cli/src/types.ts (1)
  • Hooks (89-89)
apps/cli/src/index.ts (1)
apps/cli/src/types.ts (1)
  • HooksSchema (88-88)
🔇 Additional comments (22)
apps/web/src/lib/types.ts (1)

15-15: LGTM!

The addition of "hooks" to the TechCategory union is clean and properly positioned between "packageManager" and "addons".

apps/web/src/lib/constant.ts (4)

472-497: Verify the missing icon for Husky.

Lefthook has an icon defined (line 477), but Husky's icon is an empty string (line 485). Was this intentional, or should Husky have an icon as well? Consider checking if a Husky icon exists or should be added for UI consistency.


646-646: LGTM!

All preset templates consistently set hooks to "lefthook", which showcases the new git hooks feature. This intentional deviation from the DEFAULT_STACK (which uses "none") helps promote the recommended option in the presets.

Also applies to: 673-673, 700-702, 727-727


752-752: LGTM!

The hooks field is correctly typed as a string and properly positioned in the StackState type.


775-775: LGTM!

The default value "none" for hooks aligns with the PR objectives and matches the default setting in TECH_OPTIONS.

apps/cli/src/helpers/core/command-handlers.ts (1)

82-113: Good: error-path ProjectConfig now includes hooks
Keeps the failure payload structurally consistent with the expanded config surface.

apps/cli/src/helpers/addons/ultracite-setup.ts (1)

220-230: Dependency install branching looks consistent with hooks
Installing ["husky","lint-staged"] vs ["lefthook"] matches the new hooks selection model.

apps/cli/test/addons.test.ts (1)

7-8: Good: Husky removed from addon coverage
These updates align with “hooks != addons” separation.

Please ensure runTRPCTest / TestConfig defaults hooks to "none" (or updates all callers), otherwise these configs may start failing after ProjectConfig.hooks became required.

Also applies to: 174-179

apps/cli/src/validation.ts (1)

7-22: Confirm intended UX: --yes can no longer be combined with --hooks
Adding hooks to CORE_STACK_FLAGS will make --yes --hooks lefthook error out. If you want “mostly defaults, but pick hooks”, you may want to treat hooks differently.

apps/cli/templates/hooks/lefthook/lefthook.yml.hbs (1)

1-3: [rewritten review comment]
[classification tag]

apps/web/content/docs/index.mdx (1)

313-315: No action needed. The docs correctly display --hooks as the flag name, which matches the actual CLI implementation defined in the codebase.

apps/cli/test/benchmark.test.ts (1)

430-431: LGTM!

The refactoring correctly moves "husky" from the addons array to a separate hooks field, aligning with the PR's objective to treat Git hooks as a first-class configuration option.

apps/cli/src/index.ts (2)

28-28: LGTM!

The import correctly adds HooksSchema to support the new hooks configuration option.


89-89: LGTM!

The hooks field is correctly added as an optional parameter to the CLI input schema, consistent with other optional configuration fields.

apps/cli/src/helpers/core/create-project.ts (3)

15-15: LGTM!

The imports correctly add the necessary hooks setup functionality to the project creation workflow.

Also applies to: 34-34


64-64: LGTM!

The template-based hooks setup is correctly invoked during project scaffolding. The function includes internal guards to handle the "none" case.


88-90: LGTM!

The runtime hooks setup is correctly invoked only when hooks are configured and not set to "none", ensuring proper integration with the project creation flow.

apps/cli/src/utils/config-processing.ts (2)

9-9: LGTM!

The import correctly adds the Hooks type for configuration processing.


90-92: LGTM!

The hooks configuration is correctly processed and assigned to the config object, following the same pattern as other configuration options.

apps/cli/test/hooks.test.ts (1)

5-212: Comprehensive test coverage for hooks configurations.

The test suite thoroughly covers all hooks options (husky, lefthook, none), integration with linters (biome, oxlint), combinations with addons, and default behavior. The test structure is well-organized and validates the new hooks feature effectively.

apps/cli/src/helpers/addons/addons-setup.ts (1)

63-63: LGTM!

The simplified call to setupUltracite correctly removes the hasHusky parameter, aligning with the refactoring that moves Git hooks configuration to a dedicated subsystem.

apps/cli/src/helpers/hooks/hooks-setup.ts (1)

7-25: LGTM!

The setupHooks function correctly determines the linter from addons and delegates to the appropriate hooks setup function based on the configuration.

Copy link
Contributor

@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: 1

Caution

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

⚠️ Outside diff range comments (2)
apps/cli/README.md (2)

32-46: Add missing "Git Hooks" category to the Features table.

The PR refactors Git hooks into their own section with a new --hooks option, but the Features table lacks a "Git Hooks" row. Additionally, line 44 still lists "Husky (Git hooks)" under Addons, which contradicts the PR objective to remove Husky from addons.

Revise the Features table to:

  1. Add a new "Git Hooks" row (between existing categories and Addons) listing the options
  2. Remove "Husky (Git hooks)" from the Addons row

Apply this diff to add the Git Hooks category and update Addons:

 | **Styling**              | Tailwind CSS with shadcn/ui components                                                                                                                                                                                                                     |
+| **Git Hooks**            | • Lefthook<br>• Husky<br>• None                                                                                                                                                                                                                             |
 | **Addons**               | • PWA support<br>• Tauri (desktop applications)<br>• Starlight (documentation site)<br>• Biome (linting and formatting)<br>• Husky (Git hooks)<br>• Turborepo (optimized builds)                                                                           |
+| **Addons**               | • PWA support<br>• Tauri (desktop applications)<br>• Starlight (documentation site)<br>• Biome (linting and formatting)<br>• Turborepo (optimized builds)                                                                                                   |

196-196: Update Compatibility Notes to reflect Husky's relocation from addons.

Line 196 references Husky as part of the Addons 'none' option, but Husky has been moved to the Git Hooks category. Update this reference for accuracy.

Apply this diff to correct the compatibility note:

- **Addons 'none'**: Skips all addons (PWA, Tauri, Starlight, Biome, Husky, Turborepo).
+ **Addons 'none'**: Skips all addons (PWA, Tauri, Starlight, Biome, Turborepo).
♻️ Duplicate comments (2)
apps/cli/README.md (1)

62-62: Fix inconsistent spacing in the --hooks option description.

The option has inconsistent alignment compared to other option descriptions. Align the description consistently with other options on this page.

Apply this diff to correct the spacing:

- --hooks <type>                   Git hooks manager (lefthook, husky, none)
+ --hooks <type>                  Git hooks manager (lefthook, husky, none)
apps/cli/src/helpers/hooks/hooks-setup.ts (1)

60-106: Fix template/file I/O to be bundler-safe + align with Bun guidelines.
__dirname (Line 96) is brittle under bundling, and fs-extra read/write here conflicts with repo guidance to use Bun.file()/Bun.write(). This was already flagged previously.

Suggested direction:

-import fs from "fs-extra";
+import fs from "fs-extra";
 ...
-    const templatePath = path.join(__dirname, "../../../templates/hooks/lefthook/lefthook.yml.hbs");
-    const templateContent = await fs.readFile(templatePath, "utf-8");
+    const templateUrl = new URL("../../../templates/hooks/lefthook/lefthook.yml.hbs", import.meta.url);
+    const templateContent = await Bun.file(templateUrl).text();
     const template = Handlebars.compile(templateContent);
 ...
-    await fs.writeFile(lefthookYmlPath, lefthookYmlContent);
+    await Bun.write(lefthookYmlPath, lefthookYmlContent);

Also, the current lefthook setup still installs/configures lint-staged (Lines 61-87). If the intent is “lefthook doesn’t require lint-staged (stage_fixed: true + direct commands)”, consider removing lint-staged entirely for lefthook and generating the lefthook config accordingly.

Does Lefthook support staging fixed files via `stage_fixed: true`, and what is the recommended config pattern for running Biome (`biome check --write`) or Oxlint (`oxlint --fix`) on staged files without lint-staged?
🧹 Nitpick comments (2)
apps/web/src/lib/constant.ts (1)

740-761: Tighten StackState["hooks"] from string to a union of allowed IDs.
Right now any string can flow through URL/state/config without a TS-level guard. Consider a literal union (or derive from TECH_OPTIONS.hooks).

Also applies to: 763-784

apps/cli/src/helpers/hooks/hooks-setup.ts (1)

7-25: Add an explicit fallback/guard for unknown hooks values.
Since setupHooks() silently no-ops for unexpected values, consider an else that throws (or logs) to surface misconfigurations early.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c8db0c and ef64e1f.

⛔ Files ignored due to path filters (1)
  • lefthook.svg is excluded by !**/*.svg
📒 Files selected for processing (35)
  • apps/cli/README.md (1 hunks)
  • apps/cli/src/constants.ts (2 hunks)
  • apps/cli/src/helpers/addons/addons-setup.ts (1 hunks)
  • apps/cli/src/helpers/addons/ultracite-setup.ts (3 hunks)
  • apps/cli/src/helpers/core/add-addons.ts (1 hunks)
  • apps/cli/src/helpers/core/command-handlers.ts (1 hunks)
  • apps/cli/src/helpers/core/create-project.ts (4 hunks)
  • apps/cli/src/helpers/core/create-readme.ts (5 hunks)
  • apps/cli/src/helpers/core/detect-project-config.ts (1 hunks)
  • apps/cli/src/helpers/core/post-installation.ts (2 hunks)
  • apps/cli/src/helpers/core/template-manager.ts (1 hunks)
  • apps/cli/src/helpers/hooks/hooks-setup.ts (1 hunks)
  • apps/cli/src/index.ts (2 hunks)
  • apps/cli/src/prompts/addons.ts (2 hunks)
  • apps/cli/src/prompts/config-prompts.ts (5 hunks)
  • apps/cli/src/prompts/hooks.ts (1 hunks)
  • apps/cli/src/types.ts (1 hunks)
  • apps/cli/src/utils/config-processing.ts (2 hunks)
  • apps/cli/src/validation.ts (1 hunks)
  • apps/cli/templates/hooks/lefthook/lefthook.yml.hbs (1 hunks)
  • apps/cli/test/addons.test.ts (2 hunks)
  • apps/cli/test/backend-runtime.test.ts (1 hunks)
  • apps/cli/test/benchmark.test.ts (1 hunks)
  • apps/cli/test/hooks.test.ts (1 hunks)
  • apps/cli/test/integration.test.ts (4 hunks)
  • apps/cli/test/test-utils.ts (2 hunks)
  • apps/web/content/docs/cli/options.mdx (2 hunks)
  • apps/web/content/docs/index.mdx (1 hunks)
  • apps/web/content/docs/project-structure.mdx (1 hunks)
  • apps/web/src/lib/constant.ts (9 hunks)
  • apps/web/src/lib/stack-url-keys.ts (1 hunks)
  • apps/web/src/lib/stack-url-state.client.ts (1 hunks)
  • apps/web/src/lib/stack-url-state.ts (1 hunks)
  • apps/web/src/lib/stack-utils.ts (2 hunks)
  • apps/web/src/lib/types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (20)
  • apps/cli/templates/hooks/lefthook/lefthook.yml.hbs
  • apps/cli/src/helpers/core/command-handlers.ts
  • apps/web/content/docs/index.mdx
  • apps/cli/test/addons.test.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/src/prompts/config-prompts.ts
  • apps/cli/src/helpers/core/detect-project-config.ts
  • apps/cli/src/prompts/hooks.ts
  • apps/cli/test/hooks.test.ts
  • apps/web/src/lib/stack-utils.ts
  • apps/cli/test/backend-runtime.test.ts
  • apps/cli/src/helpers/core/create-project.ts
  • apps/cli/src/constants.ts
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/cli/src/index.ts
  • apps/cli/src/validation.ts
  • apps/web/src/lib/types.ts
  • apps/web/content/docs/cli/options.mdx
  • apps/cli/src/helpers/addons/addons-setup.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

Define functions using the standard function declaration syntax, not arrow functions

Files:

  • apps/cli/src/utils/config-processing.ts
  • apps/cli/src/prompts/addons.ts
  • apps/web/src/lib/stack-url-state.ts
  • apps/cli/src/helpers/core/add-addons.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/test/benchmark.test.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/test/test-utils.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/hooks/hooks-setup.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/cli/src/types.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.{ts,tsx}: Use TypeScript type aliases instead of interface declarations
Do not use explicit return types

Files:

  • apps/cli/src/utils/config-processing.ts
  • apps/cli/src/prompts/addons.ts
  • apps/web/src/lib/stack-url-state.ts
  • apps/cli/src/helpers/core/add-addons.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/test/benchmark.test.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/test/test-utils.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/hooks/hooks-setup.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/cli/src/types.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file> for running TypeScript/JavaScript files
Bun automatically loads .env files, so don't use the dotenv package
Use Bun.serve() which supports WebSockets, HTTPS, and routes instead of express
Use bun:sqlite module for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of the ws package
Prefer Bun.file over node:fs readFile/writeFile methods
Use Bun.$ template literal syntax instead of execa for shell command execution
Import .css files directly in TypeScript/JavaScript files; Bun's CSS bundler will handle bundling
Run server with bun --hot <file> to enable hot reloading during development

Files:

  • apps/cli/src/utils/config-processing.ts
  • apps/cli/src/prompts/addons.ts
  • apps/web/src/lib/stack-url-state.ts
  • apps/cli/src/helpers/core/add-addons.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/test/benchmark.test.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/test/test-utils.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/hooks/hooks-setup.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/cli/src/types.ts
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file> instead of webpack or esbuild for bundling TypeScript, JavaScript, and CSS files

Files:

  • apps/cli/src/utils/config-processing.ts
  • apps/cli/src/prompts/addons.ts
  • apps/web/src/lib/stack-url-state.ts
  • apps/cli/src/helpers/core/add-addons.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/test/benchmark.test.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/test/test-utils.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/hooks/hooks-setup.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/cli/src/types.ts
**/*.{html,tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use HTML imports with Bun.serve() for frontend instead of Vite

Files:

  • apps/cli/src/utils/config-processing.ts
  • apps/cli/src/prompts/addons.ts
  • apps/web/src/lib/stack-url-state.ts
  • apps/cli/src/helpers/core/add-addons.ts
  • apps/cli/test/integration.test.ts
  • apps/cli/test/benchmark.test.ts
  • apps/web/src/lib/stack-url-state.client.ts
  • apps/cli/test/test-utils.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/hooks/hooks-setup.ts
  • apps/cli/src/helpers/core/template-manager.ts
  • apps/web/src/lib/stack-url-keys.ts
  • apps/cli/src/types.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.test.{ts,tsx,js,jsx}: Use bun test instead of jest or vitest for running tests
Use bun:test module with test and expect functions for writing tests

Files:

  • apps/cli/test/integration.test.ts
  • apps/cli/test/benchmark.test.ts
🧠 Learnings (8)
📚 Learning: 2025-12-03T07:48:26.419Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.419Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use `bun:test` module with `test` and `expect` functions for writing tests

Applied to files:

  • apps/cli/test/test-utils.ts
📚 Learning: 2025-12-03T07:48:26.419Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.419Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `Bun.$` template literal syntax instead of `execa` for shell command execution

Applied to files:

  • apps/cli/src/helpers/hooks/hooks-setup.ts
📚 Learning: 2025-12-03T07:47:42.038Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/better-t-stack-repo.mdc:0-0
Timestamp: 2025-12-03T07:47:42.038Z
Learning: Applies to **/*.hbs : In Handlebars templates, avoid generic if/else blocks. Write explicit conditions, such as: use if (eq orm "prisma") for Prisma, and else if (eq orm "drizzle") for Drizzle

Applied to files:

  • apps/cli/src/helpers/hooks/hooks-setup.ts
📚 Learning: 2025-12-03T07:48:26.419Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.419Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Prefer `Bun.file` over `node:fs` readFile/writeFile methods

Applied to files:

  • apps/cli/src/helpers/hooks/hooks-setup.ts
📚 Learning: 2025-12-03T07:48:26.419Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.419Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `bun <file>` instead of `node <file>` or `ts-node <file>` for running TypeScript/JavaScript files

Applied to files:

  • apps/cli/src/helpers/hooks/hooks-setup.ts
📚 Learning: 2025-12-03T07:48:26.419Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.419Z
Learning: Applies to **/*.{ts,tsx,js,jsx,css} : Use `bun build <file>` instead of `webpack` or `esbuild` for bundling TypeScript, JavaScript, and CSS files

Applied to files:

  • apps/cli/src/helpers/hooks/hooks-setup.ts
📚 Learning: 2025-12-03T07:48:26.419Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.419Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Bun automatically loads .env files, so don't use the dotenv package

Applied to files:

  • apps/cli/src/helpers/hooks/hooks-setup.ts
📚 Learning: 2025-12-03T07:48:14.714Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/convex_rules.mdc:0-0
Timestamp: 2025-12-03T07:48:14.714Z
Learning: Applies to convex/schema.ts : Always define your schema in convex/schema.ts and import schema definition functions from convex/server

Applied to files:

  • apps/cli/src/types.ts
🧬 Code graph analysis (7)
apps/cli/src/utils/config-processing.ts (1)
apps/cli/src/types.ts (1)
  • Hooks (89-89)
apps/web/src/lib/stack-url-state.ts (1)
apps/web/src/lib/constant.ts (2)
  • StackState (740-761)
  • DEFAULT_STACK (763-784)
apps/web/src/lib/stack-url-state.client.ts (1)
apps/web/src/lib/constant.ts (2)
  • StackState (740-761)
  • DEFAULT_STACK (763-784)
apps/cli/test/test-utils.ts (1)
apps/cli/src/types.ts (1)
  • Hooks (89-89)
apps/cli/src/helpers/hooks/hooks-setup.ts (2)
apps/cli/src/types.ts (1)
  • ProjectConfig (167-188)
apps/cli/src/utils/add-package-deps.ts (1)
  • addPackageDependency (6-57)
apps/cli/src/helpers/core/template-manager.ts (3)
apps/cli/src/types.ts (1)
  • ProjectConfig (167-188)
packages/types/src/types.ts (1)
  • ProjectConfig (50-50)
apps/cli/src/constants.ts (1)
  • PKG_ROOT (7-7)
apps/cli/src/types.ts (2)
packages/types/src/schemas.ts (17)
  • DatabaseSchema (4-6)
  • ORMSchema (8-8)
  • BackendSchema (10-12)
  • RuntimeSchema (14-16)
  • FrontendSchema (18-32)
  • AddonsSchema (34-48)
  • ExamplesSchema (50-52)
  • PackageManagerSchema (54-54)
  • DatabaseSetupSchema (56-68)
  • APISchema (70-70)
  • AuthSchema (72-74)
  • PaymentsSchema (76-76)
  • ProjectNameSchema (90-104)
  • WebDeploySchema (78-78)
  • ServerDeploySchema (80-80)
  • DirectoryConflictSchema (82-84)
  • TemplateSchema (86-88)
packages/types/src/types.ts (23)
  • Database (29-29)
  • ORM (30-30)
  • Backend (31-31)
  • Runtime (32-32)
  • Frontend (33-33)
  • Addons (34-34)
  • Examples (35-35)
  • PackageManager (36-36)
  • DatabaseSetup (37-37)
  • API (38-38)
  • Auth (39-39)
  • Payments (40-40)
  • ProjectName (45-45)
  • WebDeploy (41-41)
  • ServerDeploy (42-42)
  • DirectoryConflict (43-43)
  • Template (44-44)
  • CreateInput (47-47)
  • AddInput (48-48)
  • CLIInput (49-49)
  • ProjectConfig (50-50)
  • BetterTStackConfig (51-51)
  • InitResult (52-52)
🔇 Additional comments (18)
apps/cli/src/prompts/addons.ts (1)

66-66: The removal of "husky" from ADDON_GROUPS.Other is correct and complete.

The refactoring successfully migrates Git hooks into a separate category. Verification confirms:

  • AddonsSchema (types.ts) no longer includes "husky"
  • HooksSchema (types.ts) properly defines "husky" as a Git hooks option
  • All hooks implementations (hooks.ts, hooks-setup.ts) correctly handle husky
  • No remaining addon-related references to husky exist
apps/cli/src/helpers/core/add-addons.ts (1)

53-53: LGTM!

The hooks field initialization follows the established pattern for other configuration fields (database, orm, backend, etc.), with proper fallback handling: input.hooks || detectedConfig.hooks || "none".

apps/cli/src/types.ts (3)

1-122: Major refactoring: Re-exports replaced with inline schema definitions.

This file was completely rewritten to define all schemas inline instead of re-exporting from @better-t-stack/types. The schemas match the original definitions from packages/types/src/schemas.ts, ensuring consistency.

Note: "husky" was intentionally removed from AddonsSchema (lines 38-51) as it's now managed through the new HooksSchema, aligning with the PR's objective to separate Git hooks into their own configuration section.


88-89: New HooksSchema introduced for Git hooks management.

The new HooksSchema with values ["husky", "lefthook", "none"] correctly implements the PR's objective to add a first-class --hooks flag. This separates Git hooks configuration from the addons system.


124-219: Type definitions updated to include hooks field.

The hooks field has been consistently added to all relevant type definitions:

  • CreateInput (line 150)
  • AddInput (line 160)
  • ProjectConfig (line 187)
  • BetterTStackConfig (line 207)

This ensures type safety across the CLI for the new Git hooks feature.

apps/cli/test/test-utils.ts (2)

7-7: LGTM!

The Hooks type import is correctly added to support the new hooks field in test configurations.


113-113: LGTM!

The default hooks: "none" value in test utilities is appropriate and consistent with other test defaults (auth: "none", payments: "none", etc.). This ensures tests don't inadvertently configure Git hooks unless explicitly specified.

apps/cli/test/benchmark.test.ts (1)

430-431: LGTM!

The test correctly reflects the architectural change where Husky is now configured via the hooks field instead of the addons array. This aligns with the PR's objective to separate Git hooks into their own configuration section.

apps/cli/src/helpers/core/template-manager.ts (1)

604-612: LGTM!

The setupHooksTemplate function follows the established pattern used by setupPaymentsTemplate (lines 533-602) and setupAuthTemplate (lines 289-531):

  • Early return when hooks are not configured
  • Path construction following template directory conventions
  • Reuses existing processAndCopyFiles helper

This provides a clean integration point for copying hook-specific templates from templates/hooks/${context.hooks} to the project directory.

apps/web/src/lib/stack-url-state.client.ts (1)

35-37: LGTM!

The hooks parser is correctly integrated into stackParsers following the established pattern:

  • Uses parseAsStringEnum with type-safe StackState["hooks"]
  • Retrieves valid values via getValidIds("hooks")
  • Defaults to DEFAULT_STACK.hooks (which is "none" per constant.ts line 774)
apps/cli/src/utils/config-processing.ts (2)

9-9: LGTM!

The Hooks type import is correctly added to support hooks processing in the processFlags function.


90-92: LGTM!

The hooks processing logic follows the established pattern used for other single-value configuration options (auth, payments, backend, etc.):

  • Conditional check for options.hooks
  • Type assertion to Hooks
  • Direct assignment to config.hooks
apps/web/src/lib/stack-url-keys.ts (1)

17-17: LGTM!

The hooks: "hooks" mapping is correctly added to stackUrlKeys. The full name "hooks" is used as the URL key, which is consistent with other short keys like "git" and "yolo" that also use their full names rather than abbreviations.

apps/web/src/lib/stack-url-state.ts (1)

49-51: Hooks URL parsing wiring looks consistent with existing enums.
Adds server-side validation + defaulting for hooks in the same style as runtime/backend/..., so load/serialize should stay predictable.

apps/web/src/lib/constant.ts (2)

472-497: Good separation: hooks moved to its own TECH_OPTIONS category.
The defaults (none) and option IDs look aligned with the intended { lefthook, husky, none }.


646-647: Presets explicitly setting hooks: "lefthook" is clear and deterministic.

Also applies to: 673-674, 700-703, 727-728

apps/web/content/docs/project-structure.mdx (1)

257-260: Docs update matches the new config surface (hooks separated from addons).
The hooks enum in the bts.jsonc example aligns with the PR’s { lefthook, husky, none } shape.

apps/cli/test/integration.test.ts (1)

38-40: Integration tests correctly migrated from addons: [..., "husky"] to hooks: "husky".

Also applies to: 186-188, 273-275, 472-474

Copy link
Contributor

@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: 4

Caution

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

⚠️ Outside diff range comments (1)
apps/cli/src/helpers/addons/addons-setup.ts (1)

130-165: setupGitHooks incorrectly installs lint-staged for Lefthook and writes invalid empty string commands.

  • Line 136: always installs lint-staged even when using Lefthook. lint-staged is only applicable to Husky; Lefthook requires separate configuration.
  • Lines 158–161: writes an empty string "" as the lint command for lint-staged, which will cause lint-staged to fail.
  • Line 131: use a type alias type GitHook = "husky" | "lefthook"; instead of string (per coding guidelines for TypeScript).

Suggested fix: split into conditional branches—only add lint-staged for Husky, and create a lefthook.yml for Lefthook with proper lint commands. For the no-linter case with Husky, omit lint-staged entirely rather than setting an empty command.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef64e1f and e69fcb2.

📒 Files selected for processing (16)
  • README.md (1 hunks)
  • apps/cli/README.md (3 hunks)
  • apps/cli/src/constants.ts (1 hunks)
  • apps/cli/src/helpers/addons/addons-setup.ts (3 hunks)
  • apps/cli/src/helpers/addons/ultracite-setup.ts (3 hunks)
  • apps/cli/src/helpers/core/create-readme.ts (1 hunks)
  • apps/cli/src/helpers/core/post-installation.ts (2 hunks)
  • apps/cli/src/prompts/addons.ts (2 hunks)
  • apps/cli/templates/addons/ruler/.ruler/bts.md.hbs (1 hunks)
  • apps/cli/test/addons.test.ts (1 hunks)
  • apps/web/content/docs/cli/options.mdx (1 hunks)
  • apps/web/content/docs/index.mdx (1 hunks)
  • apps/web/content/docs/project-structure.mdx (1 hunks)
  • apps/web/src/lib/constant.ts (1 hunks)
  • apps/web/src/lib/stack-utils.ts (1 hunks)
  • packages/types/src/schemas.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/cli/templates/addons/ruler/.ruler/bts.md.hbs
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/web/src/lib/stack-utils.ts
  • apps/cli/src/constants.ts
  • apps/web/content/docs/cli/options.mdx
  • apps/cli/src/helpers/core/create-readme.ts
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/web/content/docs/index.mdx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

Define functions using the standard function declaration syntax, not arrow functions

Files:

  • apps/cli/test/addons.test.ts
  • packages/types/src/schemas.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/prompts/addons.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.{ts,tsx}: Use TypeScript type aliases instead of interface declarations
Do not use explicit return types

Files:

  • apps/cli/test/addons.test.ts
  • packages/types/src/schemas.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/prompts/addons.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file> for running TypeScript/JavaScript files
Bun automatically loads .env files, so don't use the dotenv package
Use Bun.serve() which supports WebSockets, HTTPS, and routes instead of express
Use bun:sqlite module for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of the ws package
Prefer Bun.file over node:fs readFile/writeFile methods
Use Bun.$ template literal syntax instead of execa for shell command execution
Import .css files directly in TypeScript/JavaScript files; Bun's CSS bundler will handle bundling
Run server with bun --hot <file> to enable hot reloading during development

Files:

  • apps/cli/test/addons.test.ts
  • packages/types/src/schemas.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/prompts/addons.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.test.{ts,tsx,js,jsx}: Use bun test instead of jest or vitest for running tests
Use bun:test module with test and expect functions for writing tests

Files:

  • apps/cli/test/addons.test.ts
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file> instead of webpack or esbuild for bundling TypeScript, JavaScript, and CSS files

Files:

  • apps/cli/test/addons.test.ts
  • packages/types/src/schemas.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/prompts/addons.ts
**/*.{html,tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use HTML imports with Bun.serve() for frontend instead of Vite

Files:

  • apps/cli/test/addons.test.ts
  • packages/types/src/schemas.ts
  • apps/web/src/lib/constant.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/prompts/addons.ts
🔇 Additional comments (6)
README.md (1)

47-48: Docs: Lefthook addition looks consistent with the features list.
No issues with the wording/placement; it matches the “Addons” concept used elsewhere.

apps/cli/README.md (1)

44-45: Docs: “Lefthook, Husky (Git hooks)” grouping is clear.
Good consolidation under Addons in the feature table.

packages/types/src/schemas.ts (1)

34-49: Schema update (AddonsSchema): LGTM adding lefthook.
This keeps runtime validation aligned with the CLI/web surfaces.

apps/cli/src/prompts/addons.ts (2)

46-49: Prompt display for Lefthook: good label/hint.
No issues with the new case branch.


70-74: Addon grouping: Lefthook placement under “Other” makes sense.
Matches the “Git hooks manager” framing used in docs.

apps/web/content/docs/project-structure.mdx (1)

257-258: Docs (bts.jsonc snippet): addons list including Lefthook is fine.
No concerns with the snippet change itself.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 6 comments.

Comments suppressed due to low confidence (2)

apps/cli/test/addons.test.ts:30

  • Missing test coverage for the mutual exclusivity of lefthook and husky. Since the PR description states "You can only select one of { lefthook, husky }", there should be a test case that verifies selecting both addons results in an error. This should be added once the validation is implemented.
  describe("Universal Addons (no frontend restrictions)", () => {
    const universalAddons = ["biome", "lefthook", "husky", "turborepo", "oxlint"];

    for (const addon of universalAddons) {
      it(`should work with ${addon} addon on any frontend`, async () => {
        const result = await runTRPCTest({
          projectName: `${addon}-universal`,
          addons: [addon as Addons],
          frontend: ["tanstack-router"],
          backend: "hono",
          runtime: "bun",
          database: "sqlite",
          orm: "drizzle",
          auth: "none",
          api: "trpc",
          examples: ["none"],
          dbSetup: "none",
          webDeploy: "none",
          serverDeploy: "none",
          install: false,
        });

        expectSuccess(result);
      });
    }

apps/cli/src/helpers/addons/addons-setup.ts:161

  • The lint-staged configuration in package.json is only used by Husky, not Lefthook. When gitHook is "lefthook", this configuration should not be added to package.json since Lefthook uses its own lefthook.yml configuration file with stage_fixed: true. This creates unnecessary configuration that won't be used.
    if (linter === "oxlint") {
      packageJson["lint-staged"] = {
        "**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue,astro,svelte}": "oxlint",
      };
    } else if (linter === "biome") {
      packageJson["lint-staged"] = {
        "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": ["biome check --write ."],
      };
    } else {
      packageJson["lint-staged"] = {
        "**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue,astro,svelte}": "",
      };
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@therealsamyak
Copy link
Contributor Author

@coderabbitai rereview this PR again

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 13, 2025

@therealsamyak I'll review the changes in this PR for you.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@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: 5

♻️ Duplicate comments (2)
apps/cli/src/prompts/addons.ts (1)

70-74: Consider creating a dedicated "Git Hooks" category as indicated by the PR title.

The PR title states "refactor out Git hooks to their own section," but Lefthook and Husky remain in the "Other" category. Creating a separate "Git Hooks" group would improve organization, make the mutual exclusivity clearer, and better align with the PR's stated goal.

Apply this diff:

 const ADDON_GROUPS = {
   Documentation: ["starlight", "fumadocs"],
   Linting: ["biome", "oxlint", "ultracite"],
+  "Git Hooks": ["lefthook", "husky"],
-  Other: ["ruler", "turborepo", "pwa", "tauri", "lefthook", "husky"],
+  Other: ["ruler", "turborepo", "pwa", "tauri"],
 };

Don't forget to update the groupedOptions initialization in both getAddonsChoice and getAddonsToAdd to include the new group:

   const groupedOptions: Record<string, AddonOption[]> = {
     Documentation: [],
     Linting: [],
+    "Git Hooks": [],
     Other: [],
   };

And update the group assignment logic in both functions:

     if (ADDON_GROUPS.Documentation.includes(addon)) {
       groupedOptions.Documentation.push(option);
     } else if (ADDON_GROUPS.Linting.includes(addon)) {
       groupedOptions.Linting.push(option);
+    } else if (ADDON_GROUPS["Git Hooks"].includes(addon)) {
+      groupedOptions["Git Hooks"].push(option);
     } else if (ADDON_GROUPS.Other.includes(addon)) {
       groupedOptions.Other.push(option);
     }
apps/cli/src/helpers/addons/addons-setup.ts (1)

61-65: Enforce mutual exclusion: validate that only one git hooks manager is selected.

The current ternary logic silently prefers Husky when both husky and lefthook are present in the addons array. According to the PR objectives, "only one of {lefthook, husky} may be selected." While getCompatibleAddons prevents this in interactive mode, CLI flag usage like --addons husky,lefthook bypasses this check.

Add explicit validation to detect and reject both selections:

+  const hasHusky = addons.includes("husky");
+  const hasLefthook = addons.includes("lefthook");
+  if (hasHusky && hasLefthook) {
+    throw new Error("Only one git hooks manager can be selected: husky or lefthook.");
+  }
-  const gitHook = addons.includes("husky")
-    ? "husky"
-    : addons.includes("lefthook")
-      ? "lefthook"
-      : "";
+  const gitHook = hasHusky ? "husky" : hasLefthook ? "lefthook" : "";
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 40d61ee and 0710e58.

📒 Files selected for processing (5)
  • apps/cli/src/helpers/addons/addons-setup.ts (2 hunks)
  • apps/cli/src/helpers/addons/ultracite-setup.ts (3 hunks)
  • apps/cli/src/prompts/addons.ts (3 hunks)
  • apps/cli/src/utils/compatibility-rules.ts (1 hunks)
  • apps/web/src/app/(home)/new/_components/utils.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/cli/src/helpers/addons/ultracite-setup.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

Define functions using the standard function declaration syntax, not arrow functions

Files:

  • apps/web/src/app/(home)/new/_components/utils.ts
  • apps/cli/src/utils/compatibility-rules.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.{ts,tsx}: Use TypeScript type aliases instead of interface declarations
Do not use explicit return types

Files:

  • apps/web/src/app/(home)/new/_components/utils.ts
  • apps/cli/src/utils/compatibility-rules.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file> for running TypeScript/JavaScript files
Bun automatically loads .env files, so don't use the dotenv package
Use Bun.serve() which supports WebSockets, HTTPS, and routes instead of express
Use bun:sqlite module for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of the ws package
Prefer Bun.file over node:fs readFile/writeFile methods
Use Bun.$ template literal syntax instead of execa for shell command execution
Import .css files directly in TypeScript/JavaScript files; Bun's CSS bundler will handle bundling
Run server with bun --hot <file> to enable hot reloading during development

Files:

  • apps/web/src/app/(home)/new/_components/utils.ts
  • apps/cli/src/utils/compatibility-rules.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file> instead of webpack or esbuild for bundling TypeScript, JavaScript, and CSS files

Files:

  • apps/web/src/app/(home)/new/_components/utils.ts
  • apps/cli/src/utils/compatibility-rules.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
**/*.{html,tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use HTML imports with Bun.serve() for frontend instead of Vite

Files:

  • apps/web/src/app/(home)/new/_components/utils.ts
  • apps/cli/src/utils/compatibility-rules.ts
  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
🧬 Code graph analysis (1)
apps/cli/src/helpers/addons/addons-setup.ts (2)
apps/cli/src/helpers/addons/ultracite-setup.ts (1)
  • setupUltracite (133-228)
apps/cli/src/utils/add-package-deps.ts (1)
  • addPackageDependency (6-57)
🔇 Additional comments (2)
apps/cli/src/utils/compatibility-rules.ts (1)

253-259: LGTM: Mutual exclusion logic correctly filters interactive addon options.

The skip logic properly prevents users from selecting both husky and lefthook during interactive prompts by checking the existingAddons parameter. This aligns with the PR requirement that "only one may be selected."

Note: This handles the interactive prompt scenario. CLI flag validation (e.g., --addons husky,lefthook) should be addressed in addons-setup.ts as noted in the separate comment on that file.

apps/cli/src/prompts/addons.ts (1)

46-49: LGTM: Lefthook display case added correctly.

The display case for lefthook is properly implemented with an appropriate label and the GitHub description hint as mentioned in the PR objectives.

Copy link
Contributor

@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: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0710e58 and 251f231.

📒 Files selected for processing (2)
  • apps/cli/src/constants.ts (2 hunks)
  • apps/cli/test/addons.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/cli/test/addons.test.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

Define functions using the standard function declaration syntax, not arrow functions

Files:

  • apps/cli/src/constants.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.{ts,tsx}: Use TypeScript type aliases instead of interface declarations
Do not use explicit return types

Files:

  • apps/cli/src/constants.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file> for running TypeScript/JavaScript files
Bun automatically loads .env files, so don't use the dotenv package
Use Bun.serve() which supports WebSockets, HTTPS, and routes instead of express
Use bun:sqlite module for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of the ws package
Prefer Bun.file over node:fs readFile/writeFile methods
Use Bun.$ template literal syntax instead of execa for shell command execution
Import .css files directly in TypeScript/JavaScript files; Bun's CSS bundler will handle bundling
Run server with bun --hot <file> to enable hot reloading during development

Files:

  • apps/cli/src/constants.ts
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file> instead of webpack or esbuild for bundling TypeScript, JavaScript, and CSS files

Files:

  • apps/cli/src/constants.ts
**/*.{html,tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use HTML imports with Bun.serve() for frontend instead of Vite

Files:

  • apps/cli/src/constants.ts
🔇 Additional comments (1)
apps/cli/src/constants.ts (1)

182-182: LGTM!

The lefthook entry in ADDON_COMPATIBILITY is correctly configured with an empty array, making it compatible with all frontend options—consistent with how husky is configured.

@therealsamyak
Copy link
Contributor Author

Please don't update the branch every time I commit.

I will handle it tomorrow 😊

sorry i am used to updating outdated branches when they are outdated, but if you say its fine then i will stop, thank you :)

Resolved conflicts:
- constants.ts: Added lefthook to ADDON_COMPATIBILITY
- addons-setup.ts: Added setupLefthook function with linter integration
- addons.ts: Added lefthook to prompts in Tooling group
- bts.md.hbs: Added git hooks mention
- Deleted old files moved to template-generator
- Changed from 'commands' object syntax to 'jobs' array syntax
- Added --write flag to biome check for file fixing
- Follows latest lefthook documentation recommendations
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jan 10, 2026
- Fixed --integrations flag to pass values correctly
- Added lint-staged to integrations array when husky is selected
- Added claude hook option
- Added new agents: cursor-cli, mistral-vibe, vercel
Copy link
Contributor

@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: 4

🧹 Nitpick comments (1)
apps/cli/src/helpers/addons/addons-setup.ts (1)

124-128: Consider a more robust prepare script coordination check.

The string inclusion check .includes("lefthook install") works for the current use case but could theoretically match unrelated content in the prepare script. A more explicit approach would be to track addon state or parse the script more carefully.

♻️ Optional: More robust coordination
-const currentPrepare = packageJson.scripts?.prepare;
-const prepareScript = currentPrepare?.includes("lefthook install")
-  ? `${currentPrepare} && husky`
-  : "husky";
+const currentPrepare = packageJson.scripts?.prepare || "";
+const hasLefthookInstall = /\blefthook\s+install\b/.test(currentPrepare);
+const prepareScript = hasLefthookInstall
+  ? `${currentPrepare} && husky`
+  : "husky";

Using a regex with word boundaries would be more precise, though the current implementation is functional.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11aaae9 and 561b4ff.

📒 Files selected for processing (7)
  • apps/cli/src/constants.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/src/prompts/addons.ts
  • apps/web/content/docs/project-structure.mdx
  • packages/template-generator/src/utils/add-deps.ts
  • packages/template-generator/templates/addons/ruler/.ruler/bts.md.hbs
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/content/docs/project-structure.mdx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

Define functions using the standard function declaration syntax, not arrow functions

Files:

  • apps/cli/src/prompts/addons.ts
  • packages/template-generator/src/utils/add-deps.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/src/constants.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.{ts,tsx}: Use TypeScript type aliases instead of interface declarations
Do not use explicit return types

Files:

  • apps/cli/src/prompts/addons.ts
  • packages/template-generator/src/utils/add-deps.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/src/constants.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file> for running TypeScript/JavaScript files
Bun automatically loads .env files, so don't use the dotenv package
Use Bun.serve() which supports WebSockets, HTTPS, and routes instead of express
Use bun:sqlite module for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of the ws package
Prefer Bun.file over node:fs readFile/writeFile methods
Use Bun.$ template literal syntax instead of execa for shell command execution
Import .css files directly in TypeScript/JavaScript files; Bun's CSS bundler will handle bundling
Run server with bun --hot <file> to enable hot reloading during development

Files:

  • apps/cli/src/prompts/addons.ts
  • packages/template-generator/src/utils/add-deps.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/src/constants.ts
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file> instead of webpack or esbuild for bundling TypeScript, JavaScript, and CSS files

Files:

  • apps/cli/src/prompts/addons.ts
  • packages/template-generator/src/utils/add-deps.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/src/constants.ts
**/*.{html,tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use HTML imports with Bun.serve() for frontend instead of Vite

Files:

  • apps/cli/src/prompts/addons.ts
  • packages/template-generator/src/utils/add-deps.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/src/constants.ts
🧠 Learnings (6)
📓 Common learnings
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-18T19:15:55.011Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, the Ultracite CLI's `--integrations` flag requires git hooks to be passed as a single space-separated string (e.g., `gitHooks.join(" ")`) rather than using the spread operator like other Ultracite flags. This has been tested and confirmed to work.
📚 Learning: 2025-12-18T19:15:55.011Z
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-18T19:15:55.011Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, the Ultracite CLI's `--integrations` flag requires git hooks to be passed as a single space-separated string (e.g., `gitHooks.join(" ")`) rather than using the spread operator like other Ultracite flags. This has been tested and confirmed to work.

Applied to files:

  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/constants.ts
📚 Learning: 2025-12-25T00:49:57.385Z
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-25T00:49:57.385Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, when calling Ultracite CLI with the `--integrations` flag, `lint-staged` should be passed as a separate positional argument after the integrations string (e.g., `ultraciteArgs.push("--integrations", gitHooks.join(" "))` followed by `ultraciteArgs.push("lint-staged")`), not included within the integrations string itself. This has been tested and confirmed to work.

Applied to files:

  • apps/cli/src/prompts/addons.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
📚 Learning: 2025-12-18T19:15:48.478Z
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-18T19:15:48.478Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, when passing the Ultracite CLI --integrations flag, supply git hooks as a single space-separated string (e.g., gitHooks.join(" ")) rather than spreading the array like other Ultracite flags. This has been tested and confirmed to work. Ensure the code uses a single string created by joining the gitHooks array with spaces for the --integrations argument.

Applied to files:

  • apps/cli/src/helpers/addons/ultracite-setup.ts
📚 Learning: 2025-12-25T00:49:53.633Z
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-25T00:49:53.633Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, when invoking Ultracite CLI with the --integrations flag, pass lint-staged as a separate positional argument after the integrations string, e.g. ultraciteArgs.push("--integrations", gitHooks.join(" ")); ultraciteArgs.push("lint-staged");. Do not include lint-staged inside the integrations string. This ensures proper argument parsing and avoids unintended tokens being treated as part of the integrations list.

Applied to files:

  • apps/cli/src/helpers/addons/ultracite-setup.ts
📚 Learning: 2025-12-03T07:48:26.419Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.419Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `Bun.$` template literal syntax instead of `execa` for shell command execution

Applied to files:

  • apps/cli/src/helpers/addons/ultracite-setup.ts
🧬 Code graph analysis (2)
apps/cli/src/helpers/addons/addons-setup.ts (1)
apps/cli/src/helpers/addons/ultracite-setup.ts (1)
  • setupUltracite (120-219)
apps/cli/src/helpers/addons/ultracite-setup.ts (1)
packages/types/src/types.ts (1)
  • ProjectConfig (51-51)
⏰ 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). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (8)
apps/cli/src/constants.ts (1)

54-54: LGTM! Lefthook added to compatibility map.

The addition of lefthook: [] follows the same pattern as husky with no specific frontend compatibility constraints, which is appropriate for a Git hooks manager that works universally across project types.

apps/cli/src/helpers/addons/ultracite-setup.ts (2)

120-120: Function signature updated to support multiple git hooks.

The change from hasHusky: boolean to gitHooks: string[] is a breaking API change that enables support for both Husky and Lefthook. This aligns with the broader PR objective of adding Lefthook as an addon option.


196-201: Correct integration handling for git hooks.

The implementation correctly:

  • Uses gitHooks.join(" ") for the --integrations flag (as per learnings)
  • Only adds lint-staged when Husky is present (Lefthook uses stage_fixed: true and doesn't require lint-staged)

Based on learnings, the Ultracite CLI's --integrations flag requires git hooks to be passed as a space-separated string.

apps/cli/src/prompts/addons.ts (2)

46-49: LGTM! Lefthook display configuration added.

The label and hint text are clear and appropriate for describing Lefthook's purpose as a Git hooks manager.


79-79: Lefthook added to Tooling group alongside Husky.

The grouping is logical. Note that the current implementation allows selecting both Husky and Lefthook simultaneously. Per the PR objectives, the author plans to enforce mutual exclusion in a follow-up PR.

apps/cli/src/helpers/addons/addons-setup.ts (3)

41-48: LGTM! Clean gitHooks array construction for Ultracite.

The hasLefthook detection follows the existing pattern, and the gitHooks array construction correctly matches the setupUltracite API signature.


58-71: LGTM! Proper coordination between Husky and Lefthook setup.

The linter detection and sequential setup calls are well-structured. The ability to select both hooks simultaneously is intentional per the PR objectives.


153-180: LGTM! setupLefthook mirrors setupHusky structure effectively.

The function properly coordinates with Husky via prepare script detection (lines 163-167) and generates the lefthook.yml configuration. The symmetric design with setupHusky ensures correct prepare script regardless of execution order.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

This PR is being reviewed by Cursor Bugbot

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Prepare scripts (lefthook install, husky) require a git repo to exist.
Moving git init before install ensures these hooks can be properly set up.
The prepare script now checks if a git repo exists before running.
If no .git directory exists, the script silently succeeds, avoiding
errors during initial install when git init happens after bun install.
- Husky v9+ handles missing .git directory gracefully
- Lefthook needs git check as lefthook install fails without git repo
Ultracite creates its own linter config via CLI init command.
The biome.json template was incorrectly being copied even when
oxlint was selected as the linter.
…ager

- Moved lefthook.yml.hbs to template-generator/templates/addons/lefthook/
- Template uses {{packageManager}} for commands (bun oxlint, bun oxfmt)
- Removed programmatic generation from addons-setup.ts
- Fixes 'command not found' errors for oxlint/oxfmt
Copy link
Contributor

@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: 2

🧹 Nitpick comments (1)
packages/template-generator/templates/addons/lefthook/lefthook.yml.hbs (1)

19-24: Consider providing a working example job.

When neither Biome nor Oxlint is selected, the jobs: block will be empty (comments don't create YAML structure). While Lefthook may accept this, providing an actual example job (commented out with proper YAML syntax) would be more helpful for users.

💡 Alternative with working example
 {{else}}
-    # Add your pre-commit commands here
-    # Example:
-    # - name: lint
-    #   run: {{packageManagerRunCmd}} lint
+    # Uncomment and customize the example below:
+    # - name: lint-staged
+    #   run: {{packageManagerRunCmd}} lint-staged
+    #   stage_fixed: true
 {{/if}}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 561b4ff and e7199f9.

📒 Files selected for processing (6)
  • apps/cli/src/helpers/addons/addons-setup.ts
  • apps/cli/src/helpers/addons/ultracite-setup.ts
  • apps/cli/src/helpers/core/post-installation.ts
  • packages/template-generator/src/templates.generated.ts
  • packages/template-generator/templates/addons/lefthook/lefthook.yml.hbs
  • packages/template-generator/templates/addons/ultracite/biome.json.hbs
💤 Files with no reviewable changes (1)
  • packages/template-generator/templates/addons/ultracite/biome.json.hbs
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/cli/src/helpers/addons/addons-setup.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.hbs

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.hbs: In Handlebars templates, avoid generic if/else blocks. Write explicit conditions, such as: use if (eq orm "prisma") for Prisma, and else if (eq orm "drizzle") for Drizzle
Escape the '{{' in Handlebars templates like '{{'

Files:

  • packages/template-generator/templates/addons/lefthook/lefthook.yml.hbs
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

Define functions using the standard function declaration syntax, not arrow functions

Files:

  • apps/cli/src/helpers/addons/ultracite-setup.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.{ts,tsx}: Use TypeScript type aliases instead of interface declarations
Do not use explicit return types

Files:

  • apps/cli/src/helpers/addons/ultracite-setup.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file> for running TypeScript/JavaScript files
Bun automatically loads .env files, so don't use the dotenv package
Use Bun.serve() which supports WebSockets, HTTPS, and routes instead of express
Use bun:sqlite module for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of the ws package
Prefer Bun.file over node:fs readFile/writeFile methods
Use Bun.$ template literal syntax instead of execa for shell command execution
Import .css files directly in TypeScript/JavaScript files; Bun's CSS bundler will handle bundling
Run server with bun --hot <file> to enable hot reloading during development

Files:

  • apps/cli/src/helpers/addons/ultracite-setup.ts
**/*.{ts,tsx,js,jsx,css}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file> instead of webpack or esbuild for bundling TypeScript, JavaScript, and CSS files

Files:

  • apps/cli/src/helpers/addons/ultracite-setup.ts
**/*.{html,tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use HTML imports with Bun.serve() for frontend instead of Vite

Files:

  • apps/cli/src/helpers/addons/ultracite-setup.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-18T19:15:55.011Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, the Ultracite CLI's `--integrations` flag requires git hooks to be passed as a single space-separated string (e.g., `gitHooks.join(" ")`) rather than using the spread operator like other Ultracite flags. This has been tested and confirmed to work.
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-25T00:49:57.385Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, when calling Ultracite CLI with the `--integrations` flag, `lint-staged` should be passed as a separate positional argument after the integrations string (e.g., `ultraciteArgs.push("--integrations", gitHooks.join(" "))` followed by `ultraciteArgs.push("lint-staged")`), not included within the integrations string itself. This has been tested and confirmed to work.
📚 Learning: 2025-12-18T19:15:48.478Z
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-18T19:15:48.478Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, when passing the Ultracite CLI --integrations flag, supply git hooks as a single space-separated string (e.g., gitHooks.join(" ")) rather than spreading the array like other Ultracite flags. This has been tested and confirmed to work. Ensure the code uses a single string created by joining the gitHooks array with spaces for the --integrations argument.

Applied to files:

  • apps/cli/src/helpers/addons/ultracite-setup.ts
📚 Learning: 2025-12-25T00:49:53.633Z
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-25T00:49:53.633Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, when invoking Ultracite CLI with the --integrations flag, pass lint-staged as a separate positional argument after the integrations string, e.g. ultraciteArgs.push("--integrations", gitHooks.join(" ")); ultraciteArgs.push("lint-staged");. Do not include lint-staged inside the integrations string. This ensures proper argument parsing and avoids unintended tokens being treated as part of the integrations list.

Applied to files:

  • apps/cli/src/helpers/addons/ultracite-setup.ts
📚 Learning: 2025-12-03T07:48:26.419Z
Learnt from: CR
Repo: AmanVarshney01/create-better-t-stack PR: 0
File: .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc:0-0
Timestamp: 2025-12-03T07:48:26.419Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use `Bun.$` template literal syntax instead of `execa` for shell command execution

Applied to files:

  • apps/cli/src/helpers/addons/ultracite-setup.ts
⏰ 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: Test Suite
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (6)
apps/cli/src/helpers/addons/ultracite-setup.ts (5)

44-47: LGTM!

Type expansion for new agents is correctly structured and follows the existing pattern.


49-49: LGTM!

Addition of "claude" hook aligns with the HOOKS constant update.


91-93: LGTM!

Agent entries correctly mirror the type definitions.


99-99: LGTM!

Hook entry correctly mirrors the type definition.


127-127: LGTM!

Signature update from a boolean to a string array properly supports multiple git hooks selection.

packages/template-generator/templates/addons/lefthook/lefthook.yml.hbs (1)

1-6: LGTM: Clean configuration structure.

The header documentation and pre-commit configuration with parallel execution are well-structured. The parallel: true setting will improve performance when multiple jobs are defined.

Copy link
Contributor

@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: 3

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7199f9 and 700ad84.

📒 Files selected for processing (1)
  • packages/template-generator/templates/addons/lefthook/lefthook.yml.hbs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.hbs

📄 CodeRabbit inference engine (.cursor/rules/better-t-stack-repo.mdc)

**/*.hbs: In Handlebars templates, avoid generic if/else blocks. Write explicit conditions, such as: use if (eq orm "prisma") for Prisma, and else if (eq orm "drizzle") for Drizzle
Escape the '{{' in Handlebars templates like '{{'

Files:

  • packages/template-generator/templates/addons/lefthook/lefthook.yml.hbs
🧠 Learnings (3)
📓 Common learnings
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-18T19:15:55.011Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, the Ultracite CLI's `--integrations` flag requires git hooks to be passed as a single space-separated string (e.g., `gitHooks.join(" ")`) rather than using the spread operator like other Ultracite flags. This has been tested and confirmed to work.
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-25T00:49:57.385Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, when calling Ultracite CLI with the `--integrations` flag, `lint-staged` should be passed as a separate positional argument after the integrations string (e.g., `ultraciteArgs.push("--integrations", gitHooks.join(" "))` followed by `ultraciteArgs.push("lint-staged")`), not included within the integrations string itself. This has been tested and confirmed to work.
📚 Learning: 2025-12-25T00:49:57.385Z
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-25T00:49:57.385Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, when calling Ultracite CLI with the `--integrations` flag, `lint-staged` should be passed as a separate positional argument after the integrations string (e.g., `ultraciteArgs.push("--integrations", gitHooks.join(" "))` followed by `ultraciteArgs.push("lint-staged")`), not included within the integrations string itself. This has been tested and confirmed to work.

Applied to files:

  • packages/template-generator/templates/addons/lefthook/lefthook.yml.hbs
📚 Learning: 2025-12-18T19:15:55.011Z
Learnt from: therealsamyak
Repo: AmanVarshney01/create-better-t-stack PR: 711
File: apps/cli/src/helpers/addons/ultracite-setup.ts:200-205
Timestamp: 2025-12-18T19:15:55.011Z
Learning: In apps/cli/src/helpers/addons/ultracite-setup.ts, the Ultracite CLI's `--integrations` flag requires git hooks to be passed as a single space-separated string (e.g., `gitHooks.join(" ")`) rather than using the spread operator like other Ultracite flags. This has been tested and confirmed to work.

Applied to files:

  • packages/template-generator/templates/addons/lefthook/lefthook.yml.hbs
⏰ 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: Test Suite
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (1)
packages/template-generator/templates/addons/lefthook/lefthook.yml.hbs (1)

1-6: LGTM! Clear configuration structure.

The header comments and pre-commit hook structure are well-defined. Using parallel: true is appropriate for independent linting jobs.

jobs:
{{#if (includes addons "biome")}}
- name: biome
glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}"
Copy link
Contributor

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:

cat packages/template-generator/templates/addons/lefthook/lefthook.yml.hbs

Repository: AmanVarshney01/create-better-t-stack

Length of output: 817


🏁 Script executed:

# Search for Lefthook documentation or examples in the codebase
fd -name "lefthook*" -type f

Repository: AmanVarshney01/create-better-t-stack

Length of output: 310


🏁 Script executed:

# Look for other glob patterns in YAML/HBS files to see the pattern
rg -n 'glob:' --type yaml --type hbs -A1 -B1

Repository: AmanVarshney01/create-better-t-stack

Length of output: 498


🌐 Web query:

Lefthook glob pattern syntax documentation recursive matching

💡 Result:

Short summary — lefthook glob syntax (recursive matching)

  • By default lefthook uses the gobwas glob implementation. In that mode "**" matches 1 or more directory levels (i.e., it does NOT match files at the root). (lefthook.dev)
  • To get standard globstar behavior where "" matches 0 or more directories (so "/*.js" also matches "file.js"), set glob_matcher: doublestar at the top of lefthook.yml. (lefthook.dev)

Examples:

  • Default (gobwas): glob: "**/*.js" matches "a/b/file.js" but not "file.js". (lefthook.dev)
  • With doublestar: glob_matcher: doublestar plus glob: "**/*.js" matches "file.js" and "a/b/file.js". (lefthook.dev)

If you want, I can paste the exact lefthook.yml snippets.

Citations:


🏁 Script executed:

# Check if there's any documentation in the repo about Lefthook configuration
find . -name "README*" -o -name "*.md" | xargs grep -l -i "lefthook" 2>/dev/null | head -5

Repository: AmanVarshney01/create-better-t-stack

Length of output: 112


🏁 Script executed:

# Look for any other references to Lefthook configuration or glob patterns
rg -i "lefthook" -A 5 -B 5 --type md

Repository: AmanVarshney01/create-better-t-stack

Length of output: 5657


🏁 Script executed:

# Check if glob_matcher is set in the lefthook template
rg -n "glob_matcher" packages/template-generator/templates/addons/lefthook/

Repository: AmanVarshney01/create-better-t-stack

Length of output: 62


🏁 Script executed:

# Look for any glob patterns in other addon templates for comparison
fd -path "*/templates/addons/*" -name "*.hbs" -o -name "*.yml" | head -10

Repository: AmanVarshney01/create-better-t-stack

Length of output: 248


🏁 Script executed:

# Search for any documentation about expected glob behavior
rg -i "glob.*pattern\|root.*directory\|subdirectory" packages/template-generator/templates/ -A 2 -B 2

Repository: AmanVarshney01/create-better-t-stack

Length of output: 62


Glob pattern will not match files in subdirectories.

The pattern "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}" only matches files in the root directory. Lefthook uses the gobwas glob matcher by default, where ** matches 1+ directory levels (not root). To match files in both root and subdirectories, use either:

  • "**/*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}" with glob_matcher: doublestar at the top level, or
  • "{*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc},**/*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}}"

{{#if (includes addons "biome")}}
- name: biome
glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}"
run: {{packageManager}} biome check --write --no-errors-on-unmatched --files-ignore-unknown=true {staged_files}
Copy link
Contributor

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
# Check how packageManager variable is used in templates
echo "=== Searching for packageManager usage in templates ==="
rg -n '{{packageManager}}' packages/template-generator/templates/ -A 1 -B 1 | head -100

echo ""
echo "=== Checking for packageManagerExec or similar variables ==="
rg -n 'packageManager.*[Ee]xec|packageManager.*[Cc]md|npx' packages/template-generator/templates/ | head -50

echo ""
echo "=== Examining the specific file ==="
cat -n packages/template-generator/templates/addons/lefthook/lefthook.yml.hbs

Repository: AmanVarshney01/create-better-t-stack

Length of output: 1684


🏁 Script executed:

#!/bin/bash
# Search for variable definitions and context files

echo "=== Looking for packageManager variable definition ==="
fd -t f '\.(ts|js|json)' packages/template-generator -x rg -l 'packageManager' {}

echo ""
echo "=== Checking for context or data files related to template generation ==="
fd -t f -e json -e ts -e js packages/template-generator | grep -E '(context|data|template|config)' | head -20

echo ""
echo "=== Searching in template generator source ==="
find packages/template-generator -name '*.ts' -o -name '*.js' | xargs grep -l 'packageManager' 2>/dev/null

echo ""
echo "=== Look at package.json to understand the structure ==="
cat packages/template-generator/package.json

Repository: AmanVarshney01/create-better-t-stack

Length of output: 3168


🏁 Script executed:

#!/bin/bash
# Examine the source files that reference packageManager

echo "=== Checking package-configs.ts for packageManager handling ==="
cat -n packages/template-generator/src/post-process/package-configs.ts | head -150

echo ""
echo "=== Checking extras.ts ==="
cat -n packages/template-generator/src/template-handlers/extras.ts

Repository: AmanVarshney01/create-better-t-stack

Length of output: 7614


🏁 Script executed:

#!/bin/bash
# Continue reading package-configs.ts to see getPackageManagerConfig

echo "=== Reading getPackageManagerConfig function ==="
sed -n '146,250p' packages/template-generator/src/post-process/package-configs.ts

echo ""
echo "=== Search for where template context is created ==="
rg -n 'packageManager.*:' packages/template-generator/src --type ts -B 2 -A 2 | head -100

echo ""
echo "=== Search for where Handlebars context is built ==="
rg -n 'new Handlebars|Handlebars.compile|registerHelper|context|data' packages/template-generator/src --type ts | grep -E '(context|data|Handlebars)' | head -30

Repository: AmanVarshney01/create-better-t-stack

Length of output: 16724


🏁 Script executed:

#!/bin/bash
# Search for where Handlebars template context is built and packageManager is set

echo "=== Search for processSingleTemplate or template compilation ==="
rg -n 'processSingleTemplate|processTemplatesFromPrefix|TemplateData|registerHelper' packages/template-generator/src --type ts -A 3 | head -80

echo ""
echo "=== Search for where Handlebars variables are set ==="
rg -n 'handlebars|Handlebars|compile' packages/template-generator/src --type ts -B 2 -A 2 | head -60

echo ""
echo "=== Look at template-handlers/utils.ts ==="
cat -n packages/template-generator/src/template-handlers/utils.ts | head -100

Repository: AmanVarshney01/create-better-t-stack

Length of output: 14371


🏁 Script executed:

#!/bin/bash
# Find and examine the template processor

echo "=== Look at template-processor.ts ==="
cat -n packages/template-generator/src/core/template-processor.ts

Repository: AmanVarshney01/create-better-t-stack

Length of output: 2133


🏁 Script executed:

#!/bin/bash
# Find the ProjectConfig type definition

echo "=== Search for ProjectConfig type definition ==="
find packages -name '*.ts' | xargs grep -l 'export type ProjectConfig\|export interface ProjectConfig' 2>/dev/null

echo ""
echo "=== Check the types package ==="
cat -n packages/types/src/index.ts | head -150

Repository: AmanVarshney01/create-better-t-stack

Length of output: 297


🏁 Script executed:

#!/bin/bash
# Read the types file to find ProjectConfig definition

echo "=== Check types.ts ==="
cat -n packages/types/src/types.ts | grep -A 100 'ProjectConfig'

Repository: AmanVarshney01/create-better-t-stack

Length of output: 2174


🏁 Script executed:

#!/bin/bash
# Read the schemas file to find packageManager schema

echo "=== Check PackageManagerSchema in schemas.ts ==="
cat -n packages/types/src/schemas.ts | grep -A 5 'PackageManagerSchema'

echo ""
echo "=== Check ProjectConfigSchema in schemas.ts ==="
cat -n packages/types/src/schemas.ts | grep -B 5 -A 30 'PackageManagerSchema' | head -80

Repository: AmanVarshney01/create-better-t-stack

Length of output: 4751


Use compatible command syntax for all package managers in lefthook configuration.

The template uses {{packageManager}} biome, {{packageManager}} oxlint, and {{packageManager}} oxfmt syntax, but these commands are incompatible with how package managers execute bin scripts. This syntax would fail for all supported package managers:

  • npm biome requires npx biome or npm run <script>
  • pnpm biome requires pnpm exec biome or pnpm run <script>
  • bun biome requires bun run <script> or npx biome

Consider using npx (universal solution) or implement package manager-specific logic:

{{#if (eq packageManager "npm")}}
run: npx biome check --write --no-errors-on-unmatched --files-ignore-unknown=true {staged_files}
{{else if (eq packageManager "pnpm")}}
run: pnpm exec biome check --write --no-errors-on-unmatched --files-ignore-unknown=true {staged_files}
{{else if (eq packageManager "bun")}}
run: bun run biome check --write --no-errors-on-unmatched --files-ignore-unknown=true {staged_files}
{{/if}}

Alternatively, introduce a packageManagerExec or packageManagerRunCmd template variable to handle this abstraction.

Also applies to: 14-14, 17-17

Comment on lines +19 to +24
{{else}}
# Add your pre-commit commands here
# Example:
# - name: lint
# run: {{packageManagerRunCmd}} lint
{{/if}}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Avoid generic else block per coding guidelines.

The generic {{else}} block (line 19) violates the coding guideline that states: "In Handlebars templates, avoid generic if/else blocks. Write explicit conditions."

Consider one of these alternatives:

  1. Remove the else block and only include jobs when biome or oxlint is present
  2. Replace with an explicit condition: {{else if (not (or (includes addons "biome") (includes addons "oxlint")))}}
  3. If additional linters will be supported, add explicit else if conditions for each

Additionally, note the inconsistency: the commented example uses {{packageManagerRunCmd}} (line 23), while the actual commands use {{packageManager}} (lines 10, 14, 17).

As per coding guidelines, avoid generic if/else blocks.

♻️ Option 1: Remove generic else block

If having an empty jobs list is acceptable when no linter is present:

-{{else}}
-    # Add your pre-commit commands here
-    # Example:
-    # - name: lint
-    #   run: {{packageManagerRunCmd}} lint
 {{/if}}
♻️ Option 2: Make condition explicit
-{{else}}
+{{else if (not (or (includes addons "biome") (includes addons "oxlint")))}}
     # Add your pre-commit commands here
     # Example:
     # - name: lint
     #   run: {{packageManagerRunCmd}} lint
 {{/if}}
📝 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
{{else}}
# Add your pre-commit commands here
# Example:
# - name: lint
# run: {{packageManagerRunCmd}} lint
{{/if}}
{{else if (includes addons "biome")}}
pre-commit:
commands:
biome:
glob: "**/*.{js,jsx,ts,tsx,json}"
run: {{packageManager}} exec biome check --apply-unsafe {staged_files}
stage_fixed: true
{{else if (includes addons "oxlint")}}
pre-commit:
commands:
oxlint:
run: {{packageManager}} exec oxlint --fix {staged_files}
stage_fixed: true
oxfmt:
run: {{packageManager}} exec oxfmt --write {staged_files}
stage_fixed: true
{{/if}}
Suggested change
{{else}}
# Add your pre-commit commands here
# Example:
# - name: lint
# run: {{packageManagerRunCmd}} lint
{{/if}}
{{else if (not (or (includes addons "biome") (includes addons "oxlint")))}}
# Add your pre-commit commands here
# Example:
# - name: lint
# run: {{packageManagerRunCmd}} lint
{{/if}}

return `${pc.bold("Git hooks with Lefthook:")}\n${pc.cyan(
"•",
)} Install hooks: ${cmd} lefthook install\n`;
}
Copy link

Choose a reason for hiding this comment

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

Incorrect package manager commands for lefthook installation

Medium Severity

The getLefthookInstructions function generates incorrect shell commands for pnpm and bun users. When packageManager is pnpm, it generates pnpm lefthook install, and for bun, it generates bun lefthook install. These commands won't work because pnpm and bun don't automatically resolve binaries from node_modules/.bin when called directly. The correct commands would be pnpm exec lefthook install for pnpm and bunx lefthook install for bun, following the pattern established in the existing getPackageExecutionCommand utility.

Fix in Cursor Fix in Web

addons?.includes("husky") ||
addons?.includes("biome") ||
addons?.includes("lefthook") ||
addons?.includes("oxlint");
Copy link

Choose a reason for hiding this comment

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

Instructions reference non-existent check script

Medium Severity

The hasGitHooksOrLinting variable now includes husky and lefthook in its condition, but it's used to determine whether to show linting instructions that tell users to run ${runCmd} check. The check script is only added by setupBiome or setupOxlint, not by setupHusky or setupLefthook. When a user selects only lefthook or husky without biome/oxlint, they receive instructions to run a script that doesn't exist, causing a "missing script" error.

Additional Locations (1)

Fix in Cursor Fix in Web

@AmanVarshney01 AmanVarshney01 enabled auto-merge (squash) January 10, 2026 20:31
@AmanVarshney01 AmanVarshney01 merged commit 7b71695 into AmanVarshney01:main Jan 10, 2026
2 of 3 checks passed
const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
const lintingInstructions = hasLinting ? getLintingInstructions(runCmd) : "";
const lefthookInstructions = hasLefthook ? getLefthookInstructions(packageManager) : "";
const lintingInstructions = hasGitHooksOrLinting ? getLintingInstructions(runCmd) : "";
Copy link

Choose a reason for hiding this comment

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

Linting instructions shown when no linter is configured

Medium Severity

The variable hasGitHooksOrLinting is used to determine whether to show linting instructions (line 65), but it includes husky and lefthook in addition to biome and oxlint. The check script is only created when setupBiome() or setupOxlint() is called, not when husky or lefthook is configured. If a user selects only husky or lefthook without biome or oxlint, they'll see instructions to run the check script that doesn't exist. The variable should be split - one for git hooks and one specifically for linting tools that actually add the check script.

Fix in Cursor Fix in Web

projectDir,
});
// lefthook.yml is generated by template-generator from templates/addons/lefthook/
}
Copy link

Choose a reason for hiding this comment

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

Lefthook missing prepare script for automatic hook installation

Medium Severity

The setupLefthook function only adds the lefthook dependency but doesn't add a prepare script to package.json. In contrast, setupHusky adds prepare: "husky" which ensures hooks are installed automatically when users run npm install. Without this, lefthook users must manually run lefthook install every time they clone the repo. The PR description claims a prepare script with lefthook install is added to the template, but the implementation is missing this functionality.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants