@raftlabs/raftstack 1.6.3 → 1.7.1

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.
@@ -0,0 +1,378 @@
1
+ ---
2
+ name: asana
3
+ description: Use when working with Asana - creating tasks, updating tasks, adding comments, posting stories, searching tasks, managing projects, or any Asana MCP tool interaction. Essential for proper formatting and understanding MCP limitations.
4
+ ---
5
+
6
+ # Asana MCP Guide
7
+
8
+ ## Overview
9
+
10
+ This skill guides effective use of Asana MCP tools. **Critical limitation:** The Asana MCP server has restricted rich text support compared to the full Asana API.
11
+
12
+ ## When to Use This Skill
13
+
14
+ Trigger on ANY Asana-related request:
15
+ - "Create a task in Asana"
16
+ - "Update the Asana task"
17
+ - "Add a comment to the task"
18
+ - "Post an update on Asana"
19
+ - "Leave a note on the Asana task"
20
+ - "Search for tasks in Asana"
21
+ - "Check my Asana tasks"
22
+ - "What's in my Asana project?"
23
+ - Any mention of Asana + task/project/comment/story
24
+
25
+ ## ⚠️ Critical: MCP Rich Text Limitations
26
+
27
+ The Asana MCP server does **NOT** fully support rich text formatting:
28
+
29
+ | Tool | Rich Text Field | Plain Text Field | Rich Text Works? |
30
+ |------|-----------------|------------------|------------------|
31
+ | `asana_create_task` | `html_notes` ✅ | `notes` | **YES** |
32
+ | `asana_update_task` | ❌ None | `notes` | **NO** |
33
+ | `asana_create_task_story` | ❌ None | `text` | **NO** |
34
+
35
+ ### What This Means
36
+
37
+ ```typescript
38
+ // ✅ WORKS: Creating a NEW task with rich text
39
+ asana_create_task({
40
+ name: "New feature",
41
+ project_id: "123",
42
+ html_notes: "<body><strong>Bold text</strong> works here!</body>"
43
+ })
44
+
45
+ // ❌ DOES NOT WORK: Updating existing task with rich text
46
+ // The MCP only exposes `notes` field (plain text)
47
+ asana_update_task({
48
+ task_id: "456",
49
+ notes: "Plain text only. <strong>Tags</strong> show as literal text."
50
+ })
51
+
52
+ // ❌ DOES NOT WORK: Comments with rich text
53
+ // The MCP only exposes `text` field (plain text)
54
+ asana_create_task_story({
55
+ task_id: "456",
56
+ text: "Plain text only. No formatting supported."
57
+ })
58
+ ```
59
+
60
+ ## The Iron Rules
61
+
62
+ ### 1. Use `html_notes` ONLY for `asana_create_task`
63
+
64
+ Rich text formatting **only works** when creating new tasks.
65
+
66
+ ```typescript
67
+ // ✅ CORRECT: Use html_notes for new tasks
68
+ asana_create_task({
69
+ name: "Implementation task",
70
+ project_id: "123456",
71
+ html_notes: `<body>
72
+ <h2>Requirements</h2>
73
+ <ul>
74
+ <li>Feature A</li>
75
+ <li>Feature B</li>
76
+ </ul>
77
+ <strong>Owner:</strong> <a data-asana-gid="789012"/>
78
+ </body>`
79
+ })
80
+ ```
81
+
82
+ ### 2. Accept Plain Text for Updates and Comments
83
+
84
+ For `asana_update_task` and `asana_create_task_story`, write naturally like you're messaging a colleague:
85
+
86
+ ```typescript
87
+ // ✅ CORRECT: Plain text for updates (only option available)
88
+ asana_update_task({
89
+ task_id: "456",
90
+ notes: `Quick update - finished features A and B. Waiting on John for review, should be good to merge after that.`
91
+ })
92
+
93
+ // ✅ CORRECT: Plain text for comments (only option available)
94
+ asana_create_task_story({
95
+ task_id: "456",
96
+ text: `Found the bug! It was in utils/parser.ts line 42 - missing a trim() call. Added .map(s => s.trim()) to fix it.
97
+
98
+ PR is up: https://github.com/org/repo/pull/123`
99
+ })
100
+ ```
101
+
102
+ ### 3. Wrap `html_notes` in `<body>` Tags
103
+
104
+ When using `html_notes` (only in `asana_create_task`), always wrap content in `<body>` tags:
105
+
106
+ ```xml
107
+ <!-- ❌ BAD: No body wrapper -->
108
+ <strong>Important</strong> task details
109
+
110
+ <!-- ✅ GOOD: Properly wrapped -->
111
+ <body><strong>Important</strong> task details</body>
112
+ ```
113
+
114
+ ### 4. Use `data-asana-gid` for @-mentions (Task Creation Only)
115
+
116
+ In `html_notes` for new tasks, reference users/tasks using `data-asana-gid`:
117
+
118
+ ```xml
119
+ <body>
120
+ Assigned to <a data-asana-gid="USER_GID"/>. Please review.
121
+ See related: <a data-asana-gid="TASK_GID"/>
122
+ </body>
123
+ ```
124
+
125
+ ## Writing Natural Comments
126
+
127
+ Since most operations only support plain text, write like you're messaging a colleague - natural, simple, conversational. But also format so Asana renders it cleanly.
128
+
129
+ ### The Goal
130
+
131
+ Sound like a human, not a report generator. Skip the `===` underlines and formal structure.
132
+
133
+ ### Do This
134
+
135
+ ```
136
+ Hey, finished the code review!
137
+
138
+ Found a few things:
139
+ • auth.ts line 42 needs error handling around the API call
140
+ • types.ts line 15 has an 'any' that should be a proper interface
141
+ • query.ts could use some caching for performance
142
+
143
+ Otherwise looks good, just minor fixes needed. Let me know if you have questions!
144
+ ```
145
+
146
+ ### Not This
147
+
148
+ ```
149
+ Code Review Complete
150
+ --------------------
151
+
152
+ Found 3 issues:
153
+
154
+ 1. Missing error handling in auth.ts:42
155
+ - Add try/catch around API call
156
+
157
+ 2. Type safety issue in types.ts:15
158
+ - Change 'any' to proper interface
159
+
160
+ Overall: Good implementation, minor fixes needed.
161
+ ```
162
+
163
+ ### Natural Writing Tips
164
+
165
+ - **Write conversationally** - "Hey, quick update" or "Found the issue!" is fine
166
+ - **Keep it brief** - say what matters, then stop
167
+ - **Links work fine** - just paste URLs directly: https://github.com/org/repo/pull/123
168
+ - **Don't over-structure** - skip the headers and category labels
169
+
170
+ ### Asana Plain Text Formatting
171
+
172
+ Asana's plain text renderer has quirks. Use these patterns for clean rendering:
173
+
174
+ | Use | Not | Why |
175
+ |-----|-----|-----|
176
+ | `•` (bullet character) | `-` (dash) | Bullets render as proper list items |
177
+ | Flat lists | Nested/indented lists | Indentation doesn't preserve well |
178
+ | One blank line between sections | Multiple blank lines | Keeps spacing consistent |
179
+ | Inline items (no indent) | Indented sub-items | Sub-indentation gets flattened |
180
+
181
+ **Good list format:**
182
+ ```
183
+ Found a few things:
184
+ • First item here
185
+ • Second item here
186
+ • Third item here
187
+ ```
188
+
189
+ **Bad list format (indentation lost):**
190
+ ```
191
+ Key Features:
192
+
193
+ - First item
194
+ - Sub-item (will flatten)
195
+ - Second item
196
+ ```
197
+
198
+ ### Anti-Patterns to Avoid
199
+
200
+ | Don't Do This | Why |
201
+ |---------------|-----|
202
+ | `===` or `---` underlines | ASCII art formatting looks robotic |
203
+ | ALL CAPS FOR EMPHASIS | Comes across as shouting |
204
+ | Category headers everywhere | "Completed:", "Blocked:", "Next:" feels like a form |
205
+ | Nested indentation | Asana flattens it, looks broken |
206
+ | Report-style formatting | You're messaging a colleague, not filing a TPS report |
207
+
208
+ ## HTML Tags Reference (for `html_notes` only)
209
+
210
+ | Markdown | Asana XML |
211
+ |----------|-----------|
212
+ | `**bold**` | `<strong>bold</strong>` |
213
+ | `*italic*` | `<em>italic</em>` |
214
+ | `~~strike~~` | `<s>strike</s>` |
215
+ | `__underline__` | `<u>underline</u>` |
216
+ | `` `code` `` | `<code>code</code>` |
217
+ | `- item` | `<ul><li>item</li></ul>` |
218
+ | `1. item` | `<ol><li>item</li></ol>` |
219
+ | `> quote` | `<blockquote>quote</blockquote>` |
220
+ | ` ```block``` ` | `<pre>block</pre>` |
221
+ | `# H1` | `<h1>H1</h1>` |
222
+ | `## H2` | `<h2>H2</h2>` |
223
+ | `[text](url)` | `<a href="url">text</a>` |
224
+ | `@mention` | `<a data-asana-gid="GID"/>` |
225
+
226
+ ## Complete Examples
227
+
228
+ ### Example 1: Create Task with Rich Formatting
229
+
230
+ ```typescript
231
+ asana_create_task({
232
+ name: "Implement user authentication",
233
+ project_id: "111222333",
234
+ html_notes: `<body>
235
+ <h1>User Authentication Feature</h1>
236
+
237
+ <h2>Requirements</h2>
238
+ <ul>
239
+ <li>OAuth 2.0 with Google</li>
240
+ <li>Session management</li>
241
+ <li>Password reset flow</li>
242
+ </ul>
243
+
244
+ <h2>Technical Notes</h2>
245
+ <blockquote>Must comply with security policy SEC-2024-001</blockquote>
246
+
247
+ <strong>Owner:</strong> <a data-asana-gid="12345678901234"/>
248
+ </body>`,
249
+ due_on: "2024-03-15"
250
+ })
251
+ ```
252
+
253
+ ### Example 2: Update Task (Plain Text Only)
254
+
255
+ ```typescript
256
+ asana_update_task({
257
+ task_id: "1234567890",
258
+ notes: `Quick update on this - API endpoints are done and tests are passing (95% coverage). Documentation draft is ready too.
259
+
260
+ Currently working on integration testing and addressing review feedback.
261
+
262
+ One blocker: still waiting on design approval for the UI changes. Pinged Sarah about it yesterday.
263
+
264
+ Once that's sorted, just need to finish integration tests and we can deploy to staging.`
265
+ })
266
+ ```
267
+
268
+ ### Example 3: Add Comment (Plain Text Only)
269
+
270
+ ```typescript
271
+ asana_create_task_story({
272
+ task_id: "1234567890",
273
+ text: `Hey, finished the code review!
274
+
275
+ Found a few things:
276
+ • auth.ts line 42 needs error handling around the API call
277
+ • types.ts line 15 has an 'any' that should be a proper interface
278
+ • query.ts line 88 could use some caching for performance
279
+
280
+ Otherwise looks good - solid implementation, just minor fixes. Let me know if you have questions!
281
+
282
+ PR: https://github.com/org/repo/pull/456`
283
+ })
284
+ ```
285
+
286
+ ## Available Asana MCP Tools
287
+
288
+ ### Task Operations
289
+ | Tool | Purpose | Key Parameters |
290
+ |------|---------|----------------|
291
+ | `asana_create_task` | Create new task | `name`, `project_id`, `html_notes`, `assignee`, `due_on` |
292
+ | `asana_update_task` | Update existing task | `task_id`, `notes` (plain text), `completed`, `assignee` |
293
+ | `asana_get_task` | Get task details | `task_id`, `opt_fields` |
294
+ | `asana_delete_task` | Delete a task | `task_id` |
295
+ | `asana_search_tasks` | Search tasks | `workspace`, `text`, `assignee_any`, `completed` |
296
+
297
+ ### Comments/Stories
298
+ | Tool | Purpose | Key Parameters |
299
+ |------|---------|----------------|
300
+ | `asana_create_task_story` | Add comment | `task_id`, `text` (plain text only) |
301
+ | `asana_get_stories_for_task` | Get task comments | `task_id` |
302
+
303
+ ### Project Operations
304
+ | Tool | Purpose | Key Parameters |
305
+ |------|---------|----------------|
306
+ | `asana_get_project` | Get project details | `project_id` |
307
+ | `asana_get_projects` | List projects | `workspace` |
308
+ | `asana_get_project_sections` | Get sections | `project_id` |
309
+ | `asana_create_project` | Create project | `name`, `workspace`, `team` |
310
+
311
+ ### Search & Discovery
312
+ | Tool | Purpose | Key Parameters |
313
+ |------|---------|----------------|
314
+ | `asana_typeahead_search` | Quick search | `workspace_gid`, `resource_type`, `query` |
315
+ | `asana_list_workspaces` | Get workspaces | (none required) |
316
+ | `asana_get_user` | Get user info | `user_id` (default: "me") |
317
+
318
+ ## Common Workflows
319
+
320
+ ### Find and Update a Task
321
+ ```typescript
322
+ // 1. Search for the task
323
+ asana_typeahead_search({
324
+ workspace_gid: "WORKSPACE_ID",
325
+ resource_type: "task",
326
+ query: "authentication feature"
327
+ })
328
+
329
+ // 2. Get task details
330
+ asana_get_task({
331
+ task_id: "FOUND_TASK_ID",
332
+ opt_fields: "name,notes,assignee,due_on,completed"
333
+ })
334
+
335
+ // 3. Update the task (plain text only)
336
+ asana_update_task({
337
+ task_id: "FOUND_TASK_ID",
338
+ notes: "Updated description here (plain text)"
339
+ })
340
+ ```
341
+
342
+ ### Add Status Update Comment
343
+ ```typescript
344
+ // Get current user first (for context)
345
+ asana_get_user({}) // Returns current user info
346
+
347
+ // Add comment to task
348
+ asana_create_task_story({
349
+ task_id: "TASK_ID",
350
+ text: `Making good progress here! Got the core functionality working, moving on to testing and docs now. No blockers.`
351
+ })
352
+ ```
353
+
354
+ ## Common Mistakes
355
+
356
+ | Mistake | Impact | Fix |
357
+ |---------|--------|-----|
358
+ | Using `html_notes` in `asana_update_task` | Parameter doesn't exist, ignored | Use `notes` with plain text |
359
+ | Using `html_text` in `asana_create_task_story` | Parameter doesn't exist, ignored | Use `text` with plain text |
360
+ | Expecting markdown to render | Shows as literal `**text**` | Write naturally in plain text |
361
+ | Missing `<body>` tags in `html_notes` | May fail or render incorrectly | Always wrap in `<body>` tags |
362
+ | Not closing XML tags | Invalid XML error | Close all tags: `<li></li>` |
363
+
364
+ ## Red Flags - STOP and Check
365
+
366
+ | Thought | Reality |
367
+ |---------|---------|
368
+ | "I'll use html_notes to update the task" | `asana_update_task` doesn't have `html_notes`. Use `notes`. |
369
+ | "I'll format the comment with HTML" | `asana_create_task_story` only has `text`. Plain text only. |
370
+ | "Markdown will render in Asana" | No. Write naturally in plain text, or use `html_notes` for new tasks only. |
371
+ | "I need the workspace ID" | Call `asana_list_workspaces` first to get it. |
372
+ | "I'll @mention with @username" | Use `<a data-asana-gid="GID"/>` in `html_notes` only. |
373
+
374
+ ## References
375
+
376
+ - [Asana Rich Text Documentation](https://developers.asana.com/docs/rich-text) - Full API rich text (note: MCP has limited support)
377
+ - Asana MCP exposes subset of Asana API functionality
378
+ - Rich text via `html_notes` only available in `asana_create_task`
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: backend
3
- description: Use when writing serverless functions, API handlers, backend services, or when code has tight coupling to infrastructure, no dependency injection, or mixed concerns
3
+ description: Use when writing Lambda functions, API routes, Hono handlers, Express routes, serverless endpoints, or backend services. Use when creating API validation with Zod, implementing service layers, or structuring handler code.
4
4
  ---
5
5
 
6
6
  # Backend Development
@@ -578,6 +578,135 @@ export const handler = compose(
578
578
  )(baseHandler);
579
579
  ```
