codingwithagent 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.
Files changed (33) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +37 -0
  3. package/bin/init.js +257 -0
  4. package/package.json +56 -0
  5. package/templates/accessibility/.cursorrules +342 -0
  6. package/templates/accessibility/README.md +47 -0
  7. package/templates/antigravity/accessibility/.agent/rules/accessibility.md +501 -0
  8. package/templates/antigravity/accessibility/.agent/rules/aria-patterns.md +568 -0
  9. package/templates/antigravity/accessibility/.agent/rules/wcag-standard.md +225 -0
  10. package/templates/antigravity/accessibility/README.md +42 -0
  11. package/templates/antigravity/minimal/.agent/rules/accessibility.md +53 -0
  12. package/templates/antigravity/minimal/.agent/rules/code-quality.md +86 -0
  13. package/templates/antigravity/minimal/.agent/rules/react-components.md +164 -0
  14. package/templates/antigravity/minimal/README.md +34 -0
  15. package/templates/antigravity/standard/.agent/rules/accessibility.md +98 -0
  16. package/templates/antigravity/standard/.agent/rules/code-quality.md +166 -0
  17. package/templates/antigravity/standard/.agent/rules/pull-request-review.md +192 -0
  18. package/templates/antigravity/standard/.agent/rules/react-components.md +204 -0
  19. package/templates/antigravity/standard/.agent/rules/testing.md +197 -0
  20. package/templates/antigravity/standard/README.md +39 -0
  21. package/templates/antigravity/strict/.agent/README.md +46 -0
  22. package/templates/antigravity/strict/.agent/rules/accessibility.md +199 -0
  23. package/templates/antigravity/strict/.agent/rules/code-quality.md +268 -0
  24. package/templates/antigravity/strict/.agent/rules/pull-request-review.md +114 -0
  25. package/templates/antigravity/strict/.agent/rules/react-components.md +423 -0
  26. package/templates/antigravity/strict/.agent/rules/security.md +483 -0
  27. package/templates/antigravity/strict/.agent/rules/testing.md +280 -0
  28. package/templates/minimal/.cursorrules +48 -0
  29. package/templates/minimal/README.md +40 -0
  30. package/templates/standard/.cursorrules +184 -0
  31. package/templates/standard/README.md +43 -0
  32. package/templates/strict/.cursorrules +227 -0
  33. package/templates/strict/README.md +47 -0
