opencode-andev-rules 1.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.
- package/README.md +56 -0
- package/RULES.md +253 -0
- package/package.json +25 -0
- package/plugin.js +96 -0
- package/skills/improve-codenase-systems/HTML-REPORT.md +123 -0
- package/skills/improve-codenase-systems/SKILL.md +429 -0
- package/skills/improve-codenase-systems/agents/openai.yaml +5 -0
- package/skills/improve-codenase-systems/meta.json +8 -0
- package/skills/registry.json +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# @thaiannguyen-05/opencode-rules
|
|
2
|
+
|
|
3
|
+
Global coding rules for TypeScript & NestJS. Plugin for opencode.
|
|
4
|
+
|
|
5
|
+
## Cài Plugin
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
opencode plugin @thaiannguyen-05/opencode-rules
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
hoặc:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cd ~/.config/opencode
|
|
15
|
+
npm install @thaiannguyen-05/opencode-rules
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Thêm vào `opencode.json`:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"plugins": ["@thaiannguyen-05/opencode-rules"]
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Cài Skill
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx @thaiannguyen-05/skills-cli add improve-codenase-systems
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
hoặc:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cp -r skills/improve-codenase-systems ~/.agents/skills/
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Sử dụng
|
|
39
|
+
|
|
40
|
+
### Rules
|
|
41
|
+
|
|
42
|
+
Nói "follow the rule of andev" trong prompt.
|
|
43
|
+
|
|
44
|
+
### Skill
|
|
45
|
+
|
|
46
|
+
Nói "scan codebase" hoặc "improve codebase" trong prompt.
|
|
47
|
+
|
|
48
|
+
## Rules
|
|
49
|
+
|
|
50
|
+
Xem `RULES.md` — covers TypeScript, NestJS, validation, error handling, database, security, API design, testing, and code style.
|
|
51
|
+
|
|
52
|
+
## Skills
|
|
53
|
+
|
|
54
|
+
| Skill | Description |
|
|
55
|
+
|-------|-------------|
|
|
56
|
+
| `improve-codenase-systems` | Scan codebase for architecture issues |
|
package/RULES.md
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Rules for TypeScript & NestJS
|
|
2
|
+
|
|
3
|
+
## TypeScript
|
|
4
|
+
|
|
5
|
+
- Use `strict: true` in `tsconfig.json`
|
|
6
|
+
- No `any` type — use `unknown` and narrow with type guards
|
|
7
|
+
- Use `interface` for object shapes, `type` for unions/intersections
|
|
8
|
+
- Prefer `readonly` for immutable data
|
|
9
|
+
- Avoid type assertions (`as`) — use type guards or `satisfies` instead
|
|
10
|
+
- Use `!` non-null assertion only when value is guaranteed (add comment why)
|
|
11
|
+
- Export types from barrel files (`index.ts`)
|
|
12
|
+
|
|
13
|
+
## NestJS
|
|
14
|
+
|
|
15
|
+
- Use CLI to generate: `nest g module`, `nest g controller`, `nest g service`
|
|
16
|
+
- One module per feature (e.g., `users/`, `auth/`, `orders/`)
|
|
17
|
+
- Controllers handle HTTP — no business logic in controllers
|
|
18
|
+
- Services handle business logic — no HTTP concerns in services
|
|
19
|
+
- Use `@nestjs/swagger` decorators on DTOs and controllers
|
|
20
|
+
- Inject dependencies via constructor — never instantiate manually
|
|
21
|
+
- Use `@InjectRepository()` for TypeORM/Prisma repositories
|
|
22
|
+
- Use guards for auth (`@UseGuards`), interceptors for logging/transformation
|
|
23
|
+
- Use `ConfigModule` for environment variables — no `process.env` in code
|
|
24
|
+
- Use `@nestjs/schedule` for cron jobs — no `setInterval`
|
|
25
|
+
|
|
26
|
+
## Types
|
|
27
|
+
|
|
28
|
+
- Every module must have a `types.ts` file for module-specific types
|
|
29
|
+
- Export all types from `types.ts` — import from there in other files
|
|
30
|
+
- Use `type.ts` for: enums, interfaces, type aliases, return types
|
|
31
|
+
- Do NOT put types in `dto/` — DTOs are for validation, types are for contracts
|
|
32
|
+
- Import pattern: `import { UserRole } from './types'`
|
|
33
|
+
- Put constants in `types.ts`: event names, URLs, status codes, config values
|
|
34
|
+
- Use `enum` for constants — no hardcoded strings in business logic
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// types.ts
|
|
38
|
+
export enum EventName {
|
|
39
|
+
CREATED = 'user.created',
|
|
40
|
+
UPDATED = 'user.updated',
|
|
41
|
+
DELETED = 'user.deleted',
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export enum ApiUrl {
|
|
45
|
+
BASE = '/api/v1',
|
|
46
|
+
USERS = '/api/v1/users',
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface User { id: string; name: string; }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Validation
|
|
53
|
+
|
|
54
|
+
- Handle ALL validation before controller and service
|
|
55
|
+
- Use global `ValidationPipe` in `main.ts` — reject invalid requests early
|
|
56
|
+
- Use `class-validator` decorators on DTOs
|
|
57
|
+
- Use `class-transformer` with `ValidationPipe` globally
|
|
58
|
+
- Validate nested objects with `@ValidateNested()`
|
|
59
|
+
- Use `@IsString()`, `@IsNumber()`, `@IsEmail()`, `@IsOptional()` etc.
|
|
60
|
+
- Controllers and services receive only valid data — no validation logic inside
|
|
61
|
+
- Use `WhitelistStrategy` + `ForbidNonWhitelisted` to reject unknown fields
|
|
62
|
+
- Use `TransformPipe` to auto-transform payloads to DTO instances
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// main.ts
|
|
66
|
+
app.useGlobalPipes(
|
|
67
|
+
new ValidationPipe({
|
|
68
|
+
whitelist: true,
|
|
69
|
+
forbidNonWhitelisted: true,
|
|
70
|
+
transform: true,
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Error Handling
|
|
76
|
+
|
|
77
|
+
- Handle errors with exceptions — use custom `AppException` classes
|
|
78
|
+
- Create `src/error/` directory with exception classes and global filter
|
|
79
|
+
- `errorCode` = uppercase snake_case (e.g., `RESOURCE_NOT_FOUND`)
|
|
80
|
+
- Always return consistent JSON: `{ statusCode, timestamp, path, message, errorCode?, details? }`
|
|
81
|
+
- Log unhandled (non-HttpException) errors with logger
|
|
82
|
+
- Create helper files for domain-specific error scenarios
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// app.exception.ts
|
|
86
|
+
export class AppException extends HttpException {
|
|
87
|
+
constructor(
|
|
88
|
+
message: string,
|
|
89
|
+
statusCode: HttpStatus,
|
|
90
|
+
public readonly errorCode?: string,
|
|
91
|
+
public readonly details?: unknown,
|
|
92
|
+
) {
|
|
93
|
+
super({ message, errorCode, details, timestamp: new Date().toISOString() }, statusCode);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export class ResourceNotFoundException extends AppException {
|
|
98
|
+
constructor(resourceType: string, identifier: string | number) {
|
|
99
|
+
super(`${resourceType} with identifier ${identifier} not found`, HttpStatus.NOT_FOUND, 'RESOURCE_NOT_FOUND');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// main.ts
|
|
106
|
+
app.useGlobalFilters(new GlobalExceptionFilter(logger));
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Database
|
|
110
|
+
|
|
111
|
+
- Use Prisma or TypeORM — pick one, don't mix
|
|
112
|
+
- Keep migrations in `prisma/migrations` or `src/migrations`
|
|
113
|
+
- Use repositories or Prisma client for data access
|
|
114
|
+
- Never write raw SQL unless performance-critical (add comment why)
|
|
115
|
+
- Use `select` when only a subset of fields is required — don't over-fetch
|
|
116
|
+
- Avoid N+1 queries — use `include` or joins, not loops
|
|
117
|
+
- Run independent queries in parallel — don't `await` sequentially
|
|
118
|
+
- Use `findFirst` only when `findUnique` is possible
|
|
119
|
+
- Paginate `findMany` — never return unbounded results
|
|
120
|
+
- Use cursor pagination for large datasets, offset for small
|
|
121
|
+
- Always `orderBy` deterministic fields for pagination
|
|
122
|
+
- Handle Prisma errors — never leak raw Prisma errors to API responses
|
|
123
|
+
- Use transactions for multi-step operations — no partial writes
|
|
124
|
+
- Keep transaction scope small — no external calls inside transactions
|
|
125
|
+
- Race conditions on read-modify-write — use transactions or locks
|
|
126
|
+
- Add unique constraints for data that must be unique
|
|
127
|
+
- Soft-delete: always filter `deletedAt` in queries
|
|
128
|
+
- Avoid `include` for large relation collections — paginate or select
|
|
129
|
+
|
|
130
|
+
## Testing
|
|
131
|
+
|
|
132
|
+
- Use Jest (built into NestJS)
|
|
133
|
+
- Unit tests: `*.spec.ts` next to source files
|
|
134
|
+
- E2E tests: `test/*.e2e-spec.ts`
|
|
135
|
+
- Mock dependencies with `@nestjs/testing` `Test.createTestingModule()`
|
|
136
|
+
- Test controllers and services separately
|
|
137
|
+
- Use `supertest` for HTTP assertions in e2e
|
|
138
|
+
|
|
139
|
+
## Async & Concurrency
|
|
140
|
+
|
|
141
|
+
- Run independent async operations in parallel — don't `await` sequentially
|
|
142
|
+
- Always handle promise rejections — no fire-and-forget without error handling
|
|
143
|
+
- Add timeout for external service calls
|
|
144
|
+
- Use idempotency keys for retryable operations
|
|
145
|
+
- Retry with limits and exponential backoff
|
|
146
|
+
- Never retry non-idempotent operations unsafely
|
|
147
|
+
- Race conditions: use transactions or locks for read-modify-write
|
|
148
|
+
|
|
149
|
+
## Security
|
|
150
|
+
|
|
151
|
+
- Never hardcode secrets or credentials — use `ConfigModule`
|
|
152
|
+
- Never expose sensitive data in API responses
|
|
153
|
+
- Validate and sanitize all user input
|
|
154
|
+
- Prevent IDOR — check resource ownership
|
|
155
|
+
- Use parameterized queries — no raw SQL concatenation
|
|
156
|
+
- Validate file uploads: size, type, content
|
|
157
|
+
- Never trust client-provided file metadata
|
|
158
|
+
- Secure CORS configuration — no wildcard in production
|
|
159
|
+
- Rate limit sensitive endpoints (login, register, password reset)
|
|
160
|
+
- Never log sensitive data (passwords, tokens, PII)
|
|
161
|
+
- Use guards for auth — no manual auth checks in controllers
|
|
162
|
+
|
|
163
|
+
## API Design
|
|
164
|
+
|
|
165
|
+
- Consistent REST naming: plural nouns, HTTP verbs for actions
|
|
166
|
+
- Consistent error response format: `{ statusCode, message, errorCode }`
|
|
167
|
+
- Paginate all list endpoints — no unbounded responses
|
|
168
|
+
- Filter and limit query parameters
|
|
169
|
+
- Version API when making breaking changes
|
|
170
|
+
- Use appropriate HTTP status codes (201 for create, 204 for delete)
|
|
171
|
+
- Never expose internal implementation details in responses
|
|
172
|
+
|
|
173
|
+
## Observability
|
|
174
|
+
|
|
175
|
+
- Use structured logging (nestjs-pino)
|
|
176
|
+
- Never log sensitive data
|
|
177
|
+
- Propagate request/correlation IDs
|
|
178
|
+
- Add health check endpoints for critical dependencies
|
|
179
|
+
- Handle graceful shutdown — clean up connections
|
|
180
|
+
- Clean up timers, listeners, subscriptions
|
|
181
|
+
- Handle background job failures
|
|
182
|
+
|
|
183
|
+
## File Structure
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
andev-skill-agent/
|
|
187
|
+
├── .git/
|
|
188
|
+
├── package.json # @thaiannguyen-05/opencode-rules
|
|
189
|
+
├── plugin.js # trigger detection + GitHub fetch
|
|
190
|
+
├── README.md # usage docs
|
|
191
|
+
└── RULES.md # this file — rules for agents
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### NestJS Project Structure
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
src/
|
|
198
|
+
├── app.module.ts
|
|
199
|
+
├── main.ts
|
|
200
|
+
├── config/
|
|
201
|
+
│ └── configuration.ts
|
|
202
|
+
├── common/
|
|
203
|
+
│ ├── guards/
|
|
204
|
+
│ ├── interceptors/
|
|
205
|
+
│ ├── pipes/
|
|
206
|
+
│ └── decorators/
|
|
207
|
+
├── error/
|
|
208
|
+
│ ├── app.exception.ts
|
|
209
|
+
│ ├── global.exception.ts
|
|
210
|
+
│ └── [domain]-error.helper.ts
|
|
211
|
+
├── modules/
|
|
212
|
+
│ ├── users/
|
|
213
|
+
│ │ ├── users.module.ts
|
|
214
|
+
│ │ ├── users.controller.ts
|
|
215
|
+
│ │ ├── users.service.ts
|
|
216
|
+
│ │ ├── types.ts
|
|
217
|
+
│ │ ├── dto/
|
|
218
|
+
│ │ └── entities/
|
|
219
|
+
│ ├── auth/
|
|
220
|
+
│ │ ├── auth.module.ts
|
|
221
|
+
│ │ ├── auth.controller.ts
|
|
222
|
+
│ │ ├── auth.service.ts
|
|
223
|
+
│ │ ├── types.ts
|
|
224
|
+
│ │ ├── dto/
|
|
225
|
+
│ │ └── entities/
|
|
226
|
+
│ └── orders/
|
|
227
|
+
│ ├── orders.module.ts
|
|
228
|
+
│ ├── orders.controller.ts
|
|
229
|
+
│ ├── orders.service.ts
|
|
230
|
+
│ ├── types.ts
|
|
231
|
+
│ ├── dto/
|
|
232
|
+
│ └── entities/
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Code Style
|
|
236
|
+
|
|
237
|
+
- Use Prettier for formatting
|
|
238
|
+
- Use ESLint with `@typescript-eslint` rules
|
|
239
|
+
- Max 300 lines per file — split if超过
|
|
240
|
+
- One export per file — no barrel exports in middle of file
|
|
241
|
+
- Sort imports: node builtins → external → internal → relative
|
|
242
|
+
- Use `path aliases` (`@/`) for clean imports
|
|
243
|
+
- Prefer `async/await` over `.then()` chains
|
|
244
|
+
- Use `enum` for constants — no hardcoded strings
|
|
245
|
+
|
|
246
|
+
```json
|
|
247
|
+
// .eslintrc
|
|
248
|
+
{
|
|
249
|
+
"rules": {
|
|
250
|
+
"max-lines": ["error", { "max": 300, "skipBlankLines": true, "skipComments": true }]
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-andev-rules",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "On-demand rule injection plugin for opencode. Say 'follow the rule of andev' to load rules from GitHub.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "plugin.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./plugin.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"opencode",
|
|
12
|
+
"opencode-plugin",
|
|
13
|
+
"rules",
|
|
14
|
+
"nestjs",
|
|
15
|
+
"typescript"
|
|
16
|
+
],
|
|
17
|
+
"author": "thaiannguyen-05",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"@opencode-ai/plugin": ">=1.15.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@opencode-ai/plugin": "1.17.11"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/plugin.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// @thaiannguyen-05/opencode-rules
|
|
2
|
+
//
|
|
3
|
+
// On-demand rule injection plugin for opencode.
|
|
4
|
+
// When user says "follow the rule of andev" (or similar), fetches RULES.md
|
|
5
|
+
// from GitHub and injects it into the system prompt.
|
|
6
|
+
//
|
|
7
|
+
// Hooks:
|
|
8
|
+
// - chat.message: detect trigger phrase in user input
|
|
9
|
+
// - experimental.chat.system.transform: inject rules into system prompt
|
|
10
|
+
|
|
11
|
+
const RULES_URL = 'https://raw.githubusercontent.com/thaiannguyen-05/rules-for-global-agents/main/RULES.md';
|
|
12
|
+
|
|
13
|
+
// Trigger patterns — case-insensitive
|
|
14
|
+
const TRIGGER_PATTERNS = [
|
|
15
|
+
/follow\s+(the\s+)?rules?\s+of\s+andev/i,
|
|
16
|
+
/follow\s+andev\s+rules?/i,
|
|
17
|
+
/use\s+andev\s+rules?/i,
|
|
18
|
+
/apply\s+andev\s+rules?/i,
|
|
19
|
+
/andev\s+rules/i,
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
// In-memory cache: rules text per session
|
|
23
|
+
let cachedRules = null;
|
|
24
|
+
let fetchFailed = false;
|
|
25
|
+
|
|
26
|
+
function detectTrigger(text) {
|
|
27
|
+
if (!text) return false;
|
|
28
|
+
return TRIGGER_PATTERNS.some((pattern) => pattern.test(text));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function fetchRules() {
|
|
32
|
+
// Return cache if available
|
|
33
|
+
if (cachedRules !== null) return cachedRules;
|
|
34
|
+
|
|
35
|
+
// Don't retry if fetch already failed
|
|
36
|
+
if (fetchFailed) return null;
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetch(RULES_URL, {
|
|
40
|
+
headers: { Accept: 'text/plain' },
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
fetchFailed = true;
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const text = await response.text();
|
|
49
|
+
|
|
50
|
+
if (!text || text.trim().length === 0) {
|
|
51
|
+
fetchFailed = true;
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
cachedRules = text;
|
|
56
|
+
return cachedRules;
|
|
57
|
+
} catch {
|
|
58
|
+
fetchFailed = true;
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const AndevRulesPlugin = async (_ctx) => {
|
|
64
|
+
let triggerDetected = false;
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
// Detect trigger phrase in user messages
|
|
68
|
+
'chat.message': async (_input, output) => {
|
|
69
|
+
if (!output || !output.parts) return;
|
|
70
|
+
|
|
71
|
+
for (const part of output.parts) {
|
|
72
|
+
if (part && part.type === 'text' && part.text) {
|
|
73
|
+
if (detectTrigger(part.text)) {
|
|
74
|
+
triggerDetected = true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// Inject rules into system prompt when trigger was detected
|
|
81
|
+
'experimental.chat.system.transform': async (_input, output) => {
|
|
82
|
+
if (!output || !Array.isArray(output.system)) return;
|
|
83
|
+
if (!triggerDetected) return;
|
|
84
|
+
|
|
85
|
+
const rules = await fetchRules();
|
|
86
|
+
if (rules) {
|
|
87
|
+
output.system.push(`## Rules from andev (follow these rules)\n\n${rules}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Reset trigger for next message
|
|
91
|
+
triggerDetected = false;
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export default AndevRulesPlugin;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# HTML Report Format
|
|
2
|
+
|
|
3
|
+
The architectural review is rendered as a single self-contained HTML file in the OS temp directory. Tailwind and Mermaid both come from CDNs. Mermaid handles graph-shaped diagrams reliably; hand-built divs and inline SVG handle the more editorial visuals (mass diagrams, cross-sections). Mix the two: don't lean on Mermaid for everything, it'll start to look generic.
|
|
4
|
+
|
|
5
|
+
## Scaffold
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<!doctype html>
|
|
9
|
+
<html lang="en">
|
|
10
|
+
<head>
|
|
11
|
+
<meta charset="utf-8" />
|
|
12
|
+
<title>Architecture review for {{repo name}}</title>
|
|
13
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
14
|
+
<script type="module">
|
|
15
|
+
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
|
|
16
|
+
mermaid.initialize({ startOnLoad: true, theme: "neutral", securityLevel: "loose" });
|
|
17
|
+
</script>
|
|
18
|
+
<style>
|
|
19
|
+
/* small custom layer for things Tailwind doesn't cover cleanly:
|
|
20
|
+
dashed seam lines, hand-drawn-feeling arrow heads, etc. */
|
|
21
|
+
.seam { stroke-dasharray: 4 4; }
|
|
22
|
+
.leak { stroke: #dc2626; }
|
|
23
|
+
.deep { background: linear-gradient(135deg, #0f172a, #1e293b); }
|
|
24
|
+
</style>
|
|
25
|
+
</head>
|
|
26
|
+
<body class="bg-stone-50 text-slate-900 font-sans">
|
|
27
|
+
<main class="max-w-5xl mx-auto px-6 py-12 space-y-12">
|
|
28
|
+
<header>...</header>
|
|
29
|
+
<section id="candidates" class="space-y-10">...</section>
|
|
30
|
+
<section id="top-recommendation">...</section>
|
|
31
|
+
</main>
|
|
32
|
+
</body>
|
|
33
|
+
</html>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Header
|
|
37
|
+
|
|
38
|
+
Repo name, date, and a compact legend: solid box = module, dashed line = seam, red arrow = leakage, thick dark box = deep module. No introduction paragraph. Straight into the candidates.
|
|
39
|
+
|
|
40
|
+
## Candidate card
|
|
41
|
+
|
|
42
|
+
The diagrams carry the weight. Prose is sparse, plain, and uses the glossary terms (from the `/codebase-design` skill) without ceremony.
|
|
43
|
+
|
|
44
|
+
Each candidate is one `<article>`:
|
|
45
|
+
|
|
46
|
+
- **Title**: short, names the deepening (e.g. "Collapse the Order intake pipeline").
|
|
47
|
+
- **Badge row**: recommendation strength (`Strong` = emerald, `Worth exploring` = amber, `Speculative` = slate), plus a tag for the dependency category (`in-process`, `local-substitutable`, `ports & adapters`, `mock`).
|
|
48
|
+
- **Files**: monospaced list, `font-mono text-sm`.
|
|
49
|
+
- **Before / After diagram**: the centrepiece. Two columns, side by side. See patterns below.
|
|
50
|
+
- **Problem**: one sentence. What hurts.
|
|
51
|
+
- **Solution**: one sentence. What changes.
|
|
52
|
+
- **Wins**: bullets, ≤6 words each. e.g. "Tests hit one interface", "Pricing logic stops leaking", "Delete 4 shallow wrappers".
|
|
53
|
+
- **ADR callout** (if applicable): one line in an amber-tinted box.
|
|
54
|
+
|
|
55
|
+
No paragraphs of explanation. If the diagram needs a paragraph to be understood, redraw the diagram.
|
|
56
|
+
|
|
57
|
+
## Diagram patterns
|
|
58
|
+
|
|
59
|
+
Pick the pattern that fits the candidate. Mix them. Don't make every diagram look the same. Variety is part of the point.
|
|
60
|
+
|
|
61
|
+
### Mermaid graph (the workhorse for dependencies / call flow)
|
|
62
|
+
|
|
63
|
+
Use a Mermaid `flowchart` or `graph` when the point is "X calls Y calls Z, and look at the mess." Wrap it in a Tailwind-styled card so it doesn't feel parachuted in. Style with classDef to colour leakage edges red and the deep module dark. Sequence diagrams work well for "before: 6 round-trips; after: 1."
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<div class="rounded-lg border border-slate-200 bg-white p-4">
|
|
67
|
+
<pre class="mermaid">
|
|
68
|
+
flowchart LR
|
|
69
|
+
A[OrderHandler] --> B[OrderValidator]
|
|
70
|
+
B --> C[OrderRepo]
|
|
71
|
+
C -.leak.-> D[PricingClient]
|
|
72
|
+
classDef leak stroke:#dc2626,stroke-width:2px;
|
|
73
|
+
class C,D leak
|
|
74
|
+
</pre>
|
|
75
|
+
</div>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Hand-built boxes-and-arrows (when Mermaid's layout fights you)
|
|
79
|
+
|
|
80
|
+
Modules as `<div>`s with borders and labels. Arrows as inline SVG `<line>` or `<path>` elements positioned absolutely over a relative container. Reach for this when you want the "after" diagram to feel like one thick-bordered deep module with greyed-out internals, since Mermaid won't render that with the right weight.
|
|
81
|
+
|
|
82
|
+
### Cross-section (good for layered shallowness)
|
|
83
|
+
|
|
84
|
+
Stack horizontal bands (`h-12 border-l-4`) to show layers a call passes through. Before: 6 thin layers each doing nothing. After: 1 thick band labelled with the consolidated responsibility.
|
|
85
|
+
|
|
86
|
+
### Mass diagram (good for "interface as wide as implementation")
|
|
87
|
+
|
|
88
|
+
Two rectangles per module: one for interface surface area, one for implementation. Before: interface rectangle is nearly as tall as the implementation rectangle (shallow). After: interface rectangle is short, implementation rectangle is tall (deep).
|
|
89
|
+
|
|
90
|
+
### Call-graph collapse
|
|
91
|
+
|
|
92
|
+
Before: a tree of function calls rendered as nested boxes. After: the same tree collapsed into one box, with the now-internal calls shown faded inside it.
|
|
93
|
+
|
|
94
|
+
## Style guidance
|
|
95
|
+
|
|
96
|
+
- Lean editorial, not corporate-dashboard. Generous whitespace. Serif optional for headings (`font-serif` works well with stone/slate).
|
|
97
|
+
- Colour sparingly: one accent (emerald or indigo) plus red for leakage and amber for warnings.
|
|
98
|
+
- Keep diagrams ~320px tall so before/after sits comfortably side by side without scrolling.
|
|
99
|
+
- Use `text-xs uppercase tracking-wider` for module labels inside diagrams, so they read as schematic, not as UI.
|
|
100
|
+
- The only scripts are the Tailwind CDN and the Mermaid ESM import. The report is otherwise static: no app code, no interactivity beyond Mermaid's own rendering.
|
|
101
|
+
|
|
102
|
+
## Top recommendation section
|
|
103
|
+
|
|
104
|
+
One larger card. Candidate name, one sentence on why, anchor link to its card. That's it.
|
|
105
|
+
|
|
106
|
+
## Tone
|
|
107
|
+
|
|
108
|
+
Plain English, concise, but the architectural nouns and verbs come straight from the `/codebase-design` skill. Concision is not an excuse to drift.
|
|
109
|
+
|
|
110
|
+
**Use exactly:** module, interface, implementation, depth, deep, shallow, seam, adapter, leverage, locality.
|
|
111
|
+
|
|
112
|
+
**Never substitute:** component, service, unit (for module) · API, signature (for interface) · boundary (for seam) · layer, wrapper (for module, when you mean module).
|
|
113
|
+
|
|
114
|
+
**Phrasings that fit the style:**
|
|
115
|
+
|
|
116
|
+
- "Order intake module is shallow: interface nearly matches the implementation."
|
|
117
|
+
- "Pricing leaks across the seam."
|
|
118
|
+
- "Deepen: one interface, one place to test."
|
|
119
|
+
- "Two adapters justify the seam: HTTP in prod, in-memory in tests."
|
|
120
|
+
|
|
121
|
+
**Wins bullets** name the gain in glossary terms: *"locality: bugs concentrate in one module"*, *"leverage: one interface, N call sites"*, *"interface shrinks; implementation absorbs the wrappers"*. Don't write *"easier to maintain"* or *"cleaner code"*, because those terms aren't in the glossary and don't earn their place.
|
|
122
|
+
|
|
123
|
+
No hedging, no throat-clearing, no "it's worth noting that…". If a sentence could be a bullet, make it a bullet. If a bullet could be cut, cut it. If a term isn't in the `/codebase-design` glossary, reach for one that is before inventing a new one.
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: improve-codenase-systems
|
|
3
|
+
description: Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Improve Codenase Systems
|
|
8
|
+
|
|
9
|
+
Surface architectural friction and propose **deepening opportunities**: refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability.
|
|
10
|
+
|
|
11
|
+
This command is _informed_ by the project's domain model and built on a shared design vocabulary:
|
|
12
|
+
|
|
13
|
+
- Call the Skill tool with "codebase-design" for the architecture vocabulary (**module**, **interface**, **depth**, **seam**, **adapter**, **leverage**, **locality**) and its principles (the deletion test, "the interface is the test surface", "one adapter = hypothetical seam, two = real"). Use these terms exactly in every suggestion, and don't drift into "component," "service," "API," or "boundary."
|
|
14
|
+
- The domain language in `CONTEXT.md` gives names to good seams; ADRs in `docs/adr/` record decisions this command should not re-litigate.
|
|
15
|
+
|
|
16
|
+
## Scan Checklist
|
|
17
|
+
|
|
18
|
+
### Architecture & Code Organization
|
|
19
|
+
|
|
20
|
+
- duplicated or highly similar code that should be reused
|
|
21
|
+
- business logic placed directly inside UI components when it should be extracted
|
|
22
|
+
- API/data fetching logic duplicated across components
|
|
23
|
+
- API calls implemented directly in components instead of an appropriate service/query layer
|
|
24
|
+
- components with too many responsibilities
|
|
25
|
+
- components that are unnecessarily large and should be split
|
|
26
|
+
- hooks that contain unrelated responsibilities
|
|
27
|
+
- utilities/helpers duplicated across the codebase
|
|
28
|
+
- inappropriate abstraction or abstraction created without actual reuse
|
|
29
|
+
- unnecessary abstraction or over-engineering
|
|
30
|
+
- tight coupling between unrelated components or modules
|
|
31
|
+
- circular dependencies
|
|
32
|
+
- incorrect dependency direction
|
|
33
|
+
- feature-specific logic placed in global/shared modules unnecessarily
|
|
34
|
+
- shared/common modules becoming dumping grounds
|
|
35
|
+
- inconsistent project structure for similar features
|
|
36
|
+
|
|
37
|
+
### React / Next.js
|
|
38
|
+
|
|
39
|
+
- unnecessary useEffect
|
|
40
|
+
- useEffect used for derived state
|
|
41
|
+
- state that can be derived instead of stored
|
|
42
|
+
- duplicated state
|
|
43
|
+
- unnecessary local state
|
|
44
|
+
- state synchronization that can cause inconsistent data
|
|
45
|
+
- stale closure issues
|
|
46
|
+
- missing cleanup for effects, subscriptions, timers, or event listeners
|
|
47
|
+
- side effects executed during render
|
|
48
|
+
- unnecessary re-renders
|
|
49
|
+
- expensive computation executed on every render
|
|
50
|
+
- incorrect or unnecessary useMemo/useCallback
|
|
51
|
+
- memoization used without measurable benefit
|
|
52
|
+
- missing memoization for clearly expensive operations
|
|
53
|
+
- unstable keys in list rendering
|
|
54
|
+
- array index used as key when list order can change
|
|
55
|
+
- prop drilling that should be solved with a more appropriate state/data architecture
|
|
56
|
+
- unnecessary Context usage causing broad re-renders
|
|
57
|
+
- incorrect dependency array
|
|
58
|
+
- disabling eslint hook dependency rules without a valid reason
|
|
59
|
+
|
|
60
|
+
### Next.js Specific
|
|
61
|
+
|
|
62
|
+
- unnecessary "use client"
|
|
63
|
+
- Server Components unnecessarily converted into Client Components
|
|
64
|
+
- data fetching on the client when server-side fetching is more appropriate
|
|
65
|
+
- duplicated server and client data fetching
|
|
66
|
+
- incorrect caching strategy
|
|
67
|
+
- missing or incorrect revalidation strategy
|
|
68
|
+
- unnecessary dynamic rendering
|
|
69
|
+
- unnecessary dynamic imports
|
|
70
|
+
- heavy client-side dependencies that could remain server-side
|
|
71
|
+
- server-only code imported into client code
|
|
72
|
+
- environment variables exposed to the client unnecessarily
|
|
73
|
+
- incorrect usage of Server Actions
|
|
74
|
+
- missing loading or error boundaries where appropriate
|
|
75
|
+
|
|
76
|
+
### Data Fetching & State Management
|
|
77
|
+
|
|
78
|
+
- duplicated API requests
|
|
79
|
+
- unnecessary repeated fetching
|
|
80
|
+
- missing request cancellation where race conditions can occur
|
|
81
|
+
- stale data issues
|
|
82
|
+
- race conditions between requests
|
|
83
|
+
- missing loading states
|
|
84
|
+
- missing error states
|
|
85
|
+
- missing empty states
|
|
86
|
+
- inconsistent error handling
|
|
87
|
+
- data transformed repeatedly in multiple places
|
|
88
|
+
- server state incorrectly stored as local/global client state
|
|
89
|
+
- global state used for local component state
|
|
90
|
+
- local state used where shared state management is required
|
|
91
|
+
- missing debounce or throttle for search, resize, scroll, or frequent user input
|
|
92
|
+
- unnecessary polling
|
|
93
|
+
- incorrect cache invalidation
|
|
94
|
+
|
|
95
|
+
### Performance
|
|
96
|
+
|
|
97
|
+
- unnecessary re-renders
|
|
98
|
+
- expensive operations during render
|
|
99
|
+
- large lists rendered without virtualization when necessary
|
|
100
|
+
- unnecessary large bundle dependencies
|
|
101
|
+
- importing entire libraries when smaller imports are available
|
|
102
|
+
- duplicate dependencies with overlapping functionality
|
|
103
|
+
- unnecessary images/assets loaded eagerly
|
|
104
|
+
- missing lazy loading where beneficial
|
|
105
|
+
- unnecessary network requests
|
|
106
|
+
- sequential independent requests that can run in parallel
|
|
107
|
+
- unnecessary client-side computation
|
|
108
|
+
- avoidable bundle size increase
|
|
109
|
+
- memory leaks caused by listeners, timers, subscriptions, or retained references
|
|
110
|
+
|
|
111
|
+
### UI / UX
|
|
112
|
+
|
|
113
|
+
- duplicated UI patterns that should be reusable components
|
|
114
|
+
- hardcoded UI that should be generated from reusable components or configuration
|
|
115
|
+
- hardcoded values that should come from constants/configuration
|
|
116
|
+
- inconsistent loading behavior
|
|
117
|
+
- inconsistent error handling
|
|
118
|
+
- inconsistent empty states
|
|
119
|
+
- missing disabled/loading state for async actions
|
|
120
|
+
- duplicate submissions
|
|
121
|
+
- missing feedback for long-running operations
|
|
122
|
+
- UI state that can become inconsistent with server state
|
|
123
|
+
- forms without proper validation
|
|
124
|
+
- validation logic duplicated across components
|
|
125
|
+
- validation rules inconsistent with backend constraints
|
|
126
|
+
- missing responsive behavior where applicable
|
|
127
|
+
|
|
128
|
+
### Accessibility
|
|
129
|
+
|
|
130
|
+
- non-semantic HTML where semantic elements are appropriate
|
|
131
|
+
- interactive elements implemented with non-interactive elements
|
|
132
|
+
- missing accessible labels
|
|
133
|
+
- missing aria attributes when required
|
|
134
|
+
- keyboard inaccessible interactions
|
|
135
|
+
- missing focus management
|
|
136
|
+
- dialogs/modals without proper focus handling
|
|
137
|
+
- images without meaningful alt text
|
|
138
|
+
- form controls without associated labels
|
|
139
|
+
|
|
140
|
+
### TypeScript
|
|
141
|
+
|
|
142
|
+
- usage of any
|
|
143
|
+
- unnecessary type assertions using "as"
|
|
144
|
+
- unsafe type assertions
|
|
145
|
+
- duplicated type definitions
|
|
146
|
+
- manually defined types that should be inferred
|
|
147
|
+
- ambiguous or overly generic types
|
|
148
|
+
- missing explicit types where inference is insufficient
|
|
149
|
+
- inconsistent nullable/optional handling
|
|
150
|
+
- type definitions placed in inappropriate files
|
|
151
|
+
- feature/domain types mixed with unrelated UI files
|
|
152
|
+
- API response types duplicated instead of reused
|
|
153
|
+
- incorrect use of interface vs type based on project conventions
|
|
154
|
+
|
|
155
|
+
### Code Quality
|
|
156
|
+
|
|
157
|
+
- ambiguous function names
|
|
158
|
+
- ambiguous variable names
|
|
159
|
+
- misleading names
|
|
160
|
+
- dead code
|
|
161
|
+
- unused imports
|
|
162
|
+
- unused dependencies
|
|
163
|
+
- commented-out code
|
|
164
|
+
- magic numbers or strings
|
|
165
|
+
- duplicated constants
|
|
166
|
+
- hardcoded configuration
|
|
167
|
+
- unnecessary complexity
|
|
168
|
+
- deeply nested conditions that can be simplified
|
|
169
|
+
- duplicated conditional logic
|
|
170
|
+
- functions doing too many things
|
|
171
|
+
- components doing too many things
|
|
172
|
+
- unclear or inconsistent error messages
|
|
173
|
+
|
|
174
|
+
### Backend / NestJS
|
|
175
|
+
|
|
176
|
+
- business logic implemented inside controllers
|
|
177
|
+
- database logic implemented inside controllers
|
|
178
|
+
- infrastructure logic mixed with business/domain logic
|
|
179
|
+
- duplicated business logic across services
|
|
180
|
+
- duplicated database access logic
|
|
181
|
+
- services with too many responsibilities
|
|
182
|
+
- classes violating single responsibility
|
|
183
|
+
- functions with too many responsibilities
|
|
184
|
+
- overly large services that should be split
|
|
185
|
+
- inappropriate abstraction
|
|
186
|
+
- unnecessary abstraction or over-engineering
|
|
187
|
+
- circular dependencies
|
|
188
|
+
- incorrect dependency direction
|
|
189
|
+
- module boundary violations
|
|
190
|
+
- tight coupling between modules
|
|
191
|
+
- direct access to another module's internal implementation
|
|
192
|
+
- feature-specific logic placed in shared/common modules unnecessarily
|
|
193
|
+
- shared/common modules becoming dumping grounds
|
|
194
|
+
- duplicated utilities/helpers
|
|
195
|
+
|
|
196
|
+
### NestJS Framework
|
|
197
|
+
|
|
198
|
+
- controller containing business logic
|
|
199
|
+
- controller directly accessing Prisma/database
|
|
200
|
+
- missing DTO for request input where DTO validation is appropriate
|
|
201
|
+
- missing validation decorators
|
|
202
|
+
- validation implemented manually when DTO validation should handle it
|
|
203
|
+
- incorrect DTO validation
|
|
204
|
+
- DTO used for unrelated responsibilities
|
|
205
|
+
- entity/database model exposed directly as API response
|
|
206
|
+
- missing or inconsistent serialization/transformation
|
|
207
|
+
- guards implemented incorrectly
|
|
208
|
+
- authorization checks missing
|
|
209
|
+
- resource ownership checks missing
|
|
210
|
+
- authentication handled manually instead of using appropriate guards/middleware
|
|
211
|
+
- business logic implemented inside guards/interceptors unnecessarily
|
|
212
|
+
- incorrect usage of middleware, guards, interceptors, pipes, or filters
|
|
213
|
+
- duplicated exception handling
|
|
214
|
+
- missing global exception handling where appropriate
|
|
215
|
+
- dependency injected but not actually needed
|
|
216
|
+
- unnecessary request-scoped providers
|
|
217
|
+
- incorrect provider/module scope
|
|
218
|
+
|
|
219
|
+
### API Design
|
|
220
|
+
|
|
221
|
+
- ambiguous endpoint naming
|
|
222
|
+
- inconsistent REST conventions
|
|
223
|
+
- inconsistent request/response structure
|
|
224
|
+
- inconsistent error response format
|
|
225
|
+
- sensitive data returned in API response
|
|
226
|
+
- missing pagination for potentially large datasets
|
|
227
|
+
- missing filtering limits
|
|
228
|
+
- unbounded request parameters
|
|
229
|
+
- missing validation for query/path/body parameters
|
|
230
|
+
- API exposing internal implementation details
|
|
231
|
+
- inconsistent status codes
|
|
232
|
+
- idempotency issues
|
|
233
|
+
- duplicate request processing
|
|
234
|
+
- missing rate limiting for sensitive endpoints
|
|
235
|
+
- breaking API changes without versioning strategy where applicable
|
|
236
|
+
|
|
237
|
+
### Prisma & Database
|
|
238
|
+
|
|
239
|
+
- Prisma/database query directly inside controller
|
|
240
|
+
- duplicated Prisma query logic
|
|
241
|
+
- Prisma access scattered across unrelated modules without clear data access boundaries
|
|
242
|
+
- N+1 queries
|
|
243
|
+
- database queries inside loops
|
|
244
|
+
- sequential independent queries that can run in parallel
|
|
245
|
+
- fetching unnecessary fields
|
|
246
|
+
- missing select when only a subset of fields is required
|
|
247
|
+
- unnecessary include
|
|
248
|
+
- over-fetching relations
|
|
249
|
+
- loading large relation collections into memory
|
|
250
|
+
- findMany without pagination for potentially large datasets
|
|
251
|
+
- offset pagination used for very large datasets where cursor pagination is more appropriate
|
|
252
|
+
- incorrect cursor pagination
|
|
253
|
+
- pagination without deterministic orderBy
|
|
254
|
+
- missing filtering constraints
|
|
255
|
+
- duplicate count/data queries that can be optimized
|
|
256
|
+
- unnecessary database round trips
|
|
257
|
+
- findFirst used when findUnique is possible
|
|
258
|
+
- findUnique used with fields that are not actually unique
|
|
259
|
+
- unsafe raw SQL
|
|
260
|
+
- unnecessary raw SQL when Prisma can handle the query
|
|
261
|
+
- Prisma errors leaked directly to API responses
|
|
262
|
+
- missing handling for expected Prisma errors
|
|
263
|
+
- inconsistent nullable/optional handling between DTO, application code, and Prisma schema
|
|
264
|
+
- incorrect usage of checked/unchecked Prisma inputs
|
|
265
|
+
|
|
266
|
+
### Transactions & Data Integrity
|
|
267
|
+
|
|
268
|
+
- multi-step database operations without a required transaction
|
|
269
|
+
- transaction scope unnecessarily large
|
|
270
|
+
- external API/network calls executed inside database transactions
|
|
271
|
+
- long-running operations inside transactions
|
|
272
|
+
- incorrect interactive transaction usage
|
|
273
|
+
- transaction used where a batch transaction would be sufficient
|
|
274
|
+
- operations that can leave partial/inconsistent data
|
|
275
|
+
- race conditions during read-modify-write operations
|
|
276
|
+
- missing unique constraints for data that must be unique
|
|
277
|
+
- missing database constraints for important invariants
|
|
278
|
+
- application-only validation where database constraints are also required
|
|
279
|
+
- incorrect cascade delete behavior
|
|
280
|
+
- dangerous cascade delete
|
|
281
|
+
- inconsistent soft-delete filtering
|
|
282
|
+
- queries that accidentally include soft-deleted records
|
|
283
|
+
- operations that bypass soft-delete rules
|
|
284
|
+
|
|
285
|
+
### Prisma Schema
|
|
286
|
+
|
|
287
|
+
- missing index for frequently queried fields
|
|
288
|
+
- missing composite index for common multi-field queries
|
|
289
|
+
- unnecessary indexes
|
|
290
|
+
- duplicated or redundant indexes
|
|
291
|
+
- incorrect unique constraints
|
|
292
|
+
- missing unique constraints
|
|
293
|
+
- incorrect nullable/required fields
|
|
294
|
+
- incorrect relation definitions
|
|
295
|
+
- missing or inappropriate onDelete/onUpdate behavior
|
|
296
|
+
- string fields that should use enums
|
|
297
|
+
- duplicated enum/value definitions
|
|
298
|
+
- inconsistent naming
|
|
299
|
+
- schema changes that may cause data loss
|
|
300
|
+
- schema changes inconsistent with existing migrations
|
|
301
|
+
- migration risks that require manual data migration
|
|
302
|
+
|
|
303
|
+
### Async & Concurrency
|
|
304
|
+
|
|
305
|
+
- unnecessary sequential await operations
|
|
306
|
+
- independent async operations that can run in parallel
|
|
307
|
+
- unhandled promise rejection
|
|
308
|
+
- fire-and-forget async operations without error handling
|
|
309
|
+
- race conditions
|
|
310
|
+
- missing concurrency control
|
|
311
|
+
- duplicate processing caused by concurrent requests
|
|
312
|
+
- missing idempotency for retryable operations
|
|
313
|
+
- retry logic without limits
|
|
314
|
+
- retry logic without backoff where appropriate
|
|
315
|
+
- retrying non-idempotent operations unsafely
|
|
316
|
+
- missing timeout for external service calls
|
|
317
|
+
- blocking/synchronous operations inside request handling
|
|
318
|
+
|
|
319
|
+
### Error Handling
|
|
320
|
+
|
|
321
|
+
- empty catch blocks
|
|
322
|
+
- swallowed errors
|
|
323
|
+
- catch blocks that lose the original error/context
|
|
324
|
+
- generic Error used instead of appropriate application/domain exceptions
|
|
325
|
+
- inconsistent exception handling
|
|
326
|
+
- sensitive internal information exposed in errors
|
|
327
|
+
- missing error handling for external dependencies
|
|
328
|
+
- errors logged but not propagated when they should be
|
|
329
|
+
- errors propagated without meaningful context
|
|
330
|
+
- retryable and non-retryable errors handled identically
|
|
331
|
+
|
|
332
|
+
### Security
|
|
333
|
+
|
|
334
|
+
- missing authentication
|
|
335
|
+
- missing authorization
|
|
336
|
+
- IDOR/resource ownership vulnerabilities
|
|
337
|
+
- trusting user input without validation
|
|
338
|
+
- unsafe raw SQL
|
|
339
|
+
- SQL injection risk
|
|
340
|
+
- command injection risk
|
|
341
|
+
- path traversal
|
|
342
|
+
- SSRF
|
|
343
|
+
- unsafe file upload handling
|
|
344
|
+
- missing file size validation
|
|
345
|
+
- missing MIME/type validation
|
|
346
|
+
- trusting client-provided file metadata
|
|
347
|
+
- hardcoded secrets or credentials
|
|
348
|
+
- secrets exposed in logs
|
|
349
|
+
- sensitive information logged
|
|
350
|
+
- sensitive information returned in API responses
|
|
351
|
+
- insecure CORS configuration
|
|
352
|
+
- missing rate limiting for sensitive operations
|
|
353
|
+
- insecure token handling
|
|
354
|
+
- missing permission checks
|
|
355
|
+
- unsafe deserialization
|
|
356
|
+
|
|
357
|
+
### Observability & Reliability
|
|
358
|
+
|
|
359
|
+
- missing structured logging
|
|
360
|
+
- inconsistent log format
|
|
361
|
+
- sensitive data in logs
|
|
362
|
+
- missing error context
|
|
363
|
+
- missing correlation/request ID propagation where appropriate
|
|
364
|
+
- missing logging for critical failures
|
|
365
|
+
- excessive logging in hot paths
|
|
366
|
+
- missing health checks for critical dependencies
|
|
367
|
+
- missing graceful shutdown handling
|
|
368
|
+
- resource leaks
|
|
369
|
+
- timers/listeners/subscriptions not cleaned up
|
|
370
|
+
- connections not closed properly
|
|
371
|
+
- background jobs without failure handling
|
|
372
|
+
- critical operations without metrics or monitoring hooks where appropriate
|
|
373
|
+
|
|
374
|
+
## Process
|
|
375
|
+
|
|
376
|
+
### 1. Explore
|
|
377
|
+
|
|
378
|
+
**Scope before you scan: YAGNI.** Deepening a module pays off by making future changes to it easier, so put extra weight on the parts of the codebase that have recently changed. Decide *where* to look before you look:
|
|
379
|
+
|
|
380
|
+
- If the user named a direction (a module, a subsystem, a pain point), take it, and skip the inference below.
|
|
381
|
+
- Otherwise, walk back a good stretch of the commit history (`git log --oneline`) to find the codebase's hot spots, the files and areas that keep coming up, and let those paths pull your attention first. If the changes are scattered with no clear hot spot, widen the net.
|
|
382
|
+
|
|
383
|
+
Read the project's domain glossary (`CONTEXT.md`) and any ADRs in the area you're touching first.
|
|
384
|
+
|
|
385
|
+
Then spawn a sub-agent to walk the codebase. Don't follow rigid heuristics; explore organically and note where you experience friction:
|
|
386
|
+
|
|
387
|
+
- Where does understanding one concept require bouncing between many small modules?
|
|
388
|
+
- Where are modules **shallow**, with an interface nearly as complex as the implementation?
|
|
389
|
+
- Where have pure functions been extracted just for testability, but the real bugs hide in how they're called (no **locality**)?
|
|
390
|
+
- Where do tightly-coupled modules leak across their seams?
|
|
391
|
+
- Which parts of the codebase are untested, or hard to test through their current interface?
|
|
392
|
+
|
|
393
|
+
Apply the **deletion test** to anything you suspect is shallow: would deleting it concentrate complexity, or just move it? A "yes, concentrates" is the signal you want.
|
|
394
|
+
|
|
395
|
+
### 2. Present candidates as an HTML report
|
|
396
|
+
|
|
397
|
+
Write a self-contained HTML file to the OS temp directory so nothing lands in the repo. Resolve the temp dir from `$TMPDIR`, falling back to `/tmp` (or `%TEMP%` on Windows), and write to `<tmpdir>/architecture-review-<timestamp>.html` so each run gets a fresh file. Open it for the user (`xdg-open <path>` on Linux, `open <path>` on macOS, `start <path>` on Windows) and tell them the absolute path.
|
|
398
|
+
|
|
399
|
+
The report uses **Tailwind via CDN** for layout and styling, and **Mermaid via CDN** for diagrams where a graph/flow/sequence reliably communicates the structure. Mix Mermaid with hand-crafted CSS/SVG visuals: use Mermaid when relationships are graph-shaped (call graphs, dependencies, sequences), and hand-built divs/SVG when you want something more editorial (mass diagrams, cross-sections, collapse animations). Each candidate gets a **before/after visualisation**. Be visual.
|
|
400
|
+
|
|
401
|
+
For each candidate, render a card with:
|
|
402
|
+
|
|
403
|
+
- **Files**: which files/modules are involved
|
|
404
|
+
- **Problem**: why the current architecture is causing friction
|
|
405
|
+
- **Solution**: plain English description of what would change
|
|
406
|
+
- **Benefits**: explained in terms of locality and leverage, and how tests would improve
|
|
407
|
+
- **Before / After diagram**: side-by-side, custom-drawn, illustrating the shallowness and the deepening
|
|
408
|
+
- **Recommendation strength**: one of `Strong`, `Worth exploring`, `Speculative`, rendered as a badge
|
|
409
|
+
|
|
410
|
+
End the report with a **Top recommendation** section: which candidate you'd tackle first and why.
|
|
411
|
+
|
|
412
|
+
**Use CONTEXT.md vocabulary for the domain, and the `/codebase-design` vocabulary for the architecture.** If `CONTEXT.md` defines "Order," talk about "the Order intake module," not "the FooBarHandler," and not "the Order service."
|
|
413
|
+
|
|
414
|
+
**ADR conflicts**: if a candidate contradicts an existing ADR, only surface it when the friction is real enough to warrant revisiting the ADR. Mark it clearly in the card (e.g. a warning callout: _"contradicts ADR-0007, but worth reopening because…"_). Don't list every theoretical refactor an ADR forbids.
|
|
415
|
+
|
|
416
|
+
See [HTML-REPORT.md](HTML-REPORT.md) for the full HTML scaffold, diagram patterns, and styling guidance.
|
|
417
|
+
|
|
418
|
+
Do NOT propose interfaces yet. After the file is written, ask the user: "Which of these would you like to explore?"
|
|
419
|
+
|
|
420
|
+
### 3. Grilling loop
|
|
421
|
+
|
|
422
|
+
Once the user picks a candidate, call the Skill tool with "grilling" to walk the decision tree with them: constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive.
|
|
423
|
+
|
|
424
|
+
Side effects happen inline as decisions crystallize; call the Skill tool with "domain-modeling" to keep the domain model current as you go:
|
|
425
|
+
|
|
426
|
+
- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md`. Create the file lazily if it doesn't exist.
|
|
427
|
+
- **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there.
|
|
428
|
+
- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing; skip ephemeral reasons ("not worth it right now") and self-evident ones.
|
|
429
|
+
- **Want to explore alternative interfaces for the deepened module?** Call the Skill tool with "codebase-design" and use its design-it-twice parallel sub-agent pattern.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "improve-codenase-systems",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Scan codebase for architecture issues and deepening opportunities",
|
|
5
|
+
"author": "thaiannguyen-05",
|
|
6
|
+
"tags": ["architecture", "review", "nestjs", "react"],
|
|
7
|
+
"files": ["SKILL.md", "HTML-REPORT.md", "agents/openai.yaml"]
|
|
8
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "andev-skills",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI agent skills by thaiannguyen-05",
|
|
5
|
+
"skills": [
|
|
6
|
+
{
|
|
7
|
+
"name": "improve-codenase-systems",
|
|
8
|
+
"description": "Scan codebase for architecture issues and deepening opportunities",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"tags": ["architecture", "review", "nestjs", "react", "codebase"],
|
|
11
|
+
"path": "skills/improve-codenase-systems",
|
|
12
|
+
"files": ["SKILL.md", "HTML-REPORT.md", "agents/openai.yaml"]
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|