580
580
 
581
+ ## Hono.js Patterns
582
+
583
+ Hono is a fast, lightweight framework for serverless and edge. Same patterns apply - layer separation, DI, Zod validation.
584
+
585
+ ### Basic Hono Handler with Zod
586
+
587
+ ```typescript
588
+ import { Hono } from 'hono';
589
+ import { zValidator } from '@hono/zod-validator';
590
+ import { z } from 'zod';
591
+
592
+ const app = new Hono();
593
+
594
+ const CreateUserSchema = z.object({
595
+ email: z.string().email(),
596
+ name: z.string().min(1),
597
+ });
598
+
599
+ // ✅ GOOD: Validation middleware + typed body
600
+ app.post(
601
+ '/users',
602
+ zValidator('json', CreateUserSchema),
603
+ async (c) => {
604
+ const body = c.req.valid('json'); // Typed as { email: string; name: string }
605
+ const user = await userService.createUser(body);
606
+ return c.json(user, 201);
607
+ }
608
+ );
609
+ ```
610
+
611
+ ### Hono with Dependency Injection
612
+
613
+ ```typescript
614
+ // ✅ GOOD: Factory pattern for testable Hono apps
615
+ import { Hono } from 'hono';
616
+
617
+ export function createApp(deps: {
618
+ userService: UserService;
619
+ authService: AuthService;
620
+ }) {
621
+ const app = new Hono();
622
+
623
+ app.post('/users', async (c) => {
624
+ const body = await c.req.json();
625
+ const user = await deps.userService.createUser(body);
626
+ return c.json(user, 201);
627
+ });
628
+
629
+ app.post('/login', async (c) => {
630
+ const { email, password } = await c.req.json();
631
+ const token = await deps.authService.login(email, password);
632
+ return c.json({ token });
633
+ });
634
+
635
+ return app;
636
+ }
637
+
638
+ // Production: inject real services
639
+ const app = createApp({
640
+ userService: createUserService(db),
641
+ authService: createAuthService(db),
642
+ });
643
+
644
+ export default app;
645
+
646
+ // Test: inject mocks
647
+ const testApp = createApp({
648
+ userService: { createUser: vi.fn() },
649
+ authService: { login: vi.fn() },
650
+ });
651
+ ```
652
+
653
+ ### Hono Error Handling Middleware
654
+
655
+ ```typescript
656
+ import { Hono } from 'hono';
657
+ import { HTTPException } from 'hono/http-exception';
658
+
659
+ const app = new Hono();
660
+
661
+ // Global error handler
662
+ app.onError((err, c) => {
663
+ if (err instanceof AppError) {
664
+ return c.json(
665
+ { error: err.message, code: err.code },
666
+ err.statusCode
667
+ );
668
+ }
669
+
670
+ if (err instanceof HTTPException) {
671
+ return c.json({ error: err.message }, err.status);
672
+ }
673
+
674
+ console.error('Unexpected error:', err);
675
+ return c.json({ error: 'Internal server error' }, 500);
676
+ });
677
+
678
+ // Route handlers throw AppError
679
+ app.get('/users/:id', async (c) => {
680
+ const user = await userService.getUser(c.req.param('id'));
681
+ if (!user) {
682
+ throw new NotFoundError('User not found');
683
+ }
684
+ return c.json(user);
685
+ });
686
+ ```
687
+
688
+ ### Hono Middleware Composition
689
+
690
+ ```typescript
691
+ import { Hono } from 'hono';
692
+ import { cors } from 'hono/cors';
693
+ import { logger } from 'hono/logger';
694
+ import { jwt } from 'hono/jwt';
695
+
696
+ const app = new Hono();
697
+
698
+ // Apply middleware in order
699
+ app.use('*', logger());
700
+ app.use('*', cors());
701
+ app.use('/api/*', jwt({ secret: process.env.JWT_SECRET }));
702
+
703
+ // Protected routes
704
+ app.get('/api/me', (c) => {
705
+ const payload = c.get('jwtPayload');
706
+ return c.json({ userId: payload.sub });
707
+ });
708
+ ```
709
+
581
710
  ## Testing Strategy
