codeforge-dev 1.7.0 → 1.8.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/.devcontainer/.env +4 -6
- package/.devcontainer/.env.example +29 -0
- package/.devcontainer/.gitignore +8 -0
- package/.devcontainer/.secrets.example +12 -0
- package/.devcontainer/CHANGELOG.md +130 -0
- package/.devcontainer/CLAUDE.md +56 -19
- package/.devcontainer/README.md +111 -56
- package/.devcontainer/config/{main-system-prompt.md → defaults/main-system-prompt.md} +72 -0
- package/.devcontainer/config/file-manifest.json +20 -0
- package/.devcontainer/devcontainer.json +20 -0
- package/.devcontainer/docs/configuration-reference.md +90 -0
- package/.devcontainer/docs/keybindings.md +100 -0
- package/.devcontainer/docs/optional-features.md +129 -0
- package/.devcontainer/docs/plugins.md +154 -0
- package/.devcontainer/docs/troubleshooting.md +128 -0
- package/.devcontainer/features/agent-browser/install.sh +6 -0
- package/.devcontainer/features/ast-grep/install.sh +6 -0
- package/.devcontainer/features/biome/README.md +27 -0
- package/.devcontainer/features/biome/install.sh +6 -0
- package/.devcontainer/features/ccburn/install.sh +6 -0
- package/.devcontainer/features/ccstatusline/devcontainer-feature.json +5 -0
- package/.devcontainer/features/ccstatusline/install.sh +7 -0
- package/.devcontainer/features/ccusage/install.sh +6 -0
- package/.devcontainer/features/claude-monitor/install.sh +6 -0
- package/.devcontainer/features/dprint/README.md +30 -0
- package/.devcontainer/features/dprint/devcontainer-feature.json +18 -0
- package/.devcontainer/features/dprint/install.sh +131 -0
- package/.devcontainer/features/hadolint/README.md +35 -0
- package/.devcontainer/features/hadolint/devcontainer-feature.json +13 -0
- package/.devcontainer/features/hadolint/install.sh +86 -0
- package/.devcontainer/features/lsp-servers/devcontainer-feature.json +5 -0
- package/.devcontainer/features/lsp-servers/install.sh +7 -0
- package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +5 -0
- package/.devcontainer/features/mcp-qdrant/install.sh +13 -6
- package/.devcontainer/features/mcp-reasoner/devcontainer-feature.json +5 -0
- package/.devcontainer/features/mcp-reasoner/install.sh +8 -1
- package/.devcontainer/features/notify-hook/devcontainer-feature.json +5 -0
- package/.devcontainer/features/notify-hook/install.sh +7 -0
- package/.devcontainer/features/ruff/README.md +26 -0
- package/.devcontainer/features/ruff/devcontainer-feature.json +21 -0
- package/.devcontainer/features/ruff/install.sh +74 -0
- package/.devcontainer/features/shellcheck/README.md +38 -0
- package/.devcontainer/features/shellcheck/devcontainer-feature.json +13 -0
- package/.devcontainer/features/shellcheck/install.sh +24 -0
- package/.devcontainer/features/shfmt/README.md +37 -0
- package/.devcontainer/features/shfmt/devcontainer-feature.json +13 -0
- package/.devcontainer/features/shfmt/install.sh +85 -0
- package/.devcontainer/features/splitrail/devcontainer-feature.json +5 -0
- package/.devcontainer/features/splitrail/install.sh +7 -0
- package/.devcontainer/features/tmux/install.sh +8 -0
- package/.devcontainer/features/tree-sitter/install.sh +6 -0
- package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +3 -10
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/.claude-plugin/plugin.json +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/__pycache__/format-on-stop.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-on-stop.py +114 -9
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/.claude-plugin/plugin.json +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/hooks/hooks.json +4 -5
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/__pycache__/lint-file.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py +478 -76
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/.claude-plugin/plugin.json +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/AGENT-REDIRECTION.md +226 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/architect.md +17 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/bash-exec.md +4 -4
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/claude-guide.md +14 -23
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/debug-logs.md +2 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/dependency-analyst.md +2 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/doc-writer.md +13 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/explorer.md +2 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/generalist.md +10 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/migrator.md +6 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/refactorer.md +4 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/spec-writer.md +36 -23
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/statusline-config.md +3 -3
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/test-writer.md +3 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/hooks/hooks.json +39 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/advisory-test-runner.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/collect-edited-files.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/commit-reminder.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/git-state-injector.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/redirect-builtin-agents.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/ticket-linker.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/todo-harvester.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/advisory-test-runner.py +174 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/collect-edited-files.py +8 -6
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/commit-reminder.py +90 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/git-state-injector.py +114 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/skill-suggester.py +61 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/ticket-linker.py +137 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/todo-harvester.py +130 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/SKILL.md +224 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/references/error-handling.md +166 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/references/rest-conventions.md +215 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/ast-grep-patterns/SKILL.md +211 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/ast-grep-patterns/references/language-patterns.md +327 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/SKILL.md +134 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/references/ecosystem-commands.md +264 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/references/license-compliance.md +80 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/SKILL.md +153 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/references/api-doc-templates.md +221 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/references/docstring-formats.md +296 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/SKILL.md +150 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/references/javascript-migrations.md +179 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/references/python-migrations.md +141 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/SKILL.md +32 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/__pycache__/block-dangerous.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/__pycache__/guard-protected.cpython-314.pyc +0 -0
- package/.devcontainer/scripts/check-setup.sh +72 -0
- package/.devcontainer/scripts/setup-aliases.sh +43 -3
- package/.devcontainer/scripts/setup-auth.sh +74 -0
- package/.devcontainer/scripts/setup-config.sh +112 -22
- package/.devcontainer/scripts/setup-update-claude.sh +8 -0
- package/.devcontainer/scripts/setup.sh +46 -13
- package/README.md +23 -190
- package/package.json +1 -1
- package/setup.js +245 -71
- package/.devcontainer/features/claude-code/README.md +0 -498
- package/.devcontainer/features/claude-code/config/settings.json +0 -72
- package/.devcontainer/features/claude-code/config/system-prompt.md +0 -118
- package/.devcontainer/features/claude-code/config/world-building-sp.md +0 -1432
- package/.devcontainer/features/claude-code/devcontainer-feature.json +0 -42
- package/.devcontainer/features/claude-code/install.sh +0 -466
- package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/.claude-plugin/plugin.json +0 -7
- package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/hooks/hooks.json +0 -17
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/.claude-plugin/plugin.json +0 -6
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/config/planning-instructions.md +0 -14
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/functional-conjuring-map.md +0 -989
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/hooks/hooks.json +0 -33
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/__pycache__/post-enhance-task.cpython-314.pyc +0 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhance-planning.py +0 -71
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-plan.sh +0 -68
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-task.sh +0 -120
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-plan.py +0 -133
- package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-task.py +0 -253
- /package/.devcontainer/config/{keybindings.json → defaults/keybindings.json} +0 -0
- /package/.devcontainer/config/{settings.json → defaults/settings.json} +0 -0
package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/todo-harvester.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
TODO/FIXME harvester — SessionStart hook that surfaces tech debt markers.
|
|
4
|
+
|
|
5
|
+
Greps the codebase for TODO/FIXME/HACK/XXX comments and injects a count
|
|
6
|
+
plus the top items as additionalContext so Claude can proactively mention
|
|
7
|
+
tech debt when relevant.
|
|
8
|
+
|
|
9
|
+
Reads hook input from stdin (JSON). Returns JSON on stdout.
|
|
10
|
+
Always exits 0 (advisory, never blocking).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
|
|
18
|
+
GREP_TIMEOUT = 5
|
|
19
|
+
MAX_ITEMS = 10
|
|
20
|
+
TOTAL_OUTPUT_CAP = 800
|
|
21
|
+
|
|
22
|
+
SOURCE_INCLUDES = [
|
|
23
|
+
"--include=*.py",
|
|
24
|
+
"--include=*.ts",
|
|
25
|
+
"--include=*.tsx",
|
|
26
|
+
"--include=*.js",
|
|
27
|
+
"--include=*.jsx",
|
|
28
|
+
"--include=*.go",
|
|
29
|
+
"--include=*.rs",
|
|
30
|
+
"--include=*.sh",
|
|
31
|
+
"--include=*.svelte",
|
|
32
|
+
"--include=*.vue",
|
|
33
|
+
"--include=*.rb",
|
|
34
|
+
"--include=*.java",
|
|
35
|
+
"--include=*.kt",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
EXCLUDE_DIRS = [
|
|
39
|
+
"--exclude-dir=node_modules",
|
|
40
|
+
"--exclude-dir=.git",
|
|
41
|
+
"--exclude-dir=__pycache__",
|
|
42
|
+
"--exclude-dir=.venv",
|
|
43
|
+
"--exclude-dir=venv",
|
|
44
|
+
"--exclude-dir=dist",
|
|
45
|
+
"--exclude-dir=build",
|
|
46
|
+
"--exclude-dir=vendor",
|
|
47
|
+
"--exclude-dir=.next",
|
|
48
|
+
"--exclude-dir=.nuxt",
|
|
49
|
+
"--exclude-dir=target",
|
|
50
|
+
"--exclude-dir=.mypy_cache",
|
|
51
|
+
"--exclude-dir=.pytest_cache",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def main():
|
|
56
|
+
try:
|
|
57
|
+
json.load(sys.stdin)
|
|
58
|
+
except (json.JSONDecodeError, ValueError):
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
cwd = os.getcwd()
|
|
62
|
+
|
|
63
|
+
cmd = (
|
|
64
|
+
["grep", "-rn", "-E", r"\b(TODO|FIXME|HACK|XXX)\b"]
|
|
65
|
+
+ SOURCE_INCLUDES
|
|
66
|
+
+ EXCLUDE_DIRS
|
|
67
|
+
+ [cwd]
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
result = subprocess.run(
|
|
72
|
+
cmd,
|
|
73
|
+
capture_output=True,
|
|
74
|
+
text=True,
|
|
75
|
+
timeout=GREP_TIMEOUT,
|
|
76
|
+
)
|
|
77
|
+
except (FileNotFoundError, OSError, subprocess.TimeoutExpired):
|
|
78
|
+
sys.exit(0)
|
|
79
|
+
|
|
80
|
+
# grep returns 1 for no matches — that's fine
|
|
81
|
+
output = result.stdout.strip()
|
|
82
|
+
if not output:
|
|
83
|
+
sys.exit(0)
|
|
84
|
+
|
|
85
|
+
lines = output.splitlines()
|
|
86
|
+
total_count = len(lines)
|
|
87
|
+
|
|
88
|
+
# Count unique files
|
|
89
|
+
files_seen: set[str] = set()
|
|
90
|
+
items: list[str] = []
|
|
91
|
+
|
|
92
|
+
for line in lines:
|
|
93
|
+
# Format: filepath:linenum:content
|
|
94
|
+
parts = line.split(":", 2)
|
|
95
|
+
if len(parts) >= 3:
|
|
96
|
+
filepath = parts[0]
|
|
97
|
+
files_seen.add(filepath)
|
|
98
|
+
|
|
99
|
+
if len(items) < MAX_ITEMS:
|
|
100
|
+
# Make path relative to cwd for readability
|
|
101
|
+
rel_path = os.path.relpath(filepath, cwd)
|
|
102
|
+
line_num = parts[1]
|
|
103
|
+
content = parts[2].strip()
|
|
104
|
+
|
|
105
|
+
# Trim long lines
|
|
106
|
+
if len(content) > 80:
|
|
107
|
+
content = content[:77] + "..."
|
|
108
|
+
|
|
109
|
+
items.append(f" {rel_path}:{line_num}: {content}")
|
|
110
|
+
|
|
111
|
+
file_count = len(files_seen)
|
|
112
|
+
header = (
|
|
113
|
+
f"[Tech Debt] {total_count} TODO/FIXME items found across {file_count} files"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if items:
|
|
117
|
+
body = header + "\nTop items:\n" + "\n".join(items)
|
|
118
|
+
else:
|
|
119
|
+
body = header
|
|
120
|
+
|
|
121
|
+
# Cap total output
|
|
122
|
+
if len(body) > TOTAL_OUTPUT_CAP:
|
|
123
|
+
body = body[:TOTAL_OUTPUT_CAP] + "\n...(truncated)"
|
|
124
|
+
|
|
125
|
+
json.dump({"additionalContext": body}, sys.stdout)
|
|
126
|
+
sys.exit(0)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
if __name__ == "__main__":
|
|
130
|
+
main()
|
package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/SKILL.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: api-design
|
|
3
|
+
description: >-
|
|
4
|
+
This skill should be used when the user asks to "design an API",
|
|
5
|
+
"design REST endpoints", "plan API versioning", "choose pagination strategy",
|
|
6
|
+
"design error responses", "API conventions", "endpoint design",
|
|
7
|
+
"create API documentation", or discusses REST API design, HTTP method
|
|
8
|
+
semantics, status code selection, authentication patterns, or rate limiting.
|
|
9
|
+
version: 0.1.0
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# API Design
|
|
13
|
+
|
|
14
|
+
## Mental Model
|
|
15
|
+
|
|
16
|
+
APIs are **contracts**. Once published, they are promises to consumers. Design for the consumer, not the implementation. Consistency beats cleverness — a predictable API with simple conventions is easier to use than a clever API with special cases.
|
|
17
|
+
|
|
18
|
+
**Key principles:**
|
|
19
|
+
- **Resources, not actions.** URLs name things (`/users`, `/orders`), HTTP methods express actions (GET, POST, PUT, DELETE). Verbs in URLs signal a design problem.
|
|
20
|
+
- **Consistency above all.** If one endpoint uses `snake_case`, all endpoints use `snake_case`. If one endpoint paginates with `cursor`, all endpoints paginate with `cursor`.
|
|
21
|
+
- **Explicit over implicit.** Document every behavior. Default values, sort orders, pagination limits — if a consumer has to guess, the API is underspecified.
|
|
22
|
+
- **Idempotency by design.** GET, PUT, DELETE are idempotent. POST creates new resources. Design operations so repeating a request does not produce unexpected side effects.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Resource Naming
|
|
27
|
+
|
|
28
|
+
Use plural nouns for collections, singular identifiers for individual resources:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
GET /users → List users
|
|
32
|
+
POST /users → Create a user
|
|
33
|
+
GET /users/{id} → Get a specific user
|
|
34
|
+
PUT /users/{id} → Replace a user
|
|
35
|
+
PATCH /users/{id} → Partially update a user
|
|
36
|
+
DELETE /users/{id} → Delete a user
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Nested resources** for clear ownership:
|
|
40
|
+
```
|
|
41
|
+
GET /users/{id}/orders → List orders for a user
|
|
42
|
+
POST /users/{id}/orders → Create an order for a user
|
|
43
|
+
GET /users/{id}/orders/{oid} → Get a specific order
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Limit nesting to 2 levels. Beyond that, promote the resource to a top-level endpoint with a query filter: `/orders?user_id=123`.
|
|
47
|
+
|
|
48
|
+
**Naming rules:**
|
|
49
|
+
- Use `kebab-case` for multi-word paths: `/order-items`, not `/orderItems`
|
|
50
|
+
- Use `snake_case` for query parameters: `?sort_by=created_at`
|
|
51
|
+
- Use `snake_case` for JSON fields (aligns with Python/Ruby; for JavaScript-heavy APIs, `camelCase` is also acceptable — pick one and be consistent)
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## HTTP Method Semantics
|
|
56
|
+
|
|
57
|
+
| Method | Purpose | Idempotent | Request Body | Success Code |
|
|
58
|
+
|--------|---------|-----------|-------------|-------------|
|
|
59
|
+
| **GET** | Read resource(s) | Yes | No | 200 |
|
|
60
|
+
| **POST** | Create resource | No | Yes | 201 |
|
|
61
|
+
| **PUT** | Full replace | Yes | Yes | 200 |
|
|
62
|
+
| **PATCH** | Partial update | No* | Yes | 200 |
|
|
63
|
+
| **DELETE** | Remove resource | Yes | No | 204 |
|
|
64
|
+
| **HEAD** | Headers only (like GET) | Yes | No | 200 |
|
|
65
|
+
| **OPTIONS** | Describe capabilities | Yes | No | 204 |
|
|
66
|
+
|
|
67
|
+
*PATCH is idempotent if using JSON Merge Patch; not idempotent with JSON Patch operations.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Pagination Patterns
|
|
72
|
+
|
|
73
|
+
### Cursor-Based (Recommended)
|
|
74
|
+
|
|
75
|
+
Best for real-time data, large datasets, and stable traversal:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
GET /users?cursor=eyJpZCI6MTIzfQ&limit=20
|
|
79
|
+
|
|
80
|
+
{
|
|
81
|
+
"data": [...],
|
|
82
|
+
"pagination": {
|
|
83
|
+
"next_cursor": "eyJpZCI6MTQzfQ",
|
|
84
|
+
"has_more": true
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Cursors are opaque tokens (base64-encoded state). Consumers pass them back unchanged.
|
|
90
|
+
|
|
91
|
+
### Offset-Based
|
|
92
|
+
|
|
93
|
+
Simpler but unstable under concurrent writes:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
GET /users?offset=40&limit=20
|
|
97
|
+
|
|
98
|
+
{
|
|
99
|
+
"data": [...],
|
|
100
|
+
"pagination": {
|
|
101
|
+
"offset": 40,
|
|
102
|
+
"limit": 20,
|
|
103
|
+
"total": 156
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Decision Framework
|
|
109
|
+
|
|
110
|
+
| Factor | Cursor | Offset |
|
|
111
|
+
|--------|--------|--------|
|
|
112
|
+
| Data stability | Handles inserts/deletes during pagination | Rows shift when data changes |
|
|
113
|
+
| Performance at scale | O(1) per page | O(n) for large offsets |
|
|
114
|
+
| "Jump to page N" | Not supported | Supported |
|
|
115
|
+
| Implementation complexity | Higher | Lower |
|
|
116
|
+
|
|
117
|
+
**Default**: Cursor pagination for new APIs. Offset only when consumers explicitly need page-number navigation.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Versioning
|
|
122
|
+
|
|
123
|
+
### URL Path Versioning (Recommended)
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
/v1/users
|
|
127
|
+
/v2/users
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Visible in every request. Easy to route, test, and document. Most widely understood.
|
|
131
|
+
|
|
132
|
+
### Header Versioning
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
Accept: application/vnd.api+json; version=2
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Cleaner URLs but harder to test (can't bookmark/share versioned URLs) and harder to route.
|
|
139
|
+
|
|
140
|
+
### Decision Framework
|
|
141
|
+
|
|
142
|
+
| Factor | URL Path | Header |
|
|
143
|
+
|--------|----------|--------|
|
|
144
|
+
| Discoverability | High — visible in URL | Low — hidden in headers |
|
|
145
|
+
| Cacheability | Easy — URL-based caching | Requires Vary header |
|
|
146
|
+
| Testing | Easy — curl/browser | Requires setting headers |
|
|
147
|
+
| Aesthetics | "Ugly" URLs | Clean URLs |
|
|
148
|
+
|
|
149
|
+
**Default**: URL path versioning. It is the most pragmatic choice for most APIs.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Authentication Patterns
|
|
154
|
+
|
|
155
|
+
| Pattern | Best For | Considerations |
|
|
156
|
+
|---------|----------|---------------|
|
|
157
|
+
| **API Keys** | Server-to-server, simple integrations | Easy to implement. No expiry by default — rotate regularly. Send via header (`X-API-Key` or `Authorization: Bearer`), never in URL. |
|
|
158
|
+
| **JWT (Bearer Tokens)** | Stateless auth, microservices | Self-contained claims. Set short expiry (15-60 min) + refresh tokens. Verify signature and expiry on every request. |
|
|
159
|
+
| **OAuth 2.0** | Third-party integrations, user consent | Authorization Code flow for web apps, Client Credentials for machine-to-machine. Use PKCE for public clients. |
|
|
160
|
+
| **Session Cookies** | Browser-based web apps | Set `HttpOnly`, `Secure`, `SameSite=Strict`. CSRF protection required. |
|
|
161
|
+
|
|
162
|
+
**Default**: JWT for APIs consumed by multiple clients. API keys for simple server-to-server integrations.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Rate Limiting
|
|
167
|
+
|
|
168
|
+
### Token Bucket (Recommended)
|
|
169
|
+
|
|
170
|
+
Allows bursts up to bucket size, then enforces sustained rate:
|
|
171
|
+
- Bucket holds N tokens (e.g., 100)
|
|
172
|
+
- Tokens refill at R per second (e.g., 10/s)
|
|
173
|
+
- Each request costs 1 token (or more for expensive operations)
|
|
174
|
+
|
|
175
|
+
### Response Headers
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
X-RateLimit-Limit: 100
|
|
179
|
+
X-RateLimit-Remaining: 42
|
|
180
|
+
X-RateLimit-Reset: 1640000000
|
|
181
|
+
Retry-After: 30
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Return `429 Too Many Requests` with `Retry-After` header when limit is exceeded.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## API Design Checklist
|
|
189
|
+
|
|
190
|
+
Before implementing, verify:
|
|
191
|
+
|
|
192
|
+
- [ ] Every endpoint uses a plural noun (not a verb) in the URL
|
|
193
|
+
- [ ] HTTP methods match their semantic purpose
|
|
194
|
+
- [ ] Response format is consistent across all endpoints
|
|
195
|
+
- [ ] Error responses follow a standard format (see reference)
|
|
196
|
+
- [ ] Pagination strategy is consistent across all list endpoints
|
|
197
|
+
- [ ] Authentication mechanism is documented
|
|
198
|
+
- [ ] Rate limits are defined and communicated via headers
|
|
199
|
+
- [ ] Versioning strategy is chosen and applied
|
|
200
|
+
- [ ] All parameters have documented types, constraints, and defaults
|
|
201
|
+
- [ ] Idempotency behavior is documented for each endpoint
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Ambiguity Policy
|
|
206
|
+
|
|
207
|
+
| Ambiguity | Default |
|
|
208
|
+
|-----------|---------|
|
|
209
|
+
| **API style** | REST with JSON request/response bodies |
|
|
210
|
+
| **Pagination** | Cursor-based with `limit` parameter (default 20, max 100) |
|
|
211
|
+
| **Versioning** | URL path versioning (`/v1/`) |
|
|
212
|
+
| **Naming convention** | `snake_case` for JSON fields, `kebab-case` for URL paths |
|
|
213
|
+
| **Auth pattern** | JWT Bearer tokens (unless server-to-server, then API keys) |
|
|
214
|
+
| **Error format** | RFC 7807 Problem Details |
|
|
215
|
+
| **Rate limiting** | Token bucket with standard headers |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Reference Files
|
|
220
|
+
|
|
221
|
+
| File | Contents |
|
|
222
|
+
|------|----------|
|
|
223
|
+
| [REST Conventions](references/rest-conventions.md) | Complete HTTP method semantics, status code reference by category, resource naming examples, query parameter patterns, content negotiation, and HATEOAS guidance |
|
|
224
|
+
| [Error Handling](references/error-handling.md) | RFC 7807 Problem Details format, error code taxonomy, consistency rules, client-vs-server error exposure, and retry guidance headers |
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# API Error Handling Reference
|
|
2
|
+
|
|
3
|
+
## RFC 7807 Problem Details
|
|
4
|
+
|
|
5
|
+
The standard format for HTTP API error responses. Use `application/problem+json` as the content type.
|
|
6
|
+
|
|
7
|
+
### Format
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"type": "https://api.example.com/errors/validation-error",
|
|
12
|
+
"title": "Validation Error",
|
|
13
|
+
"status": 422,
|
|
14
|
+
"detail": "The 'email' field must be a valid email address.",
|
|
15
|
+
"instance": "/users/signup",
|
|
16
|
+
"errors": [
|
|
17
|
+
{
|
|
18
|
+
"field": "email",
|
|
19
|
+
"message": "Must be a valid email address",
|
|
20
|
+
"value": "not-an-email"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Fields
|
|
27
|
+
|
|
28
|
+
| Field | Required | Description |
|
|
29
|
+
|-------|----------|-------------|
|
|
30
|
+
| `type` | Yes | URI reference identifying the error type. Use a stable URL (can be a documentation page). |
|
|
31
|
+
| `title` | Yes | Short human-readable summary. Same for all instances of this error type. |
|
|
32
|
+
| `status` | Yes | HTTP status code (repeated from response for convenience). |
|
|
33
|
+
| `detail` | Yes | Human-readable explanation specific to this occurrence. |
|
|
34
|
+
| `instance` | No | URI identifying the specific occurrence (request path or trace ID). |
|
|
35
|
+
|
|
36
|
+
Extension fields (like `errors` for validation) are allowed and encouraged for structured detail.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Error Code Taxonomy
|
|
41
|
+
|
|
42
|
+
Organize errors into categories with consistent type URIs:
|
|
43
|
+
|
|
44
|
+
### Validation Errors (400 / 422)
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"type": "https://api.example.com/errors/validation-error",
|
|
49
|
+
"title": "Validation Error",
|
|
50
|
+
"status": 422,
|
|
51
|
+
"detail": "Request body contains invalid fields.",
|
|
52
|
+
"errors": [
|
|
53
|
+
{ "field": "email", "message": "Required field is missing" },
|
|
54
|
+
{ "field": "age", "message": "Must be a positive integer", "value": -5 }
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Use `422` for semantic validation failures (valid JSON but invalid data). Use `400` for malformed requests (invalid JSON, wrong content type).
|
|
60
|
+
|
|
61
|
+
### Authentication Errors (401)
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"type": "https://api.example.com/errors/authentication-required",
|
|
66
|
+
"title": "Authentication Required",
|
|
67
|
+
"status": 401,
|
|
68
|
+
"detail": "The access token has expired. Request a new token."
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Always include `WWW-Authenticate` header with 401 responses.
|
|
73
|
+
|
|
74
|
+
### Authorization Errors (403)
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"type": "https://api.example.com/errors/insufficient-permissions",
|
|
79
|
+
"title": "Insufficient Permissions",
|
|
80
|
+
"status": 403,
|
|
81
|
+
"detail": "Your API key does not have access to the 'admin' scope."
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Not Found (404)
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"type": "https://api.example.com/errors/resource-not-found",
|
|
90
|
+
"title": "Resource Not Found",
|
|
91
|
+
"status": 404,
|
|
92
|
+
"detail": "No user found with ID '999'."
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Conflict (409)
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"type": "https://api.example.com/errors/resource-conflict",
|
|
101
|
+
"title": "Resource Conflict",
|
|
102
|
+
"status": 409,
|
|
103
|
+
"detail": "A user with email 'jane@example.com' already exists."
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Rate Limit (429)
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"type": "https://api.example.com/errors/rate-limit-exceeded",
|
|
112
|
+
"title": "Rate Limit Exceeded",
|
|
113
|
+
"status": 429,
|
|
114
|
+
"detail": "You have exceeded 100 requests per minute. Try again in 30 seconds."
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Always include `Retry-After` header (seconds or HTTP-date).
|
|
119
|
+
|
|
120
|
+
### Internal Error (500)
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"type": "https://api.example.com/errors/internal-error",
|
|
125
|
+
"title": "Internal Server Error",
|
|
126
|
+
"status": 500,
|
|
127
|
+
"detail": "An unexpected error occurred. Reference: trace-abc123."
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Error Response Consistency Rules
|
|
134
|
+
|
|
135
|
+
1. **Same shape, always.** Every error response uses the same top-level structure. Consumers should never need to handle different error formats.
|
|
136
|
+
2. **Match status code to error.** The `status` field in the body matches the HTTP status code. Never return `200 OK` with an error body.
|
|
137
|
+
3. **Human-readable details.** The `detail` field should help the consumer fix the problem. "Invalid request" is useless. "The 'email' field must be a valid email address" is actionable.
|
|
138
|
+
4. **Machine-readable type.** The `type` URI enables programmatic error handling. Consumers can switch on the type without parsing the detail string.
|
|
139
|
+
5. **No stack traces in production.** Internal error details (stack traces, database errors, internal paths) are logged server-side. Clients receive a reference ID to correlate with server logs.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Error Logging vs. Error Exposure
|
|
144
|
+
|
|
145
|
+
| Information | Log Server-Side | Return to Client |
|
|
146
|
+
|-------------|----------------|-----------------|
|
|
147
|
+
| Stack trace | Yes | No |
|
|
148
|
+
| Database error messages | Yes | No |
|
|
149
|
+
| Internal file paths | Yes | No |
|
|
150
|
+
| Request ID / trace ID | Yes | Yes (in `instance` or `detail`) |
|
|
151
|
+
| Validation errors | Yes | Yes (specific field-level errors) |
|
|
152
|
+
| Rate limit status | Yes | Yes (via headers + body) |
|
|
153
|
+
| Business rule violations | Yes | Yes (as actionable `detail`) |
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Retry Guidance Headers
|
|
158
|
+
|
|
159
|
+
| Header | Purpose | Format |
|
|
160
|
+
|--------|---------|--------|
|
|
161
|
+
| `Retry-After` | When to retry after 429 or 503 | Seconds (`30`) or HTTP-date (`Wed, 21 Oct 2025 07:28:00 GMT`) |
|
|
162
|
+
| `X-RateLimit-Limit` | Maximum requests allowed in window | Integer (`100`) |
|
|
163
|
+
| `X-RateLimit-Remaining` | Requests remaining in current window | Integer (`42`) |
|
|
164
|
+
| `X-RateLimit-Reset` | Unix timestamp when window resets | Integer (`1640000000`) |
|
|
165
|
+
|
|
166
|
+
Include all rate limit headers on **every response** (not just 429s) so clients can proactively manage their request rate.
|