Skill for defining coding rules and conventions. Ensures consistent code style and specifies coding standards for AI collaboration. Triggers: convention, coding style, naming rules, 컨벤션, コンベンション, 编码风格
Defines coding conventions, naming rules, and architecture patterns for AI collaboration.
/plugin marketplace add popup-studio-ai/bkit-claude-code/plugin install bkit@bkit-marketplaceThis skill is limited to using the following tools:
Define code writing rules
Maintain consistent code style. Especially important when collaborating with AI - clarify what style AI should use when writing code.
Project Root/
├── CONVENTIONS.md # Full conventions
└── docs/01-plan/
├── naming.md # Naming rules
└── structure.md # Structure rules
| Level | Application Level |
|---|---|
| Starter | Basic (essential rules only) |
| Dynamic | Extended (including API, state management) |
| Enterprise | Extended (per-service rules) |
src/
├── components/ # Reusable components
├── features/ # Feature modules
├── hooks/ # Custom hooks
├── utils/ # Utilities
└── types/ # Type definitions
❌ Organizing env vars just before deployment
→ Missing variables, naming inconsistency, deployment delays
✅ Establish convention at design stage
→ Consistent naming, clear categorization, fast deployment
| Prefix | Purpose | Exposure Scope | Example |
|---|---|---|---|
NEXT_PUBLIC_ | Client-exposed | Browser | NEXT_PUBLIC_API_URL |
DB_ | Database | Server only | DB_HOST, DB_PASSWORD |
API_ | External API keys | Server only | API_STRIPE_SECRET |
AUTH_ | Authentication | Server only | AUTH_SECRET, AUTH_GOOGLE_ID |
SMTP_ | Email service | Server only | SMTP_HOST, SMTP_PASSWORD |
STORAGE_ | File storage | Server only | STORAGE_S3_BUCKET |
⚠️ Security Principles
- Never expose anything except NEXT_PUBLIC_* to client
- API keys and passwords must be server-only variables
- Never commit sensitive info in .env files
Project Root/
├── .env.example # Template (in Git, values empty)
├── .env.local # Local development (Git ignored)
├── .env.development # Development env defaults
├── .env.staging # Staging env defaults
├── .env.production # Production defaults (no sensitive info)
└── .env.test # Test environment
# .env.example - This file is included in Git
# Set actual values in .env.local
# ===== App Settings =====
NODE_ENV=development
NEXT_PUBLIC_APP_URL=http://localhost:3000
# ===== Database =====
DB_HOST=
DB_PORT=5432
DB_NAME=
DB_USER=
DB_PASSWORD=
# ===== Authentication =====
AUTH_SECRET= # openssl rand -base64 32
AUTH_GOOGLE_ID=
AUTH_GOOGLE_SECRET=
# ===== External Services =====
NEXT_PUBLIC_API_URL=
API_STRIPE_SECRET=
SMTP_HOST=
SMTP_USER=
SMTP_PASSWORD=
| Variable Type | .env.example | .env.local | CI/CD Secrets |
|---|---|---|---|
| App URL | Template | Local value | Per-env value |
| API endpoints | Template | Local/dev | Per-env value |
| DB password | Empty | Local value | ✅ Secrets |
| API keys | Empty | Test key | ✅ Secrets |
| JWT Secret | Empty | Local value | ✅ Secrets |
// lib/env.ts - Validate env vars at app startup
import { z } from 'zod';
const envSchema = z.object({
// Required
DATABASE_URL: z.string().url(),
AUTH_SECRET: z.string().min(32),
// Optional (with defaults)
NODE_ENV: z.enum(['development', 'staging', 'production']).default('development'),
// Client-exposed
NEXT_PUBLIC_APP_URL: z.string().url(),
});
// Validation and type inference
export const env = envSchema.parse(process.env);
// Type-safe usage
// env.DATABASE_URL ← autocomplete supported
Naming Consistency
File Structure
Security
Clean Architecture = Code resilient to change
❌ Developing without architecture
→ Spaghetti code, multiple file changes for each modification
✅ Define layers at design stage
→ Separation of concerns, easy testing, easy maintenance
src/
├── presentation/ # or app/, pages/
│ ├── components/ # UI components
│ ├── hooks/ # State management hooks
│ └── pages/ # Page components
│
├── application/ # or services/, features/
│ ├── use-cases/ # Business use cases
│ └── services/ # API service wrappers
│
├── domain/ # or types/, entities/
│ ├── entities/ # Domain entities
│ ├── types/ # Type definitions
│ └── constants/ # Domain constants
│
└── infrastructure/ # or lib/, api/
├── api/ # API clients
├── db/ # Database connections
└── external/ # External services
| Layer | Responsibility | Can Depend On | Cannot Depend On |
|---|---|---|---|
| Presentation | UI rendering, user events | Application, Domain | Infrastructure directly |
| Application | Business logic orchestration | Domain, Infrastructure | Presentation |
| Domain | Core business rules, types | Nothing (independent) | All external layers |
| Infrastructure | External system connections | Domain | Application, Presentation |
// ❌ Bad: Presentation directly calls Infrastructure
// components/UserList.tsx
import { apiClient } from '@/lib/api/client'; // Direct import forbidden!
export function UserList() {
const users = apiClient.get('/users'); // ❌
}
// ✅ Good: Presentation → Application → Infrastructure
// hooks/useUsers.ts
import { userService } from '@/services/user.service';
export function useUsers() {
return useQuery({
queryKey: ['users'],
queryFn: userService.getList, // ✅ Call through Service
});
}
// components/UserList.tsx
import { useUsers } from '@/hooks/useUsers';
export function UserList() {
const { data: users } = useUsers(); // ✅ Call through Hook
}
// ===== Allowed import directions =====
// In presentation/:
import { User } from '@/domain/types'; // ✅ Domain OK
import { useUsers } from '@/hooks/useUsers'; // ✅ Same layer OK
import { userService } from '@/services/user'; // ✅ Application OK
// In application/:
import { User } from '@/domain/types'; // ✅ Domain OK
import { apiClient } from '@/lib/api/client'; // ✅ Infrastructure OK
// In domain/:
// Minimize external imports (pure types/logic only)
// In infrastructure/:
import { User } from '@/domain/types'; // ✅ Domain OK
// ===== Forbidden imports =====
// In domain/:
import { apiClient } from '@/lib/api/client'; // ❌ Infrastructure forbidden
import { Button } from '@/components/ui/button'; // ❌ Presentation forbidden
// In infrastructure/:
import { useUsers } from '@/hooks/useUsers'; // ❌ Presentation forbidden
| Level | Architecture Application |
|---|---|
| Starter | Simple structure (components, lib) |
| Dynamic | 3-4 layer separation (recommended structure) |
| Enterprise | Strict layer separation + DI container |
src/
├── components/ # UI components
├── lib/ # Utilities, API
└── types/ # Type definitions
src/
├── components/ # Presentation
│ └── ui/
├── features/ # Feature modules (Application + Presentation)
│ ├── auth/
│ └── product/
├── hooks/ # Presentation (state management)
├── services/ # Application
├── types/ # Domain
└── lib/ # Infrastructure
└── api/
src/
├── presentation/
│ ├── components/
│ ├── hooks/
│ └── pages/
├── application/
│ ├── use-cases/
│ └── services/
├── domain/
│ ├── entities/
│ └── types/
└── infrastructure/
├── api/
└── db/
Conventions defined in this Phase are verified in later Phases:
| Definition (Phase 2) | Verification (Phase 8) |
|---|---|
| Naming rules | Naming consistency check |
| Folder structure | Structure consistency check |
| Environment variable convention | Env var naming check |
| Clean architecture principles | Dependency direction check |
See templates/pipeline/phase-2-convention.template.md
Phase 3: Mockup Development → Rules are set, now rapid prototyping
// ❌ Handles only specific case
function formatUserName(user: User) {
return `${user.firstName} ${user.lastName}`
}
// ✅ Generic
function formatFullName(firstName: string, lastName: string) {
return `${firstName} ${lastName}`
}
// Usage
formatFullName(user.firstName, user.lastName)
formatFullName(author.first, author.last)
// ❌ Tied to specific type
function calculateOrderTotal(order: Order) {
return order.items.reduce((sum, item) => sum + item.price, 0)
}
// ✅ Generalized with interface
interface HasPrice { price: number }
function calculateTotal<T extends HasPrice>(items: T[]) {
return items.reduce((sum, item) => sum + item.price, 0)
}
// Can be used in various places
calculateTotal(order.items)
calculateTotal(cart.products)
calculateTotal(invoice.lineItems)
// ❌ Hardcoded structure
function UserCard({ user }: { user: User }) {
return (
<div className="card">
<img src={user.avatar} />
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
)
}
// ✅ Composable
function Card({ children, className }: CardProps) {
return <div className={cn("card", className)}>{children}</div>
}
function Avatar({ src, alt }: AvatarProps) {
return <img src={src} alt={alt} className="avatar" />
}
// Use by combining
<Card>
<Avatar src={user.avatar} alt={user.name} />
<h3>{user.name}</h3>
<p>{user.email}</p>
</Card>
// ❌ Limited props
interface ButtonProps {
label: string
onClick: () => void
}
// ✅ Extend HTML attributes
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'default' | 'outline' | 'ghost'
size?: 'sm' | 'md' | 'lg'
}
// All button attributes available
<Button type="submit" disabled={isLoading}>
Save
</Button>
1. Same logic used 2+ times
2. Logic is complex enough to need a name
3. Logic that needs testing
4. Can be used in other files
1. Same UI pattern repeats
2. Has independent state
3. Is a reusable unit
4. JSX over 50 lines
// ❌ Listing conditionals
function getStatusColor(status: string) {
if (status === 'active') return 'green'
if (status === 'pending') return 'yellow'
if (status === 'error') return 'red'
return 'gray'
}
// ✅ Configuration object
const STATUS_CONFIG = {
active: { color: 'green', label: 'Active' },
pending: { color: 'yellow', label: 'Pending' },
error: { color: 'red', label: 'Error' },
} as const
function getStatusConfig(status: keyof typeof STATUS_CONFIG) {
return STATUS_CONFIG[status] ?? { color: 'gray', label: status }
}
// Adding new status = just add config
// ❌ Listing switch statements
function processPayment(method: string, amount: number) {
switch (method) {
case 'card':
// Card payment logic
break
case 'bank':
// Bank transfer logic
break
}
}
// ✅ Strategy pattern
interface PaymentStrategy {
process(amount: number): Promise<Result>
}
const paymentStrategies: Record<string, PaymentStrategy> = {
card: new CardPayment(),
bank: new BankTransfer(),
}
function processPayment(method: string, amount: number) {
const strategy = paymentStrategies[method]
if (!strategy) throw new Error(`Unknown method: ${method}`)
return strategy.process(amount)
}
// Adding new payment method = just add strategy
// Extensible system
interface Plugin {
name: string
init(): void
execute(data: unknown): unknown
}
class PluginManager {
private plugins: Plugin[] = []
register(plugin: Plugin) {
this.plugins.push(plugin)
}
executeAll(data: unknown) {
return this.plugins.reduce(
(result, plugin) => plugin.execute(result),
data
)
}
}
// New feature = add plugin
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.