-
- Notifications
You must be signed in to change notification settings - Fork 233
feat(cli): add Claude Code integration #720
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(cli): add Claude Code integration #720
Conversation
Add comprehensive Claude Code integration that generates optimized configuration files for Better T Stack projects: - CLAUDE.md: Dynamic project context file with stack-specific patterns - .claude/commands/: Custom slash commands (dev, add-route, add-procedure, etc.) - .claude/spec/: Project specification templates Features: - Stack-aware content generation (TanStack Router/Start, Next.js, Nuxt, SvelteKit, Solid) - Backend patterns (Hono, Express, Fastify, Elysia, Convex) - API layer patterns (tRPC, oRPC) - Database/ORM patterns (Drizzle, Prisma, Mongoose) - Auth patterns (Better-Auth, Clerk) - Conditional command generation based on selected stack This integration helps Claude Code understand project structure and conventions for better assistance with Better T Stack projects. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| @kareemschultz is attempting to deploy a commit to the Better T Stack Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughAdds a new Claude integration module that generates CLAUDE.md and a .claude/ directory (commands and spec files) tailored to the project configuration, and invokes this setup during project scaffolding. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (1)
apps/cli/src/helpers/core/claude-setup.ts (1)
258-304: Project structure “tree” markers are inconsistent (multiple└──siblings).
In several branches you emit multiple│ └── ...entries underapps/; that reads like each is the last child. Consider using├──for intermediate siblings and└──only for the final one to avoid confusing output.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/cli/src/helpers/core/claude-setup.ts(1 hunks)apps/cli/src/helpers/core/create-project.ts(2 hunks)
🧰 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/helpers/core/claude-setup.tsapps/cli/src/helpers/core/create-project.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/core/claude-setup.tsapps/cli/src/helpers/core/create-project.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)
**/*.{ts,tsx,js,jsx}: Usebun <file>instead ofnode <file>orts-node <file>for running TypeScript/JavaScript files
Bun automatically loads .env files, so don't use the dotenv package
UseBun.serve()which supports WebSockets, HTTPS, and routes instead ofexpress
Usebun:sqlitemodule for SQLite instead ofbetter-sqlite3
UseBun.redisfor Redis instead ofioredis
UseBun.sqlfor Postgres instead ofpgorpostgres.js
Use built-inWebSocketinstead of thewspackage
PreferBun.fileovernode:fsreadFile/writeFile methods
UseBun.$template literal syntax instead ofexecafor shell command execution
Import .css files directly in TypeScript/JavaScript files; Bun's CSS bundler will handle bundling
Run server withbun --hot <file>to enable hot reloading during development
Files:
apps/cli/src/helpers/core/claude-setup.tsapps/cli/src/helpers/core/create-project.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 ofwebpackoresbuildfor bundling TypeScript, JavaScript, and CSS files
Files:
apps/cli/src/helpers/core/claude-setup.tsapps/cli/src/helpers/core/create-project.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/core/claude-setup.tsapps/cli/src/helpers/core/create-project.ts
🧠 Learnings (3)
📚 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/**/*.ts : Use query, mutation, and action to define public functions; use internalQuery, internalMutation, and internalAction to define private, internal functions Applied to files:
apps/cli/src/helpers/core/claude-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/**/*.ts : Always add 'use node'; to the top of files containing actions that use Node.js built-in modules Applied to files:
apps/cli/src/helpers/core/claude-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/**/*.ts : Be strict with types, particularly around id's of documents; if a function takes in an id for a document in the 'users' table, take in Id<'users'> rather than string Applied to files:
apps/cli/src/helpers/core/claude-setup.ts
🔇 Additional comments (2)
apps/cli/src/helpers/core/create-project.ts (1)
16-16: Claude setup failure is currently non-fatal (by design?)—please confirm that’s intended.
createProject()awaitssetupClaudeIntegration(), but the callee catches/logs errors and does not throw; scaffolding will continue “successfully” even if Claude artifacts fail to write. If you want Claude integration to be best-effort, consider surfacing a clear warning via the same UX channel as the rest of the CLI (e.g.,log.warn) so users don’t miss it; if you want it to be required, let the error propagate.Also applies to: 104-108
apps/cli/src/helpers/core/claude-setup.ts (1)
1-3: Confirm runtime/tooling direction: guidelines prefer Bun file APIs, but this usesfs-extra.
Your repo guidelines say to preferBun.file/Bun.writepatterns; this new module usesfs-extrathroughout. If the CLI is intentionally Node-based, ignore; otherwise, consider aligning file IO with Bun conventions for consistency.Also applies to: 45-63
| export async function setupClaudeIntegration( | ||
| projectDir: string, | ||
| options: ProjectConfig, | ||
| ): Promise<void> { | ||
| try { | ||
| // Create CLAUDE.md at project root | ||
| await createClaudeMd(projectDir, options); | ||
| | ||
| // Create .claude/commands/ directory with relevant commands | ||
| await createClaudeCommands(projectDir, options); | ||
| | ||
| // Create .claude/spec/ directory with project templates | ||
| await createClaudeSpec(projectDir, options); | ||
| | ||
| consola.success("Claude Code integration configured successfully"); | ||
| } catch (error) { | ||
| consola.error("Failed to set up Claude Code integration:", error); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t swallow Claude setup errors silently (or at least downgrade to “warn” and keep going explicitly).
Right now failures log via consola.error but don’t propagate, so the parent flow can’t decide whether to stop or present a structured warning. Consider either (a) rethrowing after logging, or (b) returning a boolean/result so createProject() can display a clear “Claude setup skipped/failed” message.
🤖 Prompt for AI Agents
In apps/cli/src/helpers/core/claude-setup.ts around lines 22-40, the catch block currently logs failures with consola.error but swallows the error so callers cannot decide whether to stop or show a structured warning; change the function to either (A) rethrow after logging (consola.error(...); throw error) so the parent flow can handle termination, or (B) change the signature to return a boolean/result and in the catch return false (and log at warn level if you want to continue), then update createProject() to check the returned value and display “Claude setup skipped/failed” or fail the process accordingly—pick one approach and implement the matching caller-side handling. - Change error handling to warn level for non-critical Claude setup failures - Remove all explicit return types per repo guidelines - Remove duplicate docblock comment - Update type safety guidance to prefer inferred return types - Add getDlxCmd helper for package manager compatibility (npx/bunx/pnpm dlx) - Change interface to type alias in component template - Fix tree structure ASCII art with proper markers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
apps/cli/src/helpers/core/claude-setup.ts (2)
170-180: Empty interpolations may leave extra blank lines in output.When conditions are false, empty strings are interpolated, leaving multiple consecutive blank lines in the generated CLAUDE.md. Consider using a pattern similar to
stackRows(lines 114-123) where sections are conditionally pushed to an array, then joined.-${hasFrontend ? generateFrontendPatterns(options) : ""} - -${hasBackend && !isConvex ? generateBackendPatterns(options) : ""} - -${isConvex ? generateConvexPatterns(options) : ""} - -${hasApi && !isConvex ? generateApiPatterns(options) : ""} - -${hasDatabase && !isConvex ? generateDatabasePatterns(options) : ""} - -${hasAuth ? generateAuthPatterns(options) : ""} +${[ + hasFrontend ? generateFrontendPatterns(options) : "", + hasBackend && !isConvex ? generateBackendPatterns(options) : "", + isConvex ? generateConvexPatterns(options) : "", + hasApi && !isConvex ? generateApiPatterns(options) : "", + hasDatabase && !isConvex ? generateDatabasePatterns(options) : "", + hasAuth ? generateAuthPatterns(options) : "", +].filter(Boolean).join("\n")}
350-351: Remove no-op.map()call.The
.map((l) => l)transformation returns each element unchanged and serves no purpose.- return lines.map((l) => l).join("\n"); + return lines.join("\n");Also applies to lines 387, 900.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/cli/src/helpers/core/claude-setup.ts(1 hunks)
🧰 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/helpers/core/claude-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/core/claude-setup.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)
**/*.{ts,tsx,js,jsx}: Usebun <file>instead ofnode <file>orts-node <file>for running TypeScript/JavaScript files
Bun automatically loads .env files, so don't use the dotenv package
UseBun.serve()which supports WebSockets, HTTPS, and routes instead ofexpress
Usebun:sqlitemodule for SQLite instead ofbetter-sqlite3
UseBun.redisfor Redis instead ofioredis
UseBun.sqlfor Postgres instead ofpgorpostgres.js
Use built-inWebSocketinstead of thewspackage
PreferBun.fileovernode:fsreadFile/writeFile methods
UseBun.$template literal syntax instead ofexecafor shell command execution
Import .css files directly in TypeScript/JavaScript files; Bun's CSS bundler will handle bundling
Run server withbun --hot <file>to enable hot reloading during development
Files:
apps/cli/src/helpers/core/claude-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 ofwebpackoresbuildfor bundling TypeScript, JavaScript, and CSS files
Files:
apps/cli/src/helpers/core/claude-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/core/claude-setup.ts
🧠 Learnings (8)
📚 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 **/*.{ts,tsx} : Do not use explicit return types Applied to files:
apps/cli/src/helpers/core/claude-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/**/*.ts : Be strict with types, particularly around id's of documents; if a function takes in an id for a document in the 'users' table, take in Id<'users'> rather than string Applied to files:
apps/cli/src/helpers/core/claude-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 **/*.{ts,tsx} : Use TypeScript type aliases instead of interface declarations Applied to files:
apps/cli/src/helpers/core/claude-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/**/*.ts : Always add 'use node'; to the top of files containing actions that use Node.js built-in modules Applied to files:
apps/cli/src/helpers/core/claude-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 {package.json,bun.lockb} : Use `bun install` instead of `npm install`, `yarn install`, or `pnpm install` Applied to files:
apps/cli/src/helpers/core/claude-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 package.json : Use `bun run <script>` instead of `npm run <script>`, `yarn run <script>`, or `pnpm run <script>` Applied to files:
apps/cli/src/helpers/core/claude-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/core/claude-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/core/claude-setup.ts
🔇 Additional comments (2)
apps/cli/src/helpers/core/claude-setup.ts (2)
1221-1258: LGTM!The component template correctly uses a type alias for
UserCardProps, consistent with coding guidelines.
1371-1448: LGTM!Database migration commands properly use
getDlxCmd()for Prisma CLI calls, respecting the user's package manager choice.
| function getDlxCmd(packageManager: PackageManager) { | ||
| if (packageManager === "bun") return "bunx"; | ||
| if (packageManager === "pnpm") return "pnpm dlx"; | ||
| return "npx"; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing yarn case in getDlxCmd.
When packageManager is "yarn", this falls through to the default "npx", which is incorrect. Yarn users should get "yarn dlx".
function getDlxCmd(packageManager: PackageManager) { if (packageManager === "bun") return "bunx"; if (packageManager === "pnpm") return "pnpm dlx"; + if (packageManager === "yarn") return "yarn dlx"; return "npx"; }📝 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.
| function getDlxCmd(packageManager: PackageManager) { | |
| if (packageManager === "bun") return "bunx"; | |
| if (packageManager === "pnpm") return "pnpm dlx"; | |
| return "npx"; | |
| } | |
| function getDlxCmd(packageManager: PackageManager) { | |
| if (packageManager === "bun") return "bunx"; | |
| if (packageManager === "pnpm") return "pnpm dlx"; | |
| if (packageManager === "yarn") return "yarn dlx"; | |
| return "npx"; | |
| } |
🤖 Prompt for AI Agents
In apps/cli/src/helpers/core/claude-setup.ts around lines 21 to 25, getDlxCmd is missing a branch for packageManager === "yarn" so it falls through to "npx"; add an explicit case that returns "yarn dlx" when packageManager is "yarn", keeping existing bun and pnpm branches and the default return for other managers. | // Always include dev command | ||
| commands["dev.md"] = `--- | ||
| description: Start development environment | ||
| --- | ||
| | ||
| Start all applications in development mode: | ||
| | ||
| \`\`\`bash | ||
| ${runCmd} dev | ||
| \`\`\` | ||
| | ||
| This will start: | ||
| ${hasFrontend ? "- Web app (usually http://localhost:3001 or 5173)" : ""} | ||
| ${hasBackend && !isConvex ? "- API server (usually http://localhost:3000)" : ""} | ||
| ${isConvex ? "- Convex backend (connected to cloud)" : ""} | ||
| `; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Empty lines in dev.md output when conditions are false.
The conditional strings produce empty lines in the generated markdown when hasFrontend or hasBackend is false, resulting in awkward formatting.
Consider building the list dynamically:
+ const startItems = [ + hasFrontend ? "- Web app (usually http://localhost:3001 or 5173)" : "", + hasBackend && !isConvex ? "- API server (usually http://localhost:3000)" : "", + isConvex ? "- Convex backend (connected to cloud)" : "", + ].filter(Boolean).join("\n"); + commands["dev.md"] = `--- description: Start development environment --- Start all applications in development mode: \`\`\`bash ${runCmd} dev \`\`\` This will start: -${hasFrontend ? "- Web app (usually http://localhost:3001 or 5173)" : ""} -${hasBackend && !isConvex ? "- API server (usually http://localhost:3000)" : ""} -${isConvex ? "- Convex backend (connected to cloud)" : ""} +${startItems} `;🤖 Prompt for AI Agents
In apps/cli/src/helpers/core/claude-setup.ts around lines 916 to 931, the template for "dev.md" includes conditional string interpolations that leave empty blank lines when hasFrontend or hasBackend/isConvex are false; change this to build the bullet list dynamically: create an array, push the frontend/backend/convex list items only when their conditions are true, then join the array with "\n" (or "\n\n" if you need spacing) and interpolate that single string into the template so no empty lines are produced when a condition is false. | i have a ruler addon which is exactly same to this? |
Summary
This PR adds comprehensive Claude Code integration to Better T Stack CLI, generating optimized configuration files for scaffolded projects that help Claude Code understand and work better with Better T Stack projects.
What's included
CLAUDE.md (generated at project root)
.claude/commands/ (conditional based on stack)
dev.md- Start development environmentadd-feature.md- Scaffold new featureadd-route.md- Add new route/page (when frontend is selected)add-procedure.md- Add API procedure (when tRPC/oRPC is selected)add-component.md- Add UI componentdb-migrate.md- Database migrations (when database is selected)db-studio.md- Open database GUIlint.md- Run linting (when Biome/Oxlint is selected)convex-deploy.md- Deploy Convex (when Convex backend is selected)test.md- Run tests.claude/spec/
PROJECT_SPEC.md- Template for capturing project requirementsFEATURE_TEMPLATE.md- Template for new feature specificationsImplementation details
Created
apps/cli/src/helpers/core/claude-setup.tswith:setupClaudeIntegration()- Main entry pointgenerateClaudeMdContent()- Dynamic CLAUDE.md generationgetRelevantCommands()- Conditional command generationModified
apps/cli/src/helpers/core/create-project.tsto callsetupClaudeIntegration()aftercreateReadme()Testing
Screenshots/Examples
Example CLAUDE.md stack overview for a TanStack + Hono + tRPC project:
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.