@jigyasudham/veto 0.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/.claude/settings.local.json +9 -0
- package/README.md +190 -0
- package/dist/adapters/claude.js +57 -0
- package/dist/adapters/codex.js +58 -0
- package/dist/adapters/gemini.js +58 -0
- package/dist/adapters/index.js +156 -0
- package/dist/agents/development/api.js +116 -0
- package/dist/agents/development/backend.js +82 -0
- package/dist/agents/development/coder.js +207 -0
- package/dist/agents/development/database.js +81 -0
- package/dist/agents/development/debugger.js +234 -0
- package/dist/agents/development/devops.js +84 -0
- package/dist/agents/development/frontend.js +83 -0
- package/dist/agents/development/migration.js +141 -0
- package/dist/agents/development/performance.js +142 -0
- package/dist/agents/development/refactor.js +85 -0
- package/dist/agents/development/reviewer.js +260 -0
- package/dist/agents/development/tester.js +143 -0
- package/dist/agents/executor.js +144 -0
- package/dist/agents/memory/context-manager.js +167 -0
- package/dist/agents/memory/decision-logger.js +157 -0
- package/dist/agents/memory/knowledge-base.js +120 -0
- package/dist/agents/memory/pattern-learner.js +140 -0
- package/dist/agents/memory/project-mapper.js +114 -0
- package/dist/agents/quality/accessibility.js +89 -0
- package/dist/agents/quality/code-quality.js +109 -0
- package/dist/agents/quality/compatibility.js +55 -0
- package/dist/agents/quality/documentation.js +95 -0
- package/dist/agents/quality/error-handling.js +87 -0
- package/dist/agents/research/competitor-analyzer.js +44 -0
- package/dist/agents/research/cost-analyzer.js +51 -0
- package/dist/agents/research/estimator.js +57 -0
- package/dist/agents/research/ethics-bias.js +111 -0
- package/dist/agents/research/researcher.js +112 -0
- package/dist/agents/research/risk-assessor.js +61 -0
- package/dist/agents/research/tech-advisor.js +52 -0
- package/dist/agents/security/auth.js +269 -0
- package/dist/agents/security/dependency-audit.js +273 -0
- package/dist/agents/security/penetration.js +245 -0
- package/dist/agents/security/privacy.js +259 -0
- package/dist/agents/security/scanner.js +288 -0
- package/dist/agents/security/secrets.js +212 -0
- package/dist/agents/types.js +2 -0
- package/dist/agents/workflow/automation.js +56 -0
- package/dist/agents/workflow/file-manager.js +49 -0
- package/dist/agents/workflow/git-agent.js +52 -0
- package/dist/agents/workflow/reporter.js +48 -0
- package/dist/agents/workflow/search-agent.js +39 -0
- package/dist/agents/workflow/task-coordinator.js +40 -0
- package/dist/agents/workflow/task-planner.js +46 -0
- package/dist/cli.js +132 -0
- package/dist/council/decision-engine.js +136 -0
- package/dist/council/devil-advocate.js +106 -0
- package/dist/council/index.js +37 -0
- package/dist/council/lead-developer.js +108 -0
- package/dist/council/legal-compliance.js +142 -0
- package/dist/council/product-manager.js +92 -0
- package/dist/council/security.js +162 -0
- package/dist/council/system-architect.js +122 -0
- package/dist/council/types.js +2 -0
- package/dist/council/ux-designer.js +109 -0
- package/dist/memory/local.js +182 -0
- package/dist/memory/schema.js +116 -0
- package/dist/memory/sync.js +199 -0
- package/dist/router/complexity-scorer.js +78 -0
- package/dist/router/context-compressor.js +58 -0
- package/dist/router/index.js +29 -0
- package/dist/router/learning-updater.js +186 -0
- package/dist/router/model-selector.js +51 -0
- package/dist/router/rate-monitor.js +73 -0
- package/dist/server.js +949 -0
- package/dist/skills/development/skill-api-design.js +313 -0
- package/dist/skills/development/skill-auth.js +255 -0
- package/dist/skills/development/skill-ci-cd.js +2 -0
- package/dist/skills/development/skill-crud.js +193 -0
- package/dist/skills/development/skill-db-schema.js +2 -0
- package/dist/skills/development/skill-docker.js +2 -0
- package/dist/skills/development/skill-env-setup.js +2 -0
- package/dist/skills/development/skill-scaffold.js +299 -0
- package/dist/skills/intelligence/skill-complexity-score.js +66 -0
- package/dist/skills/intelligence/skill-cost-track.js +36 -0
- package/dist/skills/intelligence/skill-learning-loop.js +66 -0
- package/dist/skills/intelligence/skill-pattern-detect.js +35 -0
- package/dist/skills/intelligence/skill-rate-watch.js +58 -0
- package/dist/skills/memory/skill-context-compress.js +82 -0
- package/dist/skills/memory/skill-cross-sync.js +88 -0
- package/dist/skills/memory/skill-decision-log.js +103 -0
- package/dist/skills/memory/skill-session-restore.js +44 -0
- package/dist/skills/memory/skill-session-save.js +78 -0
- package/dist/skills/quality/skill-accessibility.js +2 -0
- package/dist/skills/quality/skill-code-review.js +60 -0
- package/dist/skills/quality/skill-docs-gen.js +2 -0
- package/dist/skills/quality/skill-perf-audit.js +2 -0
- package/dist/skills/quality/skill-security-scan.js +67 -0
- package/dist/skills/quality/skill-test-suite.js +274 -0
- package/dist/skills/workflow/skill-deploy.js +2 -0
- package/dist/skills/workflow/skill-git-workflow.js +2 -0
- package/dist/skills/workflow/skill-rollback.js +2 -0
- package/dist/skills/workflow/skill-task-breakdown.js +2 -0
- package/package.json +30 -0
- package/src/adapters/claude.ts +70 -0
- package/src/adapters/codex.ts +71 -0
- package/src/adapters/gemini.ts +71 -0
- package/src/adapters/index.ts +217 -0
- package/src/agents/development/api.ts +120 -0
- package/src/agents/development/backend.ts +85 -0
- package/src/agents/development/coder.ts +213 -0
- package/src/agents/development/database.ts +83 -0
- package/src/agents/development/debugger.ts +238 -0
- package/src/agents/development/devops.ts +86 -0
- package/src/agents/development/frontend.ts +85 -0
- package/src/agents/development/migration.ts +144 -0
- package/src/agents/development/performance.ts +144 -0
- package/src/agents/development/refactor.ts +86 -0
- package/src/agents/development/reviewer.ts +268 -0
- package/src/agents/development/tester.ts +151 -0
- package/src/agents/executor.ts +158 -0
- package/src/agents/memory/context-manager.ts +171 -0
- package/src/agents/memory/decision-logger.ts +160 -0
- package/src/agents/memory/knowledge-base.ts +124 -0
- package/src/agents/memory/pattern-learner.ts +143 -0
- package/src/agents/memory/project-mapper.ts +118 -0
- package/src/agents/quality/accessibility.ts +99 -0
- package/src/agents/quality/code-quality.ts +115 -0
- package/src/agents/quality/compatibility.ts +58 -0
- package/src/agents/quality/documentation.ts +105 -0
- package/src/agents/quality/error-handling.ts +96 -0
- package/src/agents/research/competitor-analyzer.ts +45 -0
- package/src/agents/research/cost-analyzer.ts +54 -0
- package/src/agents/research/estimator.ts +60 -0
- package/src/agents/research/ethics-bias.ts +113 -0
- package/src/agents/research/researcher.ts +114 -0
- package/src/agents/research/risk-assessor.ts +63 -0
- package/src/agents/research/tech-advisor.ts +55 -0
- package/src/agents/security/auth.ts +287 -0
- package/src/agents/security/dependency-audit.ts +337 -0
- package/src/agents/security/penetration.ts +262 -0
- package/src/agents/security/privacy.ts +285 -0
- package/src/agents/security/scanner.ts +322 -0
- package/src/agents/security/secrets.ts +249 -0
- package/src/agents/types.ts +66 -0
- package/src/agents/workflow/automation.ts +59 -0
- package/src/agents/workflow/file-manager.ts +52 -0
- package/src/agents/workflow/git-agent.ts +55 -0
- package/src/agents/workflow/reporter.ts +51 -0
- package/src/agents/workflow/search-agent.ts +40 -0
- package/src/agents/workflow/task-coordinator.ts +41 -0
- package/src/agents/workflow/task-planner.ts +47 -0
- package/src/cli.ts +143 -0
- package/src/council/decision-engine.ts +171 -0
- package/src/council/devil-advocate.ts +116 -0
- package/src/council/index.ts +44 -0
- package/src/council/lead-developer.ts +118 -0
- package/src/council/legal-compliance.ts +152 -0
- package/src/council/product-manager.ts +102 -0
- package/src/council/security.ts +172 -0
- package/src/council/system-architect.ts +132 -0
- package/src/council/types.ts +33 -0
- package/src/council/ux-designer.ts +121 -0
- package/src/memory/local.ts +305 -0
- package/src/memory/schema.ts +174 -0
- package/src/memory/sync.ts +274 -0
- package/src/router/complexity-scorer.ts +96 -0
- package/src/router/context-compressor.ts +74 -0
- package/src/router/index.ts +60 -0
- package/src/router/learning-updater.ts +271 -0
- package/src/router/model-selector.ts +83 -0
- package/src/router/rate-monitor.ts +103 -0
- package/src/server.ts +1038 -0
- package/src/skills/development/skill-api-design.ts +329 -0
- package/src/skills/development/skill-auth.ts +271 -0
- package/src/skills/development/skill-ci-cd.ts +0 -0
- package/src/skills/development/skill-crud.ts +209 -0
- package/src/skills/development/skill-db-schema.ts +0 -0
- package/src/skills/development/skill-docker.ts +0 -0
- package/src/skills/development/skill-env-setup.ts +0 -0
- package/src/skills/development/skill-scaffold.ts +323 -0
- package/src/skills/intelligence/skill-complexity-score.ts +69 -0
- package/src/skills/intelligence/skill-cost-track.ts +39 -0
- package/src/skills/intelligence/skill-learning-loop.ts +69 -0
- package/src/skills/intelligence/skill-pattern-detect.ts +38 -0
- package/src/skills/intelligence/skill-rate-watch.ts +61 -0
- package/src/skills/memory/skill-context-compress.ts +98 -0
- package/src/skills/memory/skill-cross-sync.ts +104 -0
- package/src/skills/memory/skill-decision-log.ts +119 -0
- package/src/skills/memory/skill-session-restore.ts +59 -0
- package/src/skills/memory/skill-session-save.ts +94 -0
- package/src/skills/quality/skill-accessibility.ts +0 -0
- package/src/skills/quality/skill-code-review.ts +84 -0
- package/src/skills/quality/skill-docs-gen.ts +0 -0
- package/src/skills/quality/skill-perf-audit.ts +0 -0
- package/src/skills/quality/skill-security-scan.ts +91 -0
- package/src/skills/quality/skill-test-suite.ts +290 -0
- package/src/skills/workflow/skill-deploy.ts +0 -0
- package/src/skills/workflow/skill-git-workflow.ts +0 -0
- package/src/skills/workflow/skill-rollback.ts +0 -0
- package/src/skills/workflow/skill-task-breakdown.ts +0 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"PowerShell(Get-ChildItem D:\\\\Veto\\\\src -Recurse -File | Where-Object { $_.FullName -notmatch \"node_modules\" } | Select-Object FullName | ForEach-Object { $_.FullName.Replace\\(\"D:\\\\Veto\\\\\", \"\"\\) })",
|
|
5
|
+
"PowerShell(cd D:\\\\Veto; npm run build 2>&1; Write-Host \"Exit: $LASTEXITCODE\")",
|
|
6
|
+
"PowerShell(Get-ChildItem D:\\\\Veto\\\\docs -File | Select-Object FullName; Get-ChildItem D:\\\\Veto -File | Where-Object { $_.Extension -eq \".md\" } | Select-Object FullName)"
|
|
7
|
+
]
|
|
8
|
+
}
|
|
9
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# veto
|
|
2
|
+
|
|
3
|
+
> **50 agents. 28 skills. 3 AIs. Self-learning. Zero extra cost.**
|
|
4
|
+
|
|
5
|
+
An MCP server that runs locally on your machine, plugs into Claude Code, Codex CLI, and Gemini CLI using your existing subscriptions — giving every AI a council of specialist agents, persistent cross-platform memory, a self-learning router, and the ability to say no to bad decisions.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
| Requirement | Version | Notes |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| **Node.js** | 22.5.0 or higher | Required — uses the built-in `node:sqlite` module (no native compilation). Download at [nodejs.org](https://nodejs.org). |
|
|
14
|
+
| **At least one AI CLI** | Latest | Claude Code, Gemini CLI, or Codex CLI — whichever you use. Veto works with all three. See below. |
|
|
15
|
+
|
|
16
|
+
**Check your Node version:**
|
|
17
|
+
```bash
|
|
18
|
+
node --version # must be v22.5.0 or higher
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
If you're on an older version, update Node before continuing — Veto will fail silently on Node 18 or 20 because `node:sqlite` does not exist in those versions.
|
|
22
|
+
|
|
23
|
+
**Install whichever AI CLI(s) you use — Veto works with all of them:**
|
|
24
|
+
|
|
25
|
+
| Platform | Install | Auth |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| **Claude Code** | [claude.ai/code](https://claude.ai/code) | Sign in via browser |
|
|
28
|
+
| **Gemini CLI** | `npm install -g @google/gemini-cli` | `gemini auth` |
|
|
29
|
+
| **Codex CLI** | `npm install -g @openai/codex` | `export OPENAI_API_KEY=your-key` |
|
|
30
|
+
|
|
31
|
+
You only need one to get started. Install more to enable cross-platform handoff.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx @jigyasudham/veto@latest init
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The `init` command prints the exact config snippet for your platform. Paste it into your MCP config file:
|
|
42
|
+
|
|
43
|
+
| Platform | Config file |
|
|
44
|
+
|---|---|
|
|
45
|
+
| **Claude Code** | `~/.claude/mcp_servers.json` |
|
|
46
|
+
| **Gemini CLI** | `~/.gemini/settings.json` |
|
|
47
|
+
| **Codex CLI** | `~/.codex/config.json` |
|
|
48
|
+
| **Cursor** | `~/.cursor/mcp.json` |
|
|
49
|
+
| **Windsurf** | `~/.codeium/windsurf/mcp_config.json` |
|
|
50
|
+
| **VS Code** | `.vscode/mcp.json` |
|
|
51
|
+
|
|
52
|
+
Claude Code, Gemini, Codex, Cursor, and Windsurf all use `"mcpServers"`. VS Code uses `"servers"` with `"type": "stdio"`:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"servers": {
|
|
57
|
+
"veto": {
|
|
58
|
+
"type": "stdio",
|
|
59
|
+
"command": "node",
|
|
60
|
+
"args": ["/path/to/dist/server.js"]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## What Veto Does
|
|
69
|
+
|
|
70
|
+
**Council** — Before any significant task, 7 specialist agents debate it in parallel and return a GREEN / YELLOW / RED / DEADLOCK verdict. Bad decisions get blocked before any code is written.
|
|
71
|
+
|
|
72
|
+
**Router** — Every task is scored locally (zero tokens) and sent to the right model tier. Rate limits are tracked across all 3 platforms. The router self-adjusts over time from recorded outcomes.
|
|
73
|
+
|
|
74
|
+
**50 Agents** — Domain experts for every task type. Each agent knows when it is the right tool and when to defer to another.
|
|
75
|
+
|
|
76
|
+
**Memory** — Sessions, decisions, knowledge, and coding patterns persist across every conversation and every platform.
|
|
77
|
+
|
|
78
|
+
**Cross-platform handoff** — Claude hitting its rate limit? Call `veto_handoff`, open Gemini or Codex, call `veto_continue`. Full context restored in seconds. Nothing re-explained.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## The 50 Agents
|
|
83
|
+
|
|
84
|
+
### Council Layer (8) — runs before any code is written
|
|
85
|
+
`Lead Developer` · `Product Manager` · `System Architect` · `UX Designer` · `Devil's Advocate` · `Legal & Compliance` · `Security` · `Decision Engine`
|
|
86
|
+
|
|
87
|
+
### Development (12)
|
|
88
|
+
`Coder` · `Code Reviewer` · `Tester` · `Debugger` · `Refactor` · `Database` · `API` · `Frontend` · `Backend` · `DevOps` · `Performance` · `Migration`
|
|
89
|
+
|
|
90
|
+
### Security (6)
|
|
91
|
+
`Security Scanner` · `Auth Agent` · `Data Privacy` · `Secrets Agent` · `Dependency Audit` · `Penetration Tester`
|
|
92
|
+
|
|
93
|
+
### Memory (5)
|
|
94
|
+
`Context Manager` · `Decision Logger` · `Project Mapper` · `Pattern Learner` · `Knowledge Base`
|
|
95
|
+
|
|
96
|
+
### Research (7)
|
|
97
|
+
`Researcher` · `Tech Advisor` · `Cost Analyzer` · `Competitor Analyzer` · `Risk Assessor` · `Estimator` · `Ethics & Bias`
|
|
98
|
+
|
|
99
|
+
### Quality (5)
|
|
100
|
+
`Code Quality` · `Documentation` · `Accessibility` · `Compatibility` · `Error Handling`
|
|
101
|
+
|
|
102
|
+
### Workflow (7)
|
|
103
|
+
`Task Planner` · `Task Coordinator` · `File Manager` · `Git Agent` · `Search Agent` · `Reporter` · `Automation`
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## MCP Tools (29)
|
|
108
|
+
|
|
109
|
+
| Category | Tools |
|
|
110
|
+
|---|---|
|
|
111
|
+
| Session | `veto_status` · `veto_session_save` · `veto_session_restore` · `veto_sessions_list` |
|
|
112
|
+
| Router | `veto_route_task` · `veto_rate_status` |
|
|
113
|
+
| Council | `veto_council_debate` |
|
|
114
|
+
| Agents | `veto_agent_plan` · `veto_code_review` · `veto_security_scan` · `veto_secrets_scan` · `veto_execute_parallel` |
|
|
115
|
+
| Memory | `veto_memory_store` · `veto_memory_search` · `veto_memory_delete` · `veto_project_map_update` · `veto_project_map_get` · `veto_pattern_store` · `veto_patterns_list` · `veto_memory_export` · `veto_memory_import` |
|
|
116
|
+
| Learning | `veto_record_outcome` · `veto_learning_stats` · `veto_learning_apply` |
|
|
117
|
+
| Handoff | `veto_handoff` · `veto_continue` · `veto_platform_setup` |
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Cross-Platform Handoff
|
|
122
|
+
|
|
123
|
+
No accounts. No cloud services. Works on the same machine or across machines.
|
|
124
|
+
|
|
125
|
+
**Rate limit mid-task:**
|
|
126
|
+
```
|
|
127
|
+
Claude at 90% → veto_handoff { summary, context }
|
|
128
|
+
Open Gemini → veto_continue
|
|
129
|
+
Full context restored. Continue exactly where you stopped.
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Switch machines:**
|
|
133
|
+
```
|
|
134
|
+
Machine A → veto_memory_export → veto-export.json
|
|
135
|
+
copy file any way (Dropbox, USB, scp)
|
|
136
|
+
Machine B → veto_memory_import
|
|
137
|
+
veto_session_restore → resume instantly
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Platform support:**
|
|
141
|
+
|
|
142
|
+
| Platform | Works with Veto |
|
|
143
|
+
|---|---|
|
|
144
|
+
| Claude Code | ✅ Native MCP |
|
|
145
|
+
| Gemini CLI | ✅ MCP support |
|
|
146
|
+
| Codex CLI | ✅ MCP support |
|
|
147
|
+
| Cursor | ✅ MCP support |
|
|
148
|
+
| Windsurf | ✅ MCP support |
|
|
149
|
+
| VS Code | ✅ MCP support |
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Self-Learning Router
|
|
154
|
+
|
|
155
|
+
The router gets smarter as you use it:
|
|
156
|
+
|
|
157
|
+
1. Complete a task → `veto_record_outcome` with quality score (0–100)
|
|
158
|
+
2. After 20+ outcomes → `veto_learning_apply`
|
|
159
|
+
3. Tier thresholds adjust — over-routed tasks self-correct over time
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Roadmap
|
|
164
|
+
|
|
165
|
+
| Phase | Status | Version |
|
|
166
|
+
|---|---|---|
|
|
167
|
+
| 1 — Foundation | ✅ Complete | v0.1.0 |
|
|
168
|
+
| 2 — Router | ✅ Complete | v0.2.0 |
|
|
169
|
+
| 3 — Council | ✅ Complete | v0.3.0 |
|
|
170
|
+
| 4 — Core Agents | ✅ Complete | v0.4.0 |
|
|
171
|
+
| 5 — Memory System | ✅ Complete | v0.5.0 |
|
|
172
|
+
| 6 — Self-Learning | ✅ Complete | v0.6.0 |
|
|
173
|
+
| 7 — Cross-Platform | ✅ Complete | v0.7.0 |
|
|
174
|
+
| 8 — All 50 Agents | ✅ Complete | v0.8.0 |
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Tech Stack
|
|
179
|
+
|
|
180
|
+
- **Language:** TypeScript (strict mode)
|
|
181
|
+
- **Runtime:** Node.js 22.5+ (required — uses built-in `node:sqlite`)
|
|
182
|
+
- **Dependencies:** `@modelcontextprotocol/sdk` only — one package, no native addons
|
|
183
|
+
- **Memory:** SQLite via `node:sqlite` — zero native compilation, zero configuration, works offline
|
|
184
|
+
- **Cross-machine:** File-based JSON export/import — no external services, no accounts
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
MIT © 2026 Jigyasu Dham
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Claude Code adapter — connection config, rate signal detection, handoff format
|
|
2
|
+
export const PLATFORM = 'claude';
|
|
3
|
+
export function getSetup(vetoServerPath) {
|
|
4
|
+
return {
|
|
5
|
+
platform: 'claude',
|
|
6
|
+
mcp_config: {
|
|
7
|
+
mcpServers: {
|
|
8
|
+
veto: {
|
|
9
|
+
command: 'node',
|
|
10
|
+
args: [vetoServerPath],
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
setup_steps: [
|
|
15
|
+
`1. Build veto: npm run build`,
|
|
16
|
+
`2. Add to ~/.claude/mcp_servers.json:\n${JSON.stringify({ mcpServers: { veto: { command: 'node', args: [vetoServerPath] } } }, null, 2)}`,
|
|
17
|
+
`3. Restart Claude Code`,
|
|
18
|
+
`4. Verify: call veto_status — should return { "status": "running", "version": "0.7.0" }`,
|
|
19
|
+
],
|
|
20
|
+
rate_limit_signals: [
|
|
21
|
+
'rate limit exceeded',
|
|
22
|
+
'too many requests',
|
|
23
|
+
'429',
|
|
24
|
+
'overloaded',
|
|
25
|
+
'capacity',
|
|
26
|
+
'quota exceeded',
|
|
27
|
+
],
|
|
28
|
+
continue_command: 'veto_continue',
|
|
29
|
+
notes: [
|
|
30
|
+
'Claude Code connects to Veto via stdio MCP — the server runs as a child process',
|
|
31
|
+
'All veto_* tools are available natively in Claude Code once connected',
|
|
32
|
+
'Rate limits are tracked by Veto per day — call veto_rate_status to check headroom',
|
|
33
|
+
'Before hitting the limit: call veto_handoff to get a session ID and switch instructions',
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function detectRateLimit(errorText) {
|
|
38
|
+
const signals = ['rate limit', 'too many requests', '429', 'overloaded', 'quota'];
|
|
39
|
+
return signals.some(s => errorText.toLowerCase().includes(s));
|
|
40
|
+
}
|
|
41
|
+
export function formatHandoffMessage(sessionId, targetPlatform) {
|
|
42
|
+
const target = targetPlatform === 'gemini' ? 'Gemini CLI' : 'Codex CLI';
|
|
43
|
+
return [
|
|
44
|
+
`Claude is approaching its rate limit. Switching to ${target}.`,
|
|
45
|
+
``,
|
|
46
|
+
`Session saved. ID: ${sessionId}`,
|
|
47
|
+
``,
|
|
48
|
+
`On ${target}, run:`,
|
|
49
|
+
` veto_continue`,
|
|
50
|
+
``,
|
|
51
|
+
`Veto will restore full context automatically. Nothing needs to be re-explained.`,
|
|
52
|
+
].join('\n');
|
|
53
|
+
}
|
|
54
|
+
export function formatSwitchPrompt(sessionId) {
|
|
55
|
+
return `Rate limit approaching. Session saved (ID: ${sessionId}). Call veto_continue on the next platform to resume instantly.`;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Codex CLI adapter — connection config, rate signal detection, handoff format
|
|
2
|
+
// Codex CLI: https://github.com/openai/codex
|
|
3
|
+
export const PLATFORM = 'codex';
|
|
4
|
+
export function getSetup(vetoServerPath) {
|
|
5
|
+
return {
|
|
6
|
+
platform: 'codex',
|
|
7
|
+
mcp_config: {
|
|
8
|
+
mcpServers: {
|
|
9
|
+
veto: {
|
|
10
|
+
command: 'node',
|
|
11
|
+
args: [vetoServerPath],
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
setup_steps: [
|
|
16
|
+
`1. Install Codex CLI: npm install -g @openai/codex`,
|
|
17
|
+
`2. Set API key: export OPENAI_API_KEY=your-key`,
|
|
18
|
+
`3. Add to ~/.codex/config.json:\n${JSON.stringify({ mcpServers: { veto: { command: 'node', args: [vetoServerPath] } } }, null, 2)}`,
|
|
19
|
+
`4. Restart Codex CLI`,
|
|
20
|
+
`5. Verify: call veto_status — should return { "status": "running", "version": "0.7.0" }`,
|
|
21
|
+
],
|
|
22
|
+
rate_limit_signals: [
|
|
23
|
+
'rate limit reached',
|
|
24
|
+
'too many requests',
|
|
25
|
+
'429',
|
|
26
|
+
'insufficient quota',
|
|
27
|
+
'rate_limit_exceeded',
|
|
28
|
+
'quota_exceeded',
|
|
29
|
+
],
|
|
30
|
+
continue_command: 'veto_continue',
|
|
31
|
+
notes: [
|
|
32
|
+
'Codex CLI connects to Veto via stdio MCP — same server instance as Claude and Gemini',
|
|
33
|
+
'Config path: ~/.codex/config.json (created automatically on first run)',
|
|
34
|
+
'All veto_* tools work identically on Codex as on Claude and Gemini',
|
|
35
|
+
'Codex is the Tier 1/2 overflow when both Claude and Gemini are rate-limited',
|
|
36
|
+
'Uses GPT-4o / o4-mini depending on the tier assigned by the Veto router',
|
|
37
|
+
'ChatGPT web app does NOT support MCP — Codex CLI is the only OpenAI option',
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function detectRateLimit(errorText) {
|
|
42
|
+
const signals = ['rate limit', 'too many requests', '429', 'insufficient quota', 'rate_limit_exceeded'];
|
|
43
|
+
return signals.some(s => errorText.toLowerCase().includes(s.toLowerCase()));
|
|
44
|
+
}
|
|
45
|
+
export function formatHandoffMessage(sessionId, fromPlatform) {
|
|
46
|
+
const from = fromPlatform === 'claude' ? 'Claude' : 'Gemini';
|
|
47
|
+
return [
|
|
48
|
+
`${from} handed off to Codex. Session restored.`,
|
|
49
|
+
``,
|
|
50
|
+
`Session ID: ${sessionId}`,
|
|
51
|
+
``,
|
|
52
|
+
`Codex has full context. Continue the task as if nothing interrupted.`,
|
|
53
|
+
].join('\n');
|
|
54
|
+
}
|
|
55
|
+
export function formatContinueInstructions() {
|
|
56
|
+
return 'Run: veto_continue\nVeto will restore the most recent session automatically.';
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Gemini CLI adapter — connection config, rate signal detection, handoff format
|
|
2
|
+
// Gemini CLI MCP support: https://github.com/google-gemini/gemini-cli
|
|
3
|
+
export const PLATFORM = 'gemini';
|
|
4
|
+
export function getSetup(vetoServerPath) {
|
|
5
|
+
return {
|
|
6
|
+
platform: 'gemini',
|
|
7
|
+
mcp_config: {
|
|
8
|
+
mcpServers: {
|
|
9
|
+
veto: {
|
|
10
|
+
command: 'node',
|
|
11
|
+
args: [vetoServerPath],
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
setup_steps: [
|
|
16
|
+
`1. Install Gemini CLI: npm install -g @google/gemini-cli`,
|
|
17
|
+
`2. Authenticate: gemini auth`,
|
|
18
|
+
`3. Add to ~/.gemini/settings.json:\n${JSON.stringify({ mcpServers: { veto: { command: 'node', args: [vetoServerPath] } } }, null, 2)}`,
|
|
19
|
+
`4. Restart Gemini CLI`,
|
|
20
|
+
`5. Verify: call veto_status — should return { "status": "running", "version": "0.7.0" }`,
|
|
21
|
+
],
|
|
22
|
+
rate_limit_signals: [
|
|
23
|
+
'quota exceeded',
|
|
24
|
+
'resource exhausted',
|
|
25
|
+
'429',
|
|
26
|
+
'rate limit',
|
|
27
|
+
'too many requests',
|
|
28
|
+
'RESOURCE_EXHAUSTED',
|
|
29
|
+
],
|
|
30
|
+
continue_command: 'veto_continue',
|
|
31
|
+
notes: [
|
|
32
|
+
'Gemini CLI connects to Veto via stdio MCP — same server instance as Claude',
|
|
33
|
+
'Gemini CLI uses the same ~/.gemini/settings.json for MCP server config',
|
|
34
|
+
'All veto_* tools work identically on Gemini as on Claude',
|
|
35
|
+
'Gemini Flash is the default Tier 1/2 overflow platform when Claude is rate-limited',
|
|
36
|
+
'Free tier: 1,500 requests/day (15 RPM) — Veto tracks this in rate_usage table',
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function detectRateLimit(errorText) {
|
|
41
|
+
const signals = ['quota exceeded', 'resource exhausted', '429', 'rate limit', 'RESOURCE_EXHAUSTED'];
|
|
42
|
+
return signals.some(s => errorText.toLowerCase().includes(s.toLowerCase()));
|
|
43
|
+
}
|
|
44
|
+
export function formatHandoffMessage(sessionId, fromPlatform) {
|
|
45
|
+
const from = fromPlatform === 'claude' ? 'Claude' : 'Codex';
|
|
46
|
+
return [
|
|
47
|
+
`${from} handed off to Gemini. Session restored.`,
|
|
48
|
+
``,
|
|
49
|
+
`Session ID: ${sessionId}`,
|
|
50
|
+
``,
|
|
51
|
+
`Gemini has full context. Continue the task as if nothing interrupted.`,
|
|
52
|
+
`Call veto_session_restore { "session_id": "${sessionId}" } if context needs refreshing.`,
|
|
53
|
+
].join('\n');
|
|
54
|
+
}
|
|
55
|
+
export function formatContinueInstructions() {
|
|
56
|
+
return 'Run: veto_continue\nVeto will restore the most recent session automatically.';
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=gemini.js.map
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// Handoff engine — platform detection, session save+switch, continue restore
|
|
2
|
+
import { getRateStatus } from '../router/rate-monitor.js';
|
|
3
|
+
import { saveSession, listSessions, restoreSession } from '../memory/local.js';
|
|
4
|
+
import * as claude from './claude.js';
|
|
5
|
+
import * as gemini from './gemini.js';
|
|
6
|
+
import * as codex from './codex.js';
|
|
7
|
+
// ─── Handoff ──────────────────────────────────────────────────────────────────
|
|
8
|
+
export function handoff(options) {
|
|
9
|
+
const rateStatus = getRateStatus();
|
|
10
|
+
const from = options.from_platform ?? 'claude';
|
|
11
|
+
// Pick the best available target platform
|
|
12
|
+
const to = options.to_platform ?? selectTarget(from, rateStatus);
|
|
13
|
+
const reason = rateStatus[from].status === 'critical'
|
|
14
|
+
? `${from} is at ${rateStatus[from].used_percent}% of daily limit`
|
|
15
|
+
: rateStatus[from].status === 'warning'
|
|
16
|
+
? `${from} is at ${rateStatus[from].used_percent}% — switching proactively`
|
|
17
|
+
: 'Manual platform switch requested';
|
|
18
|
+
const { session_id, saved_at } = saveSession({
|
|
19
|
+
platform: from,
|
|
20
|
+
summary: options.summary ?? `Session handed off from ${from} to ${to}`,
|
|
21
|
+
context: options.context,
|
|
22
|
+
task_state: options.task_state,
|
|
23
|
+
token_count: options.token_count ?? 0,
|
|
24
|
+
project_dir: options.project_dir,
|
|
25
|
+
});
|
|
26
|
+
const instructions = buildInstructions(session_id, from, to, rateStatus);
|
|
27
|
+
const one_liner = `Session saved (${session_id.slice(0, 8)}…). On ${to}: call veto_continue`;
|
|
28
|
+
return {
|
|
29
|
+
session_id,
|
|
30
|
+
saved_at,
|
|
31
|
+
from_platform: from,
|
|
32
|
+
to_platform: to,
|
|
33
|
+
reason,
|
|
34
|
+
rate_status: rateStatus,
|
|
35
|
+
instructions,
|
|
36
|
+
one_liner,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// ─── Continue ─────────────────────────────────────────────────────────────────
|
|
40
|
+
export function continueSession(sessionId) {
|
|
41
|
+
const now = new Date().toISOString();
|
|
42
|
+
if (sessionId) {
|
|
43
|
+
const result = restoreSession(sessionId);
|
|
44
|
+
if (!result.found || !result.session) {
|
|
45
|
+
return { found: false, message: `No session found with ID: ${sessionId}`, restored_at: now };
|
|
46
|
+
}
|
|
47
|
+
return buildContinueResult(result.session, now);
|
|
48
|
+
}
|
|
49
|
+
// No ID given — find the most recent session
|
|
50
|
+
const sessions = listSessions(1);
|
|
51
|
+
if (sessions.length === 0) {
|
|
52
|
+
return {
|
|
53
|
+
found: false,
|
|
54
|
+
message: 'No saved sessions found. Save a session first with veto_handoff or veto_session_save.',
|
|
55
|
+
restored_at: now,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const result = restoreSession(sessions[0].id);
|
|
59
|
+
if (!result.found || !result.session) {
|
|
60
|
+
return { found: false, message: 'Could not restore the most recent session.', restored_at: now };
|
|
61
|
+
}
|
|
62
|
+
return buildContinueResult(result.session, now);
|
|
63
|
+
}
|
|
64
|
+
function buildContinueResult(session, now) {
|
|
65
|
+
let context = null;
|
|
66
|
+
let task_state = null;
|
|
67
|
+
let next_action;
|
|
68
|
+
try {
|
|
69
|
+
context = session.context ? JSON.parse(session.context) : null;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
context = session.context;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
let ts = session.task_state ? JSON.parse(session.task_state) : null;
|
|
76
|
+
// saveSession double-stringifies when given a pre-serialised string — unwrap if needed
|
|
77
|
+
if (typeof ts === 'string') {
|
|
78
|
+
try {
|
|
79
|
+
ts = JSON.parse(ts);
|
|
80
|
+
}
|
|
81
|
+
catch { /* keep as string */ }
|
|
82
|
+
}
|
|
83
|
+
task_state = ts;
|
|
84
|
+
if (ts && typeof ts === 'object' && 'nextAction' in ts) {
|
|
85
|
+
next_action = String(ts['nextAction']);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
task_state = session.task_state;
|
|
90
|
+
}
|
|
91
|
+
const message = [
|
|
92
|
+
`Session restored from ${session.platform} (saved ${session.started_at.slice(0, 16)}).`,
|
|
93
|
+
session.summary ? `\nSummary: ${session.summary}` : '',
|
|
94
|
+
next_action ? `\nNext action: ${next_action}` : '',
|
|
95
|
+
`\nContinue exactly where you left off. Nothing needs to be re-explained.`,
|
|
96
|
+
].filter(Boolean).join('');
|
|
97
|
+
return {
|
|
98
|
+
found: true,
|
|
99
|
+
session_id: session.id,
|
|
100
|
+
platform: session.platform,
|
|
101
|
+
summary: session.summary ?? undefined,
|
|
102
|
+
context,
|
|
103
|
+
task_state,
|
|
104
|
+
next_action,
|
|
105
|
+
project_dir: session.project_dir ?? undefined,
|
|
106
|
+
token_count: session.token_count,
|
|
107
|
+
message,
|
|
108
|
+
restored_at: now,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// ─── Platform Setup ───────────────────────────────────────────────────────────
|
|
112
|
+
export function getPlatformSetup(platform, vetoServerPath) {
|
|
113
|
+
if (platform === 'claude')
|
|
114
|
+
return claude.getSetup(vetoServerPath);
|
|
115
|
+
if (platform === 'gemini')
|
|
116
|
+
return gemini.getSetup(vetoServerPath);
|
|
117
|
+
return codex.getSetup(vetoServerPath);
|
|
118
|
+
}
|
|
119
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
120
|
+
function selectTarget(from, rateStatus) {
|
|
121
|
+
const order = from === 'claude'
|
|
122
|
+
? ['gemini', 'codex']
|
|
123
|
+
: from === 'gemini'
|
|
124
|
+
? ['codex', 'claude']
|
|
125
|
+
: ['claude', 'gemini'];
|
|
126
|
+
for (const p of order) {
|
|
127
|
+
if (rateStatus[p].status !== 'critical')
|
|
128
|
+
return p;
|
|
129
|
+
}
|
|
130
|
+
// All critical — pick the one with most headroom remaining
|
|
131
|
+
return order.reduce((best, p) => rateStatus[p].used_percent < rateStatus[best].used_percent ? p : best, order[0]);
|
|
132
|
+
}
|
|
133
|
+
function buildInstructions(sessionId, from, to, rateStatus) {
|
|
134
|
+
const toStatus = rateStatus[to];
|
|
135
|
+
const lines = [
|
|
136
|
+
`── Veto Handoff ──────────────────────────────────────────`,
|
|
137
|
+
`From: ${from.padEnd(8)} (${rateStatus[from].used_percent}% used)`,
|
|
138
|
+
`To: ${to.padEnd(8)} (${toStatus.used_percent}% used)`,
|
|
139
|
+
`Session: ${sessionId}`,
|
|
140
|
+
``,
|
|
141
|
+
`On ${to}:`,
|
|
142
|
+
` 1. Open a new terminal with ${to} CLI`,
|
|
143
|
+
` 2. Veto is already running — same server, same memory`,
|
|
144
|
+
` 3. Call: veto_continue`,
|
|
145
|
+
` Veto restores full context automatically.`,
|
|
146
|
+
` Nothing needs to be re-explained.`,
|
|
147
|
+
``,
|
|
148
|
+
`Or if you have the session ID:`,
|
|
149
|
+
` veto_continue { "session_id": "${sessionId}" }`,
|
|
150
|
+
``,
|
|
151
|
+
`Rate resets at: ${rateStatus[from].resets_at.slice(0, 16)} UTC`,
|
|
152
|
+
`─────────────────────────────────────────────────────────`,
|
|
153
|
+
];
|
|
154
|
+
return lines.join('\n');
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
function detectApiStyle(task, context) {
|
|
2
|
+
const combined = (task + ' ' + (context ?? '')).toLowerCase();
|
|
3
|
+
if (combined.includes('graphql') || combined.includes('query') || combined.includes('mutation') || combined.includes('resolver'))
|
|
4
|
+
return 'graphql';
|
|
5
|
+
if (combined.includes('grpc') || combined.includes('protobuf') || combined.includes('proto'))
|
|
6
|
+
return 'grpc';
|
|
7
|
+
if (combined.includes('rest') || combined.includes('http') || combined.includes('endpoint') || combined.includes('route'))
|
|
8
|
+
return 'rest';
|
|
9
|
+
return 'general';
|
|
10
|
+
}
|
|
11
|
+
const styleApproach = {
|
|
12
|
+
rest: 'Design resource-oriented URLs, assign correct HTTP verbs, define request/response DTOs with validation, implement versioning strategy, document with OpenAPI, secure with JWT/OAuth.',
|
|
13
|
+
graphql: 'Define the schema first (SDL-first approach), design resolvers to avoid N+1 (DataLoader), implement mutations with input types, add field-level authorisation, paginate list queries.',
|
|
14
|
+
grpc: 'Write the .proto file first (contract-first), generate server and client stubs, implement unary and streaming RPCs as needed, add interceptors for auth/logging, define error codes.',
|
|
15
|
+
general: 'Choose REST for CRUD resource APIs, GraphQL for flexible client-driven queries, gRPC for high-performance internal services. Design the contract before writing implementation code.',
|
|
16
|
+
};
|
|
17
|
+
const styleSteps = {
|
|
18
|
+
rest: [
|
|
19
|
+
'Identify the resources (nouns) the API exposes — e.g., /users, /orders, /products',
|
|
20
|
+
'Map CRUD operations to HTTP verbs: GET (read), POST (create), PUT/PATCH (update), DELETE',
|
|
21
|
+
'Design the URL structure: /api/v1/resources/{id}/sub-resources',
|
|
22
|
+
'Define the request body DTO with all required and optional fields',
|
|
23
|
+
'Define the response body DTO — never return the raw DB row',
|
|
24
|
+
'Define error response shape: { code, message, details } for all 4xx/5xx',
|
|
25
|
+
'Choose the pagination strategy: cursor-based for large datasets, offset for small',
|
|
26
|
+
'Implement versioning: URL path versioning (/v1/) or header versioning',
|
|
27
|
+
'Secure the endpoint: add authentication middleware and role-based guards',
|
|
28
|
+
'Add rate limiting to prevent abuse',
|
|
29
|
+
'Write OpenAPI/Swagger annotation for each endpoint',
|
|
30
|
+
'Write integration tests covering happy path and all error conditions',
|
|
31
|
+
],
|
|
32
|
+
graphql: [
|
|
33
|
+
'Write the GraphQL schema (SDL) before writing any resolver code',
|
|
34
|
+
'Define types for all entities, input types for mutations, and enums for fixed values',
|
|
35
|
+
'Design query fields with pagination arguments (first, after, last, before)',
|
|
36
|
+
'Implement DataLoader for all one-to-many and many-to-many relations to batch DB queries',
|
|
37
|
+
'Implement resolvers using the service layer — no direct DB queries in resolvers',
|
|
38
|
+
'Add field-level authorisation using resolver guards',
|
|
39
|
+
'Add query depth limiting (max depth 10) and query complexity limits',
|
|
40
|
+
'Implement subscriptions only for truly real-time use cases',
|
|
41
|
+
'Add persisted queries for production performance',
|
|
42
|
+
'Write tests using graphql-tester or supertest against the schema',
|
|
43
|
+
'Document each type and field with SDL description strings',
|
|
44
|
+
],
|
|
45
|
+
grpc: [
|
|
46
|
+
'Write the .proto file defining all messages and services',
|
|
47
|
+
'Generate TypeScript server and client stubs from the proto',
|
|
48
|
+
'Implement each RPC method in the service implementation class',
|
|
49
|
+
'Add server interceptors for authentication, logging, and tracing',
|
|
50
|
+
'Implement unary RPCs for simple request/response patterns',
|
|
51
|
+
'Implement server-streaming for large result sets',
|
|
52
|
+
'Implement bidirectional streaming for real-time communication',
|
|
53
|
+
'Define a standard error model using google.rpc.Status',
|
|
54
|
+
'Add health checking via the gRPC health protocol',
|
|
55
|
+
'Write integration tests using the generated client stubs',
|
|
56
|
+
'Document each service and RPC with proto comments',
|
|
57
|
+
],
|
|
58
|
+
general: [
|
|
59
|
+
'Choose the API style based on use case (REST/GraphQL/gRPC)',
|
|
60
|
+
'Write the API contract (OpenAPI, SDL, or .proto) before implementation',
|
|
61
|
+
'Define all data types for requests and responses',
|
|
62
|
+
'Design error handling and error response shapes',
|
|
63
|
+
'Plan authentication and authorisation strategy',
|
|
64
|
+
'Design versioning strategy',
|
|
65
|
+
'Add rate limiting and throttling',
|
|
66
|
+
'Write API documentation',
|
|
67
|
+
'Write integration tests',
|
|
68
|
+
'Set up monitoring for error rates and latency',
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
export function plan(task, context) {
|
|
72
|
+
const style = detectApiStyle(task, context);
|
|
73
|
+
return {
|
|
74
|
+
agent: 'api',
|
|
75
|
+
task,
|
|
76
|
+
tier: 2,
|
|
77
|
+
approach: styleApproach[style],
|
|
78
|
+
steps: styleSteps[style],
|
|
79
|
+
checklist: [
|
|
80
|
+
'[ ] API contract (OpenAPI/SDL/proto) written before implementation',
|
|
81
|
+
'[ ] All request inputs validated and sanitised',
|
|
82
|
+
'[ ] Response DTOs never expose internal DB columns or secrets',
|
|
83
|
+
'[ ] HTTP status codes semantically correct (201 for creation, 422 for validation errors)',
|
|
84
|
+
'[ ] Authentication required on all non-public endpoints',
|
|
85
|
+
'[ ] Authorisation checked — user can only access their own resources',
|
|
86
|
+
'[ ] Rate limiting applied to prevent abuse',
|
|
87
|
+
'[ ] Pagination implemented for all list endpoints',
|
|
88
|
+
'[ ] Error responses follow a consistent shape with error code and message',
|
|
89
|
+
'[ ] Idempotency key supported on create/update endpoints',
|
|
90
|
+
'[ ] API versioning strategy documented and implemented',
|
|
91
|
+
'[ ] CORS configured correctly for the intended client origins',
|
|
92
|
+
'[ ] No internal error details (stack traces) exposed in production responses',
|
|
93
|
+
'[ ] Integration tests cover 200, 400, 401, 403, 404, 422, 500 paths',
|
|
94
|
+
'[ ] API documentation complete and accurate',
|
|
95
|
+
],
|
|
96
|
+
pitfalls: [
|
|
97
|
+
'Returning 200 OK with an error body — use the correct HTTP status code',
|
|
98
|
+
'Exposing database IDs directly — use UUIDs or opaque tokens to prevent enumeration',
|
|
99
|
+
'Not validating Content-Type — body parsers silently fail on wrong content type',
|
|
100
|
+
'Returning the full DB row in the response — exposes internal fields and breaks encapsulation',
|
|
101
|
+
'Using GET requests for operations with side effects — browsers and caches will replay them',
|
|
102
|
+
'Not implementing idempotency for payment or order endpoints — duplicate requests cause duplicate charges',
|
|
103
|
+
'Omitting pagination on list endpoints — a single request can return millions of rows',
|
|
104
|
+
],
|
|
105
|
+
patterns: [
|
|
106
|
+
'Command pattern (map HTTP requests to command objects)',
|
|
107
|
+
'DTO pattern (request and response data transfer objects)',
|
|
108
|
+
'Decorator pattern (middleware as route decorators)',
|
|
109
|
+
'Repository pattern (abstract data access from controllers)',
|
|
110
|
+
'API Gateway pattern (single entry point with cross-cutting concerns)',
|
|
111
|
+
'Circuit Breaker pattern (for outbound calls to other services)',
|
|
112
|
+
],
|
|
113
|
+
duration_estimate: '4-8 hours',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=api.js.map
|