vanara 0.1.0 → 0.3.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 (195) hide show
  1. package/README.md +14 -0
  2. package/bin/vanara.js +66 -2
  3. package/catalog/agents/api-designer/AGENT.md +156 -0
  4. package/catalog/agents/api-designer/examples/openapi-snippet.yaml +130 -0
  5. package/catalog/agents/api-designer/examples/review-notes.md +61 -0
  6. package/catalog/agents/api-designer/references/contract-and-openapi.md +82 -0
  7. package/catalog/agents/api-designer/references/design-checklist.md +68 -0
  8. package/catalog/agents/api-designer/references/versioning-and-evolution.md +63 -0
  9. package/catalog/agents/api-designer/scripts/lint-openapi.mjs +180 -0
  10. package/catalog/agents/code-reviewer/AGENT.md +184 -0
  11. package/catalog/agents/code-reviewer/examples/pr-comment-template.md +48 -0
  12. package/catalog/agents/code-reviewer/examples/sample-review-output.md +38 -0
  13. package/catalog/agents/code-reviewer/references/review-checklist.md +46 -0
  14. package/catalog/agents/code-reviewer/references/security-review.md +62 -0
  15. package/catalog/agents/code-reviewer/references/severity-rubric.md +63 -0
  16. package/catalog/agents/code-reviewer/scripts/review-guard.mjs +102 -0
  17. package/catalog/agents/debugger/AGENT.md +128 -0
  18. package/catalog/agents/debugger/examples/postmortem-template.md +59 -0
  19. package/catalog/agents/debugger/examples/repro-template.md +50 -0
  20. package/catalog/agents/debugger/references/bisection.md +63 -0
  21. package/catalog/agents/debugger/references/debugging-method.md +57 -0
  22. package/catalog/agents/debugger/references/observability.md +61 -0
  23. package/catalog/agents/debugger/scripts/parse-stacktrace.mjs +109 -0
  24. package/catalog/agents/pr-summarizer/AGENT.md +172 -0
  25. package/catalog/agents/pr-summarizer/examples/sample-pr-summary.md +46 -0
  26. package/catalog/agents/pr-summarizer/examples/weak-vs-strong-summary.md +68 -0
  27. package/catalog/agents/pr-summarizer/references/identifying-risk.md +68 -0
  28. package/catalog/agents/pr-summarizer/references/reading-the-diff.md +68 -0
  29. package/catalog/agents/pr-summarizer/references/test-plan-and-missing-tests.md +64 -0
  30. package/catalog/agents/pr-summarizer/references/writing-the-summary.md +63 -0
  31. package/catalog/agents/pr-summarizer/scripts/diff-risk.mjs +111 -0
  32. package/catalog/agents/refactoring-specialist/AGENT.md +201 -0
  33. package/catalog/agents/refactoring-specialist/examples/before-after.md +91 -0
  34. package/catalog/agents/refactoring-specialist/examples/refactor-plan.md +97 -0
  35. package/catalog/agents/refactoring-specialist/references/large-scale-refactor.md +50 -0
  36. package/catalog/agents/refactoring-specialist/references/safe-workflow.md +75 -0
  37. package/catalog/agents/refactoring-specialist/references/smell-catalog.md +76 -0
  38. package/catalog/agents/refactoring-specialist/scripts/detect-smells.mjs +150 -0
  39. package/catalog/agents/security-auditor/AGENT.md +184 -0
  40. package/catalog/agents/security-auditor/examples/audit-report.md +68 -0
  41. package/catalog/agents/security-auditor/examples/finding-template.md +31 -0
  42. package/catalog/agents/security-auditor/references/audit-workflow.md +116 -0
  43. package/catalog/agents/security-auditor/references/severity-and-reporting.md +56 -0
  44. package/catalog/agents/security-auditor/references/vuln-classes.md +129 -0
  45. package/catalog/agents/security-auditor/scripts/scan-secrets.mjs +86 -0
  46. package/catalog/agents/technical-writer/AGENT.md +179 -0
  47. package/catalog/agents/technical-writer/examples/how-to-example.md +76 -0
  48. package/catalog/agents/technical-writer/examples/reference-example.md +75 -0
  49. package/catalog/agents/technical-writer/references/clarity-and-style.md +52 -0
  50. package/catalog/agents/technical-writer/references/diataxis-and-doc-types.md +61 -0
  51. package/catalog/agents/technical-writer/references/structure-and-maintenance.md +59 -0
  52. package/catalog/agents/technical-writer/scripts/readability.mjs +103 -0
  53. package/catalog/agents/test-author/AGENT.md +193 -0
  54. package/catalog/agents/test-author/examples/test-plan.md +54 -0
  55. package/catalog/agents/test-author/examples/unit-test-example.ts +55 -0
  56. package/catalog/agents/test-author/references/aaa-and-naming.md +75 -0
  57. package/catalog/agents/test-author/references/mocking-and-fixtures.md +94 -0
  58. package/catalog/agents/test-author/references/test-types-and-pyramid.md +60 -0
  59. package/catalog/agents/test-author/scripts/check-coverage.mjs +145 -0
  60. package/catalog/agents/threat-modeler/AGENT.md +179 -0
  61. package/catalog/agents/threat-modeler/examples/dfd-example.md +55 -0
  62. package/catalog/agents/threat-modeler/examples/threat-model.md +81 -0
  63. package/catalog/agents/threat-modeler/references/dfd-and-trust-boundaries.md +76 -0
  64. package/catalog/agents/threat-modeler/references/mitigation-catalog.md +77 -0
  65. package/catalog/agents/threat-modeler/references/stride-method.md +68 -0
  66. package/catalog/agents/threat-modeler/scripts/stride-checklist.mjs +183 -0
  67. package/catalog/agents/vuln-scanner/AGENT.md +162 -0
  68. package/catalog/agents/vuln-scanner/examples/finding-template.md +57 -0
  69. package/catalog/agents/vuln-scanner/examples/scan-report.md +49 -0
  70. package/catalog/agents/vuln-scanner/references/remediation-and-severity.md +68 -0
  71. package/catalog/agents/vuln-scanner/references/scan-types-and-tools.md +83 -0
  72. package/catalog/agents/vuln-scanner/references/triage-and-false-positives.md +73 -0
  73. package/catalog/agents/vuln-scanner/scripts/parse-scan-results.mjs +133 -0
  74. package/catalog/packs/code-review-pack/PACK.md +100 -0
  75. package/catalog/packs/code-review-pack/examples/pr-flow-example.md +50 -0
  76. package/catalog/packs/code-review-pack/references/rollout-guide.md +40 -0
  77. package/catalog/packs/security-pack/PACK.md +128 -0
  78. package/catalog/packs/security-pack/examples/owasp-vuln-triage-and-fix.md +66 -0
  79. package/catalog/packs/security-pack/examples/threat-model-then-audit.md +77 -0
  80. package/catalog/packs/security-pack/references/choosing-the-right-tool.md +37 -0
  81. package/catalog/packs/security-pack/references/threat-model-to-remediation.md +69 -0
  82. package/catalog/packs/security-pack/references/workflow.md +56 -0
  83. package/catalog/skills/api-pagination/SKILL.md +83 -0
  84. package/catalog/skills/api-pagination/examples/keyset-queries.sql +38 -0
  85. package/catalog/skills/api-pagination/examples/response-shapes.md +51 -0
  86. package/catalog/skills/api-pagination/references/consuming-pages.md +39 -0
  87. package/catalog/skills/api-pagination/references/counting-and-totals.md +41 -0
  88. package/catalog/skills/api-pagination/references/cursor-encoding.md +34 -0
  89. package/catalog/skills/api-pagination/scripts/check-cursor.mjs +51 -0
  90. package/catalog/skills/caching-strategies/SKILL.md +180 -0
  91. package/catalog/skills/caching-strategies/examples/http-cache-headers.md +82 -0
  92. package/catalog/skills/caching-strategies/examples/redis-cache-aside.js +110 -0
  93. package/catalog/skills/caching-strategies/references/cache-patterns.md +93 -0
  94. package/catalog/skills/caching-strategies/references/eviction-and-ttl.md +67 -0
  95. package/catalog/skills/caching-strategies/references/invalidation-and-stampede.md +99 -0
  96. package/catalog/skills/caching-strategies/scripts/ttl-jitter.mjs +99 -0
  97. package/catalog/skills/conventional-commits/SKILL.md +135 -0
  98. package/catalog/skills/conventional-commits/examples/commit-examples.md +60 -0
  99. package/catalog/skills/conventional-commits/examples/commitlint.config.js +31 -0
  100. package/catalog/skills/conventional-commits/references/breaking-changes-semver.md +50 -0
  101. package/catalog/skills/conventional-commits/references/scoping-commits.md +42 -0
  102. package/catalog/skills/conventional-commits/references/spec.md +78 -0
  103. package/catalog/skills/conventional-commits/scripts/lint-commit.mjs +97 -0
  104. package/catalog/skills/database-migrations/SKILL.md +137 -0
  105. package/catalog/skills/database-migrations/examples/add-column-safe.sql +34 -0
  106. package/catalog/skills/database-migrations/examples/migration-config.example.json +27 -0
  107. package/catalog/skills/database-migrations/references/expand-contract.md +74 -0
  108. package/catalog/skills/database-migrations/references/rollback-and-safety.md +71 -0
  109. package/catalog/skills/database-migrations/references/zero-downtime-changes.md +84 -0
  110. package/catalog/skills/database-migrations/scripts/check-migration-reversible.mjs +139 -0
  111. package/catalog/skills/error-handling-patterns/SKILL.md +136 -0
  112. package/catalog/skills/error-handling-patterns/examples/express-error-middleware.js +93 -0
  113. package/catalog/skills/error-handling-patterns/examples/result-pattern.ts +81 -0
  114. package/catalog/skills/error-handling-patterns/references/error-taxonomy.md +91 -0
  115. package/catalog/skills/error-handling-patterns/references/language-patterns.md +102 -0
  116. package/catalog/skills/error-handling-patterns/references/retry-and-backoff.md +76 -0
  117. package/catalog/skills/error-handling-patterns/scripts/lint-empty-catch.mjs +113 -0
  118. package/catalog/skills/git-collaboration-workflows/SKILL.md +104 -0
  119. package/catalog/skills/git-collaboration-workflows/examples/branch-protection.md +48 -0
  120. package/catalog/skills/git-collaboration-workflows/examples/pr-review-playbook.md +48 -0
  121. package/catalog/skills/git-collaboration-workflows/references/branching-models.md +52 -0
  122. package/catalog/skills/git-collaboration-workflows/references/history-hygiene.md +44 -0
  123. package/catalog/skills/git-collaboration-workflows/scripts/check-branch-hygiene.mjs +62 -0
  124. package/catalog/skills/owasp-top10/SKILL.md +112 -0
  125. package/catalog/skills/owasp-top10/examples/sql-injection-fix.md +33 -0
  126. package/catalog/skills/owasp-top10/examples/xss-fix.md +27 -0
  127. package/catalog/skills/owasp-top10/references/access-control.md +36 -0
  128. package/catalog/skills/owasp-top10/references/crypto-failures.md +29 -0
  129. package/catalog/skills/owasp-top10/references/injection.md +39 -0
  130. package/catalog/skills/owasp-top10/references/ssrf-and-design.md +35 -0
  131. package/catalog/skills/owasp-top10/scripts/scan-injection.mjs +73 -0
  132. package/catalog/skills/prompt-engineering/SKILL.md +100 -0
  133. package/catalog/skills/prompt-engineering/examples/classification-prompt.md +29 -0
  134. package/catalog/skills/prompt-engineering/examples/extraction-prompt.md +25 -0
  135. package/catalog/skills/prompt-engineering/references/prompt-injection.md +34 -0
  136. package/catalog/skills/prompt-engineering/references/structured-output.md +34 -0
  137. package/catalog/skills/prompt-engineering/references/techniques.md +33 -0
  138. package/catalog/skills/prompt-engineering/scripts/validate-output.mjs +71 -0
  139. package/catalog/skills/readme-writing/SKILL.md +176 -0
  140. package/catalog/skills/readme-writing/examples/README.template.md +73 -0
  141. package/catalog/skills/readme-writing/examples/good-readme-example.md +95 -0
  142. package/catalog/skills/readme-writing/references/anatomy.md +107 -0
  143. package/catalog/skills/readme-writing/references/maintenance.md +68 -0
  144. package/catalog/skills/readme-writing/references/writing-style.md +79 -0
  145. package/catalog/skills/readme-writing/scripts/lint-readme.mjs +117 -0
  146. package/catalog/skills/refactoring-patterns/SKILL.md +178 -0
  147. package/catalog/skills/refactoring-patterns/examples/extract-function.before-after.js +57 -0
  148. package/catalog/skills/refactoring-patterns/examples/replace-conditional-with-polymorphism.before-after.ts +63 -0
  149. package/catalog/skills/refactoring-patterns/references/code-smells.md +92 -0
  150. package/catalog/skills/refactoring-patterns/references/refactoring-catalog.md +78 -0
  151. package/catalog/skills/refactoring-patterns/references/safe-workflow.md +74 -0
  152. package/catalog/skills/refactoring-patterns/scripts/detect-smells.mjs +137 -0
  153. package/catalog/skills/rest-api-design/SKILL.md +147 -0
  154. package/catalog/skills/rest-api-design/examples/error-envelope.json +26 -0
  155. package/catalog/skills/rest-api-design/examples/orders-api.openapi.yaml +106 -0
  156. package/catalog/skills/rest-api-design/references/error-handling.md +43 -0
  157. package/catalog/skills/rest-api-design/references/pagination.md +41 -0
  158. package/catalog/skills/rest-api-design/references/status-codes.md +32 -0
  159. package/catalog/skills/rest-api-design/references/versioning.md +31 -0
  160. package/catalog/skills/rest-api-design/scripts/check-envelope.mjs +62 -0
  161. package/catalog/skills/secrets-management/SKILL.md +134 -0
  162. package/catalog/skills/secrets-management/examples/env-example.md +41 -0
  163. package/catalog/skills/secrets-management/examples/rotation-runbook.md +50 -0
  164. package/catalog/skills/secrets-management/references/leak-response.md +47 -0
  165. package/catalog/skills/secrets-management/references/rotation.md +46 -0
  166. package/catalog/skills/secrets-management/references/secret-managers.md +52 -0
  167. package/catalog/skills/secrets-management/scripts/detect-hardcoded.mjs +96 -0
  168. package/catalog/skills/secure-auth/SKILL.md +135 -0
  169. package/catalog/skills/secure-auth/examples/auth-flow.md +60 -0
  170. package/catalog/skills/secure-auth/examples/jwt-verify.ts +89 -0
  171. package/catalog/skills/secure-auth/references/auth-attacks.md +76 -0
  172. package/catalog/skills/secure-auth/references/oauth2-oidc.md +65 -0
  173. package/catalog/skills/secure-auth/references/password-hashing.md +70 -0
  174. package/catalog/skills/secure-auth/references/sessions-vs-jwt.md +69 -0
  175. package/catalog/skills/secure-auth/scripts/check-password-policy.mjs +114 -0
  176. package/catalog/skills/sql-index-tuning/SKILL.md +171 -0
  177. package/catalog/skills/sql-index-tuning/examples/explain-walkthrough.sql +91 -0
  178. package/catalog/skills/sql-index-tuning/examples/index-ddl.sql +67 -0
  179. package/catalog/skills/sql-index-tuning/references/btree-internals.md +58 -0
  180. package/catalog/skills/sql-index-tuning/references/composite-and-covering.md +60 -0
  181. package/catalog/skills/sql-index-tuning/references/reading-explain.md +54 -0
  182. package/catalog/skills/sql-index-tuning/references/selectivity-and-cardinality.md +62 -0
  183. package/catalog/skills/sql-index-tuning/scripts/suggest-index.mjs +213 -0
  184. package/catalog/skills/test-plan-design/SKILL.md +154 -0
  185. package/catalog/skills/test-plan-design/examples/test-case-template.md +45 -0
  186. package/catalog/skills/test-plan-design/examples/test-plan.md +57 -0
  187. package/catalog/skills/test-plan-design/references/case-design.md +79 -0
  188. package/catalog/skills/test-plan-design/references/risk-prioritization.md +58 -0
  189. package/catalog/skills/test-plan-design/references/test-pyramid.md +58 -0
  190. package/catalog/skills/test-plan-design/scripts/coverage-gaps.mjs +109 -0
  191. package/free-tier.json +15 -0
  192. package/package.json +15 -4
  193. package/premium-index.json +1 -0
  194. package/src/config.js +31 -1
  195. package/src/license.js +143 -0
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ // detect-smells.mjs — a tiny, dependency-free heuristic scanner that flags two
3
+ // structural smells in a code string: long functions and deep nesting. It is a
4
+ // teaching aid, not a real linter — it works on brace/indent heuristics, so it
5
+ // is intentionally conservative.
6
+ //
7
+ // Usage:
8
+ // node detect-smells.mjs '<code string>' # scan a literal string
9
+ // echo '<code>' | node detect-smells.mjs # scan from stdin
10
+ // node detect-smells.mjs --selftest # run built-in cases (exit 0/1)
11
+ //
12
+ // Thresholds (tunable):
13
+ const MAX_FUNCTION_LINES = 25; // longer than this -> "long function"
14
+ const MAX_NESTING_DEPTH = 4; // deeper brace nesting than this -> "deep nesting"
15
+
16
+ // Scan a code string and return an array of { type, message, line } findings.
17
+ export function detectSmells(code, {
18
+ maxFunctionLines = MAX_FUNCTION_LINES,
19
+ maxNestingDepth = MAX_NESTING_DEPTH,
20
+ } = {}) {
21
+ const lines = code.split(/\r?\n/);
22
+ const findings = [];
23
+
24
+ // --- deep nesting: track running brace depth ---
25
+ let depth = 0;
26
+ let maxSeenDepth = 0;
27
+ let deepLine = 0;
28
+ for (let i = 0; i < lines.length; i++) {
29
+ const line = lines[i];
30
+ for (const ch of line) {
31
+ if (ch === "{") {
32
+ depth++;
33
+ if (depth > maxSeenDepth) { maxSeenDepth = depth; deepLine = i + 1; }
34
+ } else if (ch === "}") {
35
+ depth = Math.max(0, depth - 1);
36
+ }
37
+ }
38
+ }
39
+ if (maxSeenDepth > maxNestingDepth) {
40
+ findings.push({
41
+ type: "deep-nesting",
42
+ message: `nesting depth ${maxSeenDepth} exceeds ${maxNestingDepth} (consider guard clauses)`,
43
+ line: deepLine,
44
+ });
45
+ }
46
+
47
+ // --- long function: brace-counted spans from a function header ---
48
+ const fnHeader = /\b(function\b|=>\s*\{|\)\s*\{)/;
49
+ for (let i = 0; i < lines.length; i++) {
50
+ if (!fnHeader.test(lines[i]) || !lines[i].includes("{")) continue;
51
+ let d = 0, started = false, span = 0, startLine = i + 1;
52
+ for (let j = i; j < lines.length; j++) {
53
+ for (const ch of lines[j]) {
54
+ if (ch === "{") { d++; started = true; }
55
+ else if (ch === "}") d--;
56
+ }
57
+ if (started) { span++; if (d <= 0) break; }
58
+ }
59
+ if (span > maxFunctionLines) {
60
+ findings.push({
61
+ type: "long-function",
62
+ message: `function spanning ${span} lines exceeds ${maxFunctionLines} (consider Extract Function)`,
63
+ line: startLine,
64
+ });
65
+ }
66
+ }
67
+
68
+ return findings;
69
+ }
70
+
71
+ function report(label, code) {
72
+ const findings = detectSmells(code);
73
+ if (findings.length === 0) {
74
+ console.log(`✓ ${label}: no smells detected`);
75
+ return true;
76
+ }
77
+ console.error(`✗ ${label}: ${findings.length} smell(s)`);
78
+ for (const f of findings) console.error(` - [${f.type}] line ${f.line}: ${f.message}`);
79
+ return false;
80
+ }
81
+
82
+ function selftest() {
83
+ const clean = `
84
+ function add(a, b) {
85
+ return a + b;
86
+ }`;
87
+
88
+ const smelly = `
89
+ function process(x) {
90
+ if (x) {
91
+ if (x.a) {
92
+ if (x.b) {
93
+ if (x.c) {
94
+ if (x.d) {
95
+ return x.a + x.b + x.c + x.d;
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
101
+ return 0;
102
+ }`;
103
+
104
+ const longFn =
105
+ "function big() {\n" +
106
+ Array.from({ length: 30 }, (_, i) => ` const v${i} = ${i};`).join("\n") +
107
+ "\n return 0;\n}";
108
+
109
+ const cases = [
110
+ { name: "clean (no smells)", code: clean, expectSmells: false },
111
+ { name: "deeply nested", code: smelly, expectSmells: true },
112
+ { name: "long function", code: longFn, expectSmells: true },
113
+ ];
114
+
115
+ let allOk = true;
116
+ for (const c of cases) {
117
+ const found = detectSmells(c.code);
118
+ const hasSmells = found.length > 0;
119
+ const ok = hasSmells === c.expectSmells;
120
+ allOk = allOk && ok;
121
+ console.log(
122
+ `${ok ? "✓" : "✗"} selftest ${c.name}: ${hasSmells ? `${found.length} smell(s)` : "clean"} ` +
123
+ `(expected ${c.expectSmells ? "smells" : "clean"})`
124
+ );
125
+ if (!ok) for (const f of found) console.log(` [${f.type}] line ${f.line}: ${f.message}`);
126
+ }
127
+ process.exit(allOk ? 0 : 1);
128
+ }
129
+
130
+ const arg = process.argv[2];
131
+ if (arg === "--selftest") selftest();
132
+ else if (arg) process.exit(report("code", arg) ? 0 : 1);
133
+ else {
134
+ let buf = "";
135
+ process.stdin.on("data", (c) => (buf += c));
136
+ process.stdin.on("end", () => process.exit(report("stdin", buf) ? 0 : 1));
137
+ }
@@ -0,0 +1,147 @@
1
+ ---
2
+ name: rest-api-design
3
+ description: Conventions for designing clean, consistent, evolvable REST APIs — resource modeling, HTTP semantics, status codes, pagination, filtering, error envelopes, versioning, idempotency, and security. A deep reference with worked examples and runnable checks.
4
+ type: skill
5
+ version: 2.0.0
6
+ updated: 2026-06-28
7
+ ---
8
+ # REST API Design
9
+
10
+ A good REST API is **guessable**: once a consumer learns one endpoint, they can predict the rest. This
11
+ skill is the deep reference for designing one — the principles, the decisions, the trade-offs, and the
12
+ mistakes to avoid. Heavy detail lives in `references/`; copy-paste material in `examples/`; a runnable
13
+ contract check in `scripts/`.
14
+
15
+ ## Mental model
16
+
17
+ Design the API around **resources** (nouns) and use HTTP **verbs** to act on them. The protocol already
18
+ gives you a rich vocabulary — methods, status codes, headers, caching — so lean on it instead of
19
+ inventing your own conventions on top.
20
+
21
+ | Concern | REST answer |
22
+ |---|---|
23
+ | What | a resource, named as a plural noun (`/orders`) |
24
+ | Action | the HTTP method (GET/POST/PUT/PATCH/DELETE) |
25
+ | Outcome | the status code (200/201/404/409…) |
26
+ | Shape | a consistent response envelope |
27
+ | Change | an explicit versioning strategy |
28
+
29
+ ## 1. Resource modeling
30
+
31
+ 1. Name collections as **plural nouns**: `/users`, `/orders`. Never verbs in the path (`/getUsers` is wrong — the verb is `GET`).
32
+ 2. Nest to show ownership, but only **one level deep**: `/users/{id}/orders` is fine; `/users/{id}/orders/{id}/items/{id}/...` is a smell — link instead.
33
+ 3. Model real-world actions that aren't CRUD as sub-resources or controller endpoints: `POST /orders/{id}/refunds` rather than `POST /refundOrder`.
34
+ 4. Keep identifiers stable and opaque to clients; don't leak DB internals (prefer UUIDs/ULIDs over auto-increment IDs where enumeration is a risk).
35
+
36
+ ## 2. HTTP method semantics
37
+
38
+ | Method | Use | Safe | Idempotent |
39
+ |---|---|---|---|
40
+ | GET | read | yes | yes |
41
+ | POST | create / non-idempotent action | no | no |
42
+ | PUT | full replace | no | yes |
43
+ | PATCH | partial update | no | no* |
44
+ | DELETE | remove | no | yes |
45
+
46
+ Respect these contracts — clients, proxies, and caches rely on them. A `GET` must never mutate state.
47
+ See `references/status-codes.md` for the full status-code decision guide.
48
+
49
+ ## 3. Status codes
50
+
51
+ Return the **accurate** code; never `200 OK` with an error body (it breaks every client's error handling).
52
+
53
+ ```http
54
+ 201 Created Location: /orders/101 # after a successful POST
55
+ 204 No Content # successful DELETE, no body
56
+ 400 Bad Request # malformed/invalid input
57
+ 401 Unauthorized # not authenticated 403 Forbidden # authenticated, not allowed
58
+ 404 Not Found 409 Conflict 422 Unprocessable Entity 429 Too Many Requests
59
+ ```
60
+
61
+ Full guidance and edge cases (404 vs 403 to avoid leaking existence, 409 vs 422) live in
62
+ `references/status-codes.md`.
63
+
64
+ ## 4. The response envelope
65
+
66
+ Standardize one shape across **every** endpoint so clients parse uniformly. See
67
+ `examples/error-envelope.json` and validate any payload with `scripts/check-envelope.mjs`.
68
+
69
+ ```json
70
+ {
71
+ "data": [ { "id": 101, "status": "open" } ],
72
+ "meta": { "nextCursor": "eyJpZCI6MTIwfQ", "limit": 20 },
73
+ "error": null
74
+ }
75
+ ```
76
+
77
+ On error, the same shape with `data: null` and a populated `error` (see §6).
78
+
79
+ ## 5. Pagination, filtering, sorting
80
+
81
+ Every collection endpoint **must** paginate — an unbounded list is a latent outage. Prefer **cursor**
82
+ pagination for large or frequently-changing data (offset pagination scans and skips rows, getting slower
83
+ the deeper you go, and double-counts when rows are inserted mid-scan).
84
+
85
+ ```http
86
+ GET /v1/orders?status=open&sort=-created_at&limit=20&cursor=eyJpZCI6MTAwfQ
87
+ ```
88
+
89
+ The full comparison, cursor encoding, and pitfalls are in `references/pagination.md`.
90
+
91
+ ## 6. Error model
92
+
93
+ One error shape, everywhere. A machine-readable `code`, a human `message`, and optional field-level
94
+ `details`:
95
+
96
+ ```json
97
+ { "data": null, "error": {
98
+ "code": "validation_failed",
99
+ "message": "The request was invalid.",
100
+ "details": [ { "field": "email", "issue": "must be a valid email" } ]
101
+ } }
102
+ ```
103
+
104
+ Rules and the full catalogue of codes are in `references/error-handling.md`.
105
+
106
+ ## 7. Versioning & evolution
107
+
108
+ APIs are forever once published. Version explicitly (`/v1/...` in the path is the most operationally
109
+ clear) and treat additive changes as backward-compatible; breaking changes require a new version.
110
+ Strategy and a deprecation playbook: `references/versioning.md`.
111
+
112
+ ## 8. Idempotency, concurrency, security
113
+
114
+ - **Idempotency:** accept an `Idempotency-Key` header on `POST` so client retries don't double-create
115
+ (the server stores key → result and replays it). Essential for payments.
116
+ - **Concurrency:** use `ETag` + `If-Match` for optimistic locking on updates to prevent lost writes.
117
+ - **Security:** authenticate every request, authorize every action server-side (guard against IDOR by
118
+ checking ownership, not just authentication), rate-limit (return `429` + `Retry-After`), and validate
119
+ all input. Pairs with the `security-auditor` agent and the `owasp-top10` skill.
120
+
121
+ ## Common pitfalls (anti-patterns)
122
+
123
+ - **`200 OK` with `{"success": false}`** — breaks HTTP error handling; use real status codes.
124
+ - **Verbs in URLs** (`/createUser`) — the method is the verb.
125
+ - **Unbounded list endpoints** — always paginate; a `/users` that returns 2M rows is a DoS on yourself.
126
+ - **Inconsistent shapes** — one endpoint returns an array, another an object; clients can't generalize.
127
+ - **Leaking existence** via 404-vs-403 to unauthorized users — be deliberate (see status-codes ref).
128
+ - **Breaking changes without a version bump** — renaming/removing a field silently breaks consumers.
129
+
130
+ ## When NOT to use REST
131
+
132
+ REST isn't always the right tool. Prefer **GraphQL** when clients need flexible, nested selections and
133
+ you want to avoid over/under-fetching; prefer **gRPC** for low-latency internal service-to-service
134
+ calls; prefer **webhooks/event streams** for server-push. REST shines for resource-oriented public
135
+ HTTP APIs where cacheability and ubiquity matter.
136
+
137
+ ## Files in this package
138
+
139
+ - `references/status-codes.md` — full status-code decision guide + edge cases
140
+ - `references/pagination.md` — cursor vs offset, encoding, pitfalls
141
+ - `references/versioning.md` — versioning strategies + deprecation playbook
142
+ - `references/error-handling.md` — error shape, codes, validation details
143
+ - `examples/orders-api.openapi.yaml` — a complete OpenAPI snippet
144
+ - `examples/error-envelope.json` — canonical success/error payloads
145
+ - `scripts/check-envelope.mjs` — runnable Node check that a payload matches the envelope
146
+
147
+ Pairs with the `api-designer` agent, the `api-documenter` agent, and the `webhook-design` skill.
@@ -0,0 +1,26 @@
1
+ {
2
+ "success_list": {
3
+ "data": [
4
+ { "id": "101", "status": "open", "total": 49.0, "created_at": "2026-06-28T10:00:00Z" }
5
+ ],
6
+ "meta": { "nextCursor": "eyJpZCI6MTAxfQ", "limit": 20, "hasMore": true },
7
+ "error": null
8
+ },
9
+ "success_single": {
10
+ "data": { "id": "101", "status": "open", "total": 49.0 },
11
+ "error": null
12
+ },
13
+ "error_validation": {
14
+ "data": null,
15
+ "error": {
16
+ "code": "validation_failed",
17
+ "message": "The request was invalid.",
18
+ "details": [{ "field": "items", "issue": "must contain at least one item" }],
19
+ "requestId": "req_01HX2K"
20
+ }
21
+ },
22
+ "error_not_found": {
23
+ "data": null,
24
+ "error": { "code": "not_found", "message": "Order 999 not found.", "requestId": "req_01HX2M" }
25
+ }
26
+ }
@@ -0,0 +1,106 @@
1
+ openapi: 3.1.0
2
+ info:
3
+ title: Orders API
4
+ version: "1.0.0"
5
+ description: Example of the conventions in this skill — plural nouns, accurate status codes, cursor pagination, consistent envelope.
6
+ servers:
7
+ - url: https://api.example.com/v1
8
+ paths:
9
+ /orders:
10
+ get:
11
+ summary: List orders
12
+ parameters:
13
+ - { name: status, in: query, schema: { type: string, enum: [open, paid, refunded] } }
14
+ - { name: sort, in: query, schema: { type: string, example: "-created_at" } }
15
+ - { name: limit, in: query, schema: { type: integer, default: 20, maximum: 100 } }
16
+ - { name: cursor, in: query, schema: { type: string } }
17
+ responses:
18
+ "200":
19
+ description: A page of orders
20
+ content:
21
+ application/json:
22
+ schema: { $ref: "#/components/schemas/OrderList" }
23
+ "401": { $ref: "#/components/responses/Error" }
24
+ post:
25
+ summary: Create an order
26
+ parameters:
27
+ - name: Idempotency-Key
28
+ in: header
29
+ required: true
30
+ schema: { type: string }
31
+ requestBody:
32
+ required: true
33
+ content:
34
+ application/json:
35
+ schema: { $ref: "#/components/schemas/OrderInput" }
36
+ responses:
37
+ "201":
38
+ description: Created
39
+ headers:
40
+ Location: { schema: { type: string }, description: URL of the new order }
41
+ content:
42
+ application/json:
43
+ schema: { $ref: "#/components/schemas/OrderEnvelope" }
44
+ "422": { $ref: "#/components/responses/Error" }
45
+ /orders/{id}:
46
+ get:
47
+ summary: Get one order
48
+ parameters:
49
+ - { name: id, in: path, required: true, schema: { type: string } }
50
+ responses:
51
+ "200":
52
+ description: The order
53
+ content:
54
+ application/json:
55
+ schema: { $ref: "#/components/schemas/OrderEnvelope" }
56
+ "404": { $ref: "#/components/responses/Error" }
57
+ components:
58
+ responses:
59
+ Error:
60
+ description: Standard error envelope
61
+ content:
62
+ application/json:
63
+ schema: { $ref: "#/components/schemas/ErrorEnvelope" }
64
+ schemas:
65
+ Order:
66
+ type: object
67
+ properties:
68
+ id: { type: string }
69
+ status: { type: string, enum: [open, paid, refunded] }
70
+ total: { type: number }
71
+ created_at: { type: string, format: date-time }
72
+ OrderInput:
73
+ type: object
74
+ required: [items]
75
+ properties:
76
+ items:
77
+ type: array
78
+ items: { type: object, properties: { product_id: { type: string }, quantity: { type: integer } } }
79
+ Meta:
80
+ type: object
81
+ properties:
82
+ nextCursor: { type: string, nullable: true }
83
+ limit: { type: integer }
84
+ hasMore: { type: boolean }
85
+ OrderList:
86
+ type: object
87
+ properties:
88
+ data: { type: array, items: { $ref: "#/components/schemas/Order" } }
89
+ meta: { $ref: "#/components/schemas/Meta" }
90
+ error: { type: object, nullable: true }
91
+ OrderEnvelope:
92
+ type: object
93
+ properties:
94
+ data: { $ref: "#/components/schemas/Order" }
95
+ error: { type: object, nullable: true }
96
+ ErrorEnvelope:
97
+ type: object
98
+ properties:
99
+ data: { nullable: true }
100
+ error:
101
+ type: object
102
+ properties:
103
+ code: { type: string }
104
+ message: { type: string }
105
+ details: { type: array, items: { type: object } }
106
+ requestId: { type: string }
@@ -0,0 +1,43 @@
1
+ # Error Handling
2
+
3
+ One error shape across every endpoint, with machine-readable codes and human-readable messages.
4
+
5
+ ## The shape
6
+ ```json
7
+ {
8
+ "data": null,
9
+ "error": {
10
+ "code": "validation_failed",
11
+ "message": "The request was invalid.",
12
+ "details": [
13
+ { "field": "email", "issue": "must be a valid email" },
14
+ { "field": "age", "issue": "must be >= 18" }
15
+ ],
16
+ "requestId": "req_01HX..."
17
+ }
18
+ }
19
+ ```
20
+
21
+ ## Rules
22
+ - `code` is a **stable, machine-readable** string (clients branch on it) — e.g. `not_found`,
23
+ `validation_failed`, `rate_limited`, `conflict`. Don't change codes once published.
24
+ - `message` is for humans/logs; never put sensitive data or stack traces in it.
25
+ - `details` is optional, used for field-level validation errors.
26
+ - Include a `requestId` so support can correlate a client report with server logs.
27
+ - The HTTP status code and the `error.code` must agree (a `404` carries `not_found`).
28
+
29
+ ## Common codes ↔ status
30
+ | code | status |
31
+ |---|---|
32
+ | `validation_failed` | 422 |
33
+ | `not_found` | 404 |
34
+ | `unauthorized` | 401 |
35
+ | `forbidden` | 403 |
36
+ | `conflict` | 409 |
37
+ | `rate_limited` | 429 |
38
+ | `internal_error` | 500 |
39
+
40
+ ## Don'ts
41
+ - Don't return `200` with an error body.
42
+ - Don't expose internal exception messages, SQL, or stack traces.
43
+ - Don't use free-form, ever-changing error strings as the thing clients match on — that's what `code` is for.
@@ -0,0 +1,41 @@
1
+ # Pagination — Cursor vs Offset
2
+
3
+ Every collection endpoint must paginate. Choosing the wrong style causes slow queries and inconsistent
4
+ results at scale.
5
+
6
+ ## Offset/limit
7
+ ```http
8
+ GET /orders?limit=20&offset=40
9
+ ```
10
+ - **Pros:** simple, allows jumping to an arbitrary page.
11
+ - **Cons:** the database must scan and discard `offset` rows — page 10,000 is slow. Worse, if rows are
12
+ inserted/deleted while a client paginates, items get **skipped or duplicated** across pages.
13
+ - **Use when:** datasets are small, or users genuinely need page numbers.
14
+
15
+ ## Cursor (keyset) pagination
16
+ ```http
17
+ GET /orders?limit=20&cursor=eyJpZCI6MTAwfQ
18
+ ```
19
+ - The cursor encodes the position (e.g. the last seen sort key), so the DB query is
20
+ `WHERE (created_at, id) < (:lastCreated, :lastId) ORDER BY created_at DESC, id DESC LIMIT :limit`.
21
+ - **Pros:** O(limit) regardless of depth; stable under concurrent inserts/deletes.
22
+ - **Cons:** no arbitrary page jumps; requires a stable, unique sort key (add a tiebreaker like `id`).
23
+ - **Use when:** large, growing, or real-time data — the default for most production APIs.
24
+
25
+ ## Cursor encoding
26
+ Encode the cursor opaquely (base64 of the sort key) so clients treat it as a token, not a number to
27
+ manipulate:
28
+ ```
29
+ cursor = base64({"created_at":"2026-06-28T10:00:00Z","id":100})
30
+ ```
31
+
32
+ ## Response shape
33
+ ```json
34
+ { "data": [ /* ... */ ], "meta": { "nextCursor": "eyJ...", "limit": 20, "hasMore": true } }
35
+ ```
36
+
37
+ ## Pitfalls
38
+ - Always include a **unique tiebreaker** in the sort (e.g. `id`); sorting by a non-unique column alone
39
+ drops or repeats rows at page boundaries.
40
+ - Cap `limit` server-side (e.g. max 100) so a client can't request a million rows.
41
+ - Don't mix offset and cursor on the same endpoint — pick one.
@@ -0,0 +1,32 @@
1
+ # Status Codes — Decision Guide
2
+
3
+ Return the most specific accurate code. Clients, proxies, and caches all key off the status line.
4
+
5
+ ## Success
6
+ - `200 OK` — successful GET/PUT/PATCH with a body.
7
+ - `201 Created` — successful POST that created a resource. Include a `Location` header pointing to it.
8
+ - `202 Accepted` — accepted for async processing; not done yet (return a status URL).
9
+ - `204 No Content` — success with no body (typical for DELETE).
10
+
11
+ ## Client errors
12
+ - `400 Bad Request` — malformed syntax / unparseable.
13
+ - `401 Unauthorized` — not authenticated (no/invalid credentials). Add `WWW-Authenticate`.
14
+ - `403 Forbidden` — authenticated but not allowed.
15
+ - `404 Not Found` — resource doesn't exist (or you're hiding its existence — see below).
16
+ - `405 Method Not Allowed` — wrong verb for the resource. Include an `Allow` header.
17
+ - `409 Conflict` — state conflict (e.g. duplicate, version mismatch on PUT).
18
+ - `422 Unprocessable Entity` — syntactically valid but semantically invalid (failed validation).
19
+ - `429 Too Many Requests` — rate limited. Include `Retry-After`.
20
+
21
+ ## Server errors
22
+ - `500 Internal Server Error` — unexpected fault. Never leak stack traces to clients.
23
+ - `503 Service Unavailable` — overloaded/down for maintenance. Include `Retry-After`.
24
+
25
+ ## Edge cases & judgement calls
26
+ - **404 vs 403 (existence leakage):** if telling an unauthorized user a resource exists is itself a leak
27
+ (e.g. `/users/{id}` in a multi-tenant app), return `404` instead of `403` so attackers can't enumerate.
28
+ - **400 vs 422:** use `400` for unparseable requests; `422` for well-formed requests that fail business
29
+ validation. Pick one convention and apply it consistently.
30
+ - **409 vs 422 on conflict:** `409` for a conflict with current resource state (stale version, duplicate
31
+ unique key); `422` for input that can't be processed regardless of state.
32
+ - **PUT semantics:** `200`/`204` if it replaced an existing resource; `201` if PUT created one.
@@ -0,0 +1,31 @@
1
+ # Versioning & Evolution
2
+
3
+ Once an API is public, other people's software depends on it. Plan to evolve without breaking them.
4
+
5
+ ## What is a breaking change?
6
+ **Breaking** (needs a new version): removing/renaming a field or endpoint, changing a type, adding a
7
+ required request field, changing status-code or error semantics, tightening validation.
8
+
9
+ **Non-breaking** (safe in place): adding a new endpoint, adding an **optional** request field, adding a
10
+ new field to a response (clients must ignore unknown fields — document this expectation).
11
+
12
+ ## Strategies
13
+ | Strategy | Example | Notes |
14
+ |---|---|---|
15
+ | URI path | `/v1/orders` | Most operationally clear; easy routing, caching, logs. Most common. |
16
+ | Header | `Accept: application/vnd.api.v1+json` | Cleaner URLs, harder to test/debug. |
17
+ | Query param | `/orders?version=1` | Simple but easy to omit; weakest. |
18
+
19
+ Recommendation: **URI path versioning** for public APIs — explicit and unambiguous.
20
+
21
+ ## Deprecation playbook
22
+ 1. Announce the new version and a deprecation date for the old one.
23
+ 2. Add a `Deprecation` (and optional `Sunset`) header to old-version responses.
24
+ 3. Maintain both versions during a published overlap window.
25
+ 4. Monitor old-version usage; reach out to remaining consumers.
26
+ 5. Remove only after usage approaches zero and the sunset date passes.
27
+
28
+ ## Tips
29
+ - Avoid versioning as long as possible by making additive changes.
30
+ - Never silently change behavior within a version — that's a breaking change in disguise.
31
+ - Document unknown-field tolerance so adding fields stays non-breaking.
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ // Runnable check: validates that a JSON payload matches the standard response
3
+ // envelope from this skill. Demonstrates an *executed*, verifiable example asset.
4
+ //
5
+ // Usage:
6
+ // node check-envelope.mjs '<json>' # check a literal JSON string
7
+ // echo '<json>' | node check-envelope.mjs # check from stdin
8
+ // node check-envelope.mjs --selftest # run built-in test cases
9
+
10
+ const RULES = [
11
+ ['has "data" key (may be null)', (o) => 'data' in o],
12
+ ['has "error" key', (o) => 'error' in o],
13
+ ['error is null OR an object with a string code', (o) =>
14
+ o.error === null || (typeof o.error === 'object' && typeof o.error.code === 'string')],
15
+ ['on error, data is null', (o) => o.error === null || o.data === null],
16
+ ['list responses include meta.limit', (o) =>
17
+ !Array.isArray(o.data) || (o.meta && typeof o.meta.limit === 'number')],
18
+ ];
19
+
20
+ export function validateEnvelope(obj) {
21
+ const failures = RULES.filter(([, fn]) => !safe(fn, obj)).map(([msg]) => msg);
22
+ return { ok: failures.length === 0, failures };
23
+ }
24
+
25
+ function safe(fn, o) { try { return fn(o); } catch { return false; } }
26
+
27
+ function check(label, json) {
28
+ let obj;
29
+ try { obj = typeof json === 'string' ? JSON.parse(json) : json; }
30
+ catch { console.error(`✗ ${label}: invalid JSON`); return false; }
31
+ const { ok, failures } = validateEnvelope(obj);
32
+ if (ok) console.log(`✓ ${label}: valid envelope`);
33
+ else { console.error(`✗ ${label}:`); failures.forEach((f) => console.error(` - ${f}`)); }
34
+ return ok;
35
+ }
36
+
37
+ function selftest() {
38
+ const cases = {
39
+ valid_list: { data: [{ id: '1' }], meta: { limit: 20 }, error: null },
40
+ valid_error: { data: null, error: { code: 'not_found', message: 'x' } },
41
+ bad_200_error: { data: { id: '1' }, error: { code: 'oops' } }, // data not null on error -> fail
42
+ bad_list_no_meta: { data: [{ id: '1' }], error: null }, // list without meta.limit -> fail
43
+ };
44
+ let allExpected = true;
45
+ for (const [name, payload] of Object.entries(cases)) {
46
+ const { ok } = validateEnvelope(payload);
47
+ const shouldPass = name.startsWith('valid');
48
+ const correct = ok === shouldPass;
49
+ allExpected &&= correct;
50
+ console.log(`${correct ? '✓' : '✗'} selftest ${name}: ${ok ? 'valid' : 'invalid'} (expected ${shouldPass ? 'valid' : 'invalid'})`);
51
+ }
52
+ process.exit(allExpected ? 0 : 1);
53
+ }
54
+
55
+ const arg = process.argv[2];
56
+ if (arg === '--selftest') selftest();
57
+ else if (arg) process.exit(check('payload', arg) ? 0 : 1);
58
+ else {
59
+ let buf = '';
60
+ process.stdin.on('data', (c) => (buf += c));
61
+ process.stdin.on('end', () => process.exit(check('stdin', buf) ? 0 : 1));
62
+ }