@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.
Files changed (198) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/README.md +190 -0
  3. package/dist/adapters/claude.js +57 -0
  4. package/dist/adapters/codex.js +58 -0
  5. package/dist/adapters/gemini.js +58 -0
  6. package/dist/adapters/index.js +156 -0
  7. package/dist/agents/development/api.js +116 -0
  8. package/dist/agents/development/backend.js +82 -0
  9. package/dist/agents/development/coder.js +207 -0
  10. package/dist/agents/development/database.js +81 -0
  11. package/dist/agents/development/debugger.js +234 -0
  12. package/dist/agents/development/devops.js +84 -0
  13. package/dist/agents/development/frontend.js +83 -0
  14. package/dist/agents/development/migration.js +141 -0
  15. package/dist/agents/development/performance.js +142 -0
  16. package/dist/agents/development/refactor.js +85 -0
  17. package/dist/agents/development/reviewer.js +260 -0
  18. package/dist/agents/development/tester.js +143 -0
  19. package/dist/agents/executor.js +144 -0
  20. package/dist/agents/memory/context-manager.js +167 -0
  21. package/dist/agents/memory/decision-logger.js +157 -0
  22. package/dist/agents/memory/knowledge-base.js +120 -0
  23. package/dist/agents/memory/pattern-learner.js +140 -0
  24. package/dist/agents/memory/project-mapper.js +114 -0
  25. package/dist/agents/quality/accessibility.js +89 -0
  26. package/dist/agents/quality/code-quality.js +109 -0
  27. package/dist/agents/quality/compatibility.js +55 -0
  28. package/dist/agents/quality/documentation.js +95 -0
  29. package/dist/agents/quality/error-handling.js +87 -0
  30. package/dist/agents/research/competitor-analyzer.js +44 -0
  31. package/dist/agents/research/cost-analyzer.js +51 -0
  32. package/dist/agents/research/estimator.js +57 -0
  33. package/dist/agents/research/ethics-bias.js +111 -0
  34. package/dist/agents/research/researcher.js +112 -0
  35. package/dist/agents/research/risk-assessor.js +61 -0
  36. package/dist/agents/research/tech-advisor.js +52 -0
  37. package/dist/agents/security/auth.js +269 -0
  38. package/dist/agents/security/dependency-audit.js +273 -0
  39. package/dist/agents/security/penetration.js +245 -0
  40. package/dist/agents/security/privacy.js +259 -0
  41. package/dist/agents/security/scanner.js +288 -0
  42. package/dist/agents/security/secrets.js +212 -0
  43. package/dist/agents/types.js +2 -0
  44. package/dist/agents/workflow/automation.js +56 -0
  45. package/dist/agents/workflow/file-manager.js +49 -0
  46. package/dist/agents/workflow/git-agent.js +52 -0
  47. package/dist/agents/workflow/reporter.js +48 -0
  48. package/dist/agents/workflow/search-agent.js +39 -0
  49. package/dist/agents/workflow/task-coordinator.js +40 -0
  50. package/dist/agents/workflow/task-planner.js +46 -0
  51. package/dist/cli.js +132 -0
  52. package/dist/council/decision-engine.js +136 -0
  53. package/dist/council/devil-advocate.js +106 -0
  54. package/dist/council/index.js +37 -0
  55. package/dist/council/lead-developer.js +108 -0
  56. package/dist/council/legal-compliance.js +142 -0
  57. package/dist/council/product-manager.js +92 -0
  58. package/dist/council/security.js +162 -0
  59. package/dist/council/system-architect.js +122 -0
  60. package/dist/council/types.js +2 -0
  61. package/dist/council/ux-designer.js +109 -0
  62. package/dist/memory/local.js +182 -0
  63. package/dist/memory/schema.js +116 -0
  64. package/dist/memory/sync.js +199 -0
  65. package/dist/router/complexity-scorer.js +78 -0
  66. package/dist/router/context-compressor.js +58 -0
  67. package/dist/router/index.js +29 -0
  68. package/dist/router/learning-updater.js +186 -0
  69. package/dist/router/model-selector.js +51 -0
  70. package/dist/router/rate-monitor.js +73 -0
  71. package/dist/server.js +949 -0
  72. package/dist/skills/development/skill-api-design.js +313 -0
  73. package/dist/skills/development/skill-auth.js +255 -0
  74. package/dist/skills/development/skill-ci-cd.js +2 -0
  75. package/dist/skills/development/skill-crud.js +193 -0
  76. package/dist/skills/development/skill-db-schema.js +2 -0
  77. package/dist/skills/development/skill-docker.js +2 -0
  78. package/dist/skills/development/skill-env-setup.js +2 -0
  79. package/dist/skills/development/skill-scaffold.js +299 -0
  80. package/dist/skills/intelligence/skill-complexity-score.js +66 -0
  81. package/dist/skills/intelligence/skill-cost-track.js +36 -0
  82. package/dist/skills/intelligence/skill-learning-loop.js +66 -0
  83. package/dist/skills/intelligence/skill-pattern-detect.js +35 -0
  84. package/dist/skills/intelligence/skill-rate-watch.js +58 -0
  85. package/dist/skills/memory/skill-context-compress.js +82 -0
  86. package/dist/skills/memory/skill-cross-sync.js +88 -0
  87. package/dist/skills/memory/skill-decision-log.js +103 -0
  88. package/dist/skills/memory/skill-session-restore.js +44 -0
  89. package/dist/skills/memory/skill-session-save.js +78 -0
  90. package/dist/skills/quality/skill-accessibility.js +2 -0
  91. package/dist/skills/quality/skill-code-review.js +60 -0
  92. package/dist/skills/quality/skill-docs-gen.js +2 -0
  93. package/dist/skills/quality/skill-perf-audit.js +2 -0
  94. package/dist/skills/quality/skill-security-scan.js +67 -0
  95. package/dist/skills/quality/skill-test-suite.js +274 -0
  96. package/dist/skills/workflow/skill-deploy.js +2 -0
  97. package/dist/skills/workflow/skill-git-workflow.js +2 -0
  98. package/dist/skills/workflow/skill-rollback.js +2 -0
  99. package/dist/skills/workflow/skill-task-breakdown.js +2 -0
  100. package/package.json +30 -0
  101. package/src/adapters/claude.ts +70 -0
  102. package/src/adapters/codex.ts +71 -0
  103. package/src/adapters/gemini.ts +71 -0
  104. package/src/adapters/index.ts +217 -0
  105. package/src/agents/development/api.ts +120 -0
  106. package/src/agents/development/backend.ts +85 -0
  107. package/src/agents/development/coder.ts +213 -0
  108. package/src/agents/development/database.ts +83 -0
  109. package/src/agents/development/debugger.ts +238 -0
  110. package/src/agents/development/devops.ts +86 -0
  111. package/src/agents/development/frontend.ts +85 -0
  112. package/src/agents/development/migration.ts +144 -0
  113. package/src/agents/development/performance.ts +144 -0
  114. package/src/agents/development/refactor.ts +86 -0
  115. package/src/agents/development/reviewer.ts +268 -0
  116. package/src/agents/development/tester.ts +151 -0
  117. package/src/agents/executor.ts +158 -0
  118. package/src/agents/memory/context-manager.ts +171 -0
  119. package/src/agents/memory/decision-logger.ts +160 -0
  120. package/src/agents/memory/knowledge-base.ts +124 -0
  121. package/src/agents/memory/pattern-learner.ts +143 -0
  122. package/src/agents/memory/project-mapper.ts +118 -0
  123. package/src/agents/quality/accessibility.ts +99 -0
  124. package/src/agents/quality/code-quality.ts +115 -0
  125. package/src/agents/quality/compatibility.ts +58 -0
  126. package/src/agents/quality/documentation.ts +105 -0
  127. package/src/agents/quality/error-handling.ts +96 -0
  128. package/src/agents/research/competitor-analyzer.ts +45 -0
  129. package/src/agents/research/cost-analyzer.ts +54 -0
  130. package/src/agents/research/estimator.ts +60 -0
  131. package/src/agents/research/ethics-bias.ts +113 -0
  132. package/src/agents/research/researcher.ts +114 -0
  133. package/src/agents/research/risk-assessor.ts +63 -0
  134. package/src/agents/research/tech-advisor.ts +55 -0
  135. package/src/agents/security/auth.ts +287 -0
  136. package/src/agents/security/dependency-audit.ts +337 -0
  137. package/src/agents/security/penetration.ts +262 -0
  138. package/src/agents/security/privacy.ts +285 -0
  139. package/src/agents/security/scanner.ts +322 -0
  140. package/src/agents/security/secrets.ts +249 -0
  141. package/src/agents/types.ts +66 -0
  142. package/src/agents/workflow/automation.ts +59 -0
  143. package/src/agents/workflow/file-manager.ts +52 -0
  144. package/src/agents/workflow/git-agent.ts +55 -0
  145. package/src/agents/workflow/reporter.ts +51 -0
  146. package/src/agents/workflow/search-agent.ts +40 -0
  147. package/src/agents/workflow/task-coordinator.ts +41 -0
  148. package/src/agents/workflow/task-planner.ts +47 -0
  149. package/src/cli.ts +143 -0
  150. package/src/council/decision-engine.ts +171 -0
  151. package/src/council/devil-advocate.ts +116 -0
  152. package/src/council/index.ts +44 -0
  153. package/src/council/lead-developer.ts +118 -0
  154. package/src/council/legal-compliance.ts +152 -0
  155. package/src/council/product-manager.ts +102 -0
  156. package/src/council/security.ts +172 -0
  157. package/src/council/system-architect.ts +132 -0
  158. package/src/council/types.ts +33 -0
  159. package/src/council/ux-designer.ts +121 -0
  160. package/src/memory/local.ts +305 -0
  161. package/src/memory/schema.ts +174 -0
  162. package/src/memory/sync.ts +274 -0
  163. package/src/router/complexity-scorer.ts +96 -0
  164. package/src/router/context-compressor.ts +74 -0
  165. package/src/router/index.ts +60 -0
  166. package/src/router/learning-updater.ts +271 -0
  167. package/src/router/model-selector.ts +83 -0
  168. package/src/router/rate-monitor.ts +103 -0
  169. package/src/server.ts +1038 -0
  170. package/src/skills/development/skill-api-design.ts +329 -0
  171. package/src/skills/development/skill-auth.ts +271 -0
  172. package/src/skills/development/skill-ci-cd.ts +0 -0
  173. package/src/skills/development/skill-crud.ts +209 -0
  174. package/src/skills/development/skill-db-schema.ts +0 -0
  175. package/src/skills/development/skill-docker.ts +0 -0
  176. package/src/skills/development/skill-env-setup.ts +0 -0
  177. package/src/skills/development/skill-scaffold.ts +323 -0
  178. package/src/skills/intelligence/skill-complexity-score.ts +69 -0
  179. package/src/skills/intelligence/skill-cost-track.ts +39 -0
  180. package/src/skills/intelligence/skill-learning-loop.ts +69 -0
  181. package/src/skills/intelligence/skill-pattern-detect.ts +38 -0
  182. package/src/skills/intelligence/skill-rate-watch.ts +61 -0
  183. package/src/skills/memory/skill-context-compress.ts +98 -0
  184. package/src/skills/memory/skill-cross-sync.ts +104 -0
  185. package/src/skills/memory/skill-decision-log.ts +119 -0
  186. package/src/skills/memory/skill-session-restore.ts +59 -0
  187. package/src/skills/memory/skill-session-save.ts +94 -0
  188. package/src/skills/quality/skill-accessibility.ts +0 -0
  189. package/src/skills/quality/skill-code-review.ts +84 -0
  190. package/src/skills/quality/skill-docs-gen.ts +0 -0
  191. package/src/skills/quality/skill-perf-audit.ts +0 -0
  192. package/src/skills/quality/skill-security-scan.ts +91 -0
  193. package/src/skills/quality/skill-test-suite.ts +290 -0
  194. package/src/skills/workflow/skill-deploy.ts +0 -0
  195. package/src/skills/workflow/skill-git-workflow.ts +0 -0
  196. package/src/skills/workflow/skill-rollback.ts +0 -0
  197. package/src/skills/workflow/skill-task-breakdown.ts +0 -0
  198. package/tsconfig.json +20 -0