582
711
 
583
712
  | What to Test | How |
@@ -735,6 +864,7 @@ describe('Handler', () => {
735
864
  ## References
736
865
 
737
866
  - [Zod Documentation](https://zod.dev) - Validation, transforms, error formatting, branded types
867
+ - [Hono Documentation](https://hono.dev) - Lightweight framework for serverless and edge
738
868
  - [Vitest Documentation](https://vitest.dev) - Testing, mocking, vi.fn(), vi.spyOn()
739
869
  - [AWS Lambda Cold Starts](https://aws.amazon.com/blogs/compute/understanding-and-remediating-cold-starts-an-aws-lambda-perspective/) - Official optimization guide
740
870
  - [AWS Lambda Performance](https://aws.amazon.com/blogs/compute/operating-lambda-performance-optimization-part-1/) - Best practices
@@ -742,6 +872,7 @@ describe('Handler', () => {
742
872
  **Version Notes:**
743
873
  - Zod v3.24+: Improved error formatting, discriminated unions, branded types
744
874
  - Zod v4.0+: prefault(), enhanced pipe(), performance improvements
875
+ - Hono v4+: Stable, edge-ready, built-in middleware
745
876
  - Vitest v3+: mockResolvedValue, mockRejectedValue patterns
746
877
  - AWS Lambda: Node.js 20.x has faster cold starts than 18.x
747
878
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: code-quality
3
- description: Use when writing or reviewing code, creating functions, naming variables, structuring files, or when code feels messy, hard to read, or overly complex
3
+ description: Use when refactoring functions, extracting helpers, splitting large files, improving naming conventions, or reducing complexity. Use when functions exceed 30 lines, have too many parameters, or contain magic numbers. NOT for React/backend/database-specific patterns.
4
4
  ---
5
5
 
6
6
  # Code Quality
@@ -141,46 +141,59 @@ if (user.role === 'admin') { }
141
141
 
142
142
  ## Automated Enforcement
143
143
 
144
- ### ESLint Configuration
144
+ ### ESLint Configuration (Flat Config - v9+)
145
145
 
146
- Enforce code quality with automated tools:
146
+ ESLint 9+ uses flat config (`eslint.config.js`). Enforce code quality automatically:
147
147
 
148
148
  ```javascript
149
- // .eslintrc.js
150
- module.exports = {
151
- rules: {
152
- // Max function length
153
- 'max-lines-per-function': ['error', {
154
- max: 30,
155
- skipBlankLines: true,
156
- skipComments: true,
157
- }],
158
-
159
- // Cyclomatic complexity
160
- 'complexity': ['error', { max: 10 }],
161
-
162
- // Max file length
163
- 'max-lines': ['error', {
164
- max: 300,
165
- skipBlankLines: true,
166
- skipComments: true,
167
- }],
168
-
169
- // Max function params
170
- 'max-params': ['error', 3],
171
-
172
- // Max nested callbacks
173
- 'max-nested-callbacks': ['error', 2],
174
-
175
- // No magic numbers
176
- 'no-magic-numbers': ['error', {
177
- ignore: [0, 1, -1],
178
- ignoreArrayIndexes: true,
179
- }],
149
+ // eslint.config.js (ESLint v9+ flat config)
150
+ import js from '@eslint/js';
151
+ import tseslint from 'typescript-eslint';
152
+
153
+ export default tseslint.config(
154
+ js.configs.recommended,
155
+ ...tseslint.configs.recommended,
156
+ {
157
+ rules: {
158
+ // Max function length
159
+ 'max-lines-per-function': ['error', {
160
+ max: 30,
161
+ skipBlankLines: true,
162
+ skipComments: true,
163
+ }],
164
+
165
+ // Cyclomatic complexity
166
+ 'complexity': ['error', { max: 10 }],
167
+
168
+ // Max file length
169
+ 'max-lines': ['error', {
170
+ max: 300,
171
+ skipBlankLines: true,
172
+ skipComments: true,
173
+ }],
174
+
175
+ // Max function params
176
+ 'max-params': ['error', 3],
177
+
178
+ // Max nested callbacks
179
+ 'max-nested-callbacks': ['error', 2],
180
+
181
+ // No magic numbers
182
+ 'no-magic-numbers': ['error', {
183
+ ignore: [0, 1, -1],
184
+ ignoreArrayIndexes: true,
185
+ }],
186
+ },
180
187
  },
181
- };
188
+ {
189
+ // Ignore patterns (replaces .eslintignore)
190
+ ignores: ['node_modules/', 'dist/', '*.config.js'],
191
+ }
192
+ );
182
193
  ```
183
194
 
195
+ **Legacy config?** ESLint 9+ still supports `.eslintrc.js` via `ESLINT_USE_FLAT_CONFIG=false`, but flat config is the future.
196
+
184
197
  ### TypeScript-Specific Patterns
185
198
 
186
199
  ```typescript
@@ -294,14 +307,16 @@ When someone says "just make it work fast":
294
307
 
295
308
  ## References
296
309
 
310
+ - [ESLint Flat Config](https://eslint.org/docs/latest/use/configure/configuration-files-new) - ESLint 9+ configuration
297
311
  - [ESLint Complexity Rule](https://eslint.org/docs/latest/rules/complexity) - Cyclomatic complexity enforcement
298
- - [ESLintCC](https://eslintcc.github.io/) - Complexity measurement tool
312
+ - [typescript-eslint](https://typescript-eslint.io/) - TypeScript ESLint integration
299
313
  - [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/) - Advanced type patterns
300
314
  - [Clean Code (Martin)](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882) - Function length rationale
301
315
 
302
316
  **Version Notes:**
303
- - ESLint 9+: Flat config format, enhanced rule options
304
- - TypeScript 5+: Improved discriminated union narrowing
317
+ - ESLint 9+: Flat config (`eslint.config.js`), replaces `.eslintrc.*`
318
+ - TypeScript 5+: Improved discriminated union narrowing, const type parameters
319
+ - typescript-eslint 8+: Native flat config support
305
320
  - Cyclomatic complexity: Default threshold 20, recommended 10
306
321
 
307
322
  ## Common Mistakes