create-win-project 1.4.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +54 -147
  3. package/checks/check-compatibility.js +37 -8
  4. package/checks/check-generated-project.js +27 -3
  5. package/checks/check-library.js +13 -3
  6. package/checks/check-package.js +15 -0
  7. package/checks/classify-changes.js +5 -15
  8. package/checks/run-compatibility-shard.js +21 -0
  9. package/docs/README.md +20 -0
  10. package/docs/capabilities.md +13 -0
  11. package/docs/compatibility.md +13 -0
  12. package/docs/generated-project.md +12 -0
  13. package/docs/getting-started.md +26 -0
  14. package/docs/{ARCHITECTURE.md → maintainers/architecture.md} +3 -3
  15. package/docs/maintainers/ci-strategy.md +46 -0
  16. package/docs/{CONTRIBUTING.md → maintainers/contributing.md} +7 -7
  17. package/docs/migration-v2.md +13 -0
  18. package/docs/production-contract.md +24 -0
  19. package/library/INDEX.md +1 -1
  20. package/library/compatibility-impact.json +14 -0
  21. package/library/development-tools/devops/makefile/commands.md +11 -0
  22. package/library/development-tools/devops/makefile/definition.json +2 -2
  23. package/library/development-tools/devops/makefile/docker.md +9 -0
  24. package/library/development-tools/devops/makefile/validation.md +5 -0
  25. package/library/features/sqlalchemy-alembic.md +17 -0
  26. package/library/optional-features/concerns/zod/errors.md +5 -0
  27. package/library/optional-features/concerns/zod/testing.md +5 -0
  28. package/library/optional-features/concerns/zod/validation.md +21 -0
  29. package/library/optional-features/styling/css-modules/definition.json +3 -2
  30. package/library/optional-features/styling/css-modules/responsive.md +5 -0
  31. package/library/optional-features/styling/css-modules/theme.md +7 -0
  32. package/library/stacks/expo/definition.json +2 -2
  33. package/library/stacks/fastapi/architecture.md +40 -0
  34. package/library/stacks/fastapi/definition.json +39 -0
  35. package/library/stacks/fastapi/runtime.md +25 -0
  36. package/library/stacks/fastapi/security.md +26 -0
  37. package/library/stacks/fastapi/structure.md +27 -0
  38. package/library/stacks/fastapi/testing.md +23 -0
  39. package/library/stacks/nextjs/definition.json +2 -2
  40. package/library/stacks/no-frontend/definition.json +1 -1
  41. package/library/stacks/react-vite/definition.json +2 -2
  42. package/library/tested-versions.json +42 -2
  43. package/library/universal/coding-rules/definition.json +3 -3
  44. package/library/universal/coding-rules/hygiene.md +9 -0
  45. package/library/universal/coding-rules/naming.md +17 -0
  46. package/library/universal/git-conventions/branches.md +5 -0
  47. package/library/universal/git-conventions/commits.md +7 -0
  48. package/library/universal/git-conventions/definition.json +4 -2
  49. package/library/universal/git-conventions/workflow.md +5 -0
  50. package/library/universal/typescript/boundaries.md +13 -0
  51. package/library/universal/typescript/definition.json +3 -3
  52. package/library/universal/typescript/errors.md +5 -0
  53. package/library/universal/typescript/patterns.md +7 -0
  54. package/package.json +4 -6
  55. package/src/cli/arguments.js +11 -0
  56. package/src/cli/main.js +18 -0
  57. package/src/cli/questions.js +16 -15
  58. package/src/cli/system-check.js +22 -2
  59. package/src/engine/load-library.js +3 -2
  60. package/src/engine/project-files.js +10 -1
  61. package/src/engine/project-guidance.js +2 -1
  62. package/src/engine/project-shapes.js +4 -4
  63. package/src/engine/render-templates.js +3 -0
  64. package/src/engine/tested-versions.js +21 -2
  65. package/src/engine/upgrade-report.js +20 -0
  66. package/src/stacks/available-stacks.js +2 -0
  67. package/src/stacks/backends/fastapi/ci.js +3 -0
  68. package/src/stacks/backends/fastapi/create-files.js +874 -0
  69. package/src/stacks/backends/fastapi/docker.js +75 -0
  70. package/src/stacks/backends/fastapi/environment.js +3 -0
  71. package/src/stacks/backends/fastapi/index.js +32 -0
  72. package/src/stacks/compose-files.js +19 -1
  73. package/src/stacks/create-project.js +128 -7
  74. package/src/stacks/frontends/nextjs/index.js +1 -1
  75. package/src/stacks/frontends/react-native/create-files.js +4 -1
  76. package/src/stacks/frontends/react-native/environment.js +1 -1
  77. package/src/stacks/frontends/react-native/index.js +1 -1
  78. package/src/stacks/frontends/react-vite/environment.js +1 -1
  79. package/src/stacks/frontends/react-vite/index.js +1 -1
  80. package/src/stacks/shared/capability-packs.js +31 -0
  81. package/src/stacks/shared/environment.js +12 -4
  82. package/src/stacks/shared/javascript-package.js +6 -0
  83. package/templates/ci/fastapi.yml +62 -0
  84. package/templates/docker/compose-prod/fastapi.yml +52 -0
  85. package/templates/docker/compose-prod/springboot.yml +19 -1
  86. package/templates/docker/dockerfile/fastapi.dev.dockerfile +9 -0
  87. package/templates/docker/dockerfile/fastapi.prod.dockerfile +11 -0
  88. package/templates/docker/dockerfile/nextjs.prod.dockerfile +1 -0
  89. package/templates/docker/dockerfile/springboot.prod.dockerfile +4 -1
  90. package/templates/docker/dockerfile/vite.prod.dockerfile +2 -1
  91. package/templates/makefile/fastapi.mk +97 -0
  92. package/library/development-tools/devops/makefile/makefile.md +0 -556
  93. package/library/optional-features/concerns/zod.md +0 -174
  94. package/library/optional-features/styling/css-modules/css-modules-extensions.md +0 -267
  95. package/library/universal/coding-rules/coding-rules.md +0 -281
  96. package/library/universal/git-conventions/git-conventions.md +0 -186
  97. package/library/universal/typescript/typescript.md +0 -272
  98. /package/docs/{CONTENT_MODEL.md → maintainers/content-model.md} +0 -0
  99. /package/docs/{DEPENDENCY_MAINTENANCE.md → maintainers/dependencies.md} +0 -0
