@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,114 @@
|
|
|
1
|
+
function detectCategory(task) {
|
|
2
|
+
const t = task.toLowerCase();
|
|
3
|
+
if (t.includes('initial') || t.includes('first time') || t.includes('new project') || t.includes('onboard') || t.includes('create map'))
|
|
4
|
+
return 'initial-map';
|
|
5
|
+
if (t.includes('update') || t.includes('refresh') || t.includes('new file') || t.includes('delete') || t.includes('rename') || t.includes('move'))
|
|
6
|
+
return 'update';
|
|
7
|
+
if (t.includes('find') || t.includes('where') || t.includes('locate') || t.includes('search') || t.includes('query'))
|
|
8
|
+
return 'query';
|
|
9
|
+
return 'general';
|
|
10
|
+
}
|
|
11
|
+
const categoryApproach = {
|
|
12
|
+
'initial-map': 'Build the first project map by scanning the directory tree (excluding node_modules, .git, dist). Identify the tech stack from package.json / pyproject.toml / Cargo.toml. Identify key modules: entry points, router, config, schema, main service files. Store via veto_project_map_update.',
|
|
13
|
+
'update': 'Incrementally update the project map after file changes. Add new files to the structure, remove deleted files, update module roles if they changed. Call veto_project_map_update after every session that modifies files. Do not re-scan the full tree — apply a diff.',
|
|
14
|
+
'query': 'Query the project map via veto_project_map_get to answer "where is X" questions without reading the filesystem. Use the key_modules list for entry points, then follow imports. Fall back to a targeted filesystem search only if the map is stale.',
|
|
15
|
+
'general': 'Check if a project map exists (veto_project_map_get). Create one if absent. Update if the last updated_at timestamp is more than one session old. Use the map to navigate the codebase before touching the filesystem.',
|
|
16
|
+
};
|
|
17
|
+
const categorySteps = {
|
|
18
|
+
'initial-map': [
|
|
19
|
+
'Read package.json (or equivalent) to identify language, framework, and key dependencies',
|
|
20
|
+
'Scan the top-level src/ directory structure — list all directories and their purpose',
|
|
21
|
+
'Identify entry points: main.ts / index.ts / app.ts / server.ts / cli.ts',
|
|
22
|
+
'Identify router or routing config (routes.ts, app.ts, next.config.js, etc.)',
|
|
23
|
+
'Identify database layer: schema files, migration files, ORM config',
|
|
24
|
+
'Identify config files: .env, config/, settings files',
|
|
25
|
+
'Identify test directories and test runner config',
|
|
26
|
+
'Build the structure JSON: { "src/": { "agents/": [...], "router/": [...] } }',
|
|
27
|
+
'Identify key_modules: list of the 10–20 most important files with their role',
|
|
28
|
+
'Call veto_project_map_update with project_dir, structure, key_modules, tech_stack',
|
|
29
|
+
'Verify the map with veto_project_map_get and confirm all key files are present',
|
|
30
|
+
],
|
|
31
|
+
'update': [
|
|
32
|
+
'Identify which files were added, removed, or renamed in this session',
|
|
33
|
+
'Load the current map via veto_project_map_get',
|
|
34
|
+
'Apply adds: add new file paths to the appropriate structure branch',
|
|
35
|
+
'Apply removes: remove deleted file paths from the structure',
|
|
36
|
+
'Update key_modules if a new important file was added or an important file was removed',
|
|
37
|
+
'Update tech_stack if a new dependency was added (e.g. a new framework)',
|
|
38
|
+
'Call veto_project_map_update with the modified structure',
|
|
39
|
+
'Confirm the updated map reflects the current filesystem state',
|
|
40
|
+
],
|
|
41
|
+
'query': [
|
|
42
|
+
'Call veto_project_map_get to load the current map',
|
|
43
|
+
'Check updated_at — if older than the current session, consider triggering a refresh',
|
|
44
|
+
'Search key_modules for the file or module being queried',
|
|
45
|
+
'If not in key_modules, traverse the structure JSON for the relevant directory',
|
|
46
|
+
'Return the file path and module role without hitting the filesystem',
|
|
47
|
+
'If the map query is inconclusive, fall back to a targeted Glob search',
|
|
48
|
+
'If a Glob search finds a file missing from the map, trigger a map update',
|
|
49
|
+
],
|
|
50
|
+
'general': [
|
|
51
|
+
'Call veto_project_map_get to check if a map exists',
|
|
52
|
+
'If no map: run the initial-map flow',
|
|
53
|
+
'If map exists: check updated_at — update if stale',
|
|
54
|
+
'Use the map to answer navigation questions before scanning the filesystem',
|
|
55
|
+
'After each session that modifies files, call veto_project_map_update',
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
const categoryChecklist = {
|
|
59
|
+
'initial-map': [
|
|
60
|
+
'[ ] Tech stack identified: language, framework, key libraries',
|
|
61
|
+
'[ ] All src/ directories listed with role description',
|
|
62
|
+
'[ ] Entry points identified and listed in key_modules',
|
|
63
|
+
'[ ] Database / schema layer identified',
|
|
64
|
+
'[ ] Test directory and runner identified',
|
|
65
|
+
'[ ] Structure JSON stored via veto_project_map_update',
|
|
66
|
+
'[ ] Map verified via veto_project_map_get',
|
|
67
|
+
],
|
|
68
|
+
'update': [
|
|
69
|
+
'[ ] All added files included in structure',
|
|
70
|
+
'[ ] All deleted files removed from structure',
|
|
71
|
+
'[ ] key_modules updated if important file changed',
|
|
72
|
+
'[ ] veto_project_map_update called with new structure',
|
|
73
|
+
'[ ] Map verified post-update',
|
|
74
|
+
],
|
|
75
|
+
'query': [
|
|
76
|
+
'[ ] veto_project_map_get called before any filesystem scan',
|
|
77
|
+
'[ ] key_modules checked first for fast lookup',
|
|
78
|
+
'[ ] Structure JSON traversed if not in key_modules',
|
|
79
|
+
'[ ] Filesystem fallback used only if map is inconclusive',
|
|
80
|
+
'[ ] Map updated if filesystem revealed a gap',
|
|
81
|
+
],
|
|
82
|
+
'general': [
|
|
83
|
+
'[ ] Map existence checked at session start',
|
|
84
|
+
'[ ] Map updated at session end if files were modified',
|
|
85
|
+
'[ ] Map used as primary navigation source, not the filesystem',
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
export function plan(task, context) {
|
|
89
|
+
const category = detectCategory(task + ' ' + (context ?? ''));
|
|
90
|
+
return {
|
|
91
|
+
agent: 'project-mapper',
|
|
92
|
+
task,
|
|
93
|
+
tier: 1,
|
|
94
|
+
approach: categoryApproach[category],
|
|
95
|
+
steps: categorySteps[category],
|
|
96
|
+
checklist: categoryChecklist[category],
|
|
97
|
+
pitfalls: [
|
|
98
|
+
'Scanning the full project tree including node_modules — causes massive, useless maps',
|
|
99
|
+
'Only building the map once and never updating — the map becomes stale after file changes',
|
|
100
|
+
'Storing file contents in the map instead of just paths and roles — balloons storage',
|
|
101
|
+
'Not listing key_modules — forces a full structure search for common lookups',
|
|
102
|
+
'Building the map from memory instead of actually scanning — silently incorrect paths',
|
|
103
|
+
'Forgetting to update the map after a refactor that moves or renames many files',
|
|
104
|
+
],
|
|
105
|
+
patterns: [
|
|
106
|
+
'Incremental update: diff-based map updates after file changes, not full re-scans',
|
|
107
|
+
'Key modules first: maintain a flat list of the most important files for O(1) lookup',
|
|
108
|
+
'Role annotation: each entry in key_modules includes the file\'s role (entry-point, router, schema, etc.)',
|
|
109
|
+
'Lazy refresh: check updated_at and only refresh when the map is stale',
|
|
110
|
+
],
|
|
111
|
+
duration_estimate: '5-10 minutes',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=project-mapper.js.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export function plan(task, context) {
|
|
2
|
+
return {
|
|
3
|
+
agent: 'accessibility',
|
|
4
|
+
task,
|
|
5
|
+
tier: 2,
|
|
6
|
+
approach: 'Audit against WCAG 2.1 AA — the legal minimum for most jurisdictions. Work through four principles: Perceivable (can users perceive all content?), Operable (can users navigate with keyboard and assistive tech?), Understandable (is the UI predictable and error-recovery clear?), Robust (does it work with screen readers and future tech?). Flag failures by WCAG criterion number.',
|
|
7
|
+
steps: [
|
|
8
|
+
'Check colour contrast: text must meet 4.5:1 (normal) or 3:1 (large text) against background — WCAG 1.4.3',
|
|
9
|
+
'Check all images have alt text — decorative images use alt="" — WCAG 1.1.1',
|
|
10
|
+
'Check all form inputs have associated <label> elements — WCAG 1.3.1',
|
|
11
|
+
'Check the entire UI is keyboard-navigable: Tab order is logical, no keyboard traps — WCAG 2.1.1',
|
|
12
|
+
'Check focus indicator is visible on all interactive elements — WCAG 2.4.7',
|
|
13
|
+
'Check no content flashes more than 3 times per second — WCAG 2.3.1',
|
|
14
|
+
'Check page has a descriptive <title> — WCAG 2.4.2',
|
|
15
|
+
'Check heading hierarchy: h1→h2→h3, no levels skipped — WCAG 1.3.1',
|
|
16
|
+
'Check error messages identify the field and explain how to fix — WCAG 3.3.1',
|
|
17
|
+
'Check interactive elements have accessible names (aria-label where needed) — WCAG 4.1.2',
|
|
18
|
+
'Test with a screen reader (NVDA/VoiceOver) on the critical path',
|
|
19
|
+
],
|
|
20
|
+
checklist: [
|
|
21
|
+
'[ ] Colour contrast ≥ 4.5:1 for normal text, 3:1 for large text',
|
|
22
|
+
'[ ] All images have meaningful alt text',
|
|
23
|
+
'[ ] All form inputs have visible labels',
|
|
24
|
+
'[ ] Full keyboard navigation — no mouse required',
|
|
25
|
+
'[ ] Visible focus indicator on all interactive elements',
|
|
26
|
+
'[ ] No flashing content > 3 Hz',
|
|
27
|
+
'[ ] Logical heading hierarchy',
|
|
28
|
+
'[ ] Error messages identify field + explain fix',
|
|
29
|
+
'[ ] Screen reader tested on critical path',
|
|
30
|
+
],
|
|
31
|
+
pitfalls: [
|
|
32
|
+
'Using colour alone to convey information — 8% of men have colour vision deficiency',
|
|
33
|
+
'Placeholder text as a label substitute — placeholder disappears when typing',
|
|
34
|
+
'Custom interactive components without ARIA roles — screen readers see a div, not a button',
|
|
35
|
+
'Positive contrast ratio on a design tool that does not account for anti-aliasing',
|
|
36
|
+
'Fixing accessibility at end of development — 10× more expensive than building it in',
|
|
37
|
+
],
|
|
38
|
+
patterns: [
|
|
39
|
+
'POUR framework: Perceivable → Operable → Understandable → Robust',
|
|
40
|
+
'Keyboard-first development: if it works with keyboard only, it usually works with everything',
|
|
41
|
+
'Semantic HTML over ARIA: a <button> is better than a <div role="button">',
|
|
42
|
+
'Error recovery: every error must name the field and tell the user exactly how to fix it',
|
|
43
|
+
],
|
|
44
|
+
duration_estimate: '2-6 hours',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function analyze(code, context) {
|
|
48
|
+
const findings = [];
|
|
49
|
+
if (/<img(?![^>]*\balt=)/i.test(code)) {
|
|
50
|
+
findings.push({ severity: 'high', category: 'WCAG-1.1.1', description: 'Image(s) missing alt attribute', fix: 'Add alt="description" to all <img> tags. Use alt="" for decorative images.' });
|
|
51
|
+
}
|
|
52
|
+
if (/<input(?![^>]*\btype="hidden")[^>]*>(?![\s\S]*?<label)/i.test(code) && !/<label/i.test(code)) {
|
|
53
|
+
findings.push({ severity: 'high', category: 'WCAG-1.3.1', description: 'Form input(s) may lack associated label', fix: 'Add <label for="inputId"> or wrap input in <label>.' });
|
|
54
|
+
}
|
|
55
|
+
if (/onClick|onclick/.test(code) && !/<button|role="button"/i.test(code)) {
|
|
56
|
+
findings.push({ severity: 'medium', category: 'WCAG-2.1.1', description: 'Click handler on non-interactive element — may not be keyboard accessible', fix: 'Use <button> for click interactions, or add role="button" tabIndex={0} and onKeyDown handler.' });
|
|
57
|
+
}
|
|
58
|
+
if (/:focus\s*\{[^}]*outline\s*:\s*none/i.test(code) || /:focus\s*\{[^}]*outline\s*:\s*0/i.test(code)) {
|
|
59
|
+
findings.push({ severity: 'high', category: 'WCAG-2.4.7', description: 'Focus outline suppressed — keyboard users lose navigation indicator', fix: 'Remove outline:none from :focus. Use outline:none on :focus-visible only with a custom focus style.' });
|
|
60
|
+
}
|
|
61
|
+
if (/<h[1-6]/i.test(code)) {
|
|
62
|
+
const headings = (code.match(/<h([1-6])/gi) ?? []).map(h => parseInt(h[2]));
|
|
63
|
+
for (let i = 1; i < headings.length; i++) {
|
|
64
|
+
if (headings[i] - headings[i - 1] > 1) {
|
|
65
|
+
findings.push({ severity: 'medium', category: 'WCAG-1.3.1', description: `Heading level skipped: h${headings[i - 1]} → h${headings[i]}`, fix: 'Heading levels must be sequential. Do not skip levels.' });
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (/aria-label|aria-labelledby|role=/i.test(code)) {
|
|
71
|
+
if (/role="presentation"|role="none"/i.test(code) && /<img/i.test(code)) {
|
|
72
|
+
findings.push({ severity: 'low', category: 'WCAG-1.1.1', description: 'Image with role="presentation" — verify this is truly decorative', fix: 'If decorative, use alt="". If informative, remove role="presentation" and add meaningful alt.' });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const critCount = findings.filter(f => f.severity === 'critical').length;
|
|
76
|
+
const highCount = findings.filter(f => f.severity === 'high').length;
|
|
77
|
+
const score = Math.max(0, 100 - critCount * 30 - highCount * 20 - findings.filter(f => f.severity === 'medium').length * 10);
|
|
78
|
+
return {
|
|
79
|
+
agent: 'accessibility',
|
|
80
|
+
subject: context ?? 'UI code',
|
|
81
|
+
findings,
|
|
82
|
+
score,
|
|
83
|
+
verdict: score >= 85 ? 'approved' : score >= 65 ? 'approved_with_warnings' : score >= 40 ? 'needs_revision' : 'rejected',
|
|
84
|
+
summary: findings.length === 0 ? 'No accessibility issues detected in static analysis. Manual screen reader test still recommended.' : `${findings.length} accessibility issue(s). Top: ${findings[0].description}`,
|
|
85
|
+
critical_count: critCount,
|
|
86
|
+
high_count: highCount,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=accessibility.js.map
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export function plan(task, context) {
|
|
2
|
+
const t = (task + ' ' + (context ?? '')).toLowerCase();
|
|
3
|
+
const isRefactor = t.includes('refactor') || t.includes('clean') || t.includes('improve');
|
|
4
|
+
const isReview = t.includes('review') || t.includes('audit') || t.includes('check');
|
|
5
|
+
return {
|
|
6
|
+
agent: 'code-quality',
|
|
7
|
+
task,
|
|
8
|
+
tier: 2,
|
|
9
|
+
approach: isRefactor
|
|
10
|
+
? 'Refactor for quality by applying the three passes: first remove dead code and unused imports, then extract duplicated logic into shared utilities, then rename for clarity. Never change behaviour. Run tests after each pass.'
|
|
11
|
+
: isReview
|
|
12
|
+
? 'Audit code quality across five dimensions: complexity (cyclomatic), duplication, naming clarity, function length, and comment quality. Score each 1–5 and produce a prioritised fix list ordered by impact.'
|
|
13
|
+
: 'Assess code quality holistically. Identify the highest-impact improvement: the one change that most improves readability, maintainability, or testability. Deliver that change, verify nothing regressed.',
|
|
14
|
+
steps: [
|
|
15
|
+
'Measure cyclomatic complexity — flag any function above 10',
|
|
16
|
+
'Identify duplicate blocks (3+ lines appearing 2+ times) — extract to a shared utility',
|
|
17
|
+
'Check function length — flag functions over 40 lines for extraction',
|
|
18
|
+
'Check naming: are variable, function, and class names self-documenting?',
|
|
19
|
+
'Check nesting depth — flag logic nested 4+ levels deep',
|
|
20
|
+
'Identify magic numbers and strings — replace with named constants',
|
|
21
|
+
'Check for dead code: unused variables, unreachable branches, exported symbols with no consumers',
|
|
22
|
+
'Check comment quality: comments should explain WHY, not WHAT',
|
|
23
|
+
'Verify no TODO comments without a linked issue number',
|
|
24
|
+
'Run the linter — fix all warnings, not just errors',
|
|
25
|
+
],
|
|
26
|
+
checklist: [
|
|
27
|
+
'[ ] No function above cyclomatic complexity 10',
|
|
28
|
+
'[ ] No duplicate blocks of 3+ lines',
|
|
29
|
+
'[ ] No function longer than 40 lines',
|
|
30
|
+
'[ ] All names are self-documenting',
|
|
31
|
+
'[ ] Nesting depth ≤ 3 levels',
|
|
32
|
+
'[ ] No magic numbers or strings',
|
|
33
|
+
'[ ] No dead code',
|
|
34
|
+
'[ ] Linter passes with zero warnings',
|
|
35
|
+
'[ ] No TODO without issue number',
|
|
36
|
+
],
|
|
37
|
+
pitfalls: [
|
|
38
|
+
'Renaming for style without improving clarity — churn with no gain',
|
|
39
|
+
'Extracting functions so small they obscure the calling site',
|
|
40
|
+
'Removing comments that explain non-obvious WHY reasons',
|
|
41
|
+
'Refactoring without a test suite — introduces regressions silently',
|
|
42
|
+
],
|
|
43
|
+
patterns: [
|
|
44
|
+
'Three-pass refactor: dead code → duplication → naming',
|
|
45
|
+
'Complexity budget: cyclomatic ≤ 10, nesting ≤ 3, length ≤ 40 lines',
|
|
46
|
+
'Magic constant extraction: all literals that carry domain meaning become named constants',
|
|
47
|
+
],
|
|
48
|
+
duration_estimate: '1-3 hours',
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function analyze(code, context) {
|
|
52
|
+
const findings = [];
|
|
53
|
+
const lines = code.split('\n');
|
|
54
|
+
// Complexity: count decision points
|
|
55
|
+
const decisionKeywords = /\b(if|else|for|while|case|catch|\?\?|&&|\|\|)\b/g;
|
|
56
|
+
const decisions = (code.match(decisionKeywords) ?? []).length;
|
|
57
|
+
if (decisions > 15) {
|
|
58
|
+
findings.push({ severity: 'high', category: 'complexity', description: `High cyclomatic complexity: ~${decisions} decision points detected`, fix: 'Extract branches into well-named helper functions. Target < 10 per function.' });
|
|
59
|
+
}
|
|
60
|
+
else if (decisions > 8) {
|
|
61
|
+
findings.push({ severity: 'medium', category: 'complexity', description: `Moderate complexity: ~${decisions} decision points`, fix: 'Consider extracting the most complex branches into helpers.' });
|
|
62
|
+
}
|
|
63
|
+
// Long functions
|
|
64
|
+
const funcMatches = code.match(/(?:function\s+\w+|=>\s*\{|\w+\s*\([^)]*\)\s*\{)/g) ?? [];
|
|
65
|
+
if (lines.length > 50 && funcMatches.length <= 2) {
|
|
66
|
+
findings.push({ severity: 'medium', category: 'length', description: `File/function is ${lines.length} lines with few sub-functions`, fix: 'Extract logical sections into smaller, named functions.' });
|
|
67
|
+
}
|
|
68
|
+
// Magic numbers
|
|
69
|
+
const magicNumbers = code.match(/[^.]\b([2-9]\d{1,3}|1[0-9]{2,3})\b(?!\s*[,\]])/g) ?? [];
|
|
70
|
+
if (magicNumbers.length > 3) {
|
|
71
|
+
findings.push({ severity: 'low', category: 'magic-values', description: `${magicNumbers.length} potential magic numbers detected`, fix: 'Extract numeric literals into named constants with descriptive names.' });
|
|
72
|
+
}
|
|
73
|
+
// Deep nesting
|
|
74
|
+
let maxDepth = 0, currentDepth = 0;
|
|
75
|
+
for (const ch of code) {
|
|
76
|
+
if (ch === '{') {
|
|
77
|
+
currentDepth++;
|
|
78
|
+
maxDepth = Math.max(maxDepth, currentDepth);
|
|
79
|
+
}
|
|
80
|
+
else if (ch === '}')
|
|
81
|
+
currentDepth--;
|
|
82
|
+
}
|
|
83
|
+
if (maxDepth > 5) {
|
|
84
|
+
findings.push({ severity: 'medium', category: 'nesting', description: `Deep nesting detected: ${maxDepth} levels`, fix: 'Use early returns and guard clauses to reduce nesting depth to ≤ 3.' });
|
|
85
|
+
}
|
|
86
|
+
// Empty catch
|
|
87
|
+
if (/catch\s*\([^)]*\)\s*\{\s*\}/.test(code)) {
|
|
88
|
+
findings.push({ severity: 'high', category: 'error-handling', description: 'Empty catch block — exception silently swallowed', fix: 'Log the error or rethrow. Never silently swallow exceptions.' });
|
|
89
|
+
}
|
|
90
|
+
// TODO without issue
|
|
91
|
+
const todos = (code.match(/\/\/\s*TODO(?!.*#\d)/gi) ?? []).length;
|
|
92
|
+
if (todos > 0) {
|
|
93
|
+
findings.push({ severity: 'low', category: 'maintainability', description: `${todos} TODO comment(s) without linked issue number`, fix: 'Add issue number (e.g. // TODO #123) or resolve the TODO.' });
|
|
94
|
+
}
|
|
95
|
+
const critCount = findings.filter(f => f.severity === 'critical').length;
|
|
96
|
+
const highCount = findings.filter(f => f.severity === 'high').length;
|
|
97
|
+
const score = Math.max(0, 100 - critCount * 25 - highCount * 15 - findings.filter(f => f.severity === 'medium').length * 8 - findings.filter(f => f.severity === 'low').length * 3);
|
|
98
|
+
return {
|
|
99
|
+
agent: 'code-quality',
|
|
100
|
+
subject: context ?? 'code',
|
|
101
|
+
findings,
|
|
102
|
+
score,
|
|
103
|
+
verdict: score >= 85 ? 'approved' : score >= 65 ? 'approved_with_warnings' : score >= 40 ? 'needs_revision' : 'rejected',
|
|
104
|
+
summary: findings.length === 0 ? 'Code quality looks good.' : `${findings.length} quality issue(s) found. Top concern: ${findings[0].description}`,
|
|
105
|
+
critical_count: critCount,
|
|
106
|
+
high_count: highCount,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=code-quality.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export function plan(task, context) {
|
|
2
|
+
const t = (task + ' ' + (context ?? '')).toLowerCase();
|
|
3
|
+
const isBrowser = t.includes('browser') || t.includes('safari') || t.includes('firefox') || t.includes('frontend') || t.includes('css');
|
|
4
|
+
const isNode = t.includes('node') || t.includes('runtime') || t.includes('version') || t.includes('backend');
|
|
5
|
+
const isMobile = t.includes('mobile') || t.includes('ios') || t.includes('android') || t.includes('responsive');
|
|
6
|
+
const approach = isBrowser
|
|
7
|
+
? 'Check browser compatibility for every CSS property, JS API, and Web API used. Target: Chrome 100+, Firefox 100+, Safari 15+, Edge 100+. Use caniuse.com for CSS and MDN compatibility tables for JS. Flag anything below 90% global usage without a polyfill.'
|
|
8
|
+
: isNode
|
|
9
|
+
? 'Verify compatibility with the stated Node.js version range. Check: built-in modules (crypto, fs, sqlite), ES module vs CommonJS assumptions, and any native addons. Test on the minimum supported version, not just the latest.'
|
|
10
|
+
: isMobile
|
|
11
|
+
? 'Test responsive behaviour at 320px (small phone), 375px (standard phone), 768px (tablet), 1024px (small desktop), 1440px (standard desktop). Verify touch targets are ≥ 44×44px. Verify no horizontal scroll at any breakpoint.'
|
|
12
|
+
: 'Audit compatibility across the three dimensions relevant to this project: runtime version, browser support, and mobile breakpoints. Identify any usage of APIs not available in the minimum supported environment.';
|
|
13
|
+
return {
|
|
14
|
+
agent: 'compatibility',
|
|
15
|
+
task,
|
|
16
|
+
tier: 2,
|
|
17
|
+
approach,
|
|
18
|
+
steps: [
|
|
19
|
+
'Identify the minimum supported environment: Node version, browser versions, mobile screen sizes',
|
|
20
|
+
'List all external APIs, Web APIs, and language features used',
|
|
21
|
+
'Cross-reference each against the minimum environment\'s support tables (caniuse, MDN, Node.js docs)',
|
|
22
|
+
'Flag anything requiring a polyfill or transpile target adjustment',
|
|
23
|
+
'Check package.json engines field — does it accurately reflect the minimum Node version?',
|
|
24
|
+
'Check tsconfig.json target and lib — do they match the actual deployment environment?',
|
|
25
|
+
'Check CSS: use @supports for progressive enhancement, not as a workaround',
|
|
26
|
+
'Test responsive layout at 320px minimum width — the most common failure point',
|
|
27
|
+
'Verify touch targets ≥ 44×44px on all interactive elements',
|
|
28
|
+
'Check for deprecated APIs with removal dates within the support window',
|
|
29
|
+
],
|
|
30
|
+
checklist: [
|
|
31
|
+
'[ ] Minimum Node.js version documented in package.json engines field',
|
|
32
|
+
'[ ] tsconfig target and lib match deployment environment',
|
|
33
|
+
'[ ] No JS/CSS APIs used below 90% browser support without polyfill',
|
|
34
|
+
'[ ] Responsive layout tested at 320px minimum width',
|
|
35
|
+
'[ ] Touch targets ≥ 44×44px on mobile',
|
|
36
|
+
'[ ] No deprecated APIs scheduled for removal within support window',
|
|
37
|
+
'[ ] No native addons that break cross-platform (Windows/Mac/Linux)',
|
|
38
|
+
],
|
|
39
|
+
pitfalls: [
|
|
40
|
+
'Testing only on the latest browser — Safari consistently lags on Web APIs',
|
|
41
|
+
'Assuming Node 22 features are available when minimum is Node 18',
|
|
42
|
+
'Using CSS grid subgrid without checking Safari 15 support',
|
|
43
|
+
'Touch targets that pass on desktop but fail on 320px mobile',
|
|
44
|
+
'Native addons that compile on Mac but not on Windows (different MSVC requirements)',
|
|
45
|
+
],
|
|
46
|
+
patterns: [
|
|
47
|
+
'Progressive enhancement: build for the minimum, enhance for modern — not the reverse',
|
|
48
|
+
'Engines field as contract: package.json engines is a promise to users about compatibility',
|
|
49
|
+
'caniuse-first: check support tables before using any new browser API',
|
|
50
|
+
'320px rule: if it works at 320px, it works on every phone',
|
|
51
|
+
],
|
|
52
|
+
duration_estimate: '1-3 hours',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=compatibility.js.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export function plan(task, context) {
|
|
2
|
+
const t = (task + ' ' + (context ?? '')).toLowerCase();
|
|
3
|
+
const isApi = t.includes('api') || t.includes('endpoint') || t.includes('openapi');
|
|
4
|
+
const isReadme = t.includes('readme') || t.includes('getting started') || t.includes('onboard');
|
|
5
|
+
return {
|
|
6
|
+
agent: 'documentation',
|
|
7
|
+
task,
|
|
8
|
+
tier: 1,
|
|
9
|
+
approach: isApi
|
|
10
|
+
? 'Document the API using OpenAPI 3.0. Every endpoint needs: method, path, summary, request body schema, response schemas (200, 400, 401, 404, 500), and at least one request example. Generated docs should be accurate enough that a client can integrate without reading the source code.'
|
|
11
|
+
: isReadme
|
|
12
|
+
? 'Write a README that answers the four questions every new user asks: what is this, how do I install it, how do I use it, and how do I contribute. Lead with a one-sentence description, then a quick-start that works in under 5 minutes.'
|
|
13
|
+
: 'Document the public API surface. Every exported function, class, and type gets a JSDoc block with: one-line description, @param for each parameter, @returns, @throws for known errors, and one @example. Internal functions do not need JSDoc.',
|
|
14
|
+
steps: [
|
|
15
|
+
'List all exported symbols that lack documentation',
|
|
16
|
+
'For each exported function: write a one-line description that explains what it does (not how)',
|
|
17
|
+
'Document every parameter: name, type, description, whether optional, default value',
|
|
18
|
+
'Document the return value: type and what it represents',
|
|
19
|
+
'Document known thrown errors with @throws',
|
|
20
|
+
'Write at least one @example per exported function',
|
|
21
|
+
'For APIs: document request/response shape with types or OpenAPI schema',
|
|
22
|
+
'For README: include quick-start that works in under 5 minutes',
|
|
23
|
+
'Remove outdated documentation that no longer matches the code',
|
|
24
|
+
'Verify all code examples actually run correctly',
|
|
25
|
+
],
|
|
26
|
+
checklist: [
|
|
27
|
+
'[ ] Every exported symbol has a JSDoc block',
|
|
28
|
+
'[ ] Every @param has a description (not just a type)',
|
|
29
|
+
'[ ] @returns documents what is returned, not just the type',
|
|
30
|
+
'[ ] @throws present for all known error paths',
|
|
31
|
+
'[ ] At least one @example per exported function',
|
|
32
|
+
'[ ] No documentation that contradicts the current code',
|
|
33
|
+
'[ ] README quick-start tested end-to-end',
|
|
34
|
+
],
|
|
35
|
+
pitfalls: [
|
|
36
|
+
'Documenting HOW instead of WHY — "increments counter by 1" is not documentation',
|
|
37
|
+
'Copying type signatures into prose — JSDoc @param already has the type',
|
|
38
|
+
'Writing examples that do not actually run',
|
|
39
|
+
'Documenting internal functions that are not part of the public API',
|
|
40
|
+
'Leaving stale documentation that contradicts the current implementation',
|
|
41
|
+
],
|
|
42
|
+
patterns: [
|
|
43
|
+
'API-first documentation: write the docs before the implementation to clarify the contract',
|
|
44
|
+
'Example-driven: code examples are the most useful documentation format',
|
|
45
|
+
'Quick-start rule: a new developer should have something running in under 5 minutes',
|
|
46
|
+
],
|
|
47
|
+
duration_estimate: '1-4 hours',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function analyze(code, context) {
|
|
51
|
+
const findings = [];
|
|
52
|
+
const lines = code.split('\n');
|
|
53
|
+
// Count exported symbols
|
|
54
|
+
const exports = (code.match(/^export\s+(function|class|const|type|interface|async)/gm) ?? []).length;
|
|
55
|
+
// Count JSDoc blocks before exports
|
|
56
|
+
const jsdocs = (code.match(/\/\*\*[\s\S]*?\*\/\s*\nexport/g) ?? []).length;
|
|
57
|
+
const undocumented = exports - jsdocs;
|
|
58
|
+
if (undocumented > 0) {
|
|
59
|
+
findings.push({
|
|
60
|
+
severity: undocumented > 3 ? 'high' : 'medium',
|
|
61
|
+
category: 'missing-docs',
|
|
62
|
+
description: `${undocumented} of ${exports} exported symbol(s) lack JSDoc documentation`,
|
|
63
|
+
fix: 'Add /** description */ block before each exported function, class, or type.',
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// Check for param docs
|
|
67
|
+
const params = (code.match(/@param/g) ?? []).length;
|
|
68
|
+
const funcParams = (code.match(/\([^)]{5,}\)/g) ?? []).length;
|
|
69
|
+
if (exports > 0 && params === 0 && funcParams > 2) {
|
|
70
|
+
findings.push({ severity: 'medium', category: 'missing-params', description: 'No @param documentation found', fix: 'Add @param {type} name — description for each parameter.' });
|
|
71
|
+
}
|
|
72
|
+
// Check for examples
|
|
73
|
+
if (exports > 0 && !code.includes('@example')) {
|
|
74
|
+
findings.push({ severity: 'low', category: 'missing-examples', description: 'No @example blocks found', fix: 'Add at least one @example showing typical usage for each exported function.' });
|
|
75
|
+
}
|
|
76
|
+
// Check for stale "TODO: document"
|
|
77
|
+
const todoDoc = (code.match(/\/\/\s*TODO.*doc/gi) ?? []).length;
|
|
78
|
+
if (todoDoc > 0) {
|
|
79
|
+
findings.push({ severity: 'low', category: 'stale-todo', description: `${todoDoc} TODO documentation reminder(s) found`, fix: 'Write the documentation and remove the TODO.' });
|
|
80
|
+
}
|
|
81
|
+
const score = Math.max(0, 100 - findings.filter(f => f.severity === 'high').length * 20
|
|
82
|
+
- findings.filter(f => f.severity === 'medium').length * 10
|
|
83
|
+
- findings.filter(f => f.severity === 'low').length * 5);
|
|
84
|
+
return {
|
|
85
|
+
agent: 'documentation',
|
|
86
|
+
subject: context ?? 'code',
|
|
87
|
+
findings,
|
|
88
|
+
score,
|
|
89
|
+
verdict: score >= 85 ? 'approved' : score >= 65 ? 'approved_with_warnings' : score >= 40 ? 'needs_revision' : 'rejected',
|
|
90
|
+
summary: findings.length === 0 ? 'Documentation looks complete.' : `${findings.length} documentation gap(s). Top: ${findings[0].description}`,
|
|
91
|
+
critical_count: 0,
|
|
92
|
+
high_count: findings.filter(f => f.severity === 'high').length,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=documentation.js.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export function plan(task, context) {
|
|
2
|
+
return {
|
|
3
|
+
agent: 'error-handling',
|
|
4
|
+
task,
|
|
5
|
+
tier: 2,
|
|
6
|
+
approach: 'Audit error handling for the three failure classes: expected failures (validation, not found, auth), unexpected failures (bugs, network failures, DB errors), and catastrophic failures (process crash, OOM). Each class needs a different treatment. Expected failures return structured error responses. Unexpected failures are logged with context and surfaced as generic errors to users. Catastrophic failures are captured by a top-level handler and alert.',
|
|
7
|
+
steps: [
|
|
8
|
+
'Identify all async operations: DB calls, HTTP requests, file I/O, external APIs',
|
|
9
|
+
'Verify every async call has a try/catch or .catch() — no floating promises',
|
|
10
|
+
'Check that catch blocks do not silently swallow errors — log OR rethrow, never neither',
|
|
11
|
+
'Verify error messages to users do not leak stack traces or internal details in production',
|
|
12
|
+
'Check that typed errors are used — no throw new Error("raw string") for domain errors',
|
|
13
|
+
'Verify HTTP error codes are semantically correct: 400 vs 422, 401 vs 403, 404 vs 410',
|
|
14
|
+
'Check that errors include enough context to diagnose: which record, which operation, what input',
|
|
15
|
+
'Verify the top-level process error handler is in place: uncaughtException, unhandledRejection',
|
|
16
|
+
'Check that external API timeouts are configured — no indefinite hangs',
|
|
17
|
+
'Test each error path: does the system recover gracefully or leave state corrupted?',
|
|
18
|
+
],
|
|
19
|
+
checklist: [
|
|
20
|
+
'[ ] No floating promises — every async call awaited or .catch() chained',
|
|
21
|
+
'[ ] No empty or silent catch blocks',
|
|
22
|
+
'[ ] Stack traces never exposed to users in production',
|
|
23
|
+
'[ ] Typed domain errors — not raw Error strings',
|
|
24
|
+
'[ ] HTTP status codes semantically correct',
|
|
25
|
+
'[ ] Error log includes context (what failed, on what input)',
|
|
26
|
+
'[ ] process.on("uncaughtException") and ("unhandledRejection") registered',
|
|
27
|
+
'[ ] All external HTTP calls have a timeout configured',
|
|
28
|
+
'[ ] Error paths tested — system recovers without corrupting state',
|
|
29
|
+
],
|
|
30
|
+
pitfalls: [
|
|
31
|
+
'Catch-and-log without rethrowing — the caller assumes success when the operation failed',
|
|
32
|
+
'Using the same error type for expected and unexpected failures — callers cannot distinguish',
|
|
33
|
+
'Not including the original error in a re-thrown wrapper — stack trace lost',
|
|
34
|
+
'Logging only the error message without the input that caused it — impossible to reproduce',
|
|
35
|
+
'Catching Error but not checking error.name — all errors look the same',
|
|
36
|
+
],
|
|
37
|
+
patterns: [
|
|
38
|
+
'Error taxonomy: expected (return structured error) | unexpected (log + generic response) | catastrophic (alert)',
|
|
39
|
+
'Context-rich errors: every thrown error includes the operation, input, and correlation ID',
|
|
40
|
+
'Fail-fast on programming errors: throw immediately on invalid arguments — do not try to recover',
|
|
41
|
+
'Structured error types: one class per error category with a code field for programmatic handling',
|
|
42
|
+
],
|
|
43
|
+
duration_estimate: '2-4 hours',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function analyze(code, context) {
|
|
47
|
+
const findings = [];
|
|
48
|
+
// Empty catch blocks
|
|
49
|
+
const emptyCatch = (code.match(/catch\s*\([^)]*\)\s*\{\s*\}/g) ?? []).length;
|
|
50
|
+
if (emptyCatch > 0) {
|
|
51
|
+
findings.push({ severity: 'critical', category: 'silent-failure', description: `${emptyCatch} empty catch block(s) — exceptions silently swallowed`, fix: 'Log the error and/or rethrow. Never swallow exceptions silently.' });
|
|
52
|
+
}
|
|
53
|
+
// Catch with only console.log (no rethrow)
|
|
54
|
+
const logOnlyCatch = (code.match(/catch\s*\([^)]*\)\s*\{[^}]*console\.[a-z]+[^}]*\}/g) ?? [])
|
|
55
|
+
.filter(block => !block.includes('throw')).length;
|
|
56
|
+
if (logOnlyCatch > 0) {
|
|
57
|
+
findings.push({ severity: 'high', category: 'swallowed-error', description: `${logOnlyCatch} catch block(s) log but do not rethrow — caller assumes success`, fix: 'After logging, either rethrow the error or return a typed error result to the caller.' });
|
|
58
|
+
}
|
|
59
|
+
// Floating promises
|
|
60
|
+
const floatingPromise = (code.match(/(?<!\bawait\s)(?<!\breturn\s)\b\w+\([^)]*\)\.then\(/g) ?? []).length;
|
|
61
|
+
if (floatingPromise > 1) {
|
|
62
|
+
findings.push({ severity: 'high', category: 'floating-promise', description: `Potential floating promise(s) detected`, fix: 'Ensure all Promise chains are awaited or have a .catch() handler.' });
|
|
63
|
+
}
|
|
64
|
+
// Raw Error strings
|
|
65
|
+
const rawThrows = (code.match(/throw new Error\(['"`][^'"`]{1,50}['"`]\)/g) ?? []).length;
|
|
66
|
+
if (rawThrows > 2) {
|
|
67
|
+
findings.push({ severity: 'medium', category: 'untyped-errors', description: `${rawThrows} raw Error string throws — callers cannot distinguish error types`, fix: 'Create typed error classes (class NotFoundError extends Error) for each distinct error category.' });
|
|
68
|
+
}
|
|
69
|
+
// No timeout on fetch/axios
|
|
70
|
+
if (/fetch\(|axios\.|got\(|request\(/.test(code) && !/timeout/.test(code)) {
|
|
71
|
+
findings.push({ severity: 'medium', category: 'missing-timeout', description: 'External HTTP call detected without apparent timeout configuration', fix: 'Add a timeout to all external HTTP calls. Unconfigured calls hang indefinitely on network issues.' });
|
|
72
|
+
}
|
|
73
|
+
const critCount = findings.filter(f => f.severity === 'critical').length;
|
|
74
|
+
const highCount = findings.filter(f => f.severity === 'high').length;
|
|
75
|
+
const score = Math.max(0, 100 - critCount * 30 - highCount * 20 - findings.filter(f => f.severity === 'medium').length * 10);
|
|
76
|
+
return {
|
|
77
|
+
agent: 'error-handling',
|
|
78
|
+
subject: context ?? 'code',
|
|
79
|
+
findings,
|
|
80
|
+
score,
|
|
81
|
+
verdict: score >= 85 ? 'approved' : score >= 65 ? 'approved_with_warnings' : score >= 40 ? 'needs_revision' : 'rejected',
|
|
82
|
+
summary: findings.length === 0 ? 'Error handling looks solid.' : `${findings.length} error handling issue(s). Top: ${findings[0].description}`,
|
|
83
|
+
critical_count: critCount,
|
|
84
|
+
high_count: highCount,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=error-handling.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export function plan(task, context) {
|
|
2
|
+
return {
|
|
3
|
+
agent: 'competitor-analyzer',
|
|
4
|
+
task,
|
|
5
|
+
tier: 2,
|
|
6
|
+
approach: 'Analyse competitors on three dimensions: feature gap (what they have that you don\'t), differentiation gap (what you have that they don\'t), and positioning gap (where they own user perception). The goal is not to copy — it is to identify where building a feature would be table-stakes vs. where it would be genuinely differentiating.',
|
|
7
|
+
steps: [
|
|
8
|
+
'List the top 3–5 direct competitors and 2–3 indirect alternatives',
|
|
9
|
+
'For each competitor: document their core value proposition in one sentence',
|
|
10
|
+
'Build a feature matrix: rows = features, columns = products, cells = yes/no/partial',
|
|
11
|
+
'Identify table-stakes features: present in all competitors (building these is hygiene, not differentiation)',
|
|
12
|
+
'Identify differentiating features: present in 0–1 competitors (building these creates moat)',
|
|
13
|
+
'Identify features your product has that no competitor has — this is your current moat',
|
|
14
|
+
'Research competitor pricing to understand the value they signal for each tier',
|
|
15
|
+
'Read user reviews of competitors to find the most common complaints (these are opportunities)',
|
|
16
|
+
'Identify the positioning gaps: what perception do users have of each competitor?',
|
|
17
|
+
'Recommend the top 3 features by differentiation potential × build cost',
|
|
18
|
+
'Store analysis in veto_memory_store (type="reference", tags=["competitive", "strategy"])',
|
|
19
|
+
],
|
|
20
|
+
checklist: [
|
|
21
|
+
'[ ] Top 5 direct competitors identified',
|
|
22
|
+
'[ ] Feature matrix built with table-stakes vs. differentiating features separated',
|
|
23
|
+
'[ ] Current moat (unique features) identified',
|
|
24
|
+
'[ ] User review analysis for top 2 complaints per competitor',
|
|
25
|
+
'[ ] Differentiation opportunities ranked by potential × cost',
|
|
26
|
+
'[ ] Analysis stored in veto_memory_store',
|
|
27
|
+
],
|
|
28
|
+
pitfalls: [
|
|
29
|
+
'Copying competitor features without understanding why they built them — builds technical debt, not differentiation',
|
|
30
|
+
'Ignoring indirect alternatives — users often switch to a different category entirely, not a direct competitor',
|
|
31
|
+
'Treating all feature gaps as problems — some are intentional product decisions',
|
|
32
|
+
'Not reading user reviews — documentation says what a product does; reviews say what it fails to do',
|
|
33
|
+
'Analysing features in isolation from pricing — a feature only matters if the target segment can afford it',
|
|
34
|
+
],
|
|
35
|
+
patterns: [
|
|
36
|
+
'Table-stakes separation: hygiene features vs. moat features require different prioritisation logic',
|
|
37
|
+
'Complaint mining: competitor weaknesses in reviews are your clearest product opportunities',
|
|
38
|
+
'Moat-first thinking: understand your current differentiation before adding more features',
|
|
39
|
+
'Indirect competitor awareness: the biggest threat is often a product in a different category',
|
|
40
|
+
],
|
|
41
|
+
duration_estimate: '2-4 hours',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=competitor-analyzer.js.map
|