@jigyasudham/veto 0.8.2 → 1.0.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/README.md +209 -52
- package/dist/agents/executor.js +36 -3
- package/dist/cli.js +350 -51
- package/dist/context/reader.js +113 -0
- package/dist/council/index.js +3 -1
- package/dist/plugins/loader.js +49 -0
- package/dist/router/index.js +2 -2
- package/dist/router/learning-updater.js +45 -1
- package/dist/server.js +478 -14
- package/dist/watcher/index.js +77 -0
- package/dist/workflow/pipeline.js +64 -0
- package/package.json +12 -3
- package/.claude/settings.local.json +0 -9
- package/src/adapters/claude.ts +0 -70
- package/src/adapters/codex.ts +0 -71
- package/src/adapters/gemini.ts +0 -71
- package/src/adapters/index.ts +0 -217
- package/src/agents/development/api.ts +0 -120
- package/src/agents/development/backend.ts +0 -85
- package/src/agents/development/coder.ts +0 -213
- package/src/agents/development/database.ts +0 -83
- package/src/agents/development/debugger.ts +0 -238
- package/src/agents/development/devops.ts +0 -86
- package/src/agents/development/frontend.ts +0 -85
- package/src/agents/development/migration.ts +0 -144
- package/src/agents/development/performance.ts +0 -144
- package/src/agents/development/refactor.ts +0 -86
- package/src/agents/development/reviewer.ts +0 -268
- package/src/agents/development/tester.ts +0 -151
- package/src/agents/executor.ts +0 -158
- package/src/agents/memory/context-manager.ts +0 -171
- package/src/agents/memory/decision-logger.ts +0 -160
- package/src/agents/memory/knowledge-base.ts +0 -124
- package/src/agents/memory/pattern-learner.ts +0 -143
- package/src/agents/memory/project-mapper.ts +0 -118
- package/src/agents/quality/accessibility.ts +0 -99
- package/src/agents/quality/code-quality.ts +0 -115
- package/src/agents/quality/compatibility.ts +0 -58
- package/src/agents/quality/documentation.ts +0 -105
- package/src/agents/quality/error-handling.ts +0 -96
- package/src/agents/research/competitor-analyzer.ts +0 -45
- package/src/agents/research/cost-analyzer.ts +0 -54
- package/src/agents/research/estimator.ts +0 -60
- package/src/agents/research/ethics-bias.ts +0 -113
- package/src/agents/research/researcher.ts +0 -114
- package/src/agents/research/risk-assessor.ts +0 -63
- package/src/agents/research/tech-advisor.ts +0 -55
- package/src/agents/security/auth.ts +0 -287
- package/src/agents/security/dependency-audit.ts +0 -337
- package/src/agents/security/penetration.ts +0 -262
- package/src/agents/security/privacy.ts +0 -285
- package/src/agents/security/scanner.ts +0 -322
- package/src/agents/security/secrets.ts +0 -249
- package/src/agents/types.ts +0 -66
- package/src/agents/workflow/automation.ts +0 -59
- package/src/agents/workflow/file-manager.ts +0 -52
- package/src/agents/workflow/git-agent.ts +0 -55
- package/src/agents/workflow/reporter.ts +0 -51
- package/src/agents/workflow/search-agent.ts +0 -40
- package/src/agents/workflow/task-coordinator.ts +0 -41
- package/src/agents/workflow/task-planner.ts +0 -47
- package/src/cli.ts +0 -135
- package/src/council/decision-engine.ts +0 -171
- package/src/council/devil-advocate.ts +0 -116
- package/src/council/index.ts +0 -44
- package/src/council/lead-developer.ts +0 -118
- package/src/council/legal-compliance.ts +0 -152
- package/src/council/product-manager.ts +0 -102
- package/src/council/security.ts +0 -172
- package/src/council/system-architect.ts +0 -132
- package/src/council/types.ts +0 -33
- package/src/council/ux-designer.ts +0 -121
- package/src/memory/local.ts +0 -305
- package/src/memory/schema.ts +0 -174
- package/src/memory/sync.ts +0 -274
- package/src/router/complexity-scorer.ts +0 -96
- package/src/router/context-compressor.ts +0 -74
- package/src/router/index.ts +0 -60
- package/src/router/learning-updater.ts +0 -271
- package/src/router/model-selector.ts +0 -83
- package/src/router/rate-monitor.ts +0 -103
- package/src/server.ts +0 -1038
- package/src/skills/development/skill-api-design.ts +0 -329
- package/src/skills/development/skill-auth.ts +0 -271
- package/src/skills/development/skill-ci-cd.ts +0 -0
- package/src/skills/development/skill-crud.ts +0 -209
- 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 +0 -323
- package/src/skills/intelligence/skill-complexity-score.ts +0 -69
- package/src/skills/intelligence/skill-cost-track.ts +0 -39
- package/src/skills/intelligence/skill-learning-loop.ts +0 -69
- package/src/skills/intelligence/skill-pattern-detect.ts +0 -38
- package/src/skills/intelligence/skill-rate-watch.ts +0 -61
- package/src/skills/memory/skill-context-compress.ts +0 -98
- package/src/skills/memory/skill-cross-sync.ts +0 -104
- package/src/skills/memory/skill-decision-log.ts +0 -119
- package/src/skills/memory/skill-session-restore.ts +0 -59
- package/src/skills/memory/skill-session-save.ts +0 -94
- package/src/skills/quality/skill-accessibility.ts +0 -0
- package/src/skills/quality/skill-code-review.ts +0 -84
- 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 +0 -91
- package/src/skills/quality/skill-test-suite.ts +0 -290
- 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 +0 -20
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
// Decision Engine — collects all votes, calculates final verdict
|
|
2
|
-
import type { AgentVote, CouncilVerdict, DebateResult } from './types.js';
|
|
3
|
-
|
|
4
|
-
type Votes = DebateResult['votes'];
|
|
5
|
-
|
|
6
|
-
export function decide(votes: Votes): {
|
|
7
|
-
final_verdict: CouncilVerdict;
|
|
8
|
-
block_reasons: string[];
|
|
9
|
-
warnings: string[];
|
|
10
|
-
recommended: string;
|
|
11
|
-
} {
|
|
12
|
-
const entries: Array<[string, AgentVote]> = [
|
|
13
|
-
['Lead Developer', votes.lead_dev],
|
|
14
|
-
['Product Manager', votes.pm],
|
|
15
|
-
['System Architect', votes.architect],
|
|
16
|
-
['UX Designer', votes.ux],
|
|
17
|
-
["Devil's Advocate", votes.devil],
|
|
18
|
-
['Legal & Compliance', votes.legal],
|
|
19
|
-
['Security', votes.security],
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
// Expert agents — their block is always RED regardless of other votes
|
|
23
|
-
const CRITICAL = new Set(['Lead Developer', 'System Architect', 'Legal & Compliance', 'Security']);
|
|
24
|
-
|
|
25
|
-
const blockers = entries.filter(([, v]) => v.verdict === 'block');
|
|
26
|
-
const warners = entries.filter(([, v]) => v.verdict === 'warn');
|
|
27
|
-
const approvers = entries.filter(([, v]) => v.verdict === 'approve');
|
|
28
|
-
|
|
29
|
-
const criticalBlockers = blockers.filter(([name]) => CRITICAL.has(name));
|
|
30
|
-
const secondaryBlockers = blockers.filter(([name]) => !CRITICAL.has(name));
|
|
31
|
-
const criticalApprovers = approvers.filter(([name]) => CRITICAL.has(name));
|
|
32
|
-
|
|
33
|
-
const block_reasons = blockers.map(([, v]) => v.reason);
|
|
34
|
-
const warnings: string[] = [
|
|
35
|
-
...blockers.flatMap(([, v]) => v.concerns),
|
|
36
|
-
...warners.map(([, v]) => v.reason),
|
|
37
|
-
...warners.flatMap(([, v]) => v.concerns),
|
|
38
|
-
].filter((w): w is string => typeof w === 'string' && w.length > 0);
|
|
39
|
-
|
|
40
|
-
let final_verdict: CouncilVerdict;
|
|
41
|
-
|
|
42
|
-
if (criticalBlockers.length >= 1) {
|
|
43
|
-
// Any expert domain agent blocks → RED, no debate
|
|
44
|
-
final_verdict = 'RED';
|
|
45
|
-
} else if (secondaryBlockers.length >= 2 && criticalApprovers.length >= 2) {
|
|
46
|
-
// Business/UX objections vs technical approval — genuine split
|
|
47
|
-
final_verdict = 'DEADLOCK';
|
|
48
|
-
} else if (secondaryBlockers.length >= 1 || warners.length >= 1) {
|
|
49
|
-
// At least one concern but no hard expert block
|
|
50
|
-
final_verdict = 'YELLOW';
|
|
51
|
-
} else {
|
|
52
|
-
final_verdict = 'GREEN';
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const recommendations = [
|
|
56
|
-
...blockers.map(([, v]) => v.recommendation).filter(Boolean),
|
|
57
|
-
...warners.map(([, v]) => v.recommendation).filter(Boolean),
|
|
58
|
-
] as string[];
|
|
59
|
-
|
|
60
|
-
const recommended = recommendations.length > 0
|
|
61
|
-
? recommendations[0]
|
|
62
|
-
: 'Proceed with standard implementation best practices.';
|
|
63
|
-
|
|
64
|
-
return { final_verdict, block_reasons, warnings, recommended };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function formatDebate(
|
|
68
|
-
task: string,
|
|
69
|
-
votes: Votes,
|
|
70
|
-
verdict: CouncilVerdict,
|
|
71
|
-
block_reasons: string[],
|
|
72
|
-
warnings: string[],
|
|
73
|
-
recommended: string,
|
|
74
|
-
): string {
|
|
75
|
-
const AGENT_ICONS: Record<keyof Votes, string> = {
|
|
76
|
-
lead_dev: '🔵',
|
|
77
|
-
pm: '🟢',
|
|
78
|
-
architect: '🏛️ ',
|
|
79
|
-
ux: '🎨',
|
|
80
|
-
devil: '😈',
|
|
81
|
-
legal: '⚖️ ',
|
|
82
|
-
security: '🔒',
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const AGENT_LABELS: Record<keyof Votes, string> = {
|
|
86
|
-
lead_dev: 'Lead Dev: ',
|
|
87
|
-
pm: 'PM: ',
|
|
88
|
-
architect: 'Architect: ',
|
|
89
|
-
ux: 'UX: ',
|
|
90
|
-
devil: 'Devil: ',
|
|
91
|
-
legal: 'Legal: ',
|
|
92
|
-
security: 'Security: ',
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const VERDICT_BADGE: Record<string, string> = {
|
|
96
|
-
block: '[BLOCKING]',
|
|
97
|
-
warn: '[WARN]',
|
|
98
|
-
approve: '[APPROVE]',
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const VERDICT_LINE: Record<CouncilVerdict, string> = {
|
|
102
|
-
GREEN: '✅ VERDICT: GREEN — All clear. Proceed.',
|
|
103
|
-
YELLOW: '⚠️ VERDICT: YELLOW — Proceed with caution.',
|
|
104
|
-
RED: '🚫 VERDICT: RED — BLOCKED.',
|
|
105
|
-
DEADLOCK: '⚖️ VERDICT: DEADLOCK — Council is split. You decide.',
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const divider = '─'.repeat(54);
|
|
109
|
-
const lines: string[] = [
|
|
110
|
-
'',
|
|
111
|
-
'⚠️ VETO COUNCIL INITIATED',
|
|
112
|
-
divider,
|
|
113
|
-
'',
|
|
114
|
-
];
|
|
115
|
-
|
|
116
|
-
const keys: Array<keyof Votes> = ['lead_dev', 'pm', 'architect', 'ux', 'devil', 'legal', 'security'];
|
|
117
|
-
|
|
118
|
-
for (const key of keys) {
|
|
119
|
-
const vote = votes[key];
|
|
120
|
-
const icon = AGENT_ICONS[key];
|
|
121
|
-
const label = AGENT_LABELS[key];
|
|
122
|
-
const badge = VERDICT_BADGE[vote.verdict];
|
|
123
|
-
const prefix = `${icon} ${label}`;
|
|
124
|
-
const indent = ' ';
|
|
125
|
-
|
|
126
|
-
const shortReason = vote.reason.length > 46 ? vote.reason.slice(0, 43) + '...' : vote.reason;
|
|
127
|
-
lines.push(`${prefix}${shortReason} ${badge}`);
|
|
128
|
-
|
|
129
|
-
for (const concern of vote.concerns.slice(0, 2)) {
|
|
130
|
-
const short = concern.length > 46 ? concern.slice(0, 43) + '...' : concern;
|
|
131
|
-
lines.push(`${indent}↳ ${short}`);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
lines.push('');
|
|
136
|
-
lines.push(divider);
|
|
137
|
-
lines.push(VERDICT_LINE[verdict]);
|
|
138
|
-
lines.push('');
|
|
139
|
-
|
|
140
|
-
if (block_reasons.length > 0) {
|
|
141
|
-
lines.push('🚫 Blocked because:');
|
|
142
|
-
for (const r of block_reasons) {
|
|
143
|
-
lines.push(` • ${r}`);
|
|
144
|
-
}
|
|
145
|
-
lines.push('');
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (warnings.length > 0) {
|
|
149
|
-
const shown = warnings.slice(0, 4);
|
|
150
|
-
const extra = warnings.length - shown.length;
|
|
151
|
-
lines.push(`⚠️ Warning${warnings.length > 1 ? 's' : ''}:`);
|
|
152
|
-
for (const w of shown) {
|
|
153
|
-
lines.push(` • ${w}`);
|
|
154
|
-
}
|
|
155
|
-
if (extra > 0) lines.push(` • ...and ${extra} more`);
|
|
156
|
-
lines.push('');
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
lines.push(`✅ Recommended: ${recommended}`);
|
|
160
|
-
|
|
161
|
-
if (verdict === 'RED') {
|
|
162
|
-
lines.push('');
|
|
163
|
-
lines.push('Override with: proceed anyway [not recommended]');
|
|
164
|
-
} else if (verdict === 'DEADLOCK') {
|
|
165
|
-
lines.push('');
|
|
166
|
-
lines.push('Council is split. Your call: [proceed / abort]');
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
lines.push('');
|
|
170
|
-
return lines.join('\n');
|
|
171
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
// Devil's Advocate — probes every failure mode, always challenges
|
|
2
|
-
import type { AgentVote } from './types.js';
|
|
3
|
-
|
|
4
|
-
// Devil always warns — never alone blocks, never approves non-trivial tasks
|
|
5
|
-
const PROBES: Array<{ pattern: RegExp; concern: string; recommendation: string }> = [
|
|
6
|
-
{
|
|
7
|
-
pattern: /auth|login|password|session|jwt|oauth/i,
|
|
8
|
-
concern: "What happens when auth fails at 2AM? Lockout policy? Account recovery flow?",
|
|
9
|
-
recommendation: 'Rate-limit auth endpoints. Lock after N failures. Build recovery email flow.',
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
pattern: /payment|charge|billing|stripe|invoice|subscription/i,
|
|
13
|
-
concern: "Idempotency keys? Double-charge prevention? What happens when payment webhook retries?",
|
|
14
|
-
recommendation: 'Idempotency keys on all payment mutations. Handle webhook duplicates explicitly.',
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
pattern: /deploy|release|rollout|push.{0,15}(prod|main|master)/i,
|
|
18
|
-
concern: "Rollback procedure if this deploy breaks production? How fast can you revert?",
|
|
19
|
-
recommendation: 'Document rollback runbook before deploying. Consider feature flag as kill switch.',
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
pattern: /migrat|schema.?change|alter.?table|add.{0,10}column/i,
|
|
23
|
-
concern: "Tested down-migration exists? What if this runs mid-traffic on prod?",
|
|
24
|
-
recommendation: 'Test down-migration on a copy. Use zero-downtime migration patterns (expand/contract).',
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
pattern: /\bcache\b|\bredis\b/i,
|
|
28
|
-
concern: "Cache stampede on cold start? Cache poisoning from bad writes? Invalidation race?",
|
|
29
|
-
recommendation: 'Probabilistic early expiry prevents stampede. Lock on cache miss for hot keys.',
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
pattern: /email|notification|sms|push.?notif/i,
|
|
33
|
-
concern: "Delivery failure handled? Retry queue? Duplicate notification prevention?",
|
|
34
|
-
recommendation: 'Add retry queue with exponential backoff. Idempotency key per notification.',
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
pattern: /webhook|callback.?url/i,
|
|
38
|
-
concern: "Webhook replay attacks? Signature verification? What if your handler is slow?",
|
|
39
|
-
recommendation: 'Verify webhook signatures. Respond 200 immediately, process async.',
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
pattern: /\bapi\b|endpoint|http.?client|fetch|axios/i,
|
|
43
|
-
concern: "External API goes down? Timeout set? Circuit breaker? Fallback behavior?",
|
|
44
|
-
recommendation: 'Set explicit timeout (3-5s). Circuit breaker for critical external deps.',
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
pattern: /file|upload|s3|blob|storage|bucket/i,
|
|
48
|
-
concern: "Partial upload failure? Storage quota? File type validation before processing?",
|
|
49
|
-
recommendation: 'Handle partial failures. Alert at 80% storage. Validate file type + scan for malware.',
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
pattern: /cron|schedule|job|queue|worker/i,
|
|
53
|
-
concern: "Job fails silently? Duplicate concurrent runs? Stuck job detection?",
|
|
54
|
-
recommendation: 'Log all job outcomes. Prevent concurrent runs with distributed lock. Set max duration.',
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
pattern: /third.?party|external.{0,15}service|vendor|sdk/i,
|
|
58
|
-
concern: "Vendor going down? SDK breaking changes? Data you do not control changing format?",
|
|
59
|
-
recommendation: 'Wrap in adapter layer. Monitor third-party SLA separately. Pin SDK version.',
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
pattern: /delete|remove|drop|destroy|purge/i,
|
|
63
|
-
concern: "Is this reversible? Backup before? What if it runs against wrong environment?",
|
|
64
|
-
recommendation: 'Soft-delete first (add deleted_at). Hard-delete only after confirmed safe.',
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
pattern: /rate.?limit|throttle/i,
|
|
68
|
-
concern: "What happens when the rate limit is hit? User notified clearly? Retry-After header?",
|
|
69
|
-
recommendation: 'Return 429 with Retry-After. Show clear user message. Log for monitoring.',
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
pattern: /concurren|parallel|race.?condition|mutex|lock/i,
|
|
73
|
-
concern: "Race condition under concurrent requests? Lock contention under load?",
|
|
74
|
-
recommendation: 'Test with concurrent load. Use optimistic locking or distributed mutex.',
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
pattern: /secret|key|token|credential|api.?key/i,
|
|
78
|
-
concern: "Rotation strategy? What if this key leaks? Who has access and is it logged?",
|
|
79
|
-
recommendation: 'Rotate keys on schedule. Audit access logs. Use short-lived tokens where possible.',
|
|
80
|
-
},
|
|
81
|
-
];
|
|
82
|
-
|
|
83
|
-
const GENERIC: { concern: string; recommendation: string } = {
|
|
84
|
-
concern: "What is the failure mode here? What breaks at 2AM on a Sunday with no one watching?",
|
|
85
|
-
recommendation: 'Add monitoring, alerting, and a runbook for when this breaks.',
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const TRIVIAL = /^(rename|fix typo|reorder|reformat|format code|update comment|add comment)\b/i;
|
|
89
|
-
|
|
90
|
-
export function analyze(task: string): AgentVote {
|
|
91
|
-
if (TRIVIAL.test(task.trim())) {
|
|
92
|
-
return { verdict: 'approve', reason: 'Trivial change — no failure modes to probe.', concerns: [] };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const matched: Array<{ concern: string; recommendation: string }> = [];
|
|
96
|
-
|
|
97
|
-
for (const probe of PROBES) {
|
|
98
|
-
if (probe.pattern.test(task)) {
|
|
99
|
-
matched.push({ concern: probe.concern, recommendation: probe.recommendation });
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Devil always raises at least one concern for non-trivial tasks
|
|
104
|
-
if (matched.length === 0) {
|
|
105
|
-
matched.push(GENERIC);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const top = matched.slice(0, 3); // cap at 3 concerns to avoid flooding
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
verdict: 'warn',
|
|
112
|
-
reason: top[0].concern,
|
|
113
|
-
concerns: top.slice(1).map(m => m.concern),
|
|
114
|
-
recommendation: top.map(m => m.recommendation).join(' | '),
|
|
115
|
-
};
|
|
116
|
-
}
|
package/src/council/index.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// Council orchestrator — runs all 7 agents in parallel, returns debate result
|
|
2
|
-
import { analyze as leadDevAnalyze } from './lead-developer.js';
|
|
3
|
-
import { analyze as pmAnalyze } from './product-manager.js';
|
|
4
|
-
import { analyze as architectAnalyze } from './system-architect.js';
|
|
5
|
-
import { analyze as uxAnalyze } from './ux-designer.js';
|
|
6
|
-
import { analyze as devilAnalyze } from './devil-advocate.js';
|
|
7
|
-
import { analyze as legalAnalyze } from './legal-compliance.js';
|
|
8
|
-
import { analyze as securityAnalyze } from './security.js';
|
|
9
|
-
import { decide, formatDebate } from './decision-engine.js';
|
|
10
|
-
|
|
11
|
-
export type { AgentVote, AgentVerdict, CouncilVerdict, DebateInput, DebateResult } from './types.js';
|
|
12
|
-
|
|
13
|
-
import type { DebateInput, DebateResult } from './types.js';
|
|
14
|
-
|
|
15
|
-
export async function runDebate(input: DebateInput): Promise<DebateResult> {
|
|
16
|
-
const fullText = input.context ? `${input.task}\n${input.context}` : input.task;
|
|
17
|
-
|
|
18
|
-
// All 7 agents run in parallel — none depend on each other
|
|
19
|
-
const [lead_dev, pm, architect, ux, devil, legal, security] = await Promise.all([
|
|
20
|
-
Promise.resolve(leadDevAnalyze(fullText)),
|
|
21
|
-
Promise.resolve(pmAnalyze(fullText)),
|
|
22
|
-
Promise.resolve(architectAnalyze(fullText)),
|
|
23
|
-
Promise.resolve(uxAnalyze(fullText)),
|
|
24
|
-
Promise.resolve(devilAnalyze(fullText)),
|
|
25
|
-
Promise.resolve(legalAnalyze(fullText)),
|
|
26
|
-
Promise.resolve(securityAnalyze(fullText)),
|
|
27
|
-
]);
|
|
28
|
-
|
|
29
|
-
const votes = { lead_dev, pm, architect, ux, devil, legal, security };
|
|
30
|
-
const { final_verdict, block_reasons, warnings, recommended } = decide(votes);
|
|
31
|
-
const debated_at = new Date().toISOString();
|
|
32
|
-
const formatted_output = formatDebate(input.task, votes, final_verdict, block_reasons, warnings, recommended);
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
task: input.task,
|
|
36
|
-
final_verdict,
|
|
37
|
-
votes,
|
|
38
|
-
recommended,
|
|
39
|
-
block_reasons,
|
|
40
|
-
warnings,
|
|
41
|
-
debated_at,
|
|
42
|
-
formatted_output,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
// Lead Developer — code quality, security, no shortcuts
|
|
2
|
-
import type { AgentVote } from './types.js';
|
|
3
|
-
|
|
4
|
-
const BLOCK_RULES: Array<{ pattern: RegExp; reason: string; recommendation: string }> = [
|
|
5
|
-
{
|
|
6
|
-
pattern: /\bmd5\b/i,
|
|
7
|
-
reason: 'MD5 is cryptographically broken since 2008. Rainbow tables crack it instantly.',
|
|
8
|
-
recommendation: 'Use bcrypt (cost 12) for passwords. Use SHA-256 for checksums only.',
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
pattern: /\bsha-?1\b/i,
|
|
12
|
-
reason: 'SHA-1 collision attacks are practical since 2017. Deprecated for all security use.',
|
|
13
|
-
recommendation: 'Use SHA-256 or SHA-3. For passwords use bcrypt/argon2.',
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
pattern: /\b(des|3des|triple.?des)\b/i,
|
|
17
|
-
reason: 'DES/3DES is broken. Brute-forceable in hours.',
|
|
18
|
-
recommendation: 'Use AES-256-GCM for symmetric encryption.',
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
pattern: /\brc4\b/i,
|
|
22
|
-
reason: 'RC4 is broken — biased keystream, practical attacks exist.',
|
|
23
|
-
recommendation: 'Use ChaCha20-Poly1305 or AES-256-GCM.',
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
pattern: /\beval\s*\(/i,
|
|
27
|
-
reason: 'eval() enables arbitrary code execution from user input.',
|
|
28
|
-
recommendation: 'Remove eval(). Use JSON.parse() for data, redesign for logic.',
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
pattern: /\bnew\s+Function\s*\(/i,
|
|
32
|
-
reason: 'new Function() is eval() in disguise. Same arbitrary execution risk.',
|
|
33
|
-
recommendation: 'Never construct functions from strings. Redesign the logic.',
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
pattern: /hardcod.{0,20}(password|secret|key|token|credential)/i,
|
|
37
|
-
reason: 'Hardcoded credentials ship to version control and get leaked.',
|
|
38
|
-
recommendation: 'Move to environment variables. Use dotenv locally, vault in prod.',
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
pattern: /(password|secret|api.?key|token).{0,20}hardcod/i,
|
|
42
|
-
reason: 'Hardcoded credentials in source is a critical vulnerability.',
|
|
43
|
-
recommendation: 'Use process.env.VAR_NAME. Never commit secrets.',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
pattern: /disable.{0,15}(ssl|tls|cert(?:ificate)?(?:.?verif)?)/i,
|
|
47
|
-
reason: 'Disabling SSL/TLS validation exposes all traffic to MITM attacks.',
|
|
48
|
-
recommendation: 'Fix the certificate properly. Never disable validation in production.',
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
pattern: /cors.{0,15}['"]\s*\*\s*['"]/i,
|
|
52
|
-
reason: 'CORS wildcard (*) allows any origin to read API responses.',
|
|
53
|
-
recommendation: 'Specify exact allowed origins: ["https://yourdomain.com"].',
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
pattern: /innerHTML\s*=\s*.*(req\b|input\b|param|user|body)/i,
|
|
57
|
-
reason: 'Setting innerHTML from user data enables stored XSS attacks.',
|
|
58
|
-
recommendation: 'Use textContent for text. Sanitize with DOMPurify if HTML is needed.',
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
pattern: /no.{0,10}input.{0,10}(valid|sanitiz)/i,
|
|
62
|
-
reason: 'Explicitly skipping input validation is the root cause of most injections.',
|
|
63
|
-
recommendation: 'Validate all external input at the boundary with zod/joi schemas.',
|
|
64
|
-
},
|
|
65
|
-
];
|
|
66
|
-
|
|
67
|
-
const WARN_RULES: Array<{ pattern: RegExp; concern: string }> = [
|
|
68
|
-
{ pattern: /\bjquery\b/i, concern: 'jQuery in 2025 adds 30KB for what native DOM provides for free.' },
|
|
69
|
-
{ pattern: /\bvar\s+[a-zA-Z_$]/i, concern: 'var has function-scope hoisting. Use const (default) or let.' },
|
|
70
|
-
{ pattern: /catch\s*\([^)]*\)\s*\{\s*(\/\/[^\n]*)?\s*\}/i, concern: 'Empty catch silently swallows errors.' },
|
|
71
|
-
{ pattern: /console\.(log|debug)\b/i, concern: 'console.log in production leaks internal state. Use structured logging.' },
|
|
72
|
-
{ pattern: /\/\/\s*(todo|fixme|hack|xxx)\b/i, concern: 'Unresolved TODO/FIXME — resolve before shipping.' },
|
|
73
|
-
{ pattern: /:\s*any\b/i, concern: 'TypeScript any disables type safety. Use unknown or a proper type.' },
|
|
74
|
-
{ pattern: /\b(xdescribe|xit|xtest|\.skip)\b/i, concern: 'Skipped tests hide regressions. Fix or delete them.' },
|
|
75
|
-
{ pattern: /Math\.random\(\)/i, concern: 'Math.random() is not cryptographically secure. Use crypto.randomBytes().' },
|
|
76
|
-
];
|
|
77
|
-
|
|
78
|
-
const TRIVIAL = /^(rename|fix typo|reorder|reformat|format code|update comment|add comment)\b/i;
|
|
79
|
-
|
|
80
|
-
export function analyze(task: string): AgentVote {
|
|
81
|
-
if (TRIVIAL.test(task.trim())) {
|
|
82
|
-
return { verdict: 'approve', reason: 'Trivial change — no security or quality concerns.', concerns: [] };
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const blocks: Array<{ reason: string; recommendation: string }> = [];
|
|
86
|
-
const concerns: string[] = [];
|
|
87
|
-
|
|
88
|
-
for (const rule of BLOCK_RULES) {
|
|
89
|
-
if (rule.pattern.test(task)) {
|
|
90
|
-
blocks.push({ reason: rule.reason, recommendation: rule.recommendation });
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
for (const rule of WARN_RULES) {
|
|
94
|
-
if (rule.pattern.test(task)) {
|
|
95
|
-
concerns.push(rule.concern);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (blocks.length > 0) {
|
|
100
|
-
return {
|
|
101
|
-
verdict: 'block',
|
|
102
|
-
reason: blocks[0].reason,
|
|
103
|
-
concerns: [...blocks.slice(1).map(b => b.reason), ...concerns],
|
|
104
|
-
recommendation: blocks.map(b => b.recommendation).join(' | '),
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (concerns.length > 0) {
|
|
109
|
-
return {
|
|
110
|
-
verdict: 'warn',
|
|
111
|
-
reason: `${concerns.length} code quality issue${concerns.length > 1 ? 's' : ''} detected.`,
|
|
112
|
-
concerns,
|
|
113
|
-
recommendation: 'Address quality concerns before shipping to production.',
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return { verdict: 'approve', reason: 'No security or quality violations detected.', concerns: [] };
|
|
118
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
// Legal & Compliance — licenses, data privacy, GDPR, ToS, IP, regulatory
|
|
2
|
-
import type { AgentVote } from './types.js';
|
|
3
|
-
|
|
4
|
-
const BLOCK_RULES: Array<{ pattern: RegExp; reason: string; recommendation: string }> = [
|
|
5
|
-
{
|
|
6
|
-
pattern: /\b(gpl|gnu.?general.?public.?license)\b/i,
|
|
7
|
-
reason: 'GPL code in a non-GPL project creates license contamination — entire project must become GPL.',
|
|
8
|
-
recommendation: 'Audit all dependencies with license-checker. Replace GPL deps with MIT/Apache/BSD alternatives.',
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
pattern: /\bagpl\b/i,
|
|
12
|
-
reason: 'AGPL requires open-sourcing any server code users interact with over a network.',
|
|
13
|
-
recommendation: 'Either make your code AGPL, obtain a commercial license, or replace the AGPL dependency.',
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
pattern: /collect.{0,40}(personal|pii|email|phone|address).{0,40}no.{0,20}consent/i,
|
|
17
|
-
reason: 'Collecting personal data without consent violates GDPR Article 6 and CCPA — fines up to 4% global revenue.',
|
|
18
|
-
recommendation: 'Add explicit consent collection before gathering PII. Document legal basis (contract, consent, legitimate interest).',
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
pattern: /\b(hipaa|phi|protected.?health|health.?record)\b/i,
|
|
22
|
-
reason: 'Health data (PHI) requires HIPAA-compliant infrastructure, signed BAAs, and mandatory audit logging.',
|
|
23
|
-
recommendation: 'Use a HIPAA-compliant cloud provider. Sign Business Associate Agreements. Encrypt PHI at rest and in transit.',
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
pattern: /collect.{0,30}(children|child|minor|under.{0,5}1[23])/i,
|
|
27
|
-
reason: 'Collecting data from under-13s without verifiable parental consent violates COPPA (US) and GDPR.',
|
|
28
|
-
recommendation: 'Add age gate. Obtain verifiable parental consent. Consult legal counsel before proceeding.',
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
pattern: /store.{0,30}(credit.?card|card.?number|\bpan\b|\bcvv\b|\bcvc\b)/i,
|
|
32
|
-
reason: 'Storing raw card data requires PCI-DSS Level 1 compliance — years of audits and millions in infrastructure.',
|
|
33
|
-
recommendation: "Never store raw card data. Use Stripe/Braintree tokenization. Your PCI scope drops to SAQ-A.",
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
pattern: /scrape.{0,30}(facebook|twitter|linkedin|instagram|google|youtube|amazon)/i,
|
|
37
|
-
reason: 'Scraping major platforms violates their ToS and has led to multi-million dollar lawsuits (hiQ v. LinkedIn).',
|
|
38
|
-
recommendation: 'Use official APIs only. Review ToS data-use clauses. Seek legal opinion if data has commercial value.',
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
pattern: /train.{0,40}(model|ai|ml|llm).{0,40}(user.?data|customer.?data|without.{0,15}consent)/i,
|
|
42
|
-
reason: 'Training AI/ML on user data without consent violates GDPR and the ToS of most data platforms.',
|
|
43
|
-
recommendation: 'Obtain explicit opt-in consent for ML training use. Provide opt-out. Document data flows in privacy policy.',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
pattern: /\b(biometric|facial.?recogni|fingerprint).{0,30}(store|collect|process)\b/i,
|
|
47
|
-
reason: 'Biometric data has strict consent laws: BIPA (Illinois), GDPR Article 9, TX/WA state laws.',
|
|
48
|
-
recommendation: 'Obtain explicit written consent. Check state/country-specific laws. Consider if biometrics are necessary at all.',
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
pattern: /\bccpa\b.{0,30}(ignore|skip|bypass|not.{0,10}(apply|need))/i,
|
|
52
|
-
reason: 'CCPA applies to any business serving California residents above certain thresholds — not opt-in.',
|
|
53
|
-
recommendation: 'Add "Do Not Sell My Personal Information" link. Honor opt-out requests within 15 days.',
|
|
54
|
-
},
|
|
55
|
-
];
|
|
56
|
-
|
|
57
|
-
const WARN_RULES: Array<{ pattern: RegExp; concern: string; recommendation: string }> = [
|
|
58
|
-
{
|
|
59
|
-
pattern: /no.{0,25}privacy.?policy/i,
|
|
60
|
-
concern: 'Collecting any user data without a privacy policy violates GDPR, CCPA, and most app store guidelines.',
|
|
61
|
-
recommendation: 'Draft a privacy policy covering: what you collect, why, how long, who you share with, user rights.',
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
pattern: /log.{0,25}(email|ip.?address|phone|full.?name|ssn)|(email|phone|full.?name|ssn).{0,25}(log|plaintext|plain.?text)/i,
|
|
65
|
-
concern: 'Logging PII creates data retention liability and complicates right-to-erasure compliance.',
|
|
66
|
-
recommendation: 'Pseudonymize PII in logs (hash emails, truncate IPs). Set log retention to 30-90 days maximum.',
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
pattern: /no.{0,20}(delete|erasure|right.?to.?delete|data.?removal|forget)/i,
|
|
70
|
-
concern: 'GDPR Article 17 mandates honoring right-to-erasure requests within 30 days.',
|
|
71
|
-
recommendation: 'Build user data deletion endpoint. Cascade to backups. Document deletion confirmation process.',
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
pattern: /open.?source|npm.{0,20}install|pip.{0,20}install|composer|cargo.{0,20}add/i,
|
|
75
|
-
concern: 'New dependencies may carry GPL/AGPL licenses that contaminate your project license.',
|
|
76
|
-
recommendation: 'Run license-checker or FOSSA after adding deps. Block GPL/AGPL in CI for proprietary projects.',
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
pattern: /cookie|tracking|analytics|pixel|beacon/i,
|
|
80
|
-
concern: 'Cookies and tracking pixels require consent banners for EU/UK users (ePrivacy Directive + GDPR).',
|
|
81
|
-
recommendation: 'Integrate consent management platform (Cookiebot, OneTrust) for EU users.',
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
pattern: /third.?party.{0,30}(share|send|forward|transmit).{0,30}(user|personal|customer)/i,
|
|
85
|
-
concern: 'Sharing personal data with third parties requires disclosure in privacy policy and may need consent.',
|
|
86
|
-
recommendation: 'List all third-party data recipients in privacy policy. Sign Data Processing Agreements (DPAs).',
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
pattern: /retain.{0,30}(forever|indefinitely|no.?limit|no.?expiry)|store.{0,30}forever/i,
|
|
90
|
-
concern: 'Retaining personal data indefinitely violates GDPR storage limitation principle (Article 5(1)(e)).',
|
|
91
|
-
recommendation: 'Define retention periods per data type. Auto-delete or anonymize after the period expires.',
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
pattern: /\b(trademark|patent|copyright)\b/i,
|
|
95
|
-
concern: 'IP risk flagged — verify you have rights to use and distribute this.',
|
|
96
|
-
recommendation: 'Confirm licensing rights. Consult an IP attorney if building on patented methods or using third-party branding.',
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
pattern: /\b(gdpr|data.?protection)\b.{0,30}(ignore|bypass|skip|not.{0,10}(apply|worry))/i,
|
|
100
|
-
concern: "GDPR applies to any service used by EU residents regardless of where you're based.",
|
|
101
|
-
recommendation: 'Conduct a GDPR compliance review. Appoint a Data Protection Officer if required.',
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
pattern: /export.{0,20}(encryption|crypto|cipher).{0,20}(us|usa|america|international)/i,
|
|
105
|
-
concern: 'Exporting encryption software may require US export license (EAR/ECCN classification).',
|
|
106
|
-
recommendation: 'Check ECCN classification for your encryption strength. File annual self-classification if needed.',
|
|
107
|
-
},
|
|
108
|
-
];
|
|
109
|
-
|
|
110
|
-
const TRIVIAL = /^(rename|fix typo|reorder|reformat|format|update comment|add comment)\b/i;
|
|
111
|
-
|
|
112
|
-
export function analyze(task: string): AgentVote {
|
|
113
|
-
if (TRIVIAL.test(task.trim())) {
|
|
114
|
-
return { verdict: 'approve', reason: 'Trivial change — no legal concerns.', concerns: [] };
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const blocks: Array<{ reason: string; recommendation: string }> = [];
|
|
118
|
-
const concerns: string[] = [];
|
|
119
|
-
const recommendations: string[] = [];
|
|
120
|
-
|
|
121
|
-
for (const rule of BLOCK_RULES) {
|
|
122
|
-
if (rule.pattern.test(task)) {
|
|
123
|
-
blocks.push({ reason: rule.reason, recommendation: rule.recommendation });
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
for (const rule of WARN_RULES) {
|
|
127
|
-
if (rule.pattern.test(task)) {
|
|
128
|
-
concerns.push(rule.concern);
|
|
129
|
-
recommendations.push(rule.recommendation);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (blocks.length > 0) {
|
|
134
|
-
return {
|
|
135
|
-
verdict: 'block',
|
|
136
|
-
reason: blocks[0].reason,
|
|
137
|
-
concerns: blocks.slice(1).map(b => b.reason),
|
|
138
|
-
recommendation: blocks.map(b => b.recommendation).join(' | '),
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (concerns.length > 0) {
|
|
143
|
-
return {
|
|
144
|
-
verdict: 'warn',
|
|
145
|
-
reason: `${concerns.length} legal or compliance concern${concerns.length > 1 ? 's' : ''} — review before shipping.`,
|
|
146
|
-
concerns,
|
|
147
|
-
recommendation: recommendations[0],
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return { verdict: 'approve', reason: 'No legal or compliance issues detected.', concerns: [] };
|
|
152
|
-
}
|