@mmerterden/multi-agent-pipeline 20.35.0 → 20.37.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/CHANGELOG.md +26 -0
- package/README.md +6 -5
- package/README.tr.md +6 -5
- package/docs/architecture.md +3 -3
- package/docs/ecosystem.md +5 -5
- package/docs/facts.json +4 -4
- package/install/catalog-history.json +1 -1
- package/manifest.json +210 -136
- package/package.json +5 -1
- package/pipeline/commands/multi-agent/SKILL.md +33 -154
- package/pipeline/commands/multi-agent/analysis/SKILL.md +14 -68
- package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +5 -8
- package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +38 -109
- package/pipeline/commands/multi-agent/channels/SKILL.md +38 -167
- package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +37 -144
- package/pipeline/commands/multi-agent/design-check/SKILL.md +27 -131
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +22 -48
- package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +37 -180
- package/pipeline/commands/multi-agent/jira/SKILL.md +16 -46
- package/pipeline/commands/multi-agent/office-pair/SKILL.md +66 -0
- package/pipeline/commands/multi-agent/office-status/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/refactor/SKILL.md +23 -184
- package/pipeline/commands/multi-agent/resume/SKILL.md +30 -136
- package/pipeline/commands/multi-agent/review/SKILL.md +24 -199
- package/pipeline/commands/multi-agent/setup/SKILL.md +25 -181
- package/pipeline/commands/multi-agent/stack/SKILL.md +9 -53
- package/pipeline/commands/multi-agent/status/SKILL.md +13 -2
- package/pipeline/commands/multi-agent/store-ready/SKILL.md +27 -226
- package/pipeline/commands/multi-agent/sync/SKILL.md +38 -167
- package/pipeline/commands/multi-agent/test-gen/SKILL.md +2 -4
- package/pipeline/commands/multi-agent/update/SKILL.md +18 -60
- package/pipeline/contract/CHANGELOG.md +9 -0
- package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
- package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
- package/pipeline/contract/fixtures/error-unsigned.json +1 -1
- package/pipeline/contract/fixtures/runs-awaiting-question.json +1 -1
- package/pipeline/contract/fixtures/runs-empty.json +1 -1
- package/pipeline/contract/fixtures/runs-failed.json +1 -1
- package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +1 -1
- package/pipeline/contract/fixtures/runs-pr-opened.json +1 -1
- package/pipeline/contract/fixtures/runs-running.json +1 -1
- package/pipeline/contract/manifest.json +5 -5
- package/pipeline/contract/types/index.d.ts +3 -2
- package/pipeline/lib/qr-code.mjs +26 -0
- package/pipeline/multi-agent-refs/autopilot-on/details.md +148 -0
- package/pipeline/multi-agent-refs/channels/generator.md +14 -0
- package/pipeline/multi-agent-refs/channels/preview.md +13 -0
- package/pipeline/multi-agent-refs/channels/reference.md +46 -0
- package/pipeline/multi-agent-refs/complaint-analysis/phases.md +159 -0
- package/pipeline/multi-agent-refs/cross-cli-contract.md +4 -4
- package/pipeline/multi-agent-refs/design-check/export.md +1 -0
- package/pipeline/multi-agent-refs/design-check/init-inventory.md +1 -1
- package/pipeline/multi-agent-refs/design-check/scope-and-ledger.md +64 -0
- package/pipeline/multi-agent-refs/features/analysis-command-notes.md +53 -0
- package/pipeline/multi-agent-refs/garbage-collect/phases.md +60 -0
- package/pipeline/multi-agent-refs/help/en.md +1 -0
- package/pipeline/multi-agent-refs/help/tr.md +1 -0
- package/pipeline/multi-agent-refs/ios-coding-standard/plan.md +71 -0
- package/pipeline/multi-agent-refs/ios-coding-standard/resolve-and-scan.md +135 -0
- package/pipeline/multi-agent-refs/jira/picker-detail.md +71 -0
- package/pipeline/multi-agent-refs/orchestrator/copilot-pipeline.md +88 -0
- package/pipeline/multi-agent-refs/orchestrator/dispatcher.md +96 -1
- package/pipeline/multi-agent-refs/refactor/sweep.md +190 -0
- package/pipeline/multi-agent-refs/resume/details.md +115 -2
- package/pipeline/multi-agent-refs/review/inputs.md +33 -1
- package/pipeline/multi-agent-refs/review/module-guides.md +9 -1
- package/pipeline/multi-agent-refs/review/panel.md +75 -0
- package/pipeline/multi-agent-refs/review/valven.md +5 -1
- package/pipeline/multi-agent-refs/setup/hooks-and-plugins.md +4 -0
- package/pipeline/multi-agent-refs/setup/token-save-flow.md +13 -0
- package/pipeline/multi-agent-refs/stack/background.md +72 -0
- package/pipeline/multi-agent-refs/store-ready/gates.md +139 -0
- package/pipeline/multi-agent-refs/store-ready/report.md +40 -2
- package/pipeline/multi-agent-refs/sync/office.md +10 -0
- package/pipeline/multi-agent-refs/sync/overview.md +61 -0
- package/pipeline/multi-agent-refs/sync/repo.md +91 -0
- package/pipeline/multi-agent-refs/update/details.md +83 -0
- package/pipeline/schemas/runs-index.schema.json +2 -1
- package/pipeline/scripts/contract-server.mjs +1 -1
- package/pipeline/scripts/office-pair.mjs +40 -9
- package/pipeline/scripts/runs-index.mjs +34 -7
- package/pipeline/scripts/status-overview.mjs +401 -0
- package/pipeline/skills/.skill-manifest.json +69 -65
- package/pipeline/skills/.skills-index.json +15 -4
- package/pipeline/skills/shared/README.md +4 -3
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +8 -71
- package/pipeline/skills/shared/core/apple-archive-compliance/references/examples.md +67 -0
- package/pipeline/skills/shared/core/apple-archive-compliance/references/version-history.md +8 -0
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +11 -73
- package/pipeline/skills/shared/core/google-play-compliance/references/examples.md +69 -0
- package/pipeline/skills/shared/core/google-play-compliance/references/notes.md +7 -0
- package/pipeline/skills/shared/core/google-play-compliance/references/version-history.md +5 -0
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +39 -262
- package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +35 -73
- package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +37 -337
- package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +27 -122
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +8 -340
- package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +37 -180
- package/pipeline/skills/shared/core/multi-agent-office-pair/SKILL.md +19 -0
- package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +23 -184
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +14 -82
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +25 -277
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +13 -2
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +75 -219
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +11 -162
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-code.md +257 -0
- package/pipeline/skills/shared/external/android-architecture/SKILL.md +7 -58
- package/pipeline/skills/shared/external/android-architecture/references/code-examples.md +68 -0
- package/pipeline/skills/shared/external/app-intents/SKILL.md +3 -45
- package/pipeline/skills/shared/external/app-intents/references/code-examples.md +54 -0
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +5 -81
- package/pipeline/skills/shared/external/app-store-optimization/references/engagement-and-localization.md +96 -0
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +6 -112
- package/pipeline/skills/shared/external/app-store-review/references/monetization-and-submission.md +138 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +10 -117
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models-code.md +186 -0
- package/pipeline/skills/shared/external/authentication/SKILL.md +3 -50
- package/pipeline/skills/shared/external/authentication/references/code-examples.md +59 -0
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +3 -84
- package/pipeline/skills/shared/external/callkit-voip/references/code-examples.md +93 -0
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +7 -263
- package/pipeline/skills/shared/external/ci-cd-pipelines/references/pipeline-examples.md +281 -0
- package/pipeline/skills/shared/external/clean-code/SKILL.md +8 -41
- package/pipeline/skills/shared/external/clean-code/references/code-examples.md +56 -0
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +8 -146
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-code.md +214 -0
- package/pipeline/skills/shared/external/compose-components/SKILL.md +6 -80
- package/pipeline/skills/shared/external/compose-components/references/code-examples.md +89 -0
- package/pipeline/skills/shared/external/compose-navigation/SKILL.md +5 -64
- package/pipeline/skills/shared/external/compose-navigation/references/code-examples.md +70 -0
- package/pipeline/skills/shared/external/core-data/SKILL.md +9 -157
- package/pipeline/skills/shared/external/core-data/references/core-data-code.md +221 -0
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +3 -89
- package/pipeline/skills/shared/external/core-nfc/references/code-examples.md +98 -0
- package/pipeline/skills/shared/external/coreml/SKILL.md +17 -133
- package/pipeline/skills/shared/external/coreml/references/coreml-code.md +208 -0
- package/pipeline/skills/shared/external/css-modern/SKILL.md +8 -131
- package/pipeline/skills/shared/external/css-modern/references/code-examples.md +142 -0
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +6 -103
- package/pipeline/skills/shared/external/database-patterns/references/orm-examples.md +112 -0
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +7 -48
- package/pipeline/skills/shared/external/html-semantic/references/code-examples.md +54 -0
- package/pipeline/skills/shared/external/humanizer/SKILL.md +6 -19
- package/pipeline/skills/shared/external/humanizer/references/voice.md +24 -0
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +13 -170
- package/pipeline/skills/shared/external/ios-accessibility/references/assistive-tech-review.md +224 -0
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +2 -24
- package/pipeline/skills/shared/external/ios-localization/references/code-examples.md +30 -0
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +3 -58
- package/pipeline/skills/shared/external/ios-networking/references/code-examples.md +67 -0
- package/pipeline/skills/shared/external/ios-security/SKILL.md +4 -64
- package/pipeline/skills/shared/external/ios-security/references/code-examples.md +74 -0
- package/pipeline/skills/shared/external/live-activities/SKILL.md +4 -90
- package/pipeline/skills/shared/external/live-activities/references/code-examples.md +108 -0
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +9 -23
- package/pipeline/skills/shared/external/localization-reuse-map/references/project-settings.md +22 -0
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +5 -95
- package/pipeline/skills/shared/external/mapkit-location/references/code-examples.md +117 -0
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +7 -106
- package/pipeline/skills/shared/external/nextjs-app-router/references/app-router-examples.md +124 -0
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +2 -31
- package/pipeline/skills/shared/external/permissionkit/references/code-examples.md +37 -0
- package/pipeline/skills/shared/external/play-store-review/SKILL.md +6 -74
- package/pipeline/skills/shared/external/play-store-review/references/signing-and-policies.md +83 -0
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +8 -125
- package/pipeline/skills/shared/external/push-notifications/references/notification-code.md +176 -0
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +7 -158
- package/pipeline/skills/shared/external/python-patterns/references/python-examples.md +176 -0
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +7 -157
- package/pipeline/skills/shared/external/rest-api-design/references/api-examples.md +175 -0
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +11 -230
- package/pipeline/skills/shared/external/swift-architecture/references/pattern-examples.md +295 -0
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +17 -149
- package/pipeline/skills/shared/external/swift-codable/references/codable-recipes.md +207 -0
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +12 -99
- package/pipeline/skills/shared/external/swift-concurrency/references/code-examples.md +100 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/swift-6-2-additions.md +18 -0
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +9 -78
- package/pipeline/skills/shared/external/swift-formatstyle/references/formatstyle-examples.md +113 -0
- package/pipeline/skills/shared/external/swift-language/SKILL.md +3 -56
- package/pipeline/skills/shared/external/swift-language/references/code-examples.md +65 -0
- package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +1 -0
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +6 -122
- package/pipeline/skills/shared/external/swiftdata/references/code-examples.md +140 -0
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +12 -139
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/interop-code.md +198 -0
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +9 -81
- package/pipeline/skills/shared/external/tailwind-css/references/code-examples.md +91 -0
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +7 -207
- package/pipeline/skills/shared/external/testing-backend/references/testing-examples.md +225 -0
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +8 -89
- package/pipeline/skills/shared/external/typescript-patterns/references/code-examples.md +100 -0
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +2 -20
- package/pipeline/skills/shared/external/vision-framework/references/code-examples.md +26 -0
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +7 -169
- package/pipeline/skills/shared/external/vue-composition/references/vue-examples.md +187 -0
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +7 -174
- package/pipeline/skills/shared/external/web-accessibility/references/a11y-examples.md +192 -0
- package/pipeline/skills/shared/external/web-performance/SKILL.md +8 -66
- package/pipeline/skills/shared/external/web-performance/references/code-examples.md +77 -0
- package/pipeline/skills/shared/external/web-testing/SKILL.md +11 -186
- package/pipeline/skills/shared/external/web-testing/references/testing-examples.md +213 -0
- package/pipeline/skills/skills-index.md +2 -1
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/format-and-output.md +0 -0
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/publish-and-snapshot.md +0 -0
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/sources-and-recipes.md +0 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Python pattern examples
|
|
2
|
+
|
|
3
|
+
<!-- toc -->
|
|
4
|
+
- [Pydantic v2](#pydantic-v2)
|
|
5
|
+
- [Async/Await with asyncio](#asyncawait-with-asyncio)
|
|
6
|
+
- [Pattern Matching (match/case)](#pattern-matching-matchcase)
|
|
7
|
+
<!-- /toc -->
|
|
8
|
+
|
|
9
|
+
Code for Pydantic v2 models, asyncio patterns and structural pattern matching. SKILL.md keeps the principles and the rules.
|
|
10
|
+
|
|
11
|
+
## Pydantic v2
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from pydantic import BaseModel, Field, field_validator, model_validator, ConfigDict
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
|
|
17
|
+
class CreateUserRequest(BaseModel):
|
|
18
|
+
model_config = ConfigDict(str_strip_whitespace=True, frozen=True)
|
|
19
|
+
|
|
20
|
+
name: str = Field(min_length=1, max_length=100)
|
|
21
|
+
email: str = Field(pattern=r'^[\w.-]+@[\w.-]+\.\w+$')
|
|
22
|
+
age: int = Field(ge=0, le=150)
|
|
23
|
+
role: Role = Role.USER
|
|
24
|
+
tags: list[str] = Field(default_factory=list, max_length=10)
|
|
25
|
+
|
|
26
|
+
@field_validator('email')
|
|
27
|
+
@classmethod
|
|
28
|
+
def normalize_email(cls, v: str) -> str:
|
|
29
|
+
return v.lower()
|
|
30
|
+
|
|
31
|
+
@model_validator(mode='after')
|
|
32
|
+
def validate_admin_age(self) -> 'CreateUserRequest':
|
|
33
|
+
if self.role == Role.ADMIN and self.age < 18:
|
|
34
|
+
raise ValueError("Admin must be 18 or older")
|
|
35
|
+
return self
|
|
36
|
+
|
|
37
|
+
class UserResponse(BaseModel):
|
|
38
|
+
id: str
|
|
39
|
+
name: str
|
|
40
|
+
email: str
|
|
41
|
+
role: Role
|
|
42
|
+
created_at: datetime
|
|
43
|
+
|
|
44
|
+
model_config = ConfigDict(from_attributes=True)
|
|
45
|
+
|
|
46
|
+
# Validation
|
|
47
|
+
try:
|
|
48
|
+
user = CreateUserRequest(name="Alice", email="ALICE@example.com", age=25)
|
|
49
|
+
print(user.email) # "alice@example.com" (normalized)
|
|
50
|
+
except ValidationError as e:
|
|
51
|
+
print(e.errors())
|
|
52
|
+
|
|
53
|
+
# Serialization
|
|
54
|
+
user_dict = user.model_dump()
|
|
55
|
+
user_json = user.model_dump_json()
|
|
56
|
+
|
|
57
|
+
# From ORM object
|
|
58
|
+
db_user = db.query(UserModel).first()
|
|
59
|
+
response = UserResponse.model_validate(db_user)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Async/Await with asyncio
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import asyncio
|
|
66
|
+
from collections.abc import AsyncIterator
|
|
67
|
+
import httpx
|
|
68
|
+
|
|
69
|
+
# Async function
|
|
70
|
+
async def fetch_user(user_id: str) -> dict:
|
|
71
|
+
async with httpx.AsyncClient() as client:
|
|
72
|
+
response = await client.get(f"https://api.example.com/users/{user_id}")
|
|
73
|
+
response.raise_for_status()
|
|
74
|
+
return response.json()
|
|
75
|
+
|
|
76
|
+
# Parallel execution
|
|
77
|
+
async def fetch_all_users(user_ids: list[str]) -> list[dict]:
|
|
78
|
+
async with httpx.AsyncClient() as client:
|
|
79
|
+
tasks = [
|
|
80
|
+
client.get(f"https://api.example.com/users/{uid}")
|
|
81
|
+
for uid in user_ids
|
|
82
|
+
]
|
|
83
|
+
responses = await asyncio.gather(*tasks, return_exceptions=True)
|
|
84
|
+
return [
|
|
85
|
+
r.json() for r in responses
|
|
86
|
+
if not isinstance(r, Exception) and r.status_code == 200
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
# Async context manager
|
|
90
|
+
from contextlib import asynccontextmanager
|
|
91
|
+
|
|
92
|
+
@asynccontextmanager
|
|
93
|
+
async def get_db_session() -> AsyncIterator[AsyncSession]:
|
|
94
|
+
session = AsyncSession(engine)
|
|
95
|
+
try:
|
|
96
|
+
yield session
|
|
97
|
+
await session.commit()
|
|
98
|
+
except Exception:
|
|
99
|
+
await session.rollback()
|
|
100
|
+
raise
|
|
101
|
+
finally:
|
|
102
|
+
await session.close()
|
|
103
|
+
|
|
104
|
+
# Async generator
|
|
105
|
+
async def stream_events(url: str) -> AsyncIterator[dict]:
|
|
106
|
+
async with httpx.AsyncClient() as client:
|
|
107
|
+
async with client.stream("GET", url) as response:
|
|
108
|
+
async for line in response.aiter_lines():
|
|
109
|
+
if line.startswith("data: "):
|
|
110
|
+
yield json.loads(line[6:])
|
|
111
|
+
|
|
112
|
+
# Semaphore for rate limiting
|
|
113
|
+
async def fetch_with_limit(urls: list[str], max_concurrent: int = 10) -> list[str]:
|
|
114
|
+
semaphore = asyncio.Semaphore(max_concurrent)
|
|
115
|
+
|
|
116
|
+
async def fetch_one(url: str) -> str:
|
|
117
|
+
async with semaphore:
|
|
118
|
+
async with httpx.AsyncClient() as client:
|
|
119
|
+
resp = await client.get(url)
|
|
120
|
+
return resp.text
|
|
121
|
+
|
|
122
|
+
return await asyncio.gather(*[fetch_one(url) for url in urls])
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Pattern Matching (match/case)
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from dataclasses import dataclass
|
|
129
|
+
|
|
130
|
+
@dataclass
|
|
131
|
+
class Point:
|
|
132
|
+
x: float
|
|
133
|
+
y: float
|
|
134
|
+
|
|
135
|
+
def describe_point(point: Point) -> str:
|
|
136
|
+
match point:
|
|
137
|
+
case Point(x=0, y=0):
|
|
138
|
+
return "origin"
|
|
139
|
+
case Point(x=0, y=y):
|
|
140
|
+
return f"on y-axis at y={y}"
|
|
141
|
+
case Point(x=x, y=0):
|
|
142
|
+
return f"on x-axis at x={x}"
|
|
143
|
+
case Point(x=x, y=y) if x == y:
|
|
144
|
+
return f"on diagonal at {x}"
|
|
145
|
+
case _:
|
|
146
|
+
return f"at ({point.x}, {point.y})"
|
|
147
|
+
|
|
148
|
+
# Matching on type and structure
|
|
149
|
+
def handle_response(response: dict) -> str:
|
|
150
|
+
match response:
|
|
151
|
+
case {"status": 200, "data": data}:
|
|
152
|
+
return f"Success: {data}"
|
|
153
|
+
case {"status": 404}:
|
|
154
|
+
return "Not found"
|
|
155
|
+
case {"status": status} if 500 <= status < 600:
|
|
156
|
+
return f"Server error: {status}"
|
|
157
|
+
case {"error": str(message)}:
|
|
158
|
+
return f"Error: {message}"
|
|
159
|
+
case _:
|
|
160
|
+
return "Unknown response"
|
|
161
|
+
|
|
162
|
+
# Matching sequences
|
|
163
|
+
def process_command(command: list[str]) -> None:
|
|
164
|
+
match command:
|
|
165
|
+
case ["quit" | "exit"]:
|
|
166
|
+
sys.exit(0)
|
|
167
|
+
case ["go", direction]:
|
|
168
|
+
move(direction)
|
|
169
|
+
case ["pick", "up", item]:
|
|
170
|
+
pick_up(item)
|
|
171
|
+
case ["drop", *items] if items:
|
|
172
|
+
for item in items:
|
|
173
|
+
drop(item)
|
|
174
|
+
case _:
|
|
175
|
+
print(f"Unknown command: {' '.join(command)}")
|
|
176
|
+
```
|
|
@@ -82,93 +82,13 @@ GET /api/v1/posts?fields=id,title,slug # Sparse fieldsets
|
|
|
82
82
|
|
|
83
83
|
## Response Envelope
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
// Consistent response shape
|
|
87
|
-
|
|
88
|
-
// Success: single resource
|
|
89
|
-
{
|
|
90
|
-
"data": {
|
|
91
|
-
"id": "user_abc123",
|
|
92
|
-
"type": "user",
|
|
93
|
-
"attributes": {
|
|
94
|
-
"name": "Alice Johnson",
|
|
95
|
-
"email": "alice@example.com",
|
|
96
|
-
"role": "admin",
|
|
97
|
-
"created_at": "2025-01-15T10:30:00Z"
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Success: collection
|
|
103
|
-
{
|
|
104
|
-
"data": [
|
|
105
|
-
{ "id": "post_1", "type": "post", "attributes": { ... } },
|
|
106
|
-
{ "id": "post_2", "type": "post", "attributes": { ... } }
|
|
107
|
-
],
|
|
108
|
-
"meta": {
|
|
109
|
-
"total": 150,
|
|
110
|
-
"page": 1,
|
|
111
|
-
"per_page": 20,
|
|
112
|
-
"has_next": true
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Error response
|
|
117
|
-
{
|
|
118
|
-
"error": {
|
|
119
|
-
"code": "VALIDATION_ERROR",
|
|
120
|
-
"message": "Request validation failed",
|
|
121
|
-
"details": [
|
|
122
|
-
{ "field": "email", "message": "Must be a valid email address" },
|
|
123
|
-
{ "field": "age", "message": "Must be between 0 and 150" }
|
|
124
|
-
],
|
|
125
|
-
"request_id": "req_xyz789"
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
```
|
|
85
|
+
Code for this section: [REST API examples](references/api-examples.md).
|
|
129
86
|
|
|
130
87
|
## Pagination
|
|
131
88
|
|
|
132
89
|
### Cursor-Based (Recommended)
|
|
133
90
|
|
|
134
|
-
|
|
135
|
-
// Request
|
|
136
|
-
// GET /api/v1/posts?limit=20&after=eyJpZCI6InBvc3RfMTAwIn0
|
|
137
|
-
|
|
138
|
-
// Response
|
|
139
|
-
{
|
|
140
|
-
"data": [ ... ],
|
|
141
|
-
"pagination": {
|
|
142
|
-
"has_next": true,
|
|
143
|
-
"has_prev": false,
|
|
144
|
-
"next_cursor": "eyJpZCI6InBvc3RfMTIwIn0",
|
|
145
|
-
"prev_cursor": null
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Server implementation (Node.js / Express)
|
|
150
|
-
async function listPosts(req: Request, res: Response) {
|
|
151
|
-
const limit = Math.min(parseInt(req.query.limit as string) || 20, 100);
|
|
152
|
-
const cursor = req.query.after ? decodeCursor(req.query.after as string) : null;
|
|
153
|
-
|
|
154
|
-
const posts = await db.post.findMany({
|
|
155
|
-
where: cursor ? { id: { gt: cursor.id } } : undefined,
|
|
156
|
-
orderBy: { id: 'asc' },
|
|
157
|
-
take: limit + 1, // Fetch one extra to check has_next
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
const hasNext = posts.length > limit;
|
|
161
|
-
if (hasNext) posts.pop();
|
|
162
|
-
|
|
163
|
-
res.json({
|
|
164
|
-
data: posts,
|
|
165
|
-
pagination: {
|
|
166
|
-
has_next: hasNext,
|
|
167
|
-
next_cursor: hasNext ? encodeCursor({ id: posts[posts.length - 1].id }) : null,
|
|
168
|
-
},
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
```
|
|
91
|
+
Code for this section: [REST API examples](references/api-examples.md).
|
|
172
92
|
|
|
173
93
|
### Offset-Based (Simple, for small datasets)
|
|
174
94
|
|
|
@@ -279,81 +199,7 @@ app.get('/api/v1/posts/:id', async (req, res) => {
|
|
|
279
199
|
|
|
280
200
|
## OpenAPI / Swagger Documentation
|
|
281
201
|
|
|
282
|
-
|
|
283
|
-
# openapi.yaml
|
|
284
|
-
openapi: 3.1.0
|
|
285
|
-
info:
|
|
286
|
-
title: My API
|
|
287
|
-
version: 1.0.0
|
|
288
|
-
description: Production REST API
|
|
289
|
-
|
|
290
|
-
servers:
|
|
291
|
-
- url: https://api.example.com/v1
|
|
292
|
-
|
|
293
|
-
paths:
|
|
294
|
-
/users:
|
|
295
|
-
get:
|
|
296
|
-
summary: List users
|
|
297
|
-
tags: [Users]
|
|
298
|
-
parameters:
|
|
299
|
-
- name: page
|
|
300
|
-
in: query
|
|
301
|
-
schema: { type: integer, minimum: 1, default: 1 }
|
|
302
|
-
- name: per_page
|
|
303
|
-
in: query
|
|
304
|
-
schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
|
|
305
|
-
responses:
|
|
306
|
-
'200':
|
|
307
|
-
description: List of users
|
|
308
|
-
content:
|
|
309
|
-
application/json:
|
|
310
|
-
schema:
|
|
311
|
-
type: object
|
|
312
|
-
properties:
|
|
313
|
-
data:
|
|
314
|
-
type: array
|
|
315
|
-
items: { $ref: '#/components/schemas/User' }
|
|
316
|
-
meta:
|
|
317
|
-
$ref: '#/components/schemas/PaginationMeta'
|
|
318
|
-
|
|
319
|
-
post:
|
|
320
|
-
summary: Create user
|
|
321
|
-
tags: [Users]
|
|
322
|
-
requestBody:
|
|
323
|
-
required: true
|
|
324
|
-
content:
|
|
325
|
-
application/json:
|
|
326
|
-
schema: { $ref: '#/components/schemas/CreateUserRequest' }
|
|
327
|
-
responses:
|
|
328
|
-
'201':
|
|
329
|
-
description: User created
|
|
330
|
-
headers:
|
|
331
|
-
Location:
|
|
332
|
-
schema: { type: string }
|
|
333
|
-
content:
|
|
334
|
-
application/json:
|
|
335
|
-
schema:
|
|
336
|
-
type: object
|
|
337
|
-
properties:
|
|
338
|
-
data: { $ref: '#/components/schemas/User' }
|
|
339
|
-
'422':
|
|
340
|
-
description: Validation error
|
|
341
|
-
content:
|
|
342
|
-
application/json:
|
|
343
|
-
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
344
|
-
|
|
345
|
-
components:
|
|
346
|
-
schemas:
|
|
347
|
-
User:
|
|
348
|
-
type: object
|
|
349
|
-
required: [id, name, email, role, created_at]
|
|
350
|
-
properties:
|
|
351
|
-
id: { type: string, format: uuid }
|
|
352
|
-
name: { type: string }
|
|
353
|
-
email: { type: string, format: email }
|
|
354
|
-
role: { type: string, enum: [admin, user, viewer] }
|
|
355
|
-
created_at: { type: string, format: date-time }
|
|
356
|
-
```
|
|
202
|
+
Code for this section: [REST API examples](references/api-examples.md).
|
|
357
203
|
|
|
358
204
|
## Do's
|
|
359
205
|
|
|
@@ -387,3 +233,7 @@ components:
|
|
|
387
233
|
| CORS errors | Missing headers on OPTIONS response | Configure CORS middleware with allowed origins |
|
|
388
234
|
| Stale cache | No cache invalidation strategy | Use ETag + conditional requests, short max-age |
|
|
389
235
|
| Breaking clients | Schema change without versioning | Add new version, deprecate old with Sunset header |
|
|
236
|
+
|
|
237
|
+
## References
|
|
238
|
+
|
|
239
|
+
- [REST API examples](references/api-examples.md): open when shaping response envelopes, implementing cursor pagination or writing an OpenAPI spec.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# REST API examples
|
|
2
|
+
|
|
3
|
+
<!-- toc -->
|
|
4
|
+
- [Response Envelope](#response-envelope)
|
|
5
|
+
- [Cursor-Based (Recommended)](#cursor-based-recommended)
|
|
6
|
+
- [OpenAPI / Swagger Documentation](#openapi-swagger-documentation)
|
|
7
|
+
<!-- /toc -->
|
|
8
|
+
|
|
9
|
+
Response envelope shapes, cursor pagination code and an OpenAPI document. SKILL.md keeps the URL, method, status, versioning, rate limiting and caching rules.
|
|
10
|
+
|
|
11
|
+
## Response Envelope
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// Consistent response shape
|
|
15
|
+
|
|
16
|
+
// Success: single resource
|
|
17
|
+
{
|
|
18
|
+
"data": {
|
|
19
|
+
"id": "user_abc123",
|
|
20
|
+
"type": "user",
|
|
21
|
+
"attributes": {
|
|
22
|
+
"name": "Alice Johnson",
|
|
23
|
+
"email": "alice@example.com",
|
|
24
|
+
"role": "admin",
|
|
25
|
+
"created_at": "2025-01-15T10:30:00Z"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Success: collection
|
|
31
|
+
{
|
|
32
|
+
"data": [
|
|
33
|
+
{ "id": "post_1", "type": "post", "attributes": { ... } },
|
|
34
|
+
{ "id": "post_2", "type": "post", "attributes": { ... } }
|
|
35
|
+
],
|
|
36
|
+
"meta": {
|
|
37
|
+
"total": 150,
|
|
38
|
+
"page": 1,
|
|
39
|
+
"per_page": 20,
|
|
40
|
+
"has_next": true
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Error response
|
|
45
|
+
{
|
|
46
|
+
"error": {
|
|
47
|
+
"code": "VALIDATION_ERROR",
|
|
48
|
+
"message": "Request validation failed",
|
|
49
|
+
"details": [
|
|
50
|
+
{ "field": "email", "message": "Must be a valid email address" },
|
|
51
|
+
{ "field": "age", "message": "Must be between 0 and 150" }
|
|
52
|
+
],
|
|
53
|
+
"request_id": "req_xyz789"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Cursor-Based (Recommended)
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// Request
|
|
62
|
+
// GET /api/v1/posts?limit=20&after=eyJpZCI6InBvc3RfMTAwIn0
|
|
63
|
+
|
|
64
|
+
// Response
|
|
65
|
+
{
|
|
66
|
+
"data": [ ... ],
|
|
67
|
+
"pagination": {
|
|
68
|
+
"has_next": true,
|
|
69
|
+
"has_prev": false,
|
|
70
|
+
"next_cursor": "eyJpZCI6InBvc3RfMTIwIn0",
|
|
71
|
+
"prev_cursor": null
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Server implementation (Node.js / Express)
|
|
76
|
+
async function listPosts(req: Request, res: Response) {
|
|
77
|
+
const limit = Math.min(parseInt(req.query.limit as string) || 20, 100);
|
|
78
|
+
const cursor = req.query.after ? decodeCursor(req.query.after as string) : null;
|
|
79
|
+
|
|
80
|
+
const posts = await db.post.findMany({
|
|
81
|
+
where: cursor ? { id: { gt: cursor.id } } : undefined,
|
|
82
|
+
orderBy: { id: 'asc' },
|
|
83
|
+
take: limit + 1, // Fetch one extra to check has_next
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const hasNext = posts.length > limit;
|
|
87
|
+
if (hasNext) posts.pop();
|
|
88
|
+
|
|
89
|
+
res.json({
|
|
90
|
+
data: posts,
|
|
91
|
+
pagination: {
|
|
92
|
+
has_next: hasNext,
|
|
93
|
+
next_cursor: hasNext ? encodeCursor({ id: posts[posts.length - 1].id }) : null,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## OpenAPI / Swagger Documentation
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
# openapi.yaml
|
|
103
|
+
openapi: 3.1.0
|
|
104
|
+
info:
|
|
105
|
+
title: My API
|
|
106
|
+
version: 1.0.0
|
|
107
|
+
description: Production REST API
|
|
108
|
+
|
|
109
|
+
servers:
|
|
110
|
+
- url: https://api.example.com/v1
|
|
111
|
+
|
|
112
|
+
paths:
|
|
113
|
+
/users:
|
|
114
|
+
get:
|
|
115
|
+
summary: List users
|
|
116
|
+
tags: [Users]
|
|
117
|
+
parameters:
|
|
118
|
+
- name: page
|
|
119
|
+
in: query
|
|
120
|
+
schema: { type: integer, minimum: 1, default: 1 }
|
|
121
|
+
- name: per_page
|
|
122
|
+
in: query
|
|
123
|
+
schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
|
|
124
|
+
responses:
|
|
125
|
+
'200':
|
|
126
|
+
description: List of users
|
|
127
|
+
content:
|
|
128
|
+
application/json:
|
|
129
|
+
schema:
|
|
130
|
+
type: object
|
|
131
|
+
properties:
|
|
132
|
+
data:
|
|
133
|
+
type: array
|
|
134
|
+
items: { $ref: '#/components/schemas/User' }
|
|
135
|
+
meta:
|
|
136
|
+
$ref: '#/components/schemas/PaginationMeta'
|
|
137
|
+
|
|
138
|
+
post:
|
|
139
|
+
summary: Create user
|
|
140
|
+
tags: [Users]
|
|
141
|
+
requestBody:
|
|
142
|
+
required: true
|
|
143
|
+
content:
|
|
144
|
+
application/json:
|
|
145
|
+
schema: { $ref: '#/components/schemas/CreateUserRequest' }
|
|
146
|
+
responses:
|
|
147
|
+
'201':
|
|
148
|
+
description: User created
|
|
149
|
+
headers:
|
|
150
|
+
Location:
|
|
151
|
+
schema: { type: string }
|
|
152
|
+
content:
|
|
153
|
+
application/json:
|
|
154
|
+
schema:
|
|
155
|
+
type: object
|
|
156
|
+
properties:
|
|
157
|
+
data: { $ref: '#/components/schemas/User' }
|
|
158
|
+
'422':
|
|
159
|
+
description: Validation error
|
|
160
|
+
content:
|
|
161
|
+
application/json:
|
|
162
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
163
|
+
|
|
164
|
+
components:
|
|
165
|
+
schemas:
|
|
166
|
+
User:
|
|
167
|
+
type: object
|
|
168
|
+
required: [id, name, email, role, created_at]
|
|
169
|
+
properties:
|
|
170
|
+
id: { type: string, format: uuid }
|
|
171
|
+
name: { type: string }
|
|
172
|
+
email: { type: string, format: email }
|
|
173
|
+
role: { type: string, enum: [admin, user, viewer] }
|
|
174
|
+
created_at: { type: string, format: date-time }
|
|
175
|
+
```
|