kodelyth-ecc 1.2.2 → 1.4.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/AGENTS.md +101 -181
- package/CHANGELOG.md +67 -0
- package/CLAUDE.md +72 -63
- package/KODELYTH.md +79 -44
- package/README.md +244 -192
- package/VERSION +1 -1
- package/agents/dependency-doctor.md +120 -0
- package/agents/env-debugger.md +154 -0
- package/agents/flake-hunter.md +142 -0
- package/agents/git-rescue.md +133 -0
- package/agents/kodelyth-memory.md +87 -0
- package/agents/release-captain.md +190 -0
- package/bin/kodelyth-ecc.js +18 -12
- package/commands/memory.md +62 -0
- package/hooks/hooks.json +26 -0
- package/hooks/memory/capture-stop.js +88 -0
- package/hooks/memory/inject-start.js +60 -0
- package/install.ps1 +28 -9
- package/install.sh +11 -97
- package/package.json +4 -2
- package/rules/common/agent-intent-routing.md +337 -0
- package/rules/common/memory-protocol.md +56 -0
- package/scripts/memory/cli.js +200 -0
- package/scripts/memory/extract.js +176 -0
- package/scripts/memory/inject.js +145 -0
- package/scripts/memory/store.js +300 -0
- package/skills/agent-handoff/SKILL.md +184 -0
- package/skills/intent-routing/SKILL.md +134 -0
- package/skills/kodelyth-memory/SKILL.md +136 -0
- package/tests/memory/store.test.js +121 -0
- package/dashboard/lib/agent-tracker.js +0 -366
- package/dashboard/lib/aggregator.js +0 -119
- package/dashboard/lib/cost-calculator.js +0 -50
- package/dashboard/lib/platform-detector.js +0 -89
- package/dashboard/lib/readers/antigravity-reader.js +0 -113
- package/dashboard/lib/readers/claude-reader.js +0 -135
- package/dashboard/lib/readers/codex-reader.js +0 -192
- package/dashboard/lib/readers/cursor-reader.js +0 -135
- package/dashboard/lib/readers/opencode-reader.js +0 -201
- package/dashboard/lib/readers/windsurf-reader.js +0 -146
- package/dashboard/package.json +0 -24
- package/dashboard/public/index.html +0 -1221
- package/dashboard/server.js +0 -119
- package/scripts/agent-tracker-hook.js +0 -81
- package/social/readme-lens.svg +0 -140
- package/social/readme-savings.svg +0 -56
package/dashboard/server.js
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Kodelyth Lens — AI Coding Agent Dashboard
|
|
3
|
-
// Part of Kodelyth ECC — github.com/sifxprime/kodelyth-ecc
|
|
4
|
-
// Tracks usage across Claude Code, Cursor, Windsurf, Codex, Antigravity
|
|
5
|
-
|
|
6
|
-
import http from 'node:http';
|
|
7
|
-
import fs from 'node:fs';
|
|
8
|
-
import path from 'node:path';
|
|
9
|
-
import { fileURLToPath } from 'node:url';
|
|
10
|
-
import { aggregateAll } from './lib/aggregator.js';
|
|
11
|
-
|
|
12
|
-
// Load .env if present
|
|
13
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
-
const envFile = path.join(__dirname, '.env');
|
|
15
|
-
if (fs.existsSync(envFile)) {
|
|
16
|
-
for (const line of fs.readFileSync(envFile, 'utf-8').split('\n')) {
|
|
17
|
-
const m = line.match(/^([^#=\s]+)\s*=\s*(.*)$/);
|
|
18
|
-
if (m && !process.env[m[1]]) process.env[m[1]] = m[2].trim().replace(/^["']|["']$/g, '');
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const PORT = parseInt(process.env.PORT || '3456', 10);
|
|
23
|
-
const PUBLIC = path.join(__dirname, 'public');
|
|
24
|
-
const CACHE_TTL = 30_000; // 30 seconds
|
|
25
|
-
|
|
26
|
-
const MIME = {
|
|
27
|
-
'.html': 'text/html; charset=utf-8',
|
|
28
|
-
'.css': 'text/css',
|
|
29
|
-
'.js': 'application/javascript',
|
|
30
|
-
'.json': 'application/json',
|
|
31
|
-
'.svg': 'image/svg+xml',
|
|
32
|
-
'.ico': 'image/x-icon',
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
// Simple in-memory cache
|
|
36
|
-
let _cache = { data: null, ts: 0 };
|
|
37
|
-
|
|
38
|
-
async function getData(force = false) {
|
|
39
|
-
const now = Date.now();
|
|
40
|
-
if (!force && _cache.data && now - _cache.ts < CACHE_TTL) return _cache.data;
|
|
41
|
-
_cache.data = await aggregateAll();
|
|
42
|
-
_cache.ts = now;
|
|
43
|
-
return _cache.data;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const server = http.createServer(async (req, res) => {
|
|
47
|
-
const url = new URL(req.url, `http://localhost:${PORT}`);
|
|
48
|
-
|
|
49
|
-
// ── API ──────────────────────────────────────────────────────────────────
|
|
50
|
-
if (url.pathname.startsWith('/api/')) {
|
|
51
|
-
res.setHeader('Content-Type', 'application/json');
|
|
52
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
53
|
-
res.setHeader('Cache-Control', 'no-cache');
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
const force = url.searchParams.get('refresh') === '1';
|
|
57
|
-
const data = await getData(force);
|
|
58
|
-
|
|
59
|
-
const routes = {
|
|
60
|
-
'/api/all': () => data,
|
|
61
|
-
'/api/stats': () => data.stats,
|
|
62
|
-
'/api/agents': () => data.agents,
|
|
63
|
-
'/api/sessions': () => data.sessions,
|
|
64
|
-
'/api/costs': () => data.costs,
|
|
65
|
-
'/api/platforms': () => data.platforms,
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const handler = routes[url.pathname];
|
|
69
|
-
if (handler) {
|
|
70
|
-
res.end(JSON.stringify(handler()));
|
|
71
|
-
} else {
|
|
72
|
-
res.statusCode = 404;
|
|
73
|
-
res.end(JSON.stringify({ error: 'Not found' }));
|
|
74
|
-
}
|
|
75
|
-
} catch (err) {
|
|
76
|
-
res.statusCode = 500;
|
|
77
|
-
res.end(JSON.stringify({ error: err.message }));
|
|
78
|
-
}
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// ── Static files ─────────────────────────────────────────────────────────
|
|
83
|
-
let file = url.pathname === '/' ? 'index.html' : url.pathname.slice(1);
|
|
84
|
-
const filePath = path.resolve(PUBLIC, file);
|
|
85
|
-
|
|
86
|
-
// Prevent path traversal attacks
|
|
87
|
-
if (!filePath.startsWith(PUBLIC + path.sep) && filePath !== path.resolve(PUBLIC, 'index.html')) {
|
|
88
|
-
res.writeHead(403, { 'Content-Type': 'text/plain' });
|
|
89
|
-
res.end('Forbidden');
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
94
|
-
const ext = path.extname(filePath);
|
|
95
|
-
res.setHeader('Content-Type', MIME[ext] || 'text/plain');
|
|
96
|
-
res.end(fs.readFileSync(filePath));
|
|
97
|
-
} else {
|
|
98
|
-
// SPA fallback
|
|
99
|
-
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
100
|
-
res.end(fs.readFileSync(path.join(PUBLIC, 'index.html')));
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
server.listen(PORT, () => {
|
|
105
|
-
console.log('');
|
|
106
|
-
console.log(' \x1b[36m\x1b[1m██╗ ██╗ ██████╗ ██████╗ ███████╗██╗ ██╗ ██╗████████╗██╗ ██╗\x1b[0m');
|
|
107
|
-
console.log(' \x1b[36m\x1b[1m██║ ██╔╝██╔═══██╗██╔══██╗██╔════╝██║ ╚██╗ ██╔╝╚══██╔══╝██║ ██║\x1b[0m');
|
|
108
|
-
console.log(' \x1b[36m\x1b[1m█████╔╝ ██║ ██║██║ ██║█████╗ ██║ ╚████╔╝ ██║ ███████║\x1b[0m');
|
|
109
|
-
console.log(' \x1b[36m\x1b[1m██╔═██╗ ██║ ██║██║ ██║██╔══╝ ██║ ╚██╔╝ ██║ ██╔══██║\x1b[0m');
|
|
110
|
-
console.log(' \x1b[36m\x1b[1m██║ ██╗╚██████╔╝██████╔╝███████╗███████╗██║ ██║ ██║ ██║\x1b[0m');
|
|
111
|
-
console.log(' \x1b[36m\x1b[1m╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝\x1b[0m');
|
|
112
|
-
console.log('');
|
|
113
|
-
console.log(' \x1b[1mKodelyth Lens\x1b[0m — AI Coding Agent Dashboard');
|
|
114
|
-
console.log(` \x1b[32m\x1b[1m→ http://localhost:${PORT}\x1b[0m`);
|
|
115
|
-
console.log('');
|
|
116
|
-
console.log(' Tracking: Claude Code · Cursor · Windsurf · Codex · OpenCode · Antigravity');
|
|
117
|
-
console.log(' Press Ctrl+C to stop.');
|
|
118
|
-
console.log('');
|
|
119
|
-
});
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Kodelyth Lens — Agent Auto-Tracker Hook
|
|
3
|
-
// Runs as a PostToolUse hook in Claude Code.
|
|
4
|
-
// Detects ECC agent invocations from tool calls and writes to
|
|
5
|
-
// ~/.claude/kodelyth-agent-tracking.jsonl for the dashboard to read.
|
|
6
|
-
// Part of Kodelyth ECC — github.com/sifxprime/kodelyth-ecc
|
|
7
|
-
|
|
8
|
-
import fs from 'node:fs';
|
|
9
|
-
import path from 'node:path';
|
|
10
|
-
import os from 'node:os';
|
|
11
|
-
|
|
12
|
-
const ECC_AGENTS = [
|
|
13
|
-
'kodelyth-advisor', 'debug-detective', 'ux-reviewer', 'api-guardian',
|
|
14
|
-
'pair-programmer', 'migration-guide', 'code-reviewer', 'typescript-reviewer',
|
|
15
|
-
'python-reviewer', 'go-reviewer', 'rust-reviewer', 'java-reviewer',
|
|
16
|
-
'kotlin-reviewer', 'cpp-reviewer', 'csharp-reviewer', 'flutter-reviewer',
|
|
17
|
-
'build-error-resolver', 'go-build-resolver', 'rust-build-resolver',
|
|
18
|
-
'java-build-resolver', 'kotlin-build-resolver', 'cpp-build-resolver',
|
|
19
|
-
'dart-build-resolver', 'pytorch-build-resolver', 'planner', 'architect',
|
|
20
|
-
'code-architect', 'code-explorer', 'chief-of-staff', 'security-reviewer',
|
|
21
|
-
'healthcare-reviewer', 'refactor-cleaner', 'code-simplifier',
|
|
22
|
-
'performance-optimizer', 'type-design-analyzer', 'silent-failure-hunter',
|
|
23
|
-
'tdd-guide', 'e2e-runner', 'pr-test-analyzer', 'doc-updater',
|
|
24
|
-
'docs-lookup', 'comment-analyzer', 'opensource-forker', 'opensource-sanitizer',
|
|
25
|
-
'opensource-packager', 'seo-specialist', 'database-reviewer',
|
|
26
|
-
'gan-planner', 'gan-generator', 'gan-evaluator', 'loop-operator',
|
|
27
|
-
'harness-optimizer', 'claude-code-guide',
|
|
28
|
-
];
|
|
29
|
-
|
|
30
|
-
const INVOKE_PATTERNS = [
|
|
31
|
-
/\buse\s+([\w-]+)/gi,
|
|
32
|
-
/@([\w-]+)/g,
|
|
33
|
-
/invoke\s+([\w-]+)/gi,
|
|
34
|
-
/delegate\s+to\s+([\w-]+)/gi,
|
|
35
|
-
/launch\s+([\w-]+)/gi,
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
function detectAgents(text) {
|
|
39
|
-
const found = new Set();
|
|
40
|
-
for (const pattern of INVOKE_PATTERNS) {
|
|
41
|
-
pattern.lastIndex = 0;
|
|
42
|
-
let match;
|
|
43
|
-
while ((match = pattern.exec(text)) !== null) {
|
|
44
|
-
const candidate = match[1].toLowerCase();
|
|
45
|
-
if (ECC_AGENTS.includes(candidate)) found.add(candidate);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return [...found];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Read hook payload from stdin (Claude Code passes JSON on stdin)
|
|
52
|
-
let raw = '';
|
|
53
|
-
process.stdin.on('data', chunk => raw += chunk);
|
|
54
|
-
process.stdin.on('end', () => {
|
|
55
|
-
try {
|
|
56
|
-
const payload = raw ? JSON.parse(raw) : {};
|
|
57
|
-
const toolName = payload.tool_name || '';
|
|
58
|
-
const toolInput = payload.tool_input || {};
|
|
59
|
-
const toolResult = payload.tool_result|| {};
|
|
60
|
-
|
|
61
|
-
// Collect all text from tool input and result
|
|
62
|
-
const text = JSON.stringify({ toolName, toolInput, toolResult });
|
|
63
|
-
const agents = detectAgents(text);
|
|
64
|
-
|
|
65
|
-
if (!agents.length) process.exit(0);
|
|
66
|
-
|
|
67
|
-
const trackingFile = path.join(os.homedir(), '.claude', 'kodelyth-agent-tracking.jsonl');
|
|
68
|
-
const now = new Date().toISOString();
|
|
69
|
-
const date = now.slice(0, 10);
|
|
70
|
-
|
|
71
|
-
const lines = agents.map(agent =>
|
|
72
|
-
JSON.stringify({ agent, timestamp: now, date, tool: toolName, source: 'hook' })
|
|
73
|
-
).join('\n') + '\n';
|
|
74
|
-
|
|
75
|
-
fs.mkdirSync(path.dirname(trackingFile), { recursive: true });
|
|
76
|
-
fs.appendFileSync(trackingFile, lines);
|
|
77
|
-
} catch (e) {
|
|
78
|
-
// Never crash — hooks must be silent
|
|
79
|
-
}
|
|
80
|
-
process.exit(0);
|
|
81
|
-
});
|
package/social/readme-lens.svg
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 360" width="900" height="360">
|
|
2
|
-
<defs>
|
|
3
|
-
<linearGradient id="klLogoGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
4
|
-
<stop offset="0%" style="stop-color:#7c3aed;stop-opacity:1" />
|
|
5
|
-
<stop offset="100%" style="stop-color:#a78bfa;stop-opacity:1" />
|
|
6
|
-
</linearGradient>
|
|
7
|
-
<filter id="cardShadow" x="-5%" y="-5%" width="110%" height="120%">
|
|
8
|
-
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#000000" flood-opacity="0.08"/>
|
|
9
|
-
</filter>
|
|
10
|
-
</defs>
|
|
11
|
-
|
|
12
|
-
<!-- White background -->
|
|
13
|
-
<rect width="900" height="360" fill="#f8fafc" rx="12"/>
|
|
14
|
-
|
|
15
|
-
<!-- TOP BAR -->
|
|
16
|
-
<rect x="0" y="0" width="900" height="52" fill="#0f172a" rx="12"/>
|
|
17
|
-
<rect x="0" y="40" width="900" height="12" fill="#0f172a"/>
|
|
18
|
-
|
|
19
|
-
<!-- KL Logo box -->
|
|
20
|
-
<rect x="16" y="10" width="34" height="32" rx="6" fill="url(#klLogoGrad)"/>
|
|
21
|
-
<text x="33" y="31" font-family="system-ui, -apple-system, sans-serif" font-size="14" font-weight="800" fill="#ffffff" text-anchor="middle">KL</text>
|
|
22
|
-
|
|
23
|
-
<!-- Kodelyth Lens text -->
|
|
24
|
-
<text x="60" y="28" font-family="system-ui, -apple-system, sans-serif" font-size="16" font-weight="700" fill="#ffffff">Kodelyth Lens</text>
|
|
25
|
-
|
|
26
|
-
<!-- v1.1 badge -->
|
|
27
|
-
<rect x="188" y="16" width="36" height="20" rx="4" fill="#7c3aed" fill-opacity="0.3" stroke="#7c3aed" stroke-opacity="0.5" stroke-width="1"/>
|
|
28
|
-
<text x="206" y="30" font-family="system-ui, -apple-system, sans-serif" font-size="10" fill="#a78bfa" text-anchor="middle" font-weight="600">v1.1</text>
|
|
29
|
-
|
|
30
|
-
<!-- Updated indicator -->
|
|
31
|
-
<circle cx="244" cy="26" r="4" fill="#10b981"/>
|
|
32
|
-
<text x="252" y="30" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#94a3b8"> Updated 03:25:55</text>
|
|
33
|
-
|
|
34
|
-
<!-- Refresh button -->
|
|
35
|
-
<rect x="806" y="14" width="68" height="24" rx="5" fill="#1e293b" stroke="#334155" stroke-width="1"/>
|
|
36
|
-
<text x="840" y="30" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#94a3b8" text-anchor="middle">⟳ Refresh</text>
|
|
37
|
-
|
|
38
|
-
<!-- STAT CARDS ROW -->
|
|
39
|
-
<!-- Card 1: Agent Calls -->
|
|
40
|
-
<rect x="14" y="64" width="164" height="76" rx="8" fill="#ffffff" filter="url(#cardShadow)" stroke="#e2e8f0" stroke-width="1"/>
|
|
41
|
-
<rect x="14" y="64" width="4" height="76" rx="2" fill="#7c3aed"/>
|
|
42
|
-
<text x="32" y="90" font-family="system-ui, -apple-system, sans-serif" font-size="24" font-weight="700" fill="#0f172a">5.2K</text>
|
|
43
|
-
<text x="32" y="108" font-family="system-ui, -apple-system, sans-serif" font-size="9" fill="#64748b" letter-spacing="1" font-weight="600">AGENT CALLS</text>
|
|
44
|
-
<text x="32" y="125" font-family="system-ui, -apple-system, sans-serif" font-size="10" fill="#a78bfa">↑ total invocations</text>
|
|
45
|
-
|
|
46
|
-
<!-- Card 2: Total Sessions -->
|
|
47
|
-
<rect x="188" y="64" width="164" height="76" rx="8" fill="#ffffff" filter="url(#cardShadow)" stroke="#e2e8f0" stroke-width="1"/>
|
|
48
|
-
<rect x="188" y="64" width="4" height="76" rx="2" fill="#3b82f6"/>
|
|
49
|
-
<text x="206" y="90" font-family="system-ui, -apple-system, sans-serif" font-size="24" font-weight="700" fill="#0f172a">41</text>
|
|
50
|
-
<text x="206" y="108" font-family="system-ui, -apple-system, sans-serif" font-size="9" fill="#64748b" letter-spacing="1" font-weight="600">TOTAL SESSIONS</text>
|
|
51
|
-
<text x="206" y="125" font-family="system-ui, -apple-system, sans-serif" font-size="10" fill="#3b82f6">↑ all time</text>
|
|
52
|
-
|
|
53
|
-
<!-- Card 3: Cache Hit Rate -->
|
|
54
|
-
<rect x="362" y="64" width="164" height="76" rx="8" fill="#ffffff" filter="url(#cardShadow)" stroke="#e2e8f0" stroke-width="1"/>
|
|
55
|
-
<rect x="362" y="64" width="4" height="76" rx="2" fill="#10b981"/>
|
|
56
|
-
<text x="380" y="90" font-family="system-ui, -apple-system, sans-serif" font-size="24" font-weight="700" fill="#0f172a">98%</text>
|
|
57
|
-
<text x="380" y="108" font-family="system-ui, -apple-system, sans-serif" font-size="9" fill="#64748b" letter-spacing="1" font-weight="600">CACHE HIT RATE</text>
|
|
58
|
-
<text x="380" y="125" font-family="system-ui, -apple-system, sans-serif" font-size="10" fill="#10b981">↑ excellent</text>
|
|
59
|
-
|
|
60
|
-
<!-- Card 4: Total Cost -->
|
|
61
|
-
<rect x="536" y="64" width="164" height="76" rx="8" fill="#ffffff" filter="url(#cardShadow)" stroke="#e2e8f0" stroke-width="1"/>
|
|
62
|
-
<rect x="536" y="64" width="4" height="76" rx="2" fill="#f59e0b"/>
|
|
63
|
-
<text x="554" y="90" font-family="system-ui, -apple-system, sans-serif" font-size="22" font-weight="700" fill="#0f172a">$596.63</text>
|
|
64
|
-
<text x="554" y="108" font-family="system-ui, -apple-system, sans-serif" font-size="9" fill="#64748b" letter-spacing="1" font-weight="600">TOTAL COST</text>
|
|
65
|
-
<text x="554" y="125" font-family="system-ui, -apple-system, sans-serif" font-size="10" fill="#f59e0b">↓ vs $3,074 baseline</text>
|
|
66
|
-
|
|
67
|
-
<!-- Card 5: Tokens Used -->
|
|
68
|
-
<rect x="710" y="64" width="176" height="76" rx="8" fill="#ffffff" filter="url(#cardShadow)" stroke="#e2e8f0" stroke-width="1"/>
|
|
69
|
-
<rect x="710" y="64" width="4" height="76" rx="2" fill="#06b6d4"/>
|
|
70
|
-
<text x="728" y="90" font-family="system-ui, -apple-system, sans-serif" font-size="22" font-weight="700" fill="#0f172a">31.7M tok</text>
|
|
71
|
-
<text x="728" y="108" font-family="system-ui, -apple-system, sans-serif" font-size="9" fill="#64748b" letter-spacing="1" font-weight="600">TOKENS USED</text>
|
|
72
|
-
<text x="728" y="125" font-family="system-ui, -apple-system, sans-serif" font-size="10" fill="#06b6d4">↑ total processed</text>
|
|
73
|
-
|
|
74
|
-
<!-- BOTTOM SECTION -->
|
|
75
|
-
<!-- Left panel: Agent Leaderboard -->
|
|
76
|
-
<rect x="14" y="156" width="540" height="190" rx="8" fill="#ffffff" filter="url(#cardShadow)" stroke="#e2e8f0" stroke-width="1"/>
|
|
77
|
-
<text x="30" y="180" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="700" fill="#0f172a">Agent Leaderboard</text>
|
|
78
|
-
<text x="530" y="180" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#94a3b8" text-anchor="end">calls</text>
|
|
79
|
-
|
|
80
|
-
<!-- Divider -->
|
|
81
|
-
<line x1="30" y1="188" x2="540" y2="188" stroke="#f1f5f9" stroke-width="1"/>
|
|
82
|
-
|
|
83
|
-
<!-- Row 1: ux-reviewer -->
|
|
84
|
-
<text x="30" y="210" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#64748b" font-weight="600">#1</text>
|
|
85
|
-
<text x="55" y="210" font-family="'Courier New', Courier, monospace" font-size="11" fill="#06b6d4" font-weight="600">ux-reviewer</text>
|
|
86
|
-
<rect x="200" y="200" width="280" height="14" rx="7" fill="#f1f5f9"/>
|
|
87
|
-
<rect x="200" y="200" width="252" height="14" rx="7" fill="#06b6d4" fill-opacity="0.7"/>
|
|
88
|
-
<text x="495" y="212" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#0f172a" text-anchor="end" font-weight="600">2.0K</text>
|
|
89
|
-
|
|
90
|
-
<!-- Row 2: database-reviewer -->
|
|
91
|
-
<text x="30" y="235" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#64748b" font-weight="600">#2</text>
|
|
92
|
-
<text x="55" y="235" font-family="'Courier New', Courier, monospace" font-size="11" fill="#3b82f6" font-weight="600">database-reviewer</text>
|
|
93
|
-
<rect x="200" y="225" width="280" height="14" rx="7" fill="#f1f5f9"/>
|
|
94
|
-
<rect x="200" y="225" width="140" height="14" rx="7" fill="#3b82f6" fill-opacity="0.7"/>
|
|
95
|
-
<text x="495" y="237" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#0f172a" text-anchor="end" font-weight="600">1.0K</text>
|
|
96
|
-
|
|
97
|
-
<!-- Row 3: security-reviewer -->
|
|
98
|
-
<text x="30" y="260" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#64748b" font-weight="600">#3</text>
|
|
99
|
-
<text x="55" y="260" font-family="'Courier New', Courier, monospace" font-size="11" fill="#ef4444" font-weight="600">security-reviewer</text>
|
|
100
|
-
<rect x="200" y="250" width="280" height="14" rx="7" fill="#f1f5f9"/>
|
|
101
|
-
<rect x="200" y="250" width="67" height="14" rx="7" fill="#ef4444" fill-opacity="0.7"/>
|
|
102
|
-
<text x="495" y="262" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#0f172a" text-anchor="end" font-weight="600">478</text>
|
|
103
|
-
|
|
104
|
-
<!-- Row 4: debug-detective -->
|
|
105
|
-
<text x="30" y="285" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#64748b" font-weight="600">#4</text>
|
|
106
|
-
<text x="55" y="285" font-family="'Courier New', Courier, monospace" font-size="11" fill="#f97316" font-weight="600">debug-detective</text>
|
|
107
|
-
<rect x="200" y="275" width="280" height="14" rx="7" fill="#f1f5f9"/>
|
|
108
|
-
<rect x="200" y="275" width="35" height="14" rx="7" fill="#f97316" fill-opacity="0.7"/>
|
|
109
|
-
<text x="495" y="287" font-family="system-ui, -apple-system, sans-serif" font-size="11" fill="#0f172a" text-anchor="end" font-weight="600">250</text>
|
|
110
|
-
|
|
111
|
-
<text x="30" y="334" font-family="system-ui, -apple-system, sans-serif" font-size="10" fill="#94a3b8">Showing top 4 of 53 agents</text>
|
|
112
|
-
|
|
113
|
-
<!-- Right panel: Platforms -->
|
|
114
|
-
<rect x="566" y="156" width="320" height="190" rx="8" fill="#ffffff" filter="url(#cardShadow)" stroke="#e2e8f0" stroke-width="1"/>
|
|
115
|
-
<text x="582" y="180" font-family="system-ui, -apple-system, sans-serif" font-size="13" font-weight="700" fill="#0f172a">Platforms</text>
|
|
116
|
-
<line x1="582" y1="188" x2="874" y2="188" stroke="#f1f5f9" stroke-width="1"/>
|
|
117
|
-
|
|
118
|
-
<!-- Platform cards 2x2 grid -->
|
|
119
|
-
<!-- Claude Code -->
|
|
120
|
-
<rect x="582" y="198" width="140" height="58" rx="6" fill="#fff7ed" stroke="#f97316" stroke-opacity="0.4" stroke-width="1.5"/>
|
|
121
|
-
<text x="592" y="218" font-family="system-ui, -apple-system, sans-serif" font-size="11" font-weight="700" fill="#ea580c">Claude Code</text>
|
|
122
|
-
<text x="592" y="237" font-family="system-ui, -apple-system, sans-serif" font-size="22" font-weight="800" fill="#f97316">33</text>
|
|
123
|
-
<text x="712" y="237" font-family="system-ui, -apple-system, sans-serif" font-size="10" fill="#9a3412" text-anchor="end">sessions</text>
|
|
124
|
-
|
|
125
|
-
<!-- Codex CLI -->
|
|
126
|
-
<rect x="732" y="198" width="130" height="58" rx="6" fill="#f0fdf4" stroke="#22c55e" stroke-opacity="0.4" stroke-width="1.5"/>
|
|
127
|
-
<text x="742" y="218" font-family="system-ui, -apple-system, sans-serif" font-size="11" font-weight="700" fill="#16a34a">Codex CLI</text>
|
|
128
|
-
<text x="742" y="237" font-family="system-ui, -apple-system, sans-serif" font-size="22" font-weight="800" fill="#22c55e">3</text>
|
|
129
|
-
<text x="852" y="237" font-family="system-ui, -apple-system, sans-serif" font-size="10" fill="#166534" text-anchor="end">sessions</text>
|
|
130
|
-
|
|
131
|
-
<!-- Windsurf -->
|
|
132
|
-
<rect x="582" y="266" width="140" height="58" rx="6" fill="#ecfeff" stroke="#06b6d4" stroke-opacity="0.4" stroke-width="1.5"/>
|
|
133
|
-
<text x="592" y="286" font-family="system-ui, -apple-system, sans-serif" font-size="11" font-weight="700" fill="#0891b2">Windsurf ☁</text>
|
|
134
|
-
<text x="592" y="307" font-family="system-ui, -apple-system, sans-serif" font-size="18" font-weight="800" fill="#06b6d4">Active</text>
|
|
135
|
-
|
|
136
|
-
<!-- Antigravity -->
|
|
137
|
-
<rect x="732" y="266" width="130" height="58" rx="6" fill="#faf5ff" stroke="#a78bfa" stroke-opacity="0.4" stroke-width="1.5"/>
|
|
138
|
-
<text x="742" y="286" font-family="system-ui, -apple-system, sans-serif" font-size="11" font-weight="700" fill="#7c3aed">Antigravity</text>
|
|
139
|
-
<text x="742" y="305" font-family="system-ui, -apple-system, sans-serif" font-size="18" font-weight="800" fill="#a78bfa">5 proj</text>
|
|
140
|
-
</svg>
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
<svg width="900" height="280" viewBox="0 0 900 280" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif">
|
|
2
|
-
<defs>
|
|
3
|
-
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
|
4
|
-
<stop offset="0%" stop-color="#f0fdf4"/>
|
|
5
|
-
<stop offset="100%" stop-color="#ede9fe"/>
|
|
6
|
-
</linearGradient>
|
|
7
|
-
<linearGradient id="bar" x1="0" y1="0" x2="1" y2="0">
|
|
8
|
-
<stop offset="0%" stop-color="#7c3aed"/>
|
|
9
|
-
<stop offset="100%" stop-color="#059669"/>
|
|
10
|
-
</linearGradient>
|
|
11
|
-
<linearGradient id="barBg" x1="0" y1="0" x2="1" y2="0">
|
|
12
|
-
<stop offset="0%" stop-color="#dc2626" stop-opacity=".15"/>
|
|
13
|
-
<stop offset="100%" stop-color="#dc2626" stop-opacity=".08"/>
|
|
14
|
-
</linearGradient>
|
|
15
|
-
<filter id="shadow">
|
|
16
|
-
<feDropShadow dx="0" dy="4" stdDeviation="12" flood-opacity=".12"/>
|
|
17
|
-
</filter>
|
|
18
|
-
</defs>
|
|
19
|
-
|
|
20
|
-
<!-- Background -->
|
|
21
|
-
<rect width="900" height="280" rx="16" fill="url(#bg)"/>
|
|
22
|
-
<rect width="900" height="280" rx="16" fill="none" stroke="#e2e8f0" stroke-width="1"/>
|
|
23
|
-
|
|
24
|
-
<!-- Header -->
|
|
25
|
-
<text x="44" y="52" font-size="13" font-weight="600" fill="#7c3aed" letter-spacing=".5">KODELYTH ECC — COST SAVINGS</text>
|
|
26
|
-
<text x="44" y="80" font-size="28" font-weight="800" fill="#0f172a" letter-spacing="-1">Prompt caching cuts your AI bill by ~40%</text>
|
|
27
|
-
|
|
28
|
-
<!-- Three metric cards -->
|
|
29
|
-
<!-- Without ECC -->
|
|
30
|
-
<rect x="44" y="108" width="240" height="100" rx="12" fill="#fff" filter="url(#shadow)"/>
|
|
31
|
-
<rect x="44" y="108" width="240" height="4" rx="2" fill="#dc2626"/>
|
|
32
|
-
<text x="64" y="138" font-size="11" font-weight="600" fill="#94a3b8" letter-spacing=".6">WITHOUT ECC (est.)</text>
|
|
33
|
-
<text x="64" y="172" font-size="32" font-weight="800" fill="#dc2626" letter-spacing="-1.5">$143.26</text>
|
|
34
|
-
<text x="64" y="193" font-size="12" fill="#94a3b8">full prompt cost every session</text>
|
|
35
|
-
|
|
36
|
-
<!-- With ECC -->
|
|
37
|
-
<rect x="306" y="108" width="240" height="100" rx="12" fill="#fff" filter="url(#shadow)"/>
|
|
38
|
-
<rect x="306" y="108" width="240" height="4" rx="2" fill="#059669"/>
|
|
39
|
-
<text x="326" y="138" font-size="11" font-weight="600" fill="#94a3b8" letter-spacing=".6">WITH ECC (actual)</text>
|
|
40
|
-
<text x="326" y="172" font-size="32" font-weight="800" fill="#059669" letter-spacing="-1.5">$83.24</text>
|
|
41
|
-
<text x="326" y="193" font-size="12" fill="#94a3b8">cache read tokens billed at 10%</text>
|
|
42
|
-
|
|
43
|
-
<!-- Saved -->
|
|
44
|
-
<rect x="568" y="108" width="288" height="100" rx="12" fill="#fff" filter="url(#shadow)"/>
|
|
45
|
-
<rect x="568" y="108" width="288" height="4" rx="2" fill="#7c3aed"/>
|
|
46
|
-
<text x="588" y="138" font-size="11" font-weight="600" fill="#94a3b8" letter-spacing=".6">YOU SAVED</text>
|
|
47
|
-
<text x="588" y="172" font-size="32" font-weight="800" fill="#7c3aed" letter-spacing="-1.5">$60.02</text>
|
|
48
|
-
<text x="588" y="193" font-size="12" fill="#94a3b8">41.9% reduction · 68% cache hit rate</text>
|
|
49
|
-
|
|
50
|
-
<!-- Progress bar -->
|
|
51
|
-
<rect x="44" y="228" width="812" height="18" rx="9" fill="url(#barBg)"/>
|
|
52
|
-
<rect x="44" y="228" width="340" height="18" rx="9" fill="url(#bar)"/>
|
|
53
|
-
<text x="44" y="261" font-size="11" fill="#dc2626">$83.24 paid</text>
|
|
54
|
-
<text x="440" y="261" font-size="11" fill="#7c3aed" font-weight="700" text-anchor="middle">68% cache hit rate</text>
|
|
55
|
-
<text x="856" y="261" font-size="11" fill="#059669" text-anchor="end">$60.02 saved</text>
|
|
56
|
-
</svg>
|