@relipa/ai-flow-kit 0.0.6-beta.0 → 0.0.6
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.
- package/custom/rules/java/spring-boot-examples.md +329 -329
- package/custom/templates/spring-boot.md +224 -224
- package/package.json +1 -1
- package/scripts/doctor.js +89 -89
- package/scripts/hooks/session-start.js +244 -244
- package/scripts/task.js +384 -384
|
@@ -1,224 +1,224 @@
|
|
|
1
|
-
# Spring Boot AI System Prompt
|
|
2
|
-
|
|
3
|
-
You are an expert Java Spring Boot developer. Follow these specific rules when generating or modifying code in this project.
|
|
4
|
-
|
|
5
|
-
> **Code examples:** See `.rules/java/spring-boot-examples.md` — read it when generating code for a specific layer.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Project Stack
|
|
10
|
-
|
|
11
|
-
- **Java:** 17+
|
|
12
|
-
- **Spring Boot:** 3.x
|
|
13
|
-
- **Build Tool:** Maven (prefer) or Gradle
|
|
14
|
-
- **ORM:** Spring Data JPA + Hibernate
|
|
15
|
-
- **Database:** MySQL / PostgreSQL
|
|
16
|
-
- **Testing:** JUnit 5 + Mockito + AssertJ
|
|
17
|
-
- **Utilities:** Lombok, MapStruct
|
|
18
|
-
- **API Style:** RESTful JSON
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Architecture
|
|
23
|
-
|
|
24
|
-
Follow strict **Layered Architecture**:
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
Controller (HTTP layer)
|
|
28
|
-
↓
|
|
29
|
-
Service (Business logic)
|
|
30
|
-
↓
|
|
31
|
-
Repository (Data access - Spring Data JPA)
|
|
32
|
-
↓
|
|
33
|
-
Entity / Domain Model
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### Package Structure
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
com.company.project/
|
|
40
|
-
├── controller/ # \@RestController — HTTP endpoints only
|
|
41
|
-
├── service/
|
|
42
|
-
│ ├── impl/ # \@Service — business logic implementation
|
|
43
|
-
│ └── [Interface].java
|
|
44
|
-
├── repository/ # \@Repository — extends JpaRepository
|
|
45
|
-
├── entity/ # \@Entity — JPA entities
|
|
46
|
-
├── dto/
|
|
47
|
-
│ ├── request/ # Input DTOs (e.g. CreateUserRequest)
|
|
48
|
-
│ └── response/ # Output DTOs (e.g. UserResponse)
|
|
49
|
-
├── mapper/ # MapStruct mappers (Entity ↔ DTO)
|
|
50
|
-
├── exception/
|
|
51
|
-
│ ├── GlobalExceptionHandler.java # \@RestControllerAdvice
|
|
52
|
-
│ └── [CustomException].java
|
|
53
|
-
├── config/ # \@Configuration classes
|
|
54
|
-
└── util/ # Pure utility helpers (stateless)
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## Controller Rules
|
|
60
|
-
|
|
61
|
-
- Annotate with `\@RestController` + `\@RequestMapping`
|
|
62
|
-
- **Only** handle HTTP concerns: parse request, call service, return response
|
|
63
|
-
- Never put business logic in Controller
|
|
64
|
-
- Always use DTOs — never expose Entity directly
|
|
65
|
-
- Use `ResponseEntity<T>` for explicit HTTP status control
|
|
66
|
-
- Validate input with `\@Valid` + Bean Validation annotations
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## Service Rules
|
|
71
|
-
|
|
72
|
-
- Always define an **interface**, implement in the `impl/` package
|
|
73
|
-
- Annotate implementation with `\@Service`
|
|
74
|
-
- All business logic lives here
|
|
75
|
-
- Use `\@Transactional` at the method level (not class level)
|
|
76
|
-
- Throw specific custom exceptions, not generic `RuntimeException`
|
|
77
|
-
- Never return an Entity — always convert to DTO via Mapper
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
## Repository Rules
|
|
82
|
-
|
|
83
|
-
- Extend `JpaRepository<Entity, ID>`
|
|
84
|
-
- Use **method name queries** for simple queries
|
|
85
|
-
- Use `\@Query` (JPQL) for complex queries — avoid native SQL unless necessary
|
|
86
|
-
- Never add business logic here
|
|
87
|
-
- Use `\@EntityGraph` to solve N+1 problems
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
## Entity Rules
|
|
92
|
-
|
|
93
|
-
- Use Lombok: `\@Getter`, `\@Setter`, `\@NoArgsConstructor`, `\@AllArgsConstructor`, `\@Builder`
|
|
94
|
-
- Avoid `\@Data` on entities (causes issues with `equals/hashCode` + lazy loading)
|
|
95
|
-
- Always use `\@Table(name = "snake_case_table_name")`
|
|
96
|
-
- Use `\@Column(name = "snake_case_column_name")` explicitly
|
|
97
|
-
- For soft delete: add `deleted` boolean + `deletedAt` timestamp
|
|
98
|
-
- Extend `BaseEntity` for audit fields (`createdAt`, `updatedAt`)
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
## DTO Rules
|
|
103
|
-
|
|
104
|
-
- Use separate DTOs for **Request** and **Response** — never share
|
|
105
|
-
- Use Lombok: `\@Getter`, `\@Builder`, `\@AllArgsConstructor`, `\@NoArgsConstructor`
|
|
106
|
-
- Validate in the Request DTO with Bean Validation (`\@NotBlank`, `\@Email`, `\@NotNull`, `\@Size`)
|
|
107
|
-
- Never expose internal fields (password hash, audit timestamps) in the Response DTO
|
|
108
|
-
|
|
109
|
-
---
|
|
110
|
-
|
|
111
|
-
## MapStruct Mapper Rules
|
|
112
|
-
|
|
113
|
-
- Use `\@Mapper(componentModel = "spring")` — inject as a Spring bean
|
|
114
|
-
- Define explicit mappings with `\@Mapping` when field names differ
|
|
115
|
-
- Never do manual mapping (`new DTO(); dto.setField(entity.getField())`)
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
## Exception Handling
|
|
120
|
-
|
|
121
|
-
- Create custom exceptions extending `RuntimeException`
|
|
122
|
-
- Handle all exceptions in one `\@RestControllerAdvice` class
|
|
123
|
-
- Return a consistent error response format
|
|
124
|
-
- Never expose stack traces to the client
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
## Testing Rules
|
|
129
|
-
|
|
130
|
-
### Unit Tests (Service layer)
|
|
131
|
-
- Test class: `[ServiceImpl]Test.java`
|
|
132
|
-
- Mock all dependencies with `\@ExtendWith(MockitoExtension.class)` + `\@Mock`
|
|
133
|
-
- Test happy path + edge cases + exception scenarios
|
|
134
|
-
- Use AssertJ: `assertThat(result).isEqualTo(expected)`
|
|
135
|
-
|
|
136
|
-
### Integration Tests (Controller layer)
|
|
137
|
-
- Use `\@SpringBootTest` + `\@AutoConfigureMockMvc`
|
|
138
|
-
- Test full HTTP flow with `MockMvc`
|
|
139
|
-
- Use `\@Sql` or Testcontainers for database state
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
## Naming Conventions
|
|
144
|
-
|
|
145
|
-
| Element | Convention | Example |
|
|
146
|
-
|---------|-----------|---------|
|
|
147
|
-
| Class | PascalCase | `UserService`, `OrderController` |
|
|
148
|
-
| Method | camelCase | `findById`, `createOrder` |
|
|
149
|
-
| Variable | camelCase | `userResponse`, `orderId` |
|
|
150
|
-
| Constant | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` |
|
|
151
|
-
| Package | lowercase | `com.company.project.service` |
|
|
152
|
-
| DB Table | snake_case | `user_orders`, `product_items` |
|
|
153
|
-
| DB Column | snake_case | `created_at`, `user_id` |
|
|
154
|
-
| REST endpoint | kebab-case | `/api/v1/user-profiles` |
|
|
155
|
-
| Request DTO | `[Action][Resource]Request` | `CreateOrderRequest` |
|
|
156
|
-
| Response DTO | `[Resource]Response` | `OrderResponse` |
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
## API Design
|
|
161
|
-
|
|
162
|
-
- Version all APIs: `/api/v1/...`
|
|
163
|
-
- Use plural nouns for resources: `/users`, `/orders`
|
|
164
|
-
- HTTP methods: `GET` (read), `POST` (create), `PUT` (full update), `PATCH` (partial), `DELETE`
|
|
165
|
-
- Return `201 Created` for POST, `200 OK` for GET/PUT/PATCH, `204 No Content` for DELETE
|
|
166
|
-
- Use consistent pagination: `?page=0&size=20&sort=createdAt,desc`
|
|
167
|
-
|
|
168
|
-
```
|
|
169
|
-
GET /api/v1/users → 200 list
|
|
170
|
-
POST /api/v1/users → 201 created
|
|
171
|
-
GET /api/v1/users/{id} → 200 or 404
|
|
172
|
-
PUT /api/v1/users/{id} → 200 or 404
|
|
173
|
-
DELETE /api/v1/users/{id} → 204 or 404
|
|
174
|
-
GET /api/v1/users/{id}/orders → 200 nested resource
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
---
|
|
178
|
-
|
|
179
|
-
## Performance Rules
|
|
180
|
-
|
|
181
|
-
- Always use pagination — never return unbounded lists
|
|
182
|
-
- Avoid N+1: use `\@EntityGraph` or `JOIN FETCH` in JPQL
|
|
183
|
-
- Add database indexes on frequently queried columns
|
|
184
|
-
- Use `\@Transactional(readOnly = true)` on read-only service methods
|
|
185
|
-
- For heavy read operations, consider projection interfaces
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
## Security Rules
|
|
190
|
-
|
|
191
|
-
- Never log passwords, tokens, or sensitive PII
|
|
192
|
-
- Hash passwords with BCrypt: `passwordEncoder.encode(rawPassword)`
|
|
193
|
-
- Validate and sanitize all user inputs via Bean Validation
|
|
194
|
-
- Use `\@PreAuthorize` for method-level security
|
|
195
|
-
- Never return stack traces to API consumers
|
|
196
|
-
- Store secrets in environment variables / Vault — never in code
|
|
197
|
-
|
|
198
|
-
---
|
|
199
|
-
|
|
200
|
-
## Logging Rules
|
|
201
|
-
|
|
202
|
-
- Use SLF4J with Lombok `\@Slf4j`
|
|
203
|
-
- `log.info` — normal business events
|
|
204
|
-
- `log.warn` — recoverable issues (not found, validation fail)
|
|
205
|
-
- `log.error` — unexpected exceptions (always include `ex` as the second argument)
|
|
206
|
-
- Never log sensitive data (password, credit card, token)
|
|
207
|
-
|
|
208
|
-
---
|
|
209
|
-
|
|
210
|
-
## Common Anti-Patterns to Avoid
|
|
211
|
-
|
|
212
|
-
- ❌ `\@Autowired` field injection → use constructor injection (Lombok `\@RequiredArgsConstructor`)
|
|
213
|
-
- ❌ `\@Data` on JPA entities → use `\@Getter \@Setter` separately
|
|
214
|
-
- ❌ Returning `Entity` directly from Controller → always use DTO
|
|
215
|
-
- ❌ `SELECT *` or unbounded `findAll()` → always paginate
|
|
216
|
-
- ❌ Business logic in Controller → move to Service
|
|
217
|
-
- ❌ Catching and swallowing exceptions → handle properly or rethrow
|
|
218
|
-
- ❌ `new RuntimeException("something")` → create a specific custom exception
|
|
219
|
-
- ❌ Hardcoding config values → use `\@Value` or `\@ConfigurationProperties`
|
|
220
|
-
- ❌ `\@Transactional` on Controller → only on Service methods
|
|
221
|
-
|
|
222
|
-
---
|
|
223
|
-
|
|
224
|
-
When explaining changes, refer to the [Spring Boot Official Documentation](https://docs.spring.io/spring-boot/docs/current/reference/html/) and [Spring Data JPA](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/) conventions.
|
|
1
|
+
# Spring Boot AI System Prompt
|
|
2
|
+
|
|
3
|
+
You are an expert Java Spring Boot developer. Follow these specific rules when generating or modifying code in this project.
|
|
4
|
+
|
|
5
|
+
> **Code examples:** See `.rules/java/spring-boot-examples.md` — read it when generating code for a specific layer.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Project Stack
|
|
10
|
+
|
|
11
|
+
- **Java:** 17+
|
|
12
|
+
- **Spring Boot:** 3.x
|
|
13
|
+
- **Build Tool:** Maven (prefer) or Gradle
|
|
14
|
+
- **ORM:** Spring Data JPA + Hibernate
|
|
15
|
+
- **Database:** MySQL / PostgreSQL
|
|
16
|
+
- **Testing:** JUnit 5 + Mockito + AssertJ
|
|
17
|
+
- **Utilities:** Lombok, MapStruct
|
|
18
|
+
- **API Style:** RESTful JSON
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Architecture
|
|
23
|
+
|
|
24
|
+
Follow strict **Layered Architecture**:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
Controller (HTTP layer)
|
|
28
|
+
↓
|
|
29
|
+
Service (Business logic)
|
|
30
|
+
↓
|
|
31
|
+
Repository (Data access - Spring Data JPA)
|
|
32
|
+
↓
|
|
33
|
+
Entity / Domain Model
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Package Structure
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
com.company.project/
|
|
40
|
+
├── controller/ # \@RestController — HTTP endpoints only
|
|
41
|
+
├── service/
|
|
42
|
+
│ ├── impl/ # \@Service — business logic implementation
|
|
43
|
+
│ └── [Interface].java
|
|
44
|
+
├── repository/ # \@Repository — extends JpaRepository
|
|
45
|
+
├── entity/ # \@Entity — JPA entities
|
|
46
|
+
├── dto/
|
|
47
|
+
│ ├── request/ # Input DTOs (e.g. CreateUserRequest)
|
|
48
|
+
│ └── response/ # Output DTOs (e.g. UserResponse)
|
|
49
|
+
├── mapper/ # MapStruct mappers (Entity ↔ DTO)
|
|
50
|
+
├── exception/
|
|
51
|
+
│ ├── GlobalExceptionHandler.java # \@RestControllerAdvice
|
|
52
|
+
│ └── [CustomException].java
|
|
53
|
+
├── config/ # \@Configuration classes
|
|
54
|
+
└── util/ # Pure utility helpers (stateless)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Controller Rules
|
|
60
|
+
|
|
61
|
+
- Annotate with `\@RestController` + `\@RequestMapping`
|
|
62
|
+
- **Only** handle HTTP concerns: parse request, call service, return response
|
|
63
|
+
- Never put business logic in Controller
|
|
64
|
+
- Always use DTOs — never expose Entity directly
|
|
65
|
+
- Use `ResponseEntity<T>` for explicit HTTP status control
|
|
66
|
+
- Validate input with `\@Valid` + Bean Validation annotations
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Service Rules
|
|
71
|
+
|
|
72
|
+
- Always define an **interface**, implement in the `impl/` package
|
|
73
|
+
- Annotate implementation with `\@Service`
|
|
74
|
+
- All business logic lives here
|
|
75
|
+
- Use `\@Transactional` at the method level (not class level)
|
|
76
|
+
- Throw specific custom exceptions, not generic `RuntimeException`
|
|
77
|
+
- Never return an Entity — always convert to DTO via Mapper
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Repository Rules
|
|
82
|
+
|
|
83
|
+
- Extend `JpaRepository<Entity, ID>`
|
|
84
|
+
- Use **method name queries** for simple queries
|
|
85
|
+
- Use `\@Query` (JPQL) for complex queries — avoid native SQL unless necessary
|
|
86
|
+
- Never add business logic here
|
|
87
|
+
- Use `\@EntityGraph` to solve N+1 problems
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Entity Rules
|
|
92
|
+
|
|
93
|
+
- Use Lombok: `\@Getter`, `\@Setter`, `\@NoArgsConstructor`, `\@AllArgsConstructor`, `\@Builder`
|
|
94
|
+
- Avoid `\@Data` on entities (causes issues with `equals/hashCode` + lazy loading)
|
|
95
|
+
- Always use `\@Table(name = "snake_case_table_name")`
|
|
96
|
+
- Use `\@Column(name = "snake_case_column_name")` explicitly
|
|
97
|
+
- For soft delete: add `deleted` boolean + `deletedAt` timestamp
|
|
98
|
+
- Extend `BaseEntity` for audit fields (`createdAt`, `updatedAt`)
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## DTO Rules
|
|
103
|
+
|
|
104
|
+
- Use separate DTOs for **Request** and **Response** — never share
|
|
105
|
+
- Use Lombok: `\@Getter`, `\@Builder`, `\@AllArgsConstructor`, `\@NoArgsConstructor`
|
|
106
|
+
- Validate in the Request DTO with Bean Validation (`\@NotBlank`, `\@Email`, `\@NotNull`, `\@Size`)
|
|
107
|
+
- Never expose internal fields (password hash, audit timestamps) in the Response DTO
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## MapStruct Mapper Rules
|
|
112
|
+
|
|
113
|
+
- Use `\@Mapper(componentModel = "spring")` — inject as a Spring bean
|
|
114
|
+
- Define explicit mappings with `\@Mapping` when field names differ
|
|
115
|
+
- Never do manual mapping (`new DTO(); dto.setField(entity.getField())`)
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Exception Handling
|
|
120
|
+
|
|
121
|
+
- Create custom exceptions extending `RuntimeException`
|
|
122
|
+
- Handle all exceptions in one `\@RestControllerAdvice` class
|
|
123
|
+
- Return a consistent error response format
|
|
124
|
+
- Never expose stack traces to the client
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Testing Rules
|
|
129
|
+
|
|
130
|
+
### Unit Tests (Service layer)
|
|
131
|
+
- Test class: `[ServiceImpl]Test.java`
|
|
132
|
+
- Mock all dependencies with `\@ExtendWith(MockitoExtension.class)` + `\@Mock`
|
|
133
|
+
- Test happy path + edge cases + exception scenarios
|
|
134
|
+
- Use AssertJ: `assertThat(result).isEqualTo(expected)`
|
|
135
|
+
|
|
136
|
+
### Integration Tests (Controller layer)
|
|
137
|
+
- Use `\@SpringBootTest` + `\@AutoConfigureMockMvc`
|
|
138
|
+
- Test full HTTP flow with `MockMvc`
|
|
139
|
+
- Use `\@Sql` or Testcontainers for database state
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Naming Conventions
|
|
144
|
+
|
|
145
|
+
| Element | Convention | Example |
|
|
146
|
+
|---------|-----------|---------|
|
|
147
|
+
| Class | PascalCase | `UserService`, `OrderController` |
|
|
148
|
+
| Method | camelCase | `findById`, `createOrder` |
|
|
149
|
+
| Variable | camelCase | `userResponse`, `orderId` |
|
|
150
|
+
| Constant | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` |
|
|
151
|
+
| Package | lowercase | `com.company.project.service` |
|
|
152
|
+
| DB Table | snake_case | `user_orders`, `product_items` |
|
|
153
|
+
| DB Column | snake_case | `created_at`, `user_id` |
|
|
154
|
+
| REST endpoint | kebab-case | `/api/v1/user-profiles` |
|
|
155
|
+
| Request DTO | `[Action][Resource]Request` | `CreateOrderRequest` |
|
|
156
|
+
| Response DTO | `[Resource]Response` | `OrderResponse` |
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## API Design
|
|
161
|
+
|
|
162
|
+
- Version all APIs: `/api/v1/...`
|
|
163
|
+
- Use plural nouns for resources: `/users`, `/orders`
|
|
164
|
+
- HTTP methods: `GET` (read), `POST` (create), `PUT` (full update), `PATCH` (partial), `DELETE`
|
|
165
|
+
- Return `201 Created` for POST, `200 OK` for GET/PUT/PATCH, `204 No Content` for DELETE
|
|
166
|
+
- Use consistent pagination: `?page=0&size=20&sort=createdAt,desc`
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
GET /api/v1/users → 200 list
|
|
170
|
+
POST /api/v1/users → 201 created
|
|
171
|
+
GET /api/v1/users/{id} → 200 or 404
|
|
172
|
+
PUT /api/v1/users/{id} → 200 or 404
|
|
173
|
+
DELETE /api/v1/users/{id} → 204 or 404
|
|
174
|
+
GET /api/v1/users/{id}/orders → 200 nested resource
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Performance Rules
|
|
180
|
+
|
|
181
|
+
- Always use pagination — never return unbounded lists
|
|
182
|
+
- Avoid N+1: use `\@EntityGraph` or `JOIN FETCH` in JPQL
|
|
183
|
+
- Add database indexes on frequently queried columns
|
|
184
|
+
- Use `\@Transactional(readOnly = true)` on read-only service methods
|
|
185
|
+
- For heavy read operations, consider projection interfaces
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Security Rules
|
|
190
|
+
|
|
191
|
+
- Never log passwords, tokens, or sensitive PII
|
|
192
|
+
- Hash passwords with BCrypt: `passwordEncoder.encode(rawPassword)`
|
|
193
|
+
- Validate and sanitize all user inputs via Bean Validation
|
|
194
|
+
- Use `\@PreAuthorize` for method-level security
|
|
195
|
+
- Never return stack traces to API consumers
|
|
196
|
+
- Store secrets in environment variables / Vault — never in code
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Logging Rules
|
|
201
|
+
|
|
202
|
+
- Use SLF4J with Lombok `\@Slf4j`
|
|
203
|
+
- `log.info` — normal business events
|
|
204
|
+
- `log.warn` — recoverable issues (not found, validation fail)
|
|
205
|
+
- `log.error` — unexpected exceptions (always include `ex` as the second argument)
|
|
206
|
+
- Never log sensitive data (password, credit card, token)
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Common Anti-Patterns to Avoid
|
|
211
|
+
|
|
212
|
+
- ❌ `\@Autowired` field injection → use constructor injection (Lombok `\@RequiredArgsConstructor`)
|
|
213
|
+
- ❌ `\@Data` on JPA entities → use `\@Getter \@Setter` separately
|
|
214
|
+
- ❌ Returning `Entity` directly from Controller → always use DTO
|
|
215
|
+
- ❌ `SELECT *` or unbounded `findAll()` → always paginate
|
|
216
|
+
- ❌ Business logic in Controller → move to Service
|
|
217
|
+
- ❌ Catching and swallowing exceptions → handle properly or rethrow
|
|
218
|
+
- ❌ `new RuntimeException("something")` → create a specific custom exception
|
|
219
|
+
- ❌ Hardcoding config values → use `\@Value` or `\@ConfigurationProperties`
|
|
220
|
+
- ❌ `\@Transactional` on Controller → only on Service methods
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
When explaining changes, refer to the [Spring Boot Official Documentation](https://docs.spring.io/spring-boot/docs/current/reference/html/) and [Spring Data JPA](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/) conventions.
|
package/package.json
CHANGED
package/scripts/doctor.js
CHANGED
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const chalk = require('chalk');
|
|
4
|
-
const { detectRtk, isRtkHookConfigured } = require('./init');
|
|
5
|
-
|
|
6
|
-
module.exports = async function doctor() {
|
|
7
|
-
const projectDir = process.cwd();
|
|
8
|
-
const errors = [];
|
|
9
|
-
const warnings = [];
|
|
10
|
-
|
|
11
|
-
console.log(chalk.blue('Running health check for AI Flow Kit...\n'));
|
|
12
|
-
|
|
13
|
-
// ── Core setup ─────────────────────────────────────────────
|
|
14
|
-
if (!(await fs.pathExists(path.join(projectDir, '.claude', 'skills')))) {
|
|
15
|
-
errors.push('Missing `.claude/skills` directory. Did you run `aiflow init`?');
|
|
16
|
-
} else {
|
|
17
|
-
console.log(chalk.green('✓ .claude/skills exists'));
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (!(await fs.pathExists(path.join(projectDir, '.rules')))) {
|
|
21
|
-
errors.push('Missing `.rules` directory.');
|
|
22
|
-
} else {
|
|
23
|
-
console.log(chalk.green('✓ .rules exists'));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (!(await fs.pathExists(path.join(projectDir, 'CLAUDE.md')))) {
|
|
27
|
-
errors.push('Missing CLAUDE.md. AI context prompt is not set up.');
|
|
28
|
-
} else {
|
|
29
|
-
console.log(chalk.green('✓ CLAUDE.md exists'));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// ── Version tracking ───────────────────────────────────────
|
|
33
|
-
const stateFile = path.join(projectDir, '.aiflow', 'state.json');
|
|
34
|
-
if (!(await fs.pathExists(stateFile))) {
|
|
35
|
-
errors.push('Missing `.aiflow/state.json`. Version tracking is broken.');
|
|
36
|
-
} else {
|
|
37
|
-
const state = await fs.readJson(stateFile);
|
|
38
|
-
console.log(chalk.green(`✓ Version tracking active (v${state.current_version})`));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// ── SessionStart hook ──────────────────────────────────────
|
|
42
|
-
const settingsPath = path.join(projectDir, '.claude', 'settings.json');
|
|
43
|
-
if (await fs.pathExists(settingsPath)) {
|
|
44
|
-
const settings = await fs.readJson(settingsPath).catch(() => ({}));
|
|
45
|
-
const hookList = settings?.hooks?.SessionStart || [];
|
|
46
|
-
const hasSessionHook = hookList.some(h =>
|
|
47
|
-
(h.hooks || []).some(e => typeof e.command === 'string' && e.command.includes('session-start'))
|
|
48
|
-
);
|
|
49
|
-
if (hasSessionHook) {
|
|
50
|
-
console.log(chalk.green('✓ SessionStart hook configured'));
|
|
51
|
-
} else {
|
|
52
|
-
warnings.push('SessionStart hook not found in .claude/settings.json — run `aiflow init` to set it up.');
|
|
53
|
-
}
|
|
54
|
-
} else {
|
|
55
|
-
warnings.push('Missing .claude/settings.json — run `aiflow init`.');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ── RTK token compression ──────────────────────────────────
|
|
59
|
-
console.log('');
|
|
60
|
-
const rtk = await detectRtk();
|
|
61
|
-
if (rtk.installed) {
|
|
62
|
-
const hookOk = await isRtkHookConfigured(projectDir);
|
|
63
|
-
if (hookOk) {
|
|
64
|
-
console.log(chalk.green(`✓ RTK installed (${rtk.version}) — token compression active`));
|
|
65
|
-
} else {
|
|
66
|
-
console.log(chalk.green(`✓ RTK installed (${rtk.version})`));
|
|
67
|
-
warnings.push('RTK is installed but hook not configured. Run `aiflow init` to enable token compression.');
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
console.log(chalk.gray('○ RTK not installed (optional) — token compression inactive'));
|
|
71
|
-
console.log(chalk.gray(' Install: cargo install rtk | More: https://github.com/rtk-ai/rtk'));
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// ── Summary ────────────────────────────────────────────────
|
|
75
|
-
console.log('');
|
|
76
|
-
if (warnings.length > 0) {
|
|
77
|
-
console.log(chalk.yellow('Warnings:'));
|
|
78
|
-
warnings.forEach(w => console.log(chalk.yellow(` ⚠ ${w}`)));
|
|
79
|
-
console.log('');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (errors.length > 0) {
|
|
83
|
-
console.log(chalk.red('Issues found:'));
|
|
84
|
-
errors.forEach(e => console.log(chalk.red(` ✗ ${e}`)));
|
|
85
|
-
console.log(chalk.yellow('\nTry running `aiflow init` or `aiflow update` to fix these files.'));
|
|
86
|
-
} else {
|
|
87
|
-
console.log(chalk.green('✨ Everything looks healthy! You are ready to fly.'));
|
|
88
|
-
}
|
|
89
|
-
};
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { detectRtk, isRtkHookConfigured } = require('./init');
|
|
5
|
+
|
|
6
|
+
module.exports = async function doctor() {
|
|
7
|
+
const projectDir = process.cwd();
|
|
8
|
+
const errors = [];
|
|
9
|
+
const warnings = [];
|
|
10
|
+
|
|
11
|
+
console.log(chalk.blue('Running health check for AI Flow Kit...\n'));
|
|
12
|
+
|
|
13
|
+
// ── Core setup ─────────────────────────────────────────────
|
|
14
|
+
if (!(await fs.pathExists(path.join(projectDir, '.claude', 'skills')))) {
|
|
15
|
+
errors.push('Missing `.claude/skills` directory. Did you run `aiflow init`?');
|
|
16
|
+
} else {
|
|
17
|
+
console.log(chalk.green('✓ .claude/skills exists'));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!(await fs.pathExists(path.join(projectDir, '.rules')))) {
|
|
21
|
+
errors.push('Missing `.rules` directory.');
|
|
22
|
+
} else {
|
|
23
|
+
console.log(chalk.green('✓ .rules exists'));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!(await fs.pathExists(path.join(projectDir, 'CLAUDE.md')))) {
|
|
27
|
+
errors.push('Missing CLAUDE.md. AI context prompt is not set up.');
|
|
28
|
+
} else {
|
|
29
|
+
console.log(chalk.green('✓ CLAUDE.md exists'));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ── Version tracking ───────────────────────────────────────
|
|
33
|
+
const stateFile = path.join(projectDir, '.aiflow', 'state.json');
|
|
34
|
+
if (!(await fs.pathExists(stateFile))) {
|
|
35
|
+
errors.push('Missing `.aiflow/state.json`. Version tracking is broken.');
|
|
36
|
+
} else {
|
|
37
|
+
const state = await fs.readJson(stateFile);
|
|
38
|
+
console.log(chalk.green(`✓ Version tracking active (v${state.current_version})`));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── SessionStart hook ──────────────────────────────────────
|
|
42
|
+
const settingsPath = path.join(projectDir, '.claude', 'settings.json');
|
|
43
|
+
if (await fs.pathExists(settingsPath)) {
|
|
44
|
+
const settings = await fs.readJson(settingsPath).catch(() => ({}));
|
|
45
|
+
const hookList = settings?.hooks?.SessionStart || [];
|
|
46
|
+
const hasSessionHook = hookList.some(h =>
|
|
47
|
+
(h.hooks || []).some(e => typeof e.command === 'string' && e.command.includes('session-start'))
|
|
48
|
+
);
|
|
49
|
+
if (hasSessionHook) {
|
|
50
|
+
console.log(chalk.green('✓ SessionStart hook configured'));
|
|
51
|
+
} else {
|
|
52
|
+
warnings.push('SessionStart hook not found in .claude/settings.json — run `aiflow init` to set it up.');
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
warnings.push('Missing .claude/settings.json — run `aiflow init`.');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── RTK token compression ──────────────────────────────────
|
|
59
|
+
console.log('');
|
|
60
|
+
const rtk = await detectRtk();
|
|
61
|
+
if (rtk.installed) {
|
|
62
|
+
const hookOk = await isRtkHookConfigured(projectDir);
|
|
63
|
+
if (hookOk) {
|
|
64
|
+
console.log(chalk.green(`✓ RTK installed (${rtk.version}) — token compression active`));
|
|
65
|
+
} else {
|
|
66
|
+
console.log(chalk.green(`✓ RTK installed (${rtk.version})`));
|
|
67
|
+
warnings.push('RTK is installed but hook not configured. Run `aiflow init` to enable token compression.');
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
console.log(chalk.gray('○ RTK not installed (optional) — token compression inactive'));
|
|
71
|
+
console.log(chalk.gray(' Install: cargo install rtk | More: https://github.com/rtk-ai/rtk'));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── Summary ────────────────────────────────────────────────
|
|
75
|
+
console.log('');
|
|
76
|
+
if (warnings.length > 0) {
|
|
77
|
+
console.log(chalk.yellow('Warnings:'));
|
|
78
|
+
warnings.forEach(w => console.log(chalk.yellow(` ⚠ ${w}`)));
|
|
79
|
+
console.log('');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (errors.length > 0) {
|
|
83
|
+
console.log(chalk.red('Issues found:'));
|
|
84
|
+
errors.forEach(e => console.log(chalk.red(` ✗ ${e}`)));
|
|
85
|
+
console.log(chalk.yellow('\nTry running `aiflow init` or `aiflow update` to fix these files.'));
|
|
86
|
+
} else {
|
|
87
|
+
console.log(chalk.green('✨ Everything looks healthy! You are ready to fly.'));
|
|
88
|
+
}
|
|
89
|
+
};
|