@caddis/cli 0.0.0 → 0.1.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 (153) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +150 -1
  3. package/bundles/antigravity-plugin/agents/code-reviewer.md +47 -0
  4. package/bundles/antigravity-plugin/agents/preflight.md +53 -0
  5. package/bundles/antigravity-plugin/guard_agy.py +338 -0
  6. package/bundles/antigravity-plugin/hooks.json +36 -0
  7. package/bundles/antigravity-plugin/mcp_config.json +8 -0
  8. package/bundles/antigravity-plugin/mcp_ping_server.py +55 -0
  9. package/bundles/antigravity-plugin/plugin.json +5 -0
  10. package/bundles/antigravity-plugin/session_end_agy.py +57 -0
  11. package/bundles/antigravity-plugin/skills/_registry.md +115 -0
  12. package/bundles/antigravity-plugin/skills/add-rules/SKILL.md +45 -0
  13. package/bundles/antigravity-plugin/skills/api-design/SKILL.md +249 -0
  14. package/bundles/antigravity-plugin/skills/backend-development/SKILL.md +305 -0
  15. package/bundles/antigravity-plugin/skills/best-practices/SKILL.md +500 -0
  16. package/bundles/antigravity-plugin/skills/best-practices/agents/best-practices-referencer.md +263 -0
  17. package/bundles/antigravity-plugin/skills/best-practices/agents/codebase-context-builder.md +326 -0
  18. package/bundles/antigravity-plugin/skills/best-practices/agents/task-intent-analyzer.md +245 -0
  19. package/bundles/antigravity-plugin/skills/best-practices/references/anti-patterns.md +571 -0
  20. package/bundles/antigravity-plugin/skills/best-practices/references/before-after-examples.md +1114 -0
  21. package/bundles/antigravity-plugin/skills/best-practices/references/best-practices-guide.md +513 -0
  22. package/bundles/antigravity-plugin/skills/best-practices/references/common-workflows.md +692 -0
  23. package/bundles/antigravity-plugin/skills/best-practices/references/prompt-patterns.md +547 -0
  24. package/bundles/antigravity-plugin/skills/brainstorming/SKILL.md +57 -0
  25. package/bundles/antigravity-plugin/skills/ci-cd-pipeline/SKILL.md +315 -0
  26. package/bundles/antigravity-plugin/skills/code-documentation/SKILL.md +271 -0
  27. package/bundles/antigravity-plugin/skills/code-review/SKILL.md +122 -0
  28. package/bundles/antigravity-plugin/skills/codebase-audit/SKILL.md +204 -0
  29. package/bundles/antigravity-plugin/skills/context-curator/SKILL.md +157 -0
  30. package/bundles/antigravity-plugin/skills/cross-review/SKILL.md +40 -0
  31. package/bundles/antigravity-plugin/skills/css-architecture/SKILL.md +305 -0
  32. package/bundles/antigravity-plugin/skills/css-architecture/references/RESPONSIVE-DESIGN.md +604 -0
  33. package/bundles/antigravity-plugin/skills/database-design/SKILL.md +177 -0
  34. package/bundles/antigravity-plugin/skills/db-diagram/SKILL.md +148 -0
  35. package/bundles/antigravity-plugin/skills/db-diagram/scripts/sql_to_graph.py +1212 -0
  36. package/bundles/antigravity-plugin/skills/digress/SKILL.md +61 -0
  37. package/bundles/antigravity-plugin/skills/draw-io/SKILL.md +162 -0
  38. package/bundles/antigravity-plugin/skills/draw-io/references/aws-icons.md +677 -0
  39. package/bundles/antigravity-plugin/skills/draw-io/references/layout-guidelines.md +142 -0
  40. package/bundles/antigravity-plugin/skills/draw-io/references/troubleshooting.md +118 -0
  41. package/bundles/antigravity-plugin/skills/draw-io/references/workflows.md +103 -0
  42. package/bundles/antigravity-plugin/skills/draw-io/scripts/convert-drawio-to-png.sh +25 -0
  43. package/bundles/antigravity-plugin/skills/draw-io/scripts/find_aws_icon.py +79 -0
  44. package/bundles/antigravity-plugin/skills/error-handling/SKILL.md +260 -0
  45. package/bundles/antigravity-plugin/skills/excalidraw-db/SKILL.md +38 -0
  46. package/bundles/antigravity-plugin/skills/fastapi-dev/SKILL.md +300 -0
  47. package/bundles/antigravity-plugin/skills/feature-plan/SKILL.md +198 -0
  48. package/bundles/antigravity-plugin/skills/frontend-design/SKILL.md +193 -0
  49. package/bundles/antigravity-plugin/skills/frontend-design/references/my-tech-stack.md +127 -0
  50. package/bundles/antigravity-plugin/skills/gh-cli/SKILL.md +195 -0
  51. package/bundles/antigravity-plugin/skills/git-commit/SKILL.md +285 -0
  52. package/bundles/antigravity-plugin/skills/golden-plan/SKILL.md +577 -0
  53. package/bundles/antigravity-plugin/skills/handoff/SKILL.md +100 -0
  54. package/bundles/antigravity-plugin/skills/implement/SKILL.md +114 -0
  55. package/bundles/antigravity-plugin/skills/javascript-typescript/SKILL.md +142 -0
  56. package/bundles/antigravity-plugin/skills/kb/SKILL.md +60 -0
  57. package/bundles/antigravity-plugin/skills/mermaid-db/SKILL.md +33 -0
  58. package/bundles/antigravity-plugin/skills/mermaid-diagrams/SKILL.md +236 -0
  59. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/ENHANCEMENTS.md +264 -0
  60. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/MERMAID-SUMMARY.md +137 -0
  61. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/advanced-features.md +556 -0
  62. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/architecture-diagrams.md +192 -0
  63. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/c4-diagrams.md +410 -0
  64. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/class-diagrams.md +361 -0
  65. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/erd-diagrams.md +510 -0
  66. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/flowcharts.md +450 -0
  67. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/sequence-diagrams.md +394 -0
  68. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/troubleshooting.md +335 -0
  69. package/bundles/antigravity-plugin/skills/mermaid-diagrams/references/workflows.md +418 -0
  70. package/bundles/antigravity-plugin/skills/migrate-dir/SKILL.md +68 -0
  71. package/bundles/antigravity-plugin/skills/mockup/SKILL.md +242 -0
  72. package/bundles/antigravity-plugin/skills/particle-art/SKILL.md +243 -0
  73. package/bundles/antigravity-plugin/skills/particle-art/references/canvas-utils.ts +171 -0
  74. package/bundles/antigravity-plugin/skills/particle-art/references/dot-field.template.tsx +203 -0
  75. package/bundles/antigravity-plugin/skills/particle-art/references/flow-field.template.tsx +263 -0
  76. package/bundles/antigravity-plugin/skills/particle-art/references/node-shape.template.tsx +261 -0
  77. package/bundles/antigravity-plugin/skills/particle-art/references/shape-sampler.ts +281 -0
  78. package/bundles/antigravity-plugin/skills/particle-art/references/stipple-morph.template.tsx +167 -0
  79. package/bundles/antigravity-plugin/skills/particle-art/references/stipple.template.tsx +175 -0
  80. package/bundles/antigravity-plugin/skills/particle-art/references/trail-ghost.template.tsx +266 -0
  81. package/bundles/antigravity-plugin/skills/particle-art/references/usage-examples.md +320 -0
  82. package/bundles/antigravity-plugin/skills/playwright/API_REFERENCE.md +653 -0
  83. package/bundles/antigravity-plugin/skills/playwright/SKILL.md +454 -0
  84. package/bundles/antigravity-plugin/skills/playwright/lib/helpers.js +441 -0
  85. package/bundles/antigravity-plugin/skills/playwright/package.json +26 -0
  86. package/bundles/antigravity-plugin/skills/playwright/run.js +228 -0
  87. package/bundles/antigravity-plugin/skills/prd/SKILL.md +107 -0
  88. package/bundles/antigravity-plugin/skills/preflight/SKILL.md +435 -0
  89. package/bundles/antigravity-plugin/skills/python/SKILL.md +388 -0
  90. package/bundles/antigravity-plugin/skills/react-best-practices/SKILL.md +269 -0
  91. package/bundles/antigravity-plugin/skills/react-dev/README.md +404 -0
  92. package/bundles/antigravity-plugin/skills/react-dev/SKILL.md +459 -0
  93. package/bundles/antigravity-plugin/skills/react-dev/examples/generic-components.md +579 -0
  94. package/bundles/antigravity-plugin/skills/react-dev/examples/server-components.md +579 -0
  95. package/bundles/antigravity-plugin/skills/react-dev/references/event-handlers.md +574 -0
  96. package/bundles/antigravity-plugin/skills/react-dev/references/hooks.md +456 -0
  97. package/bundles/antigravity-plugin/skills/react-dev/references/react-19-patterns.md +638 -0
  98. package/bundles/antigravity-plugin/skills/react-dev/references/react-router.md +1002 -0
  99. package/bundles/antigravity-plugin/skills/react-dev/references/tanstack-router.md +587 -0
  100. package/bundles/antigravity-plugin/skills/refactoring/SKILL.md +486 -0
  101. package/bundles/antigravity-plugin/skills/resume/SKILL.md +36 -0
  102. package/bundles/antigravity-plugin/skills/security-review/SKILL.md +196 -0
  103. package/bundles/antigravity-plugin/skills/setup-project-ai/SKILL.md +61 -0
  104. package/bundles/antigravity-plugin/skills/ship/SKILL.md +107 -0
  105. package/bundles/antigravity-plugin/skills/ship-merge/SKILL.md +103 -0
  106. package/bundles/antigravity-plugin/skills/ship-pr/SKILL.md +102 -0
  107. package/bundles/antigravity-plugin/skills/skill-creator/LICENSE.txt +202 -0
  108. package/bundles/antigravity-plugin/skills/skill-creator/SKILL.md +491 -0
  109. package/bundles/antigravity-plugin/skills/skill-creator/agents/analyzer.md +274 -0
  110. package/bundles/antigravity-plugin/skills/skill-creator/agents/comparator.md +202 -0
  111. package/bundles/antigravity-plugin/skills/skill-creator/agents/grader.md +223 -0
  112. package/bundles/antigravity-plugin/skills/skill-creator/assets/eval_review.html +146 -0
  113. package/bundles/antigravity-plugin/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  114. package/bundles/antigravity-plugin/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  115. package/bundles/antigravity-plugin/skills/skill-creator/references/schemas.md +430 -0
  116. package/bundles/antigravity-plugin/skills/skill-creator/scripts/__init__.py +0 -0
  117. package/bundles/antigravity-plugin/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  118. package/bundles/antigravity-plugin/skills/skill-creator/scripts/generate_report.py +326 -0
  119. package/bundles/antigravity-plugin/skills/skill-creator/scripts/improve_description.py +247 -0
  120. package/bundles/antigravity-plugin/skills/skill-creator/scripts/package_skill.py +136 -0
  121. package/bundles/antigravity-plugin/skills/skill-creator/scripts/quick_validate.py +103 -0
  122. package/bundles/antigravity-plugin/skills/skill-creator/scripts/run_eval.py +310 -0
  123. package/bundles/antigravity-plugin/skills/skill-creator/scripts/run_loop.py +328 -0
  124. package/bundles/antigravity-plugin/skills/skill-creator/scripts/utils.py +47 -0
  125. package/bundles/antigravity-plugin/skills/sql/SKILL.md +321 -0
  126. package/bundles/antigravity-plugin/skills/tdd/SKILL.md +37 -0
  127. package/bundles/antigravity-plugin/skills/tdd-workflow/SKILL.md +188 -0
  128. package/bundles/antigravity-plugin/skills/technical-writing/SKILL.md +286 -0
  129. package/bundles/antigravity-plugin/skills/test-strategy/SKILL.md +155 -0
  130. package/bundles/antigravity-plugin/skills/ui-brief/SKILL.md +84 -0
  131. package/bundles/antigravity-plugin/skills/ui-review/SKILL.md +176 -0
  132. package/bundles/antigravity-plugin/skills/ui-review/references/framework-fixes.md +471 -0
  133. package/bundles/antigravity-plugin/skills/ui-review/references/visual-checklist.md +236 -0
  134. package/bundles/antigravity-plugin/skills/usage-review/SKILL.md +77 -0
  135. package/bundles/antigravity-plugin/skills/use-model/SKILL.md +64 -0
  136. package/bundles/antigravity-plugin/skills/using-git-worktrees/SKILL.md +217 -0
  137. package/bundles/antigravity-plugin/skills/version/SKILL.md +18 -0
  138. package/bundles/antigravity-plugin/skills/warm-editorial-ui/DESIGN_TOKENS.md +487 -0
  139. package/bundles/antigravity-plugin/skills/warm-editorial-ui/IMPLEMENTATION_GUIDE.md +177 -0
  140. package/bundles/antigravity-plugin/skills/warm-editorial-ui/SKILL.md +732 -0
  141. package/bundles/antigravity-plugin/skills/webapp-testing/LICENSE.txt +202 -0
  142. package/bundles/antigravity-plugin/skills/webapp-testing/SKILL.md +97 -0
  143. package/bundles/antigravity-plugin/skills/webapp-testing/examples/console_logging.py +35 -0
  144. package/bundles/antigravity-plugin/skills/webapp-testing/examples/element_discovery.py +40 -0
  145. package/bundles/antigravity-plugin/skills/webapp-testing/examples/static_html_automation.py +33 -0
  146. package/bundles/antigravity-plugin/skills/webapp-testing/scripts/with_server.py +106 -0
  147. package/bundles/antigravity-plugin/skills/windows-deployment/SKILL.md +880 -0
  148. package/bundles/antigravity-plugin/skills/writing-plans/SKILL.md +384 -0
  149. package/bundles/antigravity-plugin/statusline-command-agy.sh +91 -0
  150. package/bundles/antigravity-plugin/warm_start_agy.py +149 -0
  151. package/bundles/manifest.json +6 -0
  152. package/dist/cli.js +5363 -0
  153. package/package.json +61 -4