@@ -0,0 +1,67 @@
1
+ // Skill: security-scan — OWASP Top 10 scan process with tool commands
2
+ export function run(input) {
3
+ return {
4
+ skill: 'security-scan',
5
+ template: undefined,
6
+ checklist: [
7
+ // Dependency scanning
8
+ '1. Run npm audit: npx npm audit --audit-level=high (fail CI if high/critical found)',
9
+ '2. Run Snyk for deeper CVE analysis: npx snyk test --severity-threshold=high',
10
+ '3. Check for outdated packages: npx npm-check-updates (review before upgrading)',
11
+ '4. Verify lockfile integrity: npm ci (uses lockfile exactly; fails if lockfile is dirty)',
12
+ // Secrets scanning
13
+ '5. Scan for hardcoded secrets: npx gitleaks detect --source . --verbose',
14
+ '6. Run trufflehog on git history: trufflehog git file://. --since-commit HEAD~20',
15
+ '7. Verify .gitignore covers .env, *.key, *.pem, secrets.json, credentials.json',
16
+ // Static analysis
17
+ '8. Run ESLint security plugin: eslint --plugin security --rule "security/detect-eval-with-expression: error"',
18
+ '9. Run Semgrep OWASP ruleset: semgrep --config=p/owasp-top-ten --error .',
19
+ '10. Run CodeQL (GitHub Actions): use codeql-action/analyze with javascript/typescript queries',
20
+ // A01 – Broken Access Control
21
+ '11. A01 – Review every route handler: confirm auth middleware precedes data access',
22
+ '12. A01 – Search for direct object references: grep -r "req.params.id" src/ — verify ownership check in each',
23
+ // A02 – Cryptographic Failures
24
+ '13. A02 – Search for weak hashes: grep -r "md5\\|sha1\\|DES\\|RC4" src/ (flag any match)',
25
+ '14. A02 – Verify HTTPS enforcement: check for HSTS header and HTTP→HTTPS redirect in server config',
26
+ // A03 – Injection
27
+ '15. A03 – Search for SQL concatenation: grep -r "SELECT.*+" src/ — all DB calls should use parameterised queries',
28
+ '16. A03 – Search for eval: grep -rn "eval(" src/ — no eval() in production code',
29
+ // A05 – Misconfiguration
30
+ '17. A05 – Run security headers check: curl -I https://your-domain.com | grep -E "Content-Security|X-Frame|Strict-Transport|X-Content"',
31
+ '18. A05 – Verify no debug routes in production: grep -r "/debug\\|/__admin" src/routes/',
32
+ // A07 – Auth failures
33
+ '19. A07 – Confirm rate limiting on auth endpoints: grep -r "rateLimit" src/routes/auth',
34
+ '20. A07 – Verify account lockout logic exists: grep -r "lockedUntil\\|failedAttempts" src/services/auth',
35
+ // DAST
36
+ '21. Run OWASP ZAP baseline scan: docker run -t owasp/zap2docker-stable zap-baseline.py -t https://staging.example.com',
37
+ '22. Check Content-Security-Policy with observatory: npx observatory-cli your-domain.com --format report',
38
+ // Final gate
39
+ '23. Review all HIGH/CRITICAL findings; create tickets for each; do not ship with open criticals',
40
+ '24. Add security scan steps to CI pipeline so every PR is checked automatically',
41
+ ],
42
+ patterns: [
43
+ 'Shift-left security: scan in CI on every PR, not only before release',
44
+ 'Defence in depth: layered scanning (dependency + static + dynamic)',
45
+ 'Fail-fast: non-zero CI exit code on any high/critical finding',
46
+ 'Security-as-code: Semgrep rules and ZAP configs version-controlled alongside application',
47
+ ],
48
+ gotchas: [
49
+ 'npm audit reports transitive dependency vulnerabilities — check if exploitable in your code path before panicking',
50
+ 'Semgrep false positives on test files — add --exclude "tests/" to production scans',
51
+ 'ZAP baseline scan covers only unauthenticated paths — run a full scan with an auth script for coverage',
52
+ 'Gitleaks detects secrets committed at any point in history, not just HEAD — clean git history with git-filter-repo if needed',
53
+ 'Snyk requires authentication for full CVE database access — set SNYK_TOKEN in CI secrets',
54
+ ],
55
+ resources: [
56
+ 'https://owasp.org/www-project-top-ten/',
57
+ 'https://owasp.org/www-project-zap/',
58
+ 'https://semgrep.dev/p/owasp-top-ten',
59
+ 'https://github.com/gitleaks/gitleaks',
60
+ 'https://snyk.io/docs/snyk-cli/',
61
+ 'https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html',
62
+ 'https://docs.npmjs.com/cli/v10/commands/npm-audit',
63
+ 'https://observatory.mozilla.org/',
64
+ ],
65
+ };
66
+ }
67
+ //# sourceMappingURL=skill-security-scan.js.map
@@ -0,0 +1,274 @@
1
+ // Skill: test-suite — complete test suite structure guide
2
+ const TEMPLATE = `
3
+ // ── Unit test: ItemService (tests/unit/item.service.test.ts) ──────────────
4
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
5
+ import { ItemService } from '../../src/services/item.service';
6
+ import type { ItemRepository } from '../../src/repositories/item.repository';
7
+
8
+ // Test fixture factory — create minimal valid objects
9
+ function makeItem(overrides: Partial<Item> = {}): Item {
10
+ return {
11
+ id: 'item-1',
12
+ name: 'Test Item',
13
+ userId: 'user-1',
14
+ createdAt: new Date('2024-01-01'),
15
+ updatedAt: new Date('2024-01-01'),
16
+ ...overrides,
17
+ };
18
+ }
19
+
20
+ describe('ItemService', () => {
21
+ let service: ItemService;
22
+ let repo: ItemRepository;
23
+
24
+ beforeEach(() => {
25
+ repo = {
26
+ findById: vi.fn(),
27
+ findAllByUser: vi.fn(),
28
+ create: vi.fn(),
29
+ update: vi.fn(),
30
+ delete: vi.fn(),
31
+ };
32
+ service = new ItemService(repo);
33
+ });
34
+
35
+ // ── Happy path ──────────────────────────────────────────────────────────
36
+ describe('getItem', () => {
37
+ it('returns the item when found and owned by the requesting user', async () => {
38
+ const item = makeItem({ userId: 'user-1' });
39
+ vi.mocked(repo.findById).mockResolvedValue(item);
40
+
41
+ const result = await service.getItem('item-1', 'user-1');
42
+ expect(result).toEqual(item);
43
+ });
44
+
45
+ // ── Error cases ───────────────────────────────────────────────────────
46
+ it('throws NotFoundError when item does not exist', async () => {
47
+ vi.mocked(repo.findById).mockResolvedValue(null);
48
+ await expect(service.getItem('missing', 'user-1')).rejects.toThrow(NotFoundError);
49
+ });
50
+
51
+ it('throws ForbiddenError when item belongs to a different user', async () => {
52
+ const item = makeItem({ userId: 'user-2' });
53
+ vi.mocked(repo.findById).mockResolvedValue(item);
54
+ await expect(service.getItem('item-1', 'user-1')).rejects.toThrow(ForbiddenError);
55
+ });
56
+ });
57
+
58
+ // ── Edge cases ─────────────────────────────────────────────────────────
59
+ describe('createItem', () => {
60
+ it('trims whitespace from name before storing', async () => {
61
+ const item = makeItem({ name: 'Trimmed' });
62
+ vi.mocked(repo.create).mockResolvedValue(item);
63
+
64
+ await service.createItem('user-1', { name: ' Trimmed ' });
65
+ expect(repo.create).toHaveBeenCalledWith('user-1', { name: 'Trimmed' });
66
+ });
67
+
68
+ it('propagates repository errors', async () => {
69
+ vi.mocked(repo.create).mockRejectedValue(new Error('DB connection failed'));
70
+ await expect(service.createItem('user-1', { name: 'x' })).rejects.toThrow('DB connection failed');
71
+ });
72
+ });
73
+ });
74
+
75
+ // ── Integration test: Items API (tests/integration/items.test.ts) ─────────
76
+ import request from 'supertest';
77
+ import { createApp } from '../../src/app';
78
+ import { signAccessToken } from '../../src/utils/tokens';
79
+
80
+ describe('Items API', () => {
81
+ let app: Express.Application;
82
+ let authToken: string;
83
+
84
+ beforeAll(async () => {
85
+ app = await createApp({ db: testDb });
86
+ authToken = signAccessToken('user-1', 'user');
87
+ await testDb.user.create({ data: { id: 'user-1', email: 'test@example.com', passwordHash: 'x' } });
88
+ });
89
+
90
+ afterEach(async () => {
91
+ await testDb.item.deleteMany();
92
+ });
93
+
94
+ describe('GET /v1/items', () => {
95
+ it('returns empty list when no items exist', async () => {
96
+ const res = await request(app)
97
+ .get('/v1/items')
98
+ .set('Authorization', \`Bearer \${authToken}\`);
99
+
100
+ expect(res.status).toBe(200);
101
+ expect(res.body.data).toEqual([]);
102
+ });
103
+
104
+ it('returns only items belonging to the authenticated user', async () => {
105
+ await testDb.item.createMany({
106
+ data: [
107
+ { name: 'My Item', userId: 'user-1' },
108
+ { name: 'Other Item', userId: 'user-2' },
109
+ ],
110
+ });
111
+
112
+ const res = await request(app)
113
+ .get('/v1/items')
114
+ .set('Authorization', \`Bearer \${authToken}\`);
115
+
116
+ expect(res.status).toBe(200);
117
+ expect(res.body.data).toHaveLength(1);
118
+ expect(res.body.data[0].name).toBe('My Item');
119
+ });
120
+
121
+ it('returns 401 when no token provided', async () => {
122
+ const res = await request(app).get('/v1/items');
123
+ expect(res.status).toBe(401);
124
+ });
125
+ });
126
+
127
+ describe('POST /v1/items', () => {
128
+ it('creates an item and returns 201 with Location header', async () => {
129
+ const res = await request(app)
130
+ .post('/v1/items')
131
+ .set('Authorization', \`Bearer \${authToken}\`)
132
+ .send({ name: 'New Item', description: 'A test item' });
133
+
134
+ expect(res.status).toBe(201);
135
+ expect(res.headers['location']).toMatch(/\\/v1\\/items\\//);
136
+ expect(res.body.data.name).toBe('New Item');
137
+ });
138
+
139
+ it('returns 422 when name is missing', async () => {
140
+ const res = await request(app)
141
+ .post('/v1/items')
142
+ .set('Authorization', \`Bearer \${authToken}\`)
143
+ .send({ description: 'No name' });
144
+
145
+ expect(res.status).toBe(422);
146
+ expect(res.body.error.code).toBe('VALIDATION_ERROR');
147
+ });
148
+ });
149
+
150
+ describe('GET /v1/items/:id', () => {
151
+ it('returns 403 when requesting another user\\'s item', async () => {
152
+ const item = await testDb.item.create({ data: { name: 'Other', userId: 'user-2' } });
153
+
154
+ const res = await request(app)
155
+ .get(\`/v1/items/\${item.id}\`)
156
+ .set('Authorization', \`Bearer \${authToken}\`);
157
+
158
+ expect(res.status).toBe(403);
159
+ });
160
+
161
+ it('returns 404 when item does not exist', async () => {
162
+ const res = await request(app)
163
+ .get('/v1/items/00000000-0000-0000-0000-000000000000')
164
+ .set('Authorization', \`Bearer \${authToken}\`);
165
+
166
+ expect(res.status).toBe(404);
167
+ });
168
+ });
169
+ });
170
+
171
+ // ── E2E test: full user journey (tests/e2e/user-journey.test.ts) ──────────
172
+ describe('User journey: register → create item → retrieve → delete', () => {
173
+ let accessToken: string;
174
+ let itemId: string;
175
+
176
+ it('registers a new user', async () => {
177
+ const res = await request(app)
178
+ .post('/v1/auth/register')
179
+ .send({ email: 'journey@example.com', password: 'SecurePass123!' });
180
+ expect(res.status).toBe(201);
181
+ });
182
+
183
+ it('logs in and receives an access token', async () => {
184
+ const res = await request(app)
185
+ .post('/v1/auth/login')
186
+ .send({ email: 'journey@example.com', password: 'SecurePass123!' });
187
+ expect(res.status).toBe(200);
188
+ accessToken = res.body.data.accessToken;
189
+ });
190
+
191
+ it('creates an item', async () => {
192
+ const res = await request(app)
193
+ .post('/v1/items')
194
+ .set('Authorization', \`Bearer \${accessToken}\`)
195
+ .send({ name: 'Journey Item' });
196
+ expect(res.status).toBe(201);
197
+ itemId = res.body.data.id;
198
+ });
199
+
200
+ it('retrieves the created item', async () => {
201
+ const res = await request(app)
202
+ .get(\`/v1/items/\${itemId}\`)
203
+ .set('Authorization', \`Bearer \${accessToken}\`);
204
+ expect(res.status).toBe(200);
205
+ expect(res.body.data.name).toBe('Journey Item');
206
+ });
207
+
208
+ it('deletes the item', async () => {
209
+ const res = await request(app)
210
+ .delete(\`/v1/items/\${itemId}\`)
211
+ .set('Authorization', \`Bearer \${accessToken}\`);
212
+ expect(res.status).toBe(204);
213
+ });
214
+
215
+ it('confirms item is gone after deletion', async () => {
216
+ const res = await request(app)
217
+ .get(\`/v1/items/\${itemId}\`)
218
+ .set('Authorization', \`Bearer \${accessToken}\`);
219
+ expect(res.status).toBe(404);
220
+ });
221
+ });
222
+ `.trim();
223
+ export function run(input) {
224
+ return {
225
+ skill: 'test-suite',
226
+ template: TEMPLATE,
227
+ checklist: [
228
+ 'Install vitest (or jest) and supertest: npm install -D vitest supertest @types/supertest',
229
+ 'Configure vitest.config.ts: coverage provider v8, include src/**/*.ts, threshold 80%',
230
+ 'Create tests/ directory with unit/, integration/, and e2e/ subdirectories',
231
+ 'Write test fixture factories that create minimal valid objects with optional overrides',
232
+ 'Unit tests: test each service method in isolation — mock all dependencies with vi.fn()',
233
+ 'Unit test happy path: expected input produces expected output',
234
+ 'Unit test error cases: not found, forbidden, validation errors, repository failures',
235
+ 'Unit test edge cases: empty strings, boundary values, null/undefined, concurrent calls',
236
+ 'Integration tests: use supertest to send HTTP requests to the real Express app',
237
+ 'Integration test auth: verify 401 without token, 401 with expired token, 200 with valid token',
238
+ 'Integration test authorisation: verify 403 when accessing another user\'s resource',
239
+ 'Integration test validation: verify 422 with missing required fields, invalid types, out-of-range values',
240
+ 'Integration test pagination: verify page=1&pageSize=2 returns 2 items, meta.total is correct',
241
+ 'E2E tests: simulate a complete user journey from registration through full workflow to deletion',
242
+ 'Run coverage report: npx vitest run --coverage; verify no critical paths are uncovered',
243
+ 'Add CI step: vitest run --coverage --reporter=verbose fails the build if coverage < 80%',
244
+ 'Use afterEach to clean up test data; never rely on test execution order',
245
+ 'Separate test database or use transactions that are rolled back after each test',
246
+ 'Snapshot tests for complex output shapes (e.g., generated reports, API response structure)',
247
+ 'Add test for rate-limiting endpoint: 11th request in window should return 429',
248
+ ],
249
+ patterns: [
250
+ 'AAA pattern: Arrange → Act → Assert in every test case',
251
+ 'Test fixture factory: makeUser(), makeItem() functions with optional overrides',
252
+ 'Mock at the boundary: mock repositories in unit tests, use real DB in integration tests',
253
+ 'Test pyramid: many unit tests, fewer integration tests, few E2E tests',
254
+ 'Describe → nested describe → it hierarchy mirrors the module structure',
255
+ ],
256
+ gotchas: [
257
+ 'Testing implementation details instead of behaviour — tests break on every refactor',
258
+ 'Sharing mutable state between tests without cleanup — flaky test ordering dependency',
259
+ 'Mocking too deep: mocking the DB client instead of the repository causes brittle tests',
260
+ 'Not testing error paths — happy-path-only coverage misses the most bug-prone code',
261
+ 'Writing assertions on the wrong field — test passes but is not actually verifying the behaviour',
262
+ 'Not testing auth in integration tests — security bugs not caught until production',
263
+ 'Using setTimeout in tests without fake timers — slow and non-deterministic',
264
+ ],
265
+ resources: [
266
+ 'https://vitest.dev/guide/',
267
+ 'https://github.com/ladjs/supertest',
268
+ 'https://martinfowler.com/articles/practical-test-pyramid.html',
269
+ 'https://testing-library.com/docs/ (for React component tests)',
270
+ 'https://jestjs.io/docs/expect (matcher reference, compatible with vitest)',
271
+ ],
272
+ };
273
+ }
274
+ //# sourceMappingURL=skill-test-suite.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=skill-deploy.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=skill-git-workflow.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=skill-rollback.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=skill-task-breakdown.js.map
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@jigyasudham/veto",
3
+ "version": "0.8.0",
4
+ "description": "50 agents. 28 skills. 3 AIs. Self-learning. Zero extra cost.",
5
+ "keywords": ["mcp", "ai", "claude", "agents", "memory", "council"],
6
+ "author": "Jigyasu Dham <jigyasudham18@gmail.com>",
7
+ "license": "MIT",
8
+ "type": "module",
9
+ "engines": {
10
+ "node": ">=22.5.0"
11
+ },
12
+ "bin": {
13
+ "veto": "./dist/cli.js"
14
+ },
15
+ "main": "./dist/server.js",
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "dev": "tsx src/cli.ts",
19
+ "start": "node dist/server.js",
20
+ "init": "tsx src/cli.ts init"
21
+ },
22
+ "dependencies": {
23
+ "@modelcontextprotocol/sdk": "^1.0.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^22.0.0",
27
+ "tsx": "^4.7.0",
28
+ "typescript": "^5.3.0"
29
+ }
30
+ }
@@ -0,0 +1,70 @@
1
+ // Claude Code adapter — connection config, rate signal detection, handoff format
2
+
3
+ export const PLATFORM = 'claude' as const;
4
+
5
+ export type AdapterSetup = {
6
+ platform: string;
7
+ mcp_config: object;
8
+ setup_steps: string[];
9
+ rate_limit_signals: string[];
10
+ continue_command: string;
11
+ notes: string[];
12
+ };
13
+
14
+ export function getSetup(vetoServerPath: string): AdapterSetup {
15
+ return {
16
+ platform: 'claude',
17
+ mcp_config: {
18
+ mcpServers: {
19
+ veto: {
20
+ command: 'node',
21
+ args: [vetoServerPath],
22
+ },
23
+ },
24
+ },
25
+ setup_steps: [
26
+ `1. Build veto: npm run build`,
27
+ `2. Add to ~/.claude/mcp_servers.json:\n${JSON.stringify({ mcpServers: { veto: { command: 'node', args: [vetoServerPath] } } }, null, 2)}`,
28
+ `3. Restart Claude Code`,
29
+ `4. Verify: call veto_status — should return { "status": "running", "version": "0.7.0" }`,
30
+ ],
31
+ rate_limit_signals: [
32
+ 'rate limit exceeded',
33
+ 'too many requests',
34
+ '429',
35
+ 'overloaded',
36
+ 'capacity',
37
+ 'quota exceeded',
38
+ ],
39
+ continue_command: 'veto_continue',
40
+ notes: [
41
+ 'Claude Code connects to Veto via stdio MCP — the server runs as a child process',
42
+ 'All veto_* tools are available natively in Claude Code once connected',
43
+ 'Rate limits are tracked by Veto per day — call veto_rate_status to check headroom',
44
+ 'Before hitting the limit: call veto_handoff to get a session ID and switch instructions',
45
+ ],
46
+ };
47
+ }
48
+
49
+ export function detectRateLimit(errorText: string): boolean {
50
+ const signals = ['rate limit', 'too many requests', '429', 'overloaded', 'quota'];
51
+ return signals.some(s => errorText.toLowerCase().includes(s));
52
+ }
53
+
54
+ export function formatHandoffMessage(sessionId: string, targetPlatform: 'gemini' | 'codex'): string {
55
+ const target = targetPlatform === 'gemini' ? 'Gemini CLI' : 'Codex CLI';
56
+ return [
57
+ `Claude is approaching its rate limit. Switching to ${target}.`,
58
+ ``,
59
+ `Session saved. ID: ${sessionId}`,
60
+ ``,
61
+ `On ${target}, run:`,
62
+ ` veto_continue`,
63
+ ``,
64
+ `Veto will restore full context automatically. Nothing needs to be re-explained.`,
65
+ ].join('\n');
66
+ }
67
+
68
+ export function formatSwitchPrompt(sessionId: string): string {
69
+ return `Rate limit approaching. Session saved (ID: ${sessionId}). Call veto_continue on the next platform to resume instantly.`;
70
+ }
@@ -0,0 +1,71 @@
1
+ // Codex CLI adapter — connection config, rate signal detection, handoff format
2
+ // Codex CLI: https://github.com/openai/codex
3
+
4
+ export const PLATFORM = 'codex' as const;
5
+
6
+ export type AdapterSetup = {
7
+ platform: string;
8
+ mcp_config: object;
9
+ setup_steps: string[];
10
+ rate_limit_signals: string[];
11
+ continue_command: string;
12
+ notes: string[];
13
+ };
14
+
15
+ export function getSetup(vetoServerPath: string): AdapterSetup {
16
+ return {
17
+ platform: 'codex',
18
+ mcp_config: {
19
+ mcpServers: {
20
+ veto: {
21
+ command: 'node',
22
+ args: [vetoServerPath],
23
+ },
24
+ },
25
+ },
26
+ setup_steps: [
27
+ `1. Install Codex CLI: npm install -g @openai/codex`,
28
+ `2. Set API key: export OPENAI_API_KEY=your-key`,
29
+ `3. Add to ~/.codex/config.json:\n${JSON.stringify({ mcpServers: { veto: { command: 'node', args: [vetoServerPath] } } }, null, 2)}`,
30
+ `4. Restart Codex CLI`,
31
+ `5. Verify: call veto_status — should return { "status": "running", "version": "0.7.0" }`,
32
+ ],
33
+ rate_limit_signals: [
34
+ 'rate limit reached',
35
+ 'too many requests',
36
+ '429',
37
+ 'insufficient quota',
38
+ 'rate_limit_exceeded',
39
+ 'quota_exceeded',
40
+ ],
41
+ continue_command: 'veto_continue',
42
+ notes: [
43
+ 'Codex CLI connects to Veto via stdio MCP — same server instance as Claude and Gemini',
44
+ 'Config path: ~/.codex/config.json (created automatically on first run)',
45
+ 'All veto_* tools work identically on Codex as on Claude and Gemini',
46
+ 'Codex is the Tier 1/2 overflow when both Claude and Gemini are rate-limited',
47
+ 'Uses GPT-4o / o4-mini depending on the tier assigned by the Veto router',
48
+ 'ChatGPT web app does NOT support MCP — Codex CLI is the only OpenAI option',
49
+ ],
50
+ };
51
+ }
52
+
53
+ export function detectRateLimit(errorText: string): boolean {
54
+ const signals = ['rate limit', 'too many requests', '429', 'insufficient quota', 'rate_limit_exceeded'];
55
+ return signals.some(s => errorText.toLowerCase().includes(s.toLowerCase()));
56
+ }
57
+
58
+ export function formatHandoffMessage(sessionId: string, fromPlatform: 'claude' | 'gemini'): string {
59
+ const from = fromPlatform === 'claude' ? 'Claude' : 'Gemini';
60
+ return [
61
+ `${from} handed off to Codex. Session restored.`,
62
+ ``,
63
+ `Session ID: ${sessionId}`,
64
+ ``,
65
+ `Codex has full context. Continue the task as if nothing interrupted.`,
66
+ ].join('\n');
67
+ }
68
+
69
+ export function formatContinueInstructions(): string {
70
+ return 'Run: veto_continue\nVeto will restore the most recent session automatically.';
71
+ }
@@ -0,0 +1,71 @@
1
+ // Gemini CLI adapter — connection config, rate signal detection, handoff format
2
+ // Gemini CLI MCP support: https://github.com/google-gemini/gemini-cli
3
+
4
+ export const PLATFORM = 'gemini' as const;
5
+
6
+ export type AdapterSetup = {
7
+ platform: string;
8
+ mcp_config: object;
9
+ setup_steps: string[];
10
+ rate_limit_signals: string[];
11
+ continue_command: string;
12
+ notes: string[];
13
+ };
14
+
15
+ export function getSetup(vetoServerPath: string): AdapterSetup {
16
+ return {
17
+ platform: 'gemini',
18
+ mcp_config: {
19
+ mcpServers: {
20
+ veto: {
21
+ command: 'node',
22
+ args: [vetoServerPath],
23
+ },
24
+ },
25
+ },
26
+ setup_steps: [
27
+ `1. Install Gemini CLI: npm install -g @google/gemini-cli`,
28
+ `2. Authenticate: gemini auth`,
29
+ `3. Add to ~/.gemini/settings.json:\n${JSON.stringify({ mcpServers: { veto: { command: 'node', args: [vetoServerPath] } } }, null, 2)}`,
30
+ `4. Restart Gemini CLI`,
31
+ `5. Verify: call veto_status — should return { "status": "running", "version": "0.7.0" }`,
32
+ ],
33
+ rate_limit_signals: [
34
+ 'quota exceeded',
35
+ 'resource exhausted',
36
+ '429',
37
+ 'rate limit',
38
+ 'too many requests',
39
+ 'RESOURCE_EXHAUSTED',
40
+ ],
41
+ continue_command: 'veto_continue',
42
+ notes: [
43
+ 'Gemini CLI connects to Veto via stdio MCP — same server instance as Claude',
44
+ 'Gemini CLI uses the same ~/.gemini/settings.json for MCP server config',
45
+ 'All veto_* tools work identically on Gemini as on Claude',
46
+ 'Gemini Flash is the default Tier 1/2 overflow platform when Claude is rate-limited',
47
+ 'Free tier: 1,500 requests/day (15 RPM) — Veto tracks this in rate_usage table',
48
+ ],
49
+ };
50
+ }
51
+
52
+ export function detectRateLimit(errorText: string): boolean {
53
+ const signals = ['quota exceeded', 'resource exhausted', '429', 'rate limit', 'RESOURCE_EXHAUSTED'];
54
+ return signals.some(s => errorText.toLowerCase().includes(s.toLowerCase()));
55
+ }
56
+
57
+ export function formatHandoffMessage(sessionId: string, fromPlatform: 'claude' | 'codex'): string {
58
+ const from = fromPlatform === 'claude' ? 'Claude' : 'Codex';
59
+ return [
60
+ `${from} handed off to Gemini. Session restored.`,
61
+ ``,
62
+ `Session ID: ${sessionId}`,
63
+ ``,
64
+ `Gemini has full context. Continue the task as if nothing interrupted.`,
65
+ `Call veto_session_restore { "session_id": "${sessionId}" } if context needs refreshing.`,
66
+ ].join('\n');
67
+ }
68
+
69
+ export function formatContinueInstructions(): string {
70
+ return 'Run: veto_continue\nVeto will restore the most recent session automatically.';
71
+ }