@@ -0,0 +1,568 @@
1
+ ---
2
+ trigger: always_on
3
+ ---
4
+
5
+ # ARIA Patterns Agent Rules
6
+
7
+ ## Core Directive: Semantic HTML First
8
+
9
+ **Your primary rule:** Use native HTML elements. Only add ARIA when semantic HTML cannot achieve the requirement.
10
+
11
+ **Decision flow:**
12
+
13
+ 1. Can I use native HTML? (Usually YES) → Use it, no ARIA needed
14
+ 2. Does native HTML need enhancement? → Add minimal ARIA
15
+ 3. Is this a complex custom widget? → Use full ARIA pattern
16
+
17
+ ## The First Rule of ARIA
18
+
19
+ **NO ARIA IS BETTER THAN BAD ARIA**
20
+
21
+ Never add ARIA "just in case." Each ARIA attribute must serve a specific, necessary purpose.
22
+
23
+ ## Critical ARIA Attributes - When and How
24
+
25
+ ### aria-label
26
+
27
+ **Use when:** No visible label exists (icon buttons, landmark regions)
28
+
29
+ ```jsx
30
+ // ✅ GENERATE: Icon-only button
31
+ <button aria-label="Close dialog">
32
+ <X aria-hidden="true" />
33
+ </button>
34
+
35
+ // ✅ GENERATE: Distinguish multiple nav landmarks
36
+ <nav aria-label="Main navigation">...</nav>
37
+ <nav aria-label="Footer navigation">...</nav>
38
+
39
+ // ❌ NEVER: Redundant with visible text
40
+ <button aria-label="Submit">Submit</button>
41
+ ```
42
+
43
+ ### aria-labelledby
44
+
45
+ **Use when:** Visible label exists elsewhere in DOM
46
+
47
+ ```jsx
48
+ // ✅ GENERATE: Dialog with visible heading
49
+ <h2 id="dialog-title">Confirm Action</h2>
50
+ <div role="dialog" aria-labelledby="dialog-title" aria-modal="true">
51
+ {/* content */}
52
+ </div>
53
+
54
+ // ✅ GENERATE: Multiple labels (space-separated)
55
+ <div aria-labelledby="title subtitle">
56
+ <h2 id="title">Warning</h2>
57
+ <p id="subtitle">Cannot be undone</p>
58
+ </div>
59
+ ```
60
+
61
+ **Rule:** Always generate unique IDs. Use this over `aria-label` when visible text exists.
62
+
63
+ ### aria-describedby
64
+
65
+ **Use when:** Additional help text or error messages exist
66
+
67
+ ```jsx
68
+ // ✅ GENERATE: Form help text
69
+ <label htmlFor="password">Password</label>
70
+ <input
71
+ id="password"
72
+ type="password"
73
+ aria-describedby="password-help"
74
+ aria-required="true"
75
+ />
76
+ <p id="password-help">
77
+ Must be 12+ characters with numbers and symbols
78
+ </p>
79
+
80
+ // ✅ GENERATE: Error message
81
+ <input
82
+ aria-invalid={hasError}
83
+ aria-describedby={hasError ? "email-error" : undefined}
84
+ />
85
+ {hasError && (
86
+ <p id="email-error" role="alert">
87
+ Please enter valid email
88
+ </p>
89
+ )}
90
+ ```
91
+
92
+ ### aria-hidden
93
+
94
+ **Use when:** Hiding purely decorative elements
95
+
96
+ ```jsx
97
+ // ✅ GENERATE: Decorative icon
98
+ <button>
99
+ Save
100
+ <SaveIcon aria-hidden="true" />
101
+ </button>
102
+
103
+ // ❌ NEVER: On interactive elements
104
+ <button aria-hidden="true">Click</button>
105
+
106
+ // ❌ NEVER: On important content
107
+ <p aria-hidden="true">Error: Failed</p>
108
+ ```
109
+
110
+ **CRITICAL:** Never hide interactive elements or important information.
111
+
112
+ ### aria-live
113
+
114
+ **Use when:** Announcing dynamic content changes
115
+
116
+ ```jsx
117
+ // ✅ GENERATE: Status (polite - most common)
118
+ <div role="status" aria-live="polite" aria-atomic="true">
119
+ {isLoading ? 'Loading...' : `${count} results`}
120
+ </div>
121
+
122
+ // ✅ GENERATE: Critical error (assertive - rare)
123
+ <div role="alert" aria-live="assertive">
124
+ Error: Payment failed
125
+ </div>
126
+ ```
127
+
128
+ **Rules:**
129
+
130
+ - `polite` for status updates, non-critical notifications
131
+ - `assertive` ONLY for critical errors, urgent alerts
132
+ - Include `aria-atomic="true"` for complete announcements
133
+
134
+ ### aria-expanded
135
+
136
+ **Use when:** Content is collapsible/expandable
137
+
138
+ ```jsx
139
+ // ✅ GENERATE: Dropdown
140
+ <button
141
+ aria-expanded={isOpen}
142
+ aria-controls="menu"
143
+ aria-haspopup="true"
144
+ >
145
+ Menu
146
+ </button>
147
+ <ul id="menu" hidden={!isOpen}>...</ul>
148
+ ```
149
+
150
+ **Rules:** Always boolean, pair with `aria-controls`, sync with `hidden` attribute.
151
+
152
+ ### aria-invalid & aria-required
153
+
154
+ **Use when:** Form validation
155
+
156
+ ```jsx
157
+ // ✅ GENERATE: Both HTML and ARIA
158
+ <label htmlFor="email">
159
+ Email <span aria-hidden="true">*</span>
160
+ </label>
161
+ <input
162
+ id="email"
163
+ required
164
+ aria-required="true"
165
+ aria-invalid={hasError}
166
+ aria-describedby={hasError ? "error" : undefined}
167
+ />
168
+ {hasError && (
169
+ <p id="error" role="alert">Enter valid email</p>
170
+ )}
171
+ ```
172
+
173
+ ### aria-current
174
+
175
+ **Use when:** Indicating current item in navigation
176
+
177
+ ```jsx
178
+ // ✅ GENERATE: Current page
179
+ <nav aria-label="Main navigation">
180
+ <a href="/" aria-current={isHome ? "page" : undefined}>
181
+ Home
182
+ </a>
183
+ </nav>
184
+
185
+ // ✅ GENERATE: Current step
186
+ <li aria-current={step === 1 ? "step" : undefined}>
187
+ Account Info
188
+ </li>
189
+ ```
190
+
191
+ **Values:** `page`, `step`, `location`, `date`, `time`, `true`
192
+
193
+ ## Common Widget Patterns
194
+
195
+ ### Button (Use Native!)
196
+
197
+ ```jsx
198
+ // ✅ ALWAYS GENERATE
199
+ <button onClick={handleClick}>Click Me</button>
200
+
201
+ // ✅ Icon button
202
+ <button aria-label="Delete">
203
+ <TrashIcon aria-hidden="true" />
204
+ </button>
205
+
206
+ // ⚠️ ONLY IF ABSOLUTELY NECESSARY
207
+ <div
208
+ role="button"
209
+ tabIndex={0}
210
+ onClick={handleClick}
211
+ onKeyDown={(e) => {
212
+ if (e.key === 'Enter' || e.key === ' ') {
213
+ e.preventDefault();
214
+ handleClick();
215
+ }
216
+ }}
217
+ >
218
+ Custom Button
219
+ </div>
220
+ ```
221
+
222
+ ### Modal/Dialog
223
+
224
+ ```jsx
225
+ // ✅ COMPLETE PATTERN
226
+ const Modal = ({ isOpen, onClose, title, children }) => {
227
+ const dialogRef = useRef(null);
228
+
229
+ useEffect(() => {
230
+ if (isOpen) {
231
+ dialogRef.current?.focus();
232
+ }
233
+ }, [isOpen]);
234
+
235
+ if (!isOpen) return null;
236
+
237
+ return (
238
+ <div
239
+ role="dialog"
240
+ aria-modal="true"
241
+ aria-labelledby="dialog-title"
242
+ ref={dialogRef}
243
+ tabIndex={-1}
244
+ onKeyDown={(e) => e.key === "Escape" && onClose()}
245
+ >
246
+ <h2 id="dialog-title">{title}</h2>
247
+ {children}
248
+ <button onClick={onClose} aria-label="Close">
249
+ <X aria-hidden="true" />
250
+ </button>
251
+ </div>
252
+ );
253
+ };
254
+ ```
255
+
256
+ **Required:** `role="dialog"`, `aria-modal="true"`, `aria-labelledby`, focus management, Escape key.
257
+
258
+ ### Tabs
259
+
260
+ ```jsx
261
+ // ✅ FULL PATTERN WITH KEYBOARD NAV
262
+ const Tabs = ({ tabs }) => {
263
+ const [selected, setSelected] = useState(0);
264
+ const tabRefs = useRef([]);
265
+
266
+ const handleKeyDown = (e, index) => {
267
+ let newIndex = index;
268
+ if (e.key === "ArrowRight") newIndex = (index + 1) % tabs.length;
269
+ if (e.key === "ArrowLeft")
270
+ newIndex = (index - 1 + tabs.length) % tabs.length;
271
+ if (e.key === "Home") newIndex = 0;
272
+ if (e.key === "End") newIndex = tabs.length - 1;
273
+
274
+ if (newIndex !== index) {
275
+ e.preventDefault();
276
+ setSelected(newIndex);
277
+ tabRefs.current[newIndex]?.focus();
278
+ }
279
+ };
280
+
281
+ return (
282
+ <>
283
+ <div role="tablist" aria-label="Content tabs">
284
+ {tabs.map((tab, i) => (
285
+ <button
286
+ key={i}
287
+ ref={(el) => (tabRefs.current[i] = el)}
288
+ role="tab"
289
+ aria-selected={selected === i}
290
+ aria-controls={`panel-${i}`}
291
+ id={`tab-${i}`}
292
+ tabIndex={selected === i ? 0 : -1}
293
+ onClick={() => setSelected(i)}
294
+ onKeyDown={(e) => handleKeyDown(e, i)}
295
+ >
296
+ {tab.label}
297
+ </button>
298
+ ))}
299
+ </div>
300
+
301
+ {tabs.map((tab, i) => (
302
+ <div
303
+ key={i}
304
+ role="tabpanel"
305
+ id={`panel-${i}`}
306
+ aria-labelledby={`tab-${i}`}
307
+ hidden={selected !== i}
308
+ >
309
+ {tab.content}
310
+ </div>
311
+ ))}
312
+ </>
313
+ );
314
+ };
315
+ ```
316
+
317
+ **Required:** Arrow navigation, Home/End keys, roving tabindex.
318
+
319
+ ### Accordion
320
+
321
+ ```jsx
322
+ // ✅ GENERATE THIS
323
+ const Accordion = ({ title, content, isOpen, onToggle }) => {
324
+ const headingId = `heading-${useId()}`;
325
+ const panelId = `panel-${useId()}`;
326
+
327
+ return (
328
+ <div>
329
+ <h3 id={headingId}>
330
+ <button
331
+ aria-expanded={isOpen}
332
+ aria-controls={panelId}
333
+ onClick={onToggle}
334
+ >
335
+ {title}
336
+ </button>
337
+ </h3>
338
+ <div
339
+ id={panelId}
340
+ role="region"
341
+ aria-labelledby={headingId}
342
+ hidden={!isOpen}
343
+ >
344
+ {content}
345
+ </div>
346
+ </div>
347
+ );
348
+ };
349
+ ```
350
+
351
+ ### Combobox (Autocomplete)
352
+
353
+ ```jsx
354
+ // ✅ SEARCHABLE DROPDOWN
355
+ const Combobox = ({ options, value, onChange }) => {
356
+ const [isOpen, setIsOpen] = useState(false);
357
+ const [activeIndex, setActiveIndex] = useState(-1);
358
+
359
+ const handleKeyDown = (e) => {
360
+ if (e.key === "ArrowDown") {
361
+ e.preventDefault();
362
+ setIsOpen(true);
363
+ setActiveIndex((prev) => Math.min(prev + 1, options.length - 1));
364
+ }
365
+ if (e.key === "ArrowUp") {
366
+ e.preventDefault();
367
+ setActiveIndex((prev) => Math.max(prev - 1, 0));
368
+ }
369
+ if (e.key === "Enter" && activeIndex >= 0) {
370
+ onChange(options[activeIndex]);
371
+ setIsOpen(false);
372
+ }
373
+ if (e.key === "Escape") {
374
+ setIsOpen(false);
375
+ setActiveIndex(-1);
376
+ }
377
+ };
378
+
379
+ return (
380
+ <>
381
+ <input
382
+ role="combobox"
383
+ aria-expanded={isOpen}
384
+ aria-controls="listbox"
385
+ aria-activedescendant={
386
+ activeIndex >= 0 ? `option-${activeIndex}` : undefined
387
+ }
388
+ aria-autocomplete="list"
389
+ value={value}
390
+ onChange={(e) => {
391
+ onChange(e.target.value);
392
+ setIsOpen(true);
393
+ }}
394
+ onKeyDown={handleKeyDown}
395
+ />
396
+ {isOpen && (
397
+ <ul id="listbox" role="listbox">
398
+ {options.map((opt, i) => (
399
+ <li
400
+ key={i}
401
+ id={`option-${i}`}
402
+ role="option"
403
+ aria-selected={i === activeIndex}
404
+ >
405
+ {opt}
406
+ </li>
407
+ ))}
408
+ </ul>
409
+ )}
410
+ </>
411
+ );
412
+ };
413
+ ```
414
+
415
+ ### Alerts & Status
416
+
417
+ ```jsx
418
+ // ✅ Success (polite)
419
+ <div role="status" aria-live="polite">
420
+ <CheckIcon aria-hidden="true" />
421
+ Saved successfully
422
+ </div>
423
+
424
+ // ✅ Error (assertive)
425
+ <div role="alert" aria-live="assertive">
426
+ <ErrorIcon aria-hidden="true" />
427
+ Payment failed
428
+ </div>
429
+
430
+ // ✅ Loading
431
+ <div role="status" aria-live="polite" aria-busy="true">
432
+ <Spinner aria-hidden="true" />
433
+ Loading...
434
+ </div>
435
+
436
+ // ✅ Progress
437
+ <div
438
+ role="progressbar"
439
+ aria-valuenow={50}
440
+ aria-valuemin={0}
441
+ aria-valuemax={100}
442
+ >
443
+ 50% complete
444
+ </div>
445
+ ```
446
+
447
+ ### Breadcrumbs
448
+
449
+ ```jsx
450
+ // ✅ NAVIGATION BREADCRUMBS
451
+ <nav aria-label="Breadcrumb">
452
+ <ol>
453
+ <li>
454
+ <a href="/">Home</a>
455
+ </li>
456
+ <li>
457
+ <a href="/products">Products</a>
458
+ </li>
459
+ <li aria-current="page">Laptop</li>
460
+ </ol>
461
+ </nav>
462
+ ```
463
+
464
+ ### Tooltip
465
+
466
+ ```jsx
467
+ // ✅ ACCESSIBLE TOOLTIP
468
+ const Tooltip = ({ children, text }) => {
469
+ const [show, setShow] = useState(false);
470
+ const id = useId();
471
+
472
+ return (
473
+ <>
474
+ <button
475
+ aria-describedby={show ? id : undefined}
476
+ onMouseEnter={() => setShow(true)}
477
+ onMouseLeave={() => setShow(false)}
478
+ onFocus={() => setShow(true)}
479
+ onBlur={() => setShow(false)}
480
+ >
481
+ {children}
482
+ </button>
483
+ {show && (
484
+ <div id={id} role="tooltip">
485
+ {text}
486
+ </div>
487
+ )}
488
+ </>
489
+ );
490
+ };
491
+ ```
492
+
493
+ ## Critical Mistakes - Never Generate
494
+
495
+ ```jsx
496
+ // ❌ FORBIDDEN: Redundant roles
497
+ <button role="button">Click</button>
498
+ <nav role="navigation">...</nav>
499
+
500
+ // ❌ FORBIDDEN: aria-label with visible text
501
+ <button aria-label="Submit">Submit</button>
502
+
503
+ // ❌ FORBIDDEN: Hiding interactive elements
504
+ <button aria-hidden="true">Click</button>
505
+
506
+ // ❌ FORBIDDEN: Role without keyboard support
507
+ <div role="button" onClick={handleClick}>Bad</div>
508
+
509
+ // ❌ FORBIDDEN: Positive tabIndex
510
+ <button tabIndex={1}>Bad</button>
511
+
512
+ // ❌ FORBIDDEN: Empty labels
513
+ <button aria-label="">Click</button>
514
+ <img alt="image" />
515
+ ```
516
+
517
+ ## Always Generate These Instead
518
+
519
+ ```jsx
520
+ // ✅ Native elements (no role needed)
521
+ <button>Click</button>
522
+ <nav>...</nav>
523
+
524
+ // ✅ No aria-label when text visible
525
+ <button>Submit</button>
526
+
527
+ // ✅ Hide decorative only
528
+ <button>
529
+ Save <SaveIcon aria-hidden="true" />
530
+ </button>
531
+
532
+ // ✅ Full keyboard support
533
+ <div
534
+ role="button"
535
+ tabIndex={0}
536
+ onClick={handleClick}
537
+ onKeyDown={(e) => {
538
+ if (e.key === 'Enter' || e.key === ' ') {
539
+ e.preventDefault();
540
+ handleClick();
541
+ }
542
+ }}
543
+ >
544
+ Good
545
+ </div>
546
+
547
+ // ✅ Natural tab order
548
+ <button>First</button>
549
+ <button>Second</button>
550
+
551
+ // ✅ Descriptive labels
552
+ <button aria-label="Close menu">×</button>
553
+ <img alt="Company logo" />
554
+ ```
555
+
556
+ ## Validation Checklist
557
+
558
+ Before finalizing code, verify:
559
+
560
+ - [ ] Native HTML used when possible
561
+ - [ ] No redundant roles on native elements
562
+ - [ ] All IDs referenced by ARIA exist and are unique
563
+ - [ ] `aria-hidden` never on interactive elements
564
+ - [ ] Boolean ARIA uses `true`/`false`, not strings
565
+ - [ ] All interactive elements keyboard accessible
566
+ - [ ] No positive tabIndex values
567
+ - [ ] Dynamic content has live regions
568
+ - [ ] Form fields have labels