@@ -1,174 +0,0 @@
1
- # Zod + React Hook Form — Validation
2
-
3
- > **When to use:** Any project with forms or untrusted input (API responses, URL params, env vars).
4
-
5
- ---
6
-
7
- # 1. Schema First, Always
8
-
9
- Write the Zod schema before writing the form component or service. The schema is the source of truth.
10
-
11
- ```ts
12
- // features/auth/schemas/login.schema.ts
13
- import { z } from 'zod'
14
-
15
- export const loginSchema = z.object({
16
- email: z.string().email('Invalid email address'),
17
- password: z.string().min(8, 'Password must be at least 8 characters'),
18
- })
19
-
20
- export type LoginInput = z.infer<typeof loginSchema>
21
- ```
22
-
23
- Never write a separate TypeScript type for form data — infer it from the schema.
24
-
25
- ---
26
-
27
- # 2. Schema Placement
28
-
29
- ```text
30
- Feature-specific form? → features/[name]/schemas/[name].schema.ts
31
- Shared across multiple features? → src/schemas/[name].schema.ts (web)
32
- schemas/[name].schema.ts (RN)
33
- API response shape? → features/[name]/types.ts (type only, no runtime parse needed)
34
- OR parse with z.safeParse() at the API boundary if strict
35
- ```
36
-
37
- ---
38
-
39
- # 3. Web Form (React Hook Form)
40
-
41
- ```tsx
42
- // features/auth/components/LoginForm.tsx
43
- 'use client' // Next.js only — omit for React Vite
44
- import { useForm } from 'react-hook-form'
45
- import { zodResolver } from '@hookform/resolvers/zod'
46
- import { loginSchema, type LoginInput } from '../schemas/login.schema'
47
-
48
- export function LoginForm({ onSubmit }: { onSubmit: (data: LoginInput) => void }) {
49
- const {
50
- register,
51
- handleSubmit,
52
- formState: { errors, isSubmitting },
53
- } = useForm<LoginInput>({ resolver: zodResolver(loginSchema) })
54
-
55
- return (
56
- <form onSubmit={handleSubmit(onSubmit)}>
57
- <input {...register('email')} placeholder="Email" />
58
- {errors.email && <span>{errors.email.message}</span>}
59
-
60
- <input type="password" {...register('password')} placeholder="Password" />
61
- {errors.password && <span>{errors.password.message}</span>}
62
-
63
- <button type="submit" disabled={isSubmitting}>
64
- {isSubmitting ? 'Submitting...' : 'Login'}
65
- </button>
66
- </form>
67
- )
68
- }
69
- ```
70
-
71
- ---
72
-
73
- # 4. React Native Form (React Hook Form + Controller)
74
-
75
- React Native has no native `<input>` — use `Controller` to bridge RHF with RN's `TextInput`.
76
-
77
- ```tsx
78
- // features/auth/components/LoginForm.tsx (React Native)
79
- import { Controller, useForm } from 'react-hook-form'
80
- import { zodResolver } from '@hookform/resolvers/zod'
81
- import { loginSchema, type LoginInput } from '../schemas/login.schema'
82
-
83
- export function LoginForm({ onSubmit }: { onSubmit: (data: LoginInput) => void }) {
84
- const {
85
- control,
86
- handleSubmit,
87
- formState: { errors, isSubmitting },
88
- } = useForm<LoginInput>({ resolver: zodResolver(loginSchema) })
89
-
90
- return (
91
- <View className="gap-4">
92
- <Controller
93
- control={control}
94
- name="email"
95
- render={({ field }) => (
96
- <TextInput
97
- value={field.value}
98
- onChangeText={field.onChange}
99
- placeholder="Email"
100
- keyboardType="email-address"
101
- autoCapitalize="none"
102
- />
103
- )}
104
- />
105
- {errors.email && <Text>{errors.email.message}</Text>}
106
-
107
- <Controller
108
- control={control}
109
- name="password"
110
- render={({ field }) => (
111
- <TextInput
112
- value={field.value}
113
- onChangeText={field.onChange}
114
- placeholder="Password"
115
- secureTextEntry
116
- />
117
- )}
118
- />
119
- {errors.password && <Text>{errors.password.message}</Text>}
120
-
121
- <PrimaryButton
122
- title={isSubmitting ? 'Submitting...' : 'Login'}
123
- onPress={handleSubmit(onSubmit)}
124
- disabled={isSubmitting}
125
- />
126
- </View>
127
- )
128
- }
129
- ```
130
-
131
- ---
132
-
133
- # 5. Validating API Responses
134
-
135
- Parse untrusted data at the boundary, not deep in hooks or components.
136
-
137
- ```ts
138
- // features/products/services/productService.ts
139
- import { z } from 'zod'
140
-
141
- const productSchema = z.object({
142
- id: z.string(),
143
- name: z.string(),
144
- price: z.number(),
145
- })
146
-
147
- export async function getProduct(id: string) {
148
- const raw = await apiClient.get(`/products/${id}`)
149
- return productSchema.parse(raw) // throws if shape is wrong
150
- }
151
- ```
152
-
153
- ---
154
-
155
- # 6. Rules
156
-
157
- - Schema first — always write the schema before the form.
158
- - Never write a separate TypeScript type for form input — use `z.infer<typeof schema>`.
159
- - Validate on the server too (or in the service) — never trust client-only validation.
160
- - Use `z.string().min(1)` not `z.string().nonempty()` — better cross-platform compatibility.
161
- - Route on `error.code` after submission, never on `error.message`.
162
-
163
- ---
164
-
165
- # 7. Agent Quick Reference
166
-
167
- ```text
168
- New form? → schema first in features/[name]/schemas/
169
- → useForm + zodResolver
170
- → web: register(); RN: Controller + TextInput
171
- New type for form data? → z.infer<typeof schema> instead
172
- Validate API response? → z.safeParse() or schema.parse() in service
173
- Shared schema? → src/schemas/ or schemas/ (not in features/)
174
- ```
@@ -1,267 +0,0 @@
1
- # Styling: CSS Modules — Extensions
2
-
3
- Extends your existing CSS Modules playbook with missing patterns.
4
-
5
- ---
6
-
7
- ## Dark Mode with CSS Variables
8
- ```css
9
- /* styles/tokens.css — single source of truth */
10
- :root {
11
- /* Colors */
12
- --color-background: #ffffff;
13
- --color-foreground: #0a0a0a;
14
- --color-primary: #1a1a2e;
15
- --color-primary-foreground: #f0f0f0;
16
- --color-muted: #f5f5f5;
17
- --color-muted-foreground: #737373;
18
- --color-border: #e5e5e5;
19
- --color-error: #dc2626;
20
- --color-success: #16a34a;
21
-
22
- /* Typography */
23
- --font-size-sm: 0.875rem;
24
- --font-size-base: 1rem;
25
- --font-size-lg: 1.125rem;
26
- --font-size-xl: 1.25rem;
27
- --font-size-2xl: 1.5rem;
28
-
29
- /* Spacing */
30
- --space-1: 0.25rem;
31
- --space-2: 0.5rem;
32
- --space-3: 0.75rem;
33
- --space-4: 1rem;
34
- --space-6: 1.5rem;
35
- --space-8: 2rem;
36
-
37
- /* Radius */
38
- --radius-sm: 0.25rem;
39
- --radius-md: 0.375rem;
40
- --radius-lg: 0.5rem;
41
-
42
- /* Transitions */
43
- --transition-fast: 100ms ease;
44
- --transition-base: 200ms ease;
45
- --transition-slow: 500ms ease;
46
- }
47
-
48
- [data-theme="dark"] {
49
- --color-background: #0a0a0a;
50
- --color-foreground: #f0f0f0;
51
- --color-primary: #e0e0f0;
52
- --color-primary-foreground: #1a1a2e;
53
- --color-muted: #1a1a1a;
54
- --color-muted-foreground: #a3a3a3;
55
- --color-border: #262626;
56
- }
57
- ```
58
-
59
- ```typescript
60
- // app/providers.tsx — apply theme to root
61
- import { useEffect, useState } from 'react'
62
-
63
- export function ThemeProvider({ children }: { children: React.ReactNode }) {
64
- const [theme, setTheme] = useState<'light' | 'dark'>('light')
65
-
66
- useEffect(() => {
67
- document.documentElement.setAttribute('data-theme', theme)
68
- }, [theme])
69
-
70
- return <>{children}</>
71
- }
72
- ```
73
-
74
- ---
75
-
76
- ## Component Pattern with Tokens
77
- ```css
78
- /* components/ui/Button/Button.module.css */
79
- .button {
80
- display: inline-flex;
81
- align-items: center;
82
- justify-content: center;
83
- padding: var(--space-2) var(--space-4);
84
- font-size: var(--font-size-base);
85
- border-radius: var(--radius-md);
86
- transition: background-color var(--transition-base);
87
- cursor: pointer;
88
- }
89
-
90
- .primary {
91
- background-color: var(--color-primary);
92
- color: var(--color-primary-foreground);
93
- }
94
-
95
- .primary:hover {
96
- opacity: 0.9;
97
- }
98
-
99
- .ghost {
100
- background-color: transparent;
101
- color: var(--color-foreground);
102
- border: 1px solid var(--color-border);
103
- }
104
-
105
- .ghost:hover {
106
- background-color: var(--color-muted);
107
- }
108
-
109
- .disabled {
110
- opacity: 0.5;
111
- cursor: not-allowed;
112
- pointer-events: none;
113
- }
114
- ```
115
-
116
- ---
117
-
118
- ## clsx for Class Composition
119
- ```typescript
120
- // Install: npm install clsx
121
- import clsx from 'clsx'
122
- import styles from './Button.module.css'
123
-
124
- export function Button({ variant = 'primary', disabled, className }: ButtonProps) {
125
- return (
126
- <button
127
- className={clsx(
128
- styles.button,
129
- styles[variant],
130
- disabled && styles.disabled,
131
- className // allow external class override
132
- )}
133
- disabled={disabled}
134
- >
135
- {children}
136
- </button>
137
- )
138
- }
139
- ```
140
-
141
- ---
142
-
143
- ## Responsive in Module File
144
- ```css
145
- /* UserCard.module.css */
146
- .card {
147
- display: flex;
148
- flex-direction: column;
149
- padding: var(--space-4);
150
- }
151
-
152
- /* Responsive stays in the component's own module file */
153
- @media (min-width: 768px) {
154
- .card {
155
- flex-direction: row;
156
- padding: var(--space-6);
157
- }
158
- }
159
-
160
- @media (min-width: 1024px) {
161
- .card {
162
- max-width: 800px;
163
- }
164
- }
165
- ```
166
-
167
- ---
168
-
169
- ## Animation
170
- ```css
171
- /* Define @keyframes in component module if component-specific */
172
- /* Define in global.css if reused across components */
173
-
174
- /* Component-specific */
175
- @keyframes fadeIn {
176
- from { opacity: 0; transform: translateY(-8px); }
177
- to { opacity: 1; transform: translateY(0); }
178
- }
179
-
180
- .dropdown {
181
- animation: fadeIn var(--transition-base);
182
- }
183
-
184
- /* Reused animation → globals.css */
185
- @keyframes spin {
186
- to { transform: rotate(360deg); }
187
- }
188
- ```
189
-
190
- ---
191
-
192
- ## Never Mix Tailwind + CSS Modules on Same Element
193
- ```typescript
194
- // ❌ mixing both on same element
195
- <div className={clsx(styles.card, 'flex items-center')} />
196
-
197
- // ✅ pick one strategy per project (defined at init)
198
- // CSS Modules project → all styling via modules
199
- <div className={styles.card} />
200
-
201
- // Tailwind project → all styling via Tailwind
202
- <div className="flex items-center gap-4 rounded-md" />
203
- ```
204
-
205
- ---
206
-
207
- ## Global Styles Structure
208
- ```
209
- src/styles/
210
- tokens.css → CSS variables (colors, space, type, radius)
211
- globals.css → reset, base elements, @import tokens
212
- animations.css → reusable @keyframes (optional)
213
- ```
214
-
215
- ```css
216
- /* globals.css */
217
- @import './tokens.css';
218
-
219
- *, *::before, *::after {
220
- box-sizing: border-box;
221
- margin: 0;
222
- padding: 0;
223
- }
224
-
225
- body {
226
- background-color: var(--color-background);
227
- color: var(--color-foreground);
228
- font-size: var(--font-size-base);
229
- line-height: 1.5;
230
- }
231
-
232
- a {
233
- color: inherit;
234
- text-decoration: none;
235
- }
236
- ```
237
-
238
- ---
239
-
240
- ## Agent Quick Reference (Extended)
241
- ```
242
- Dark mode?
243
- → [data-theme="dark"] on html element
244
- → CSS variables in tokens.css, overrides in [data-theme="dark"]
245
- → Toggle via data-theme attribute, not class swap
246
-
247
- Conditional classes?
248
- → clsx(styles.base, condition && styles.variant)
249
- → Never string template literals
250
-
251
- Responsive?
252
- → @media queries in component's own .module.css
253
- → Mobile styles first (no query), then @media (min-width: ...)
254
-
255
- Animation?
256
- → Component-specific: @keyframes in component module
257
- → Reused: @keyframes in globals.css or animations.css
258
- → Duration: always use --transition-* tokens
259
-
260
- New token needed?
261
- → Add to tokens.css under correct section
262
- → Never hardcode values in component modules
263
-
264
- External class override allowed?
265
- → Accept className prop, pass to clsx as last arg
266
- → Allows parent to extend without modifying component
267
- ```
@@ -1,281 +0,0 @@
1
- # Coding Rules (Universal)
2
-
3
- Applies to every project regardless of stack.
4
-
5
- ---
6
-
7
- ## Core Principle
8
- ```
9
- simple + explicit + consistent
10
- over
11
- complex + abstract + theoretically pure
12
- ```
13
-
14
- Complexity must earn abstraction. Start simple. Add layers only when justified.
15
-
16
- ---
17
-
18
- ## Naming
19
-
20
- ### Files
21
- ```
22
- React components: PascalCase UserCard.tsx
23
- Hooks: camelCase useUserData.ts
24
- Utilities: camelCase formatDate.ts
25
- Constants: camelCase apiEndpoints.ts
26
- Types: camelCase userTypes.ts
27
- CSS Modules: PascalCase UserCard.module.css
28
- Java classes: PascalCase UserService.java
29
- SQL migrations: snake_case V1__create_users_table.sql
30
- ```
31
-
32
- ### Variables and Functions
33
- ```
34
- Variables: camelCase userName, isLoading, hasError
35
- Functions: camelCase getUser(), createOrder(), formatDate()
36
- Constants: SCREAMING MAX_RETRIES, API_BASE_URL
37
- React components: PascalCase UserCard, LoginForm
38
- Types/Interfaces: PascalCase UserResponse, ApiError
39
- Enums: PascalCase UserRole, OrderStatus
40
- ```
41
-
42
- ### Name for what it does, not what it is
43
- ```
44
- ❌ getData(), handleThing(), doStuff(), temp, x
45
- ✅ getUserById(), handleLoginSubmit(), formatCurrency()
46
- ```
47
-
48
- ### Boolean naming
49
- ```
50
- ❌ user, loading, error
51
- ✅ isLoading, hasError, isAuthenticated, canEdit
52
- ```
53
-
54
- ---
55
-
56
- ## Functions
57
- - One responsibility per function
58
- - Max 3 parameters — if more, use an options object
59
- - Return early to avoid deep nesting
60
-
61
- ```typescript
62
- // ❌
63
- function processUser(user, role, permissions, sendEmail, notify) { ... }
64
-
65
- // ✅
66
- function processUser(user: User, options: ProcessUserOptions) { ... }
67
-
68
- // ❌ deep nesting
69
- function getUser(id) {
70
- if (id) {
71
- if (isValid(id)) {
72
- // logic
73
- }
74
- }
75
- }
76
-
77
- // ✅ early return
78
- function getUser(id) {
79
- if (!id) return null
80
- if (!isValid(id)) return null
81
- // logic
82
- }
83
- ```
84
-
85
- ---
86
-
87
- ## Imports
88
-
89
- ### Frontend
90
- - Always use absolute imports with `@/` prefix
91
- - Never use relative `../../` imports
92
- - Barrel exports (`index.ts`) for public API of a feature only
93
-
94
- ```typescript
95
- // ❌
96
- import { UserCard } from '../../../components/ui/UserCard'
97
-
98
- // ✅
99
- import { UserCard } from '@/components/ui/UserCard'
100
- ```
101
-
102
- ### Import Order (enforced by ESLint)
103
- ```
104
- 1. External libraries
105
- 2. Internal absolute (@/)
106
- 3. Types
107
- 4. Styles / assets
108
- ```
109
-
110
- ---
111
-
112
- ## Constants
113
- - No magic numbers or strings in logic
114
- - All constants in `src/constants/index.ts` or feature-level constants file
115
-
116
- ```typescript
117
- // ❌
118
- if (role === 'ADMIN') { ... }
119
- setTimeout(fn, 900000)
120
-
121
- // ✅
122
- import { ROLES, TOKEN_EXPIRY } from '@/constants'
123
- if (role === ROLES.ADMIN) { ... }
124
- setTimeout(fn, TOKEN_EXPIRY.ACCESS)
125
- ```
126
-
127
- ---
128
-
129
- ## Async / Error Handling
130
-
131
- ### Frontend
132
- - Always async/await — never `.then()` chains
133
- - Always handle errors at the hook level, not component level
134
- - Never swallow errors silently
135
-
136
- ```typescript
137
- // ❌
138
- fetchUser().then(data => setUser(data)).catch(e => console.log(e))
139
-
140
- // ✅
141
- try {
142
- const user = await fetchUser()
143
- setUser(user)
144
- } catch (error) {
145
- logger.error('fetchUser failed:', error)
146
- throw error
147
- }
148
- ```
149
-
150
- ### Backend
151
- - Always throw `AppException` — never raw `RuntimeException`
152
- - Never expose stack traces in API responses
153
- - Always catch at the `GlobalExceptionHandler` level
154
-
155
- ```java
156
- // ❌
157
- throw new RuntimeException("User not found");
158
-
159
- // ✅
160
- throw new AppException("USER_NOT_FOUND", HttpStatus.NOT_FOUND);
161
- ```
162
-
163
- ---
164
-
165
- ## Logging
166
- - NEVER use `console.log` directly
167
- - Always use `lib/logger.ts` on frontend
168
- - Always use SLF4J logger on backend
169
-
170
- ```typescript
171
- // ❌
172
- console.log('user:', user)
173
- console.error('error:', error)
174
-
175
- // ✅
176
- import { logger } from '@/lib/logger'
177
- logger.info('user fetched:', user)
178
- logger.error('fetch failed:', error)
179
- ```
180
-
181
- ```java
182
- // ❌
183
- System.out.println("user: " + user);
184
-
185
- // ✅
186
- private static final Logger log = LoggerFactory.getLogger(UserService.class);
187
- log.info("user fetched: {}", userId);
188
- log.error("fetch failed for userId: {}", userId, e);
189
- ```
190
-
191
- ---
192
-
193
- ## Comments
194
- - Comment WHY, not WHAT
195
- - Code should be readable enough to not need WHAT comments
196
- - Remove all debug/TODO comments before committing
197
-
198
- ```typescript
199
- // ❌ explains what (obvious from code)
200
- // increment counter
201
- count++
202
-
203
- // ✅ explains why (not obvious from code)
204
- // Retry once — server returns 503 on cold start for ~200ms
205
- await retry(fetchUser, { times: 1, delay: 300 })
206
- ```
207
-
208
- ---
209
-
210
- ## No Debug Code in Commits
211
- ```
212
- ❌ console.log
213
- ❌ debugger
214
- ❌ TODO comments
215
- ❌ hardcoded test values
216
- ❌ commented-out code blocks
217
- ```
218
-
219
- ESLint and Husky enforce this — build fails if present.
220
-
221
- ---
222
-
223
- ## One Thing Per File
224
- ```
225
- ❌ UserCardAndForm.tsx — two components in one file
226
- ❌ userUtils.ts — 30 unrelated utility functions
227
-
228
- ✅ UserCard.tsx
229
- ✅ UserForm.tsx
230
- ✅ formatDate.ts
231
- ✅ formatCurrency.ts
232
- ```
233
-
234
- Exception: small helper types or constants directly related to the file.
235
-
236
- ---
237
-
238
- ## Exports
239
-
240
- ### Frontend
241
- - Named exports everywhere except page/route components
242
- - Page components use default export (Next.js / React Router requirement)
243
-
244
- ```typescript
245
- // ❌ default export for reusable components
246
- export default function UserCard() { ... }
247
-
248
- // ✅ named export
249
- export function UserCard() { ... }
250
-
251
- // ✅ default export for pages only
252
- export default function UserPage() { ... }
253
- ```
254
-
255
- ### Backend
256
- - Spring-managed beans: `@Service`, `@Repository`, `@RestController` — Spring handles export
257
- - Utility classes: `public static` methods
258
-
259
- ---
260
-
261
- ## Agent Rules
262
- ```
263
- Before writing any code:
264
- 1. Check if the functionality already exists
265
- 2. Check which layer owns this responsibility
266
- 3. Choose the simplest correct implementation
267
- 4. Follow existing patterns in the codebase — don't introduce new ones
268
-
269
- Before creating a new file:
270
- 1. Check if an existing file should be extended instead
271
- 2. Follow the selected stack's structure playbook and current feature ownership
272
-
273
- Before adding a dependency:
274
- 1. Check if existing tools already solve it
275
- 2. Prefer what the playbook recommends over personal preference
276
-
277
- Naming:
278
- 1. Name things for what they do
279
- 2. Booleans start with is/has/can
280
- 3. No abbreviations unless universally understood (id, url, dto)
281
- ```