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 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,5 @@
1
+ interface:
2
+ display_name: "Improve Codebase Architecture"
3
+ short_description: "Find and grill architecture improvements"
4
+ policy:
5
+ allow_implicit_invocation: false
@@ -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
+ }