@@ -0,0 +1,260 @@
1
+ ---
2
+ name: error-handling
3
+ description: Error handling patterns for Python and TypeScript applications. Use when designing error hierarchies, implementing retry logic, building error boundaries, or establishing logging strategies. Covers custom exceptions, result types, circuit breakers, and user-facing error messages.
4
+ ---
5
+
6
+ # Error Handling Skill
7
+
8
+ Errors are not exceptional — they are part of the contract. This skill defines how to classify, propagate, and recover from errors consistently.
9
+
10
+ ## 1. When to Apply This Skill
11
+
12
+ **Trigger conditions:**
13
+ - Designing error handling for a new service or feature
14
+ - "How should we handle errors in this module?"
15
+ - Building retry logic or circuit breakers
16
+ - Reviewing code with bare `except:` or swallowed errors
17
+ - Establishing error handling conventions for a project
18
+
19
+ ## 2. Error Classification
20
+
21
+ Every error falls into one of three categories. Handle each differently.
22
+
23
+ | Category | Retryable? | User Action? | Example |
24
+ |----------|-----------|-------------|---------|
25
+ | **Transient** | Yes | None (auto-retry) | Network timeout, DB connection dropped, rate limit |
26
+ | **Operational** | No | Inform user | Invalid input, resource not found, insufficient permissions |
27
+ | **Fatal** | No | Alert team | Corrupted state, config missing, unrecoverable assertion |
28
+
29
+ ## 3. Python Exception Hierarchy
30
+
31
+ Build domain-specific exceptions. Never raise raw `Exception`.
32
+
33
+ ```python
34
+ class AppError(Exception):
35
+ """Base error for the application."""
36
+ def __init__(self, message: str, code: str | None = None):
37
+ self.message = message
38
+ self.code = code or self.__class__.__name__
39
+ super().__init__(self.message)
40
+
41
+ # Operational errors (expected, handled gracefully)
42
+ class NotFoundError(AppError):
43
+ """Resource does not exist."""
44
+
45
+ class ValidationError(AppError):
46
+ """Input validation failed."""
47
+
48
+ class AuthorizationError(AppError):
49
+ """User not authorized for this action."""
50
+
51
+ # Transient errors (retry-eligible)
52
+ class TransientError(AppError):
53
+ """Temporary failure — safe to retry."""
54
+
55
+ class ExternalServiceError(TransientError):
56
+ """External API call failed."""
57
+
58
+ class DatabaseConnectionError(TransientError):
59
+ """Database connection lost."""
60
+ ```
61
+
62
+ ### Usage Patterns
63
+
64
+ ```python
65
+ # ✅ GOOD: Specific exception with context
66
+ def get_user(user_id: str) -> User:
67
+ user = repo.find_by_id(user_id)
68
+ if user is None:
69
+ raise NotFoundError(f"User {user_id} not found")
70
+ return user
71
+
72
+ # ✅ GOOD: Catch specific, re-raise as domain error
73
+ def fetch_external_data(url: str) -> dict:
74
+ try:
75
+ response = httpx.get(url, timeout=10)
76
+ response.raise_for_status()
77
+ return response.json()
78
+ except httpx.TimeoutException as e:
79
+ raise ExternalServiceError(f"Timeout calling {url}") from e
80
+ except httpx.HTTPStatusError as e:
81
+ raise ExternalServiceError(f"HTTP {e.response.status_code} from {url}") from e
82
+
83
+ # ❌ BAD: Bare except swallows everything
84
+ try:
85
+ result = process()
86
+ except:
87
+ pass
88
+
89
+ # ❌ BAD: Catching too broadly
90
+ try:
91
+ result = process()
92
+ except Exception:
93
+ logger.error("Something went wrong")
94
+ return None # Hides the real error
95
+ ```
96
+
97
+ ## 4. TypeScript Error Patterns
98
+
99
+ ### Result Type (for expected failures)
100
+
101
+ ```typescript
102
+ type Result<T, E = Error> =
103
+ | { ok: true; value: T }
104
+ | { ok: false; error: E };
105
+
106
+ function parseConfig(raw: string): Result<Config, ValidationError> {
107
+ try {
108
+ const parsed = JSON.parse(raw);
109
+ if (!isValidConfig(parsed)) {
110
+ return { ok: false, error: new ValidationError("Invalid config shape") };
111
+ }
112
+ return { ok: true, value: parsed as Config };
113
+ } catch {
114
+ return { ok: false, error: new ValidationError("Invalid JSON") };
115
+ }
116
+ }
117
+
118
+ // Usage — caller must handle both cases
119
+ const result = parseConfig(rawInput);
120
+ if (!result.ok) {
121
+ showError(result.error.message);
122
+ return;
123
+ }
124
+ useConfig(result.value);
125
+ ```
126
+
127
+ ### Error Boundaries (React)
128
+
129
+ ```tsx
130
+ 'use client';
131
+ import { Component, type ReactNode } from 'react';
132
+
133
+ interface Props { children: ReactNode; fallback: ReactNode; }
134
+ interface State { hasError: boolean; }
135
+
136
+ export class ErrorBoundary extends Component<Props, State> {
137
+ state: State = { hasError: false };
138
+
139
+ static getDerivedStateFromError(): State {
140
+ return { hasError: true };
141
+ }
142
+
143
+ componentDidCatch(error: Error, info: React.ErrorInfo) {
144
+ console.error('Component error:', error, info.componentStack);
145
+ // Report to error tracking service
146
+ }
147
+
148
+ render() {
149
+ return this.state.hasError ? this.props.fallback : this.props.children;
150
+ }
151
+ }
152
+ ```
153
+
154
+ ## 5. Retry with Exponential Backoff
155
+
156
+ ```python
157
+ import asyncio
158
+ import random
159
+ from loguru import logger
160
+
161
+ async def retry_with_backoff(
162
+ fn,
163
+ max_retries: int = 3,
164
+ base_delay: float = 1.0,
165
+ max_delay: float = 30.0,
166
+ retryable_exceptions: tuple = (TransientError,),
167
+ ):
168
+ """Retry a function with exponential backoff and jitter."""
169
+ for attempt in range(max_retries + 1):
170
+ try:
171
+ return await fn()
172
+ except retryable_exceptions as e:
173
+ if attempt == max_retries:
174
+ logger.error(f"All {max_retries} retries exhausted: {e}")
175
+ raise
176
+ delay = min(base_delay * (2 ** attempt), max_delay)
177
+ jitter = random.uniform(0, delay * 0.1)
178
+ logger.warning(f"Attempt {attempt + 1} failed: {e}. Retrying in {delay:.1f}s")
179
+ await asyncio.sleep(delay + jitter)
180
+ ```
181
+
182
+ ## 6. FastAPI Error Handling
183
+
184
+ ```python
185
+ from fastapi import FastAPI, Request
186
+ from fastapi.responses import JSONResponse
187
+ from loguru import logger
188
+
189
+ app = FastAPI()
190
+
191
+ @app.exception_handler(NotFoundError)
192
+ async def not_found_handler(request: Request, exc: NotFoundError):
193
+ return JSONResponse(status_code=404, content={"error": exc.code, "message": exc.message})
194
+
195
+ @app.exception_handler(ValidationError)
196
+ async def validation_handler(request: Request, exc: ValidationError):
197
+ return JSONResponse(status_code=422, content={"error": exc.code, "message": exc.message})
198
+
199
+ @app.exception_handler(AuthorizationError)
200
+ async def auth_handler(request: Request, exc: AuthorizationError):
201
+ return JSONResponse(status_code=403, content={"error": exc.code, "message": exc.message})
202
+
203
+ @app.exception_handler(Exception)
204
+ async def unhandled_handler(request: Request, exc: Exception):
205
+ logger.exception(f"Unhandled error on {request.method} {request.url}")
206
+ # Never expose internal details to the client
207
+ return JSONResponse(status_code=500, content={"error": "InternalError", "message": "An internal error occurred."})
208
+ ```
209
+
210
+ ## 7. Logging Errors Correctly
211
+
212
+ ```python
213
+ from loguru import logger
214
+
215
+ # ✅ GOOD: Log with context, not just the message
216
+ logger.error(f"Failed to process order {order_id}: {e}", order_id=order_id, customer_id=customer_id)
217
+
218
+ # ✅ GOOD: Use exception() to capture stack trace
219
+ try:
220
+ process_order(order)
221
+ except Exception as e:
222
+ logger.exception(f"Order processing failed for {order.id}")
223
+ raise
224
+
225
+ # ❌ BAD: Logging sensitive data
226
+ logger.error(f"Auth failed for {username} with password {password}")
227
+
228
+ # ❌ BAD: Logging without context
229
+ logger.error("Something failed")
230
+ ```
231
+
232
+ ## 8. Error Message Guidelines
233
+
234
+ | Audience | Include | Exclude |
235
+ |----------|---------|---------|
236
+ | **End user** | What happened, what to do next | Stack traces, internal IDs, SQL errors |
237
+ | **Developer (logs)** | Full context, request ID, stack trace | User passwords, tokens, PII |
238
+ | **API consumer** | Error code, human message, field-level detail | Server internals, file paths |
239
+
240
+ ```python
241
+ # User-facing message
242
+ "Unable to load your dashboard. Please try again in a few minutes."
243
+
244
+ # API response
245
+ {"error": "ValidationError", "message": "Email format is invalid", "field": "email"}
246
+
247
+ # Log entry
248
+ "Validation failed on POST /api/users: email='not-an-email' — rejected by regex validator"
249
+ ```
250
+
251
+ ## 9. Anti-Patterns
252
+
253
+ | Anti-Pattern | Problem | Fix |
254
+ |---|---|---|
255
+ | Bare `except:` / `except Exception:` | Catches KeyboardInterrupt, SystemExit | Catch specific exceptions |
256
+ | `return None` on error | Caller has no idea what went wrong | Raise typed exception |
257
+ | Logging and re-raising | Double logging, noisy | Log at the boundary, not at every layer |
258
+ | String error codes | Typos, no autocomplete | Enum or class-based error codes |
259
+ | Swallowing errors in background tasks | Silent failures, data loss | Log + alert + dead-letter queue |
260
+ | Generic "Something went wrong" | User can't self-serve | Specific message + action hint |
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: excalidraw-db
3
+ description: Turn a SQL artifact into an Excalidraw diagram for a design review / ARB pack / slide — higher-level, drag-the-boxes format
4
+ ---
5
+
6
+ # /excalidraw-db — diagram a SQL artifact as Excalidraw
7
+
8
+ Explain a SQL artifact as an **Excalidraw** diagram — for a design review, an ARB pack, or a slide deck,
9
+ anywhere someone needs to drag boxes around rather than read a text file. For a git-tracked reference
10
+ diagram, prefer `/mermaid-db` instead.
11
+
12
+ Input: **$ARGUMENTS**
13
+
14
+ ## Do this
15
+
16
+ Load and follow the **`db-diagram`** skill; produce the Excalidraw output specifically. In short:
17
+
18
+ 1. **Get the SQL** — same resolution as `/mermaid-db` (file / DB object via MCP or read-only `sqlcmd` /
19
+ context / pasted). Multiple objects → ONE diagram of their relationships.
20
+ 2. **Generate the `.excalidraw` directly** — run the skill's extractor with
21
+ `scripts/sql_to_graph.py --file <path> --format excalidraw` and save the output as a `.excalidraw` file.
22
+ The generator lays out deterministically on a **white canvas**: a query becomes a **left-to-right data
23
+ flow** (sources → filters → result → projection) with container-bound text that stays inside every box;
24
+ schema DDL becomes an **ER diagram** where each entity has a distinct **header + bulleted columns** and
25
+ every FK arrow is **edge-anchored and orthogonally routed so it never crosses (or hides behind) a box** —
26
+ even on hub / chain / self-reference schemas. No separate drawing skill, no MCP server.
27
+ 3. **Optional no-app preview** — for a shareable page, `--format html` emits a self-contained HTML (inline
28
+ SVG, a light/dark toggle that seeds from the OS and never half-applies, no external requests);
29
+ `--format svg` emits a standalone SVG.
30
+ 4. **Narrate + caveat** — include the business context and the execution-plan caveat (verbatim, per the
31
+ skill).
32
+
33
+ ## Rules (from the skill — non-negotiable)
34
+
35
+ - **Read-only.** Never run DDL/DML.
36
+ - **Never guess schema.** Mark anything inferred-from-SQL-only.
37
+ - **Don't maintain both formats for one artifact.** If a Mermaid diagram already exists for this object,
38
+ say so and ask: a one-off review copy, or a replacement?
@@ -0,0 +1,300 @@
1
+ ---
2
+ name: fastapi-dev
3
+ description: Build FastAPI backends with standard patterns, error handling, and testing
4
+ ---
5
+
6
+ # FastAPI Development
7
+
8
+ > **Project Context** — Read `project-config.md` in the repo root for brand tokens, shared-library paths, and deployment targets.
9
+
10
+ ## When to Use
11
+
12
+ Invoke this skill when:
13
+ - Creating a new FastAPI backend for a project
14
+ - Adding API endpoints for Streamlit frontends
15
+ - Implementing repository pattern with SQL Server
16
+ - Setting up dependency injection chains
17
+ - Configuring CORS for Streamlit ↔ FastAPI communication
18
+ - Deploying FastAPI on IIS/on-premise infrastructure
19
+
20
+ ---
21
+
22
+ ## Project Structure
23
+
24
+ ```
25
+ backend/
26
+ ├── app/
27
+ │ ├── __init__.py
28
+ │ ├── main.py # FastAPI application entry
29
+ │ ├── config.py # Settings (pydantic-settings)
30
+ │ ├── dependencies.py # Shared DI providers
31
+ │ ├── routers/
32
+ │ │ ├── __init__.py
33
+ │ │ ├── records.py # Records endpoints
34
+ │ │ └── analytics.py # Analytics endpoints
35
+ │ ├── models/
36
+ │ │ ├── __init__.py
37
+ │ │ ├── record.py # Pydantic request/response models
38
+ │ │ └── user.py
39
+ │ ├── services/
40
+ │ │ ├── __init__.py
41
+ │ │ └── record_service.py
42
+ │ └── repositories/
43
+ │ ├── __init__.py
44
+ │ └── record_repo.py
45
+ ├── tests/
46
+ ├── .env
47
+ └── requirements.txt
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Steps
53
+
54
+ ### Step 1: Configuration (pydantic-settings)
55
+
56
+ ```python
57
+ # app/config.py
58
+ from functools import lru_cache
59
+ from pydantic_settings import BaseSettings
60
+ from pydantic import Field
61
+
62
+ class Settings(BaseSettings):
63
+ app_name: str = "API"
64
+ debug: bool = False
65
+ db_server: str = Field(..., description="SQL Server host")
66
+ db_name: str = Field(..., description="Database name")
67
+ db_password: str = Field(..., repr=False)
68
+ api_key: str = Field(..., repr=False)
69
+ allowed_origins: list[str] = ["http://localhost:8501"]
70
+ ollama_base_url: str = "http://localhost:11434"
71
+ ollama_model: str = "llama3.1"
72
+
73
+ class Config:
74
+ env_file = ".env"
75
+
76
+ @lru_cache
77
+ def get_settings() -> Settings:
78
+ return Settings()
79
+ ```
80
+
81
+ ### Step 2: Pydantic Models (Base/Create/Update/Response pattern)
82
+
83
+ ```python
84
+ # app/models/record.py
85
+ from pydantic import BaseModel, ConfigDict, Field
86
+ from uuid import UUID
87
+ from datetime import datetime
88
+ from typing import Literal
89
+
90
+ class RecordBase(BaseModel):
91
+ customer_id: str = Field(..., min_length=1, max_length=50, example="CUST001")
92
+ record_type: Literal["billing", "service", "technical"]
93
+ description: str = Field(..., min_length=10, max_length=5000)
94
+ priority: int = Field(default=3, ge=1, le=5)
95
+
96
+ class RecordCreate(RecordBase):
97
+ pass
98
+
99
+ class RecordUpdate(BaseModel):
100
+ status: Literal["open", "in_progress", "resolved"] | None = None
101
+ resolution_notes: str | None = Field(None, max_length=2000)
102
+ priority: int | None = Field(None, ge=1, le=5)
103
+
104
+ class RecordResponse(RecordBase):
105
+ model_config = ConfigDict(from_attributes=True)
106
+ id: UUID
107
+ status: str
108
+ created_at: datetime
109
+ updated_at: datetime | None = None
110
+
111
+ class RecordList(BaseModel):
112
+ items: list[RecordResponse]
113
+ total: int
114
+ page: int
115
+ per_page: int
116
+ pages: int
117
+ ```
118
+
119
+ ### Step 3: Dependency Injection Chain
120
+
121
+ ```python
122
+ # app/dependencies.py
123
+ from typing import Annotated, Generator
124
+ from fastapi import Depends, Header, HTTPException
125
+ from libs.data import DatabaseAdapter
126
+
127
+ def get_db_adapter() -> Generator[DatabaseAdapter, None, None]:
128
+ adapter = DatabaseAdapter()
129
+ try:
130
+ yield adapter
131
+ finally:
132
+ adapter.close()
133
+
134
+ def get_record_repository(
135
+ db: Annotated[DatabaseAdapter, Depends(get_db_adapter)]
136
+ ) -> RecordRepository:
137
+ return RecordRepository(db)
138
+
139
+ def get_record_service(
140
+ repo: Annotated[RecordRepository, Depends(get_record_repository)]
141
+ ) -> RecordService:
142
+ return RecordService(repo)
143
+
144
+ async def verify_api_key(
145
+ x_api_key: Annotated[str, Header(..., description="API Key")]
146
+ ) -> str:
147
+ if x_api_key != get_settings().api_key:
148
+ raise HTTPException(status_code=401, detail="Invalid API key")
149
+ return x_api_key
150
+ ```
151
+
152
+ ### Step 4: Router Pattern
153
+
154
+ ```python
155
+ # app/routers/records.py
156
+ from fastapi import APIRouter, Depends, Query
157
+ from typing import Annotated
158
+
159
+ router = APIRouter(prefix="/records", tags=["records"])
160
+
161
+ @router.get("/", response_model=RecordList)
162
+ async def list_records(
163
+ service: Annotated[RecordService, Depends(get_record_service)],
164
+ page: int = Query(1, ge=1),
165
+ per_page: int = Query(25, ge=1, le=100),
166
+ status: str | None = None,
167
+ ):
168
+ return await service.list_records(page=page, per_page=per_page, status=status)
169
+
170
+ @router.post("/", response_model=RecordResponse, status_code=201)
171
+ async def create_record(
172
+ record: RecordCreate,
173
+ service: Annotated[RecordService, Depends(get_record_service)],
174
+ ):
175
+ return await service.create_record(record)
176
+ ```
177
+
178
+ ### Step 5: Application Entry
179
+
180
+ ```python
181
+ # app/main.py
182
+ from fastapi import FastAPI, Request
183
+ from fastapi.middleware.cors import CORSMiddleware
184
+ from fastapi.responses import JSONResponse
185
+ from loguru import logger
186
+
187
+ app = FastAPI(title="API", version="1.0.0")
188
+
189
+ # CORS for Streamlit frontends
190
+ app.add_middleware(
191
+ CORSMiddleware,
192
+ allow_origins=get_settings().allowed_origins,
193
+ allow_credentials=True,
194
+ allow_methods=["*"],
195
+ allow_headers=["*"],
196
+ )
197
+
198
+ app.include_router(records_router)
199
+
200
+ @app.get("/health")
201
+ async def health_check():
202
+ return {"status": "healthy", "service": "API"}
203
+
204
+ @app.exception_handler(Exception)
205
+ async def global_exception_handler(request: Request, exc: Exception):
206
+ logger.exception(f"Unhandled error on {request.url.path}: {exc}")
207
+ return JSONResponse(status_code=500, content={"detail": "An internal error occurred"})
208
+ ```
209
+
210
+ ### Step 6: SQL Server Connection
211
+
212
+ ```python
213
+ # Using pyodbc for MSSQL with Windows Auth
214
+ import pyodbc
215
+
216
+ conn_str = (
217
+ "DRIVER={ODBC Driver 18 for SQL Server};"
218
+ f"SERVER={settings.db_server};"
219
+ f"DATABASE={settings.db_name};"
220
+ "Trusted_Connection=yes;"
221
+ "Encrypt=yes;"
222
+ "TrustServerCertificate=yes;"
223
+ )
224
+ conn = pyodbc.connect(conn_str, timeout=10)
225
+ ```
226
+
227
+ ---
228
+
229
+ ## Patterns and Examples
230
+
231
+ ### Repository Pattern
232
+
233
+ ```python
234
+ class RecordRepository:
235
+ def __init__(self, adapter: DatabaseAdapter):
236
+ self._adapter = adapter
237
+
238
+ def get_by_id(self, record_id: UUID) -> dict | None:
239
+ query = "SELECT * FROM AppRecords WHERE CaseID = ?"
240
+ return self._adapter.fetch_one(query, (str(record_id),))
241
+
242
+ def get_paginated(self, page: int, per_page: int, status: str | None) -> tuple[list, int]:
243
+ count_query = "SELECT COUNT(*) FROM AppRecords"
244
+ params = []
245
+ if status:
246
+ count_query += " WHERE Status = ?"
247
+ params.append(status)
248
+ total = self._adapter.fetch_scalar(count_query, params)
249
+
250
+ query = "SELECT * FROM AppRecords"
251
+ if status:
252
+ query += " WHERE Status = ?"
253
+ query += " ORDER BY [Created Date Time] DESC OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"
254
+ params.extend([(page - 1) * per_page, per_page])
255
+ items = self._adapter.fetch_all(query, params)
256
+ return items, total
257
+ ```
258
+
259
+ ### Error Handling with loguru
260
+
261
+ ```python
262
+ from loguru import logger
263
+
264
+ @router.post("/")
265
+ async def create_record(record: RecordCreate, service: ...):
266
+ try:
267
+ result = await service.create_record(record)
268
+ logger.info(f"Created record {result.id} for customer {record.customer_id}")
269
+ return result
270
+ except ValidationError as e:
271
+ logger.warning(f"Validation error: {e}")
272
+ raise HTTPException(status_code=422, detail=str(e))
273
+ except Exception as e:
274
+ logger.exception(f"Failed to create record: {e}")
275
+ raise HTTPException(status_code=500, detail="Internal error")
276
+ ```
277
+
278
+ ---
279
+
280
+ ## Checklist
281
+
282
+ - [ ] Project structure follows standard pattern (routers/models/services/repositories)
283
+ - [ ] Pydantic models use Base/Create/Update/Response hierarchy
284
+ - [ ] Dependency injection chain: adapter -> repo -> service
285
+ - [ ] CORS configured for Streamlit frontend origins
286
+ - [ ] Health check endpoint at `/health`
287
+ - [ ] Global exception handler with loguru logging
288
+ - [ ] SQL queries use parameterized inputs (no f-strings)
289
+ - [ ] Configuration via pydantic-settings and .env file
290
+ - [ ] API key verification on protected endpoints
291
+ - [ ] All endpoints have proper response_model types
292
+
293
+ ## Related Resources
294
+
295
+ | Resource | Path |
296
+ |----------|------|
297
+ | FastAPI instructions | `instructions/fastapi.instructions.md` |
298
+ | SQL expert skill | `skills/coding/sql/SKILL.md` |
299
+ | Python instructions | `instructions/python.instructions.md` |
300
+ | Security instructions | `instructions/security.instructions.md` |