kodelyth-ecc 1.7.5 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +16 -3
  2. package/CLAUDE.md +1 -1
  3. package/README.md +8 -9
  4. package/SECURITY.md +5 -6
  5. package/bundles/enterprise.md +1 -1
  6. package/bundles/indie-hacker.md +1 -1
  7. package/bundles/red-team.md +1 -1
  8. package/hooks/hooks.json +13 -0
  9. package/hooks/memory/auto-resolve.js +68 -0
  10. package/hooks/memory/capture-correction.js +16 -0
  11. package/hooks/memory/capture-stop.js +70 -14
  12. package/package.json +1 -1
  13. package/rules/common/agents.md +1 -1
  14. package/rules/common/self-improvement-workflow.md +2 -2
  15. package/scripts/dashboard/data.js +1 -1
  16. package/scripts/dashboard/server.js +1 -1
  17. package/scripts/memory/instincts.js +282 -0
  18. package/scripts/memory/store.js +104 -0
  19. package/social/card-agents.svg +1 -1
  20. package/social/card-install.svg +1 -1
  21. package/social/card-main.svg +1 -1
  22. package/social/facebook-v150.svg +14 -13
  23. package/social/fb-ad-main.svg +128 -0
  24. package/social/fb-post-features.svg +118 -0
  25. package/social/fb-post-launch.svg +144 -0
  26. package/social/fb-post-platforms.svg +135 -0
  27. package/social/github-social-preview.svg +119 -28
  28. package/social/hype-compound-learning.svg +9 -9
  29. package/social/hype-devil-mode.svg +8 -8
  30. package/social/hype-mcp-server.svg +8 -8
  31. package/social/hype-parallel-agents.svg +8 -8
  32. package/social/hype-stats-hero.svg +3 -3
  33. package/social/og-image.svg +116 -30
  34. package/social/readme-hero.svg +4 -4
  35. package/social/section-mcp.svg +1 -1
  36. package/social/twitter-threads.md +3 -3
  37. package/social/x-card-agents-grid.svg +2 -2
  38. package/social/x-card-free.svg +2 -2
  39. package/social/x-card-hook.svg +5 -5
  40. package/tests/memory/instincts.test.js +258 -0
  41. package/tests/memory/store.test.js +82 -0
  42. package/wiki/FAQ.md +1 -1
  43. package/wiki/Home.md +3 -3
  44. package/wiki/Installation-Guide.md +2 -2
@@ -0,0 +1,282 @@
1
+ // =============================================================================
2
+ // Kodelyth ECC — Structured Instinct Schema (Improvement B)
3
+ //
4
+ // Stores learned instincts as typed JSON records in:
5
+ // ~/.kodelyth/memory/instincts.jsonl
6
+ //
7
+ // Each instinct has: pattern, trigger, confidence, last_used, outcome, decay.
8
+ // This replaces free-form markdown bullets for machine-readable learning.
9
+ // The lessons.md file is still written for human readability — this runs
10
+ // in parallel, enabling deduplication, conflict detection, and decay.
11
+ //
12
+ // Schema:
13
+ // id — deterministic hash (stable across re-runs)
14
+ // pattern — short slug: "use-pnpm-not-npm", "no-console-logs"
15
+ // rule — full human-readable text of the rule
16
+ // scope — "project" | "global"
17
+ // trigger — keywords/phrases that activate this instinct
18
+ // confidence — 0.0–1.0 (starts at 0.7, confirmed → 1.0, decays with age)
19
+ // source — "correction" | "manual" | "evolved"
20
+ // created — ISO date
21
+ // last_used — ISO date
22
+ // use_count — how many sessions this was applied
23
+ // outcome — null | "success" | "failure"
24
+ // project_path — absolute path of origin project
25
+ // stale — true if unused for 30+ days (set by decay check)
26
+ // =============================================================================
27
+
28
+ 'use strict';
29
+
30
+ const fs = require('fs');
31
+ const os = require('os');
32
+ const path = require('path');
33
+ const crypto = require('crypto');
34
+
35
+ const MEMORY_DIR = process.env.KODELYTH_MEMORY_DIR
36
+ || path.join(os.homedir(), '.kodelyth', 'memory');
37
+
38
+ const INSTINCTS_FILE = path.join(MEMORY_DIR, 'instincts.jsonl');
39
+ const STALE_DAYS = 30;
40
+
41
+ // ── Helpers ──────────────────────────────────────────────────────────────────
42
+
43
+ function ensureDir() {
44
+ if (!fs.existsSync(MEMORY_DIR)) fs.mkdirSync(MEMORY_DIR, { recursive: true });
45
+ }
46
+
47
+ function slugify(text) {
48
+ return String(text || '')
49
+ .toLowerCase()
50
+ .replace(/[^a-z0-9]+/g, '-')
51
+ .replace(/^-+|-+$/g, '')
52
+ .slice(0, 60) || 'rule';
53
+ }
54
+
55
+ function deterministicId(rule, projectPath) {
56
+ return crypto
57
+ .createHash('sha256')
58
+ .update(`${rule}::${projectPath || ''}`)
59
+ .digest('hex')
60
+ .slice(0, 12);
61
+ }
62
+
63
+ function extractTriggers(ruleText) {
64
+ // Heuristic: extract key words that would activate this rule
65
+ const FILLER = new Set(['use','don\'t','never','always','please','stop','avoid',
66
+ 'instead','of','not','the','a','an','and','or','in','on','at','to','is',
67
+ 'are','was','were','do','does','did','should','would','could']);
68
+ return ruleText
69
+ .toLowerCase()
70
+ .replace(/[^a-z0-9\s]/g, ' ')
71
+ .split(/\s+/)
72
+ .filter(w => w.length >= 3 && !FILLER.has(w))
73
+ .slice(0, 6);
74
+ }
75
+
76
+ function today() {
77
+ return new Date().toISOString().split('T')[0];
78
+ }
79
+
80
+ function daysSince(isoDate) {
81
+ if (!isoDate) return 999;
82
+ return Math.floor((Date.now() - new Date(isoDate).getTime()) / 86400000);
83
+ }
84
+
85
+ // ── Read / Write ──────────────────────────────────────────────────────────────
86
+
87
+ function loadAll() {
88
+ if (!fs.existsSync(INSTINCTS_FILE)) return [];
89
+ return fs.readFileSync(INSTINCTS_FILE, 'utf8')
90
+ .split('\n')
91
+ .filter(Boolean)
92
+ .map(line => { try { return JSON.parse(line); } catch { return null; } })
93
+ .filter(Boolean);
94
+ }
95
+
96
+ function saveAll(instincts) {
97
+ ensureDir();
98
+ fs.writeFileSync(
99
+ INSTINCTS_FILE,
100
+ instincts.map(i => JSON.stringify(i)).join('\n') + '\n',
101
+ 'utf8'
102
+ );
103
+ }
104
+
105
+ // ── Public API ────────────────────────────────────────────────────────────────
106
+
107
+ /**
108
+ * Add a new instinct from a correction string.
109
+ * If a record with the same id already exists, increments use_count.
110
+ * Returns the instinct record.
111
+ */
112
+ function captureFromCorrection(ruleText, projectPath, scope = 'project') {
113
+ ensureDir();
114
+ const id = deterministicId(ruleText, projectPath);
115
+ const all = loadAll();
116
+
117
+ const existing = all.find(i => i.id === id);
118
+ if (existing) {
119
+ existing.use_count = (existing.use_count || 1) + 1;
120
+ existing.last_used = today();
121
+ existing.confidence = Math.min(1.0, (existing.confidence || 0.7) + 0.1);
122
+ existing.stale = false;
123
+ saveAll(all);
124
+ return existing;
125
+ }
126
+
127
+ const instinct = {
128
+ id,
129
+ pattern: slugify(ruleText),
130
+ rule: ruleText.trim(),
131
+ scope,
132
+ trigger: extractTriggers(ruleText),
133
+ confidence: 0.7,
134
+ source: 'correction',
135
+ created: today(),
136
+ last_used: today(),
137
+ use_count: 1,
138
+ outcome: null,
139
+ project_path: projectPath || null,
140
+ stale: false,
141
+ };
142
+
143
+ fs.appendFileSync(INSTINCTS_FILE, JSON.stringify(instinct) + '\n', 'utf8');
144
+ return instinct;
145
+ }
146
+
147
+ /**
148
+ * Record the outcome of an instinct (success/failure) by id.
149
+ * A failure reduces confidence; a success boosts it.
150
+ */
151
+ function recordOutcome(id, outcome) {
152
+ const all = loadAll();
153
+ const instinct = all.find(i => i.id === id);
154
+ if (!instinct) return null;
155
+
156
+ instinct.outcome = outcome;
157
+ instinct.last_used = today();
158
+
159
+ if (outcome === 'success') {
160
+ instinct.confidence = Math.min(1.0, (instinct.confidence || 0.7) + 0.15);
161
+ } else if (outcome === 'failure') {
162
+ instinct.confidence = Math.max(0.0, (instinct.confidence || 0.7) - 0.3);
163
+ }
164
+
165
+ saveAll(all);
166
+ return instinct;
167
+ }
168
+
169
+ /**
170
+ * Decay check (Improvement D).
171
+ * Marks instincts unused for STALE_DAYS+ as stale: true.
172
+ * Returns list of newly-stale instincts for review prompting.
173
+ */
174
+ function runDecayCheck() {
175
+ const all = loadAll();
176
+ const newlyStale = [];
177
+
178
+ for (const instinct of all) {
179
+ const age = daysSince(instinct.last_used);
180
+ const wasStale = instinct.stale;
181
+ instinct.stale = age >= STALE_DAYS;
182
+ if (instinct.stale && !wasStale) {
183
+ newlyStale.push(instinct);
184
+ }
185
+ }
186
+
187
+ if (newlyStale.length > 0) saveAll(all);
188
+ return newlyStale;
189
+ }
190
+
191
+ /**
192
+ * Return all instincts, optionally filtered.
193
+ * Filters: { scope, stale, minConfidence, projectPath }
194
+ */
195
+ function list({ scope, stale, minConfidence, projectPath } = {}) {
196
+ let all = loadAll();
197
+ if (scope !== undefined) all = all.filter(i => i.scope === scope);
198
+ if (stale !== undefined) all = all.filter(i => Boolean(i.stale) === stale);
199
+ if (minConfidence !== undefined) all = all.filter(i => (i.confidence || 0) >= minConfidence);
200
+ if (projectPath !== undefined) all = all.filter(i => i.project_path === projectPath);
201
+ return all;
202
+ }
203
+
204
+ /**
205
+ * Prune instincts with confidence below threshold (auto-cleanup of bad patterns).
206
+ */
207
+ function pruneWeak(threshold = 0.2) {
208
+ const all = loadAll();
209
+ const kept = all.filter(i => (i.confidence || 0.7) >= threshold);
210
+ const pruned = all.length - kept.length;
211
+ if (pruned > 0) saveAll(kept);
212
+ return pruned;
213
+ }
214
+
215
+ /**
216
+ * Improvement C: outcome tracking via store.js bridge.
217
+ *
218
+ * Called when a follow-up file edit implies a previous instinct didn't stick.
219
+ * Finds instincts that match both the project_path AND a fragment of the
220
+ * problem text, then records a failure outcome on each match.
221
+ *
222
+ * This is intentionally fuzzy: a partial match (>20% token overlap between
223
+ * problemHint and instinct.rule) is enough to trigger a downgrade. The goal
224
+ * is to surface bad patterns, not to be surgically precise.
225
+ *
226
+ * Returns the list of downgraded instinct ids.
227
+ */
228
+ function recordOutcomeByProject(projectPath, problemHint, outcome) {
229
+ if (!projectPath && !problemHint) return [];
230
+ const all = loadAll();
231
+ const changed = [];
232
+
233
+ const hintTokens = problemHint
234
+ ? new Set(
235
+ String(problemHint)
236
+ .toLowerCase()
237
+ .replace(/[^a-z0-9\s]/g, ' ')
238
+ .split(/\s+/)
239
+ .filter(t => t.length >= 3)
240
+ )
241
+ : null;
242
+
243
+ for (const instinct of all) {
244
+ // Project match (when provided)
245
+ if (projectPath && instinct.project_path && instinct.project_path !== projectPath) continue;
246
+
247
+ // Problem text fuzzy match (when provided)
248
+ if (hintTokens && hintTokens.size > 0) {
249
+ const ruleTokens = String(instinct.rule || '')
250
+ .toLowerCase()
251
+ .replace(/[^a-z0-9\s]/g, ' ')
252
+ .split(/\s+/)
253
+ .filter(t => t.length >= 3);
254
+ const overlap = ruleTokens.filter(t => hintTokens.has(t)).length;
255
+ const ratio = overlap / Math.max(hintTokens.size, 1);
256
+ if (ratio < 0.2) continue; // < 20% overlap — not a match
257
+ }
258
+
259
+ instinct.outcome = outcome;
260
+ instinct.last_used = today();
261
+ if (outcome === 'success') {
262
+ instinct.confidence = Math.min(1.0, (instinct.confidence || 0.7) + 0.15);
263
+ } else if (outcome === false || outcome === 'failure') {
264
+ instinct.confidence = Math.max(0.0, (instinct.confidence || 0.7) - 0.3);
265
+ }
266
+ changed.push(instinct.id);
267
+ }
268
+
269
+ if (changed.length > 0) saveAll(all);
270
+ return changed;
271
+ }
272
+
273
+ module.exports = {
274
+ captureFromCorrection,
275
+ recordOutcome,
276
+ recordOutcomeByProject,
277
+ runDecayCheck,
278
+ list,
279
+ pruneWeak,
280
+ INSTINCTS_FILE,
281
+ STALE_DAYS,
282
+ };
@@ -257,6 +257,106 @@ function forget(memoryId) {
257
257
  return found;
258
258
  }
259
259
 
260
+ // ── Improvement C: outcome tracking ──────────────────────────────────────────
261
+ // Mark a memory as resolved:true (success) or resolved:false (failure).
262
+ // Called automatically by the PostToolUse hook when an edited file matches
263
+ // a memory's files[] list — a follow-up fix implies the previous approach
264
+ // didn't fully work, so resolved=false. The user can also call this manually.
265
+ //
266
+ // Side-effect: also propagates to instincts.js if the instinct module exists,
267
+ // so low-confidence instincts sourced from the same session get downgraded.
268
+ function resolveMemory(memoryId, resolved) {
269
+ if (!fs.existsSync(PATHS.log)) return false;
270
+ const lines = fs.readFileSync(PATHS.log, 'utf8').split('\n').filter(Boolean);
271
+ let found = false;
272
+ const updated = lines.map(line => {
273
+ try {
274
+ const m = JSON.parse(line);
275
+ if (m.id === memoryId && !m.deleted) {
276
+ found = true;
277
+ return JSON.stringify({
278
+ ...m,
279
+ resolved,
280
+ resolved_at: new Date().toISOString(),
281
+ });
282
+ }
283
+ return line;
284
+ } catch {
285
+ return line;
286
+ }
287
+ });
288
+ if (found) {
289
+ fs.writeFileSync(PATHS.log, updated.join('\n') + '\n');
290
+ }
291
+ return found;
292
+ }
293
+
294
+ // Find unresolved memories whose files[] overlap with the given file path.
295
+ // Returns [ { id, problem, files, captured_at } ] — caller decides what to do.
296
+ function findMemoriesForFile(filePath, options = {}) {
297
+ const { projectRoot = null, limit = 5 } = options;
298
+ if (!fs.existsSync(PATHS.log)) return [];
299
+
300
+ const normFile = path.normalize(filePath);
301
+ const lines = fs.readFileSync(PATHS.log, 'utf8').split('\n').filter(Boolean);
302
+ const matches = [];
303
+
304
+ for (const line of lines) {
305
+ let m;
306
+ try { m = JSON.parse(line); } catch { continue; }
307
+ if (m.deleted || m.resolved !== undefined) continue; // skip already resolved
308
+ if (!Array.isArray(m.files) || m.files.length === 0) continue;
309
+ if (projectRoot && m.project_path && m.project_path !== projectRoot) continue;
310
+
311
+ const hit = m.files.some(f => {
312
+ const normF = path.normalize(String(f));
313
+ return normF === normFile || normFile.endsWith(normF) || normF.endsWith(normFile);
314
+ });
315
+
316
+ if (hit) {
317
+ matches.push({
318
+ id: m.id,
319
+ problem: m.problem,
320
+ approach: m.approach,
321
+ files: m.files,
322
+ captured_at: m.captured_at,
323
+ project_path: m.project_path,
324
+ });
325
+ if (matches.length >= limit) break;
326
+ }
327
+ }
328
+
329
+ return matches;
330
+ }
331
+
332
+ // Auto-resolve hook: called by PostToolUse (Write|Edit) with the file just edited.
333
+ // Marks any unresolved memory that listed this file as resolved:false, then
334
+ // nudges the instinct schema to downgrade the matching instinct's confidence.
335
+ function autoResolveOnEdit(filePath, projectRoot = null) {
336
+ const affected = findMemoriesForFile(filePath, { projectRoot });
337
+ if (affected.length === 0) return [];
338
+
339
+ const resolved = [];
340
+ for (const m of affected) {
341
+ const ok = resolveMemory(m.id, false);
342
+ if (ok) resolved.push(m);
343
+ }
344
+
345
+ // Propagate to instinct schema — best-effort only
346
+ try {
347
+ const instinctsPath = path.join(__dirname, 'instincts.js');
348
+ if (fs.existsSync(instinctsPath)) {
349
+ const instincts = require(instinctsPath);
350
+ for (const m of resolved) {
351
+ // Match by project_path + approximate problem text
352
+ instincts.recordOutcomeByProject(m.project_path, m.problem, false);
353
+ }
354
+ }
355
+ } catch { /* never block */ }
356
+
357
+ return resolved;
358
+ }
359
+
260
360
  function rebuildIndex() {
261
361
  const memories = readMemories();
262
362
  let index = { tokens: {}, docCount: 0, avgDocLength: 0, totalLength: 0 };
@@ -297,4 +397,8 @@ module.exports = {
297
397
  stats,
298
398
  tokenise,
299
399
  projectHash,
400
+ // Improvement C
401
+ resolveMemory,
402
+ findMemoriesForFile,
403
+ autoResolveOnEdit,
300
404
  };
@@ -14,7 +14,7 @@
14
14
  <rect width="700" height="420" fill="none" stroke="#21262d" stroke-width="1" rx="12"/>
15
15
 
16
16
  <!-- Header -->
17
- <text x="28" y="42" font-size="16" font-weight="800" fill="#ffffff">62 Agents — The Complete Roster</text>
17
+ <text x="28" y="42" font-size="16" font-weight="800" fill="#ffffff">70 Agents — The Complete Roster</text>
18
18
  <rect x="28" y="54" width="644" height="1" fill="#21262d"/>
19
19
 
20
20
  <!-- Row 1 -->
@@ -37,7 +37,7 @@
37
37
  <text x="48" y="200" font-family="'Courier New', Courier, monospace" font-size="12" fill="#e6edf3"> npx kodelyth-ecc --target windsurf-project</text>
38
38
  <text x="48" y="216" font-family="'Courier New', Courier, monospace" font-size="11" fill="#484f58"> # or Antigravity:</text>
39
39
  <text x="48" y="232" font-family="'Courier New', Courier, monospace" font-size="12" fill="#e6edf3"> npx kodelyth-ecc --target antigravity</text>
40
- <text x="48" y="256" font-family="'Courier New', Courier, monospace" font-size="11" fill="#10b981"> Installing v1.7.5 — 70 agents, 194 skills, 97 commands...</text>
40
+ <text x="48" y="256" font-family="'Courier New', Courier, monospace" font-size="11" fill="#10b981"> Installing v1.8.0 — 70 agents, 194 skills, 97 commands...</text>
41
41
  <text x="48" y="292" font-family="'Courier New', Courier, monospace" font-size="11" fill="#10b981"> Done. Type /kodelyth-quickstart to begin.</text>
42
42
 
43
43
  <!-- Footer -->
@@ -17,7 +17,7 @@
17
17
 
18
18
  <!-- Badge -->
19
19
  <rect x="28" y="24" width="72" height="20" rx="10" fill="#a78bfa" fill-opacity="0.15" stroke="#a78bfa" stroke-opacity="0.4" stroke-width="1"/>
20
- <text x="64" y="38" font-size="9" fill="#a78bfa" text-anchor="middle" font-weight="700" letter-spacing="0.5">v1.7.5</text>
20
+ <text x="64" y="38" font-size="9" fill="#a78bfa" text-anchor="middle" font-weight="700" letter-spacing="0.5">v1.8.0</text>
21
21
 
22
22
  <!-- Title -->
23
23
  <text x="28" y="88" font-size="38" font-weight="900" letter-spacing="-1.5" fill="#ffffff">Kodelyth</text>
@@ -38,7 +38,7 @@
38
38
 
39
39
  <!-- Version + NEW badges -->
40
40
  <rect x="80" y="28" width="76" height="24" rx="12" fill="#a78bfa" fill-opacity="0.15" stroke="#a78bfa" stroke-opacity="0.5" stroke-width="1"/>
41
- <text x="118" y="44" font-size="11" fill="#a78bfa" text-anchor="middle" font-weight="700" letter-spacing="0.5">v1.7.5</text>
41
+ <text x="118" y="44" font-size="11" fill="#a78bfa" text-anchor="middle" font-weight="700" letter-spacing="0.5">v1.8.0</text>
42
42
 
43
43
  <!-- Title -->
44
44
  <text x="80" y="120" font-size="52" font-weight="900" letter-spacing="-2" fill="#ffffff">Kodelyth ECC</text>
@@ -50,7 +50,7 @@
50
50
  <!-- Card 1: Compound Learning -->
51
51
  <rect x="80" y="250" width="315" height="120" rx="10" fill="url(#card1)" stroke="#059669" stroke-opacity="0.35" stroke-width="1"/>
52
52
  <rect x="80" y="250" width="4" height="120" rx="2" fill="#059669"/>
53
- <text x="100" y="275" font-size="10" font-weight="700" fill="#10b981" letter-spacing="1">COMPOUND LEARNING v1.7.5</text>
53
+ <text x="100" y="275" font-size="10" font-weight="700" fill="#10b981" letter-spacing="1">COMPOUND LEARNING v1.8.0</text>
54
54
  <text x="100" y="296" font-size="13" fill="#e6edf3">Corrections → tasks/lessons.md</text>
55
55
  <text x="100" y="313" font-size="13" fill="#e6edf3">Next session: never repeats the mistake</text>
56
56
  <text x="100" y="334" font-size="11" fill="#484f58">capture-correction.js · read-lessons.js</text>
@@ -59,7 +59,7 @@
59
59
  <!-- Card 2: Parallel Agents -->
60
60
  <rect x="410" y="250" width="315" height="120" rx="10" fill="#161b22" stroke="#f59e0b" stroke-opacity="0.35" stroke-width="1"/>
61
61
  <rect x="410" y="250" width="4" height="120" rx="2" fill="#f59e0b"/>
62
- <text x="430" y="275" font-size="10" font-weight="700" fill="#f59e0b" letter-spacing="1">PARALLEL AGENTS v1.7.5</text>
62
+ <text x="430" y="275" font-size="10" font-weight="700" fill="#f59e0b" letter-spacing="1">PARALLEL AGENTS v1.8.0</text>
63
63
  <text x="430" y="296" font-size="13" fill="#e6edf3">/project-launch → 5 agents, 10 min</text>
64
64
  <text x="430" y="313" font-size="13" fill="#e6edf3">/team-review → 4 agents, 15 min</text>
65
65
  <text x="430" y="334" font-size="11" fill="#484f58">image-architect: Gemini / DALL-E / SVG</text>
@@ -68,7 +68,7 @@
68
68
  <!-- Card 3: Semantic Routing -->
69
69
  <rect x="740" y="250" width="380" height="120" rx="10" fill="url(#card2)" stroke="#38bdf8" stroke-opacity="0.4" stroke-width="1"/>
70
70
  <rect x="740" y="250" width="4" height="120" rx="2" fill="#38bdf8"/>
71
- <text x="760" y="275" font-size="10" font-weight="700" fill="#38bdf8" letter-spacing="1">SEMANTIC ROUTING v1.7.5</text>
71
+ <text x="760" y="275" font-size="10" font-weight="700" fill="#38bdf8" letter-spacing="1">SEMANTIC ROUTING v1.8.0</text>
72
72
  <text x="760" y="296" font-size="13" fill="#e6edf3">Paste code → auto-routes to code-reviewer</text>
73
73
  <text x="760" y="313" font-size="13" fill="#e6edf3">Paste error → auto-routes to debug-detective</text>
74
74
  <text x="760" y="334" font-size="11" fill="#484f58">Emotion as signal · 2–3× more patterns</text>
@@ -76,15 +76,16 @@
76
76
 
77
77
  <line x1="80" y1="392" x2="1120" y2="392" stroke="#21262d" stroke-width="1"/>
78
78
 
79
- <!-- Stats -->
80
- <text x="540" y="438" font-size="68" font-weight="900" fill="#ffffff" text-anchor="middle">70</text>
81
- <text x="540" y="460" font-size="10" fill="#484f58" text-anchor="middle" letter-spacing="3">SPECIALIST AGENTS</text>
82
- <line x1="456" y1="410" x2="456" y2="472" stroke="#21262d" stroke-width="1"/>
83
- <line x1="624" y1="410" x2="624" y2="472" stroke="#21262d" stroke-width="1"/>
84
- <text x="368" y="438" font-size="34" font-weight="800" fill="#ffffff" text-anchor="middle">194</text>
85
- <text x="368" y="458" font-size="10" fill="#484f58" text-anchor="middle" letter-spacing="2">SKILLS</text>
86
- <text x="712" y="438" font-size="34" font-weight="800" fill="#ffffff" text-anchor="middle">97</text>
87
- <text x="712" y="458" font-size="10" fill="#484f58" text-anchor="middle" letter-spacing="2">COMMANDS</text>
79
+ <!-- Stats — "70" baseline lowered so cap-top sits below the separator line -->
80
+ <text x="540" y="447" font-size="68" font-weight="900" fill="#ffffff" text-anchor="middle">70</text>
81
+ <text x="540" y="468" font-size="10" fill="#484f58" text-anchor="middle" letter-spacing="3">SPECIALIST AGENTS</text>
82
+ <!-- Divider lines extended up to y=394 to fully contain the tall "70" numeral -->
83
+ <line x1="456" y1="394" x2="456" y2="474" stroke="#21262d" stroke-width="1"/>
84
+ <line x1="624" y1="394" x2="624" y2="474" stroke="#21262d" stroke-width="1"/>
85
+ <text x="368" y="444" font-size="34" font-weight="800" fill="#ffffff" text-anchor="middle">194</text>
86
+ <text x="368" y="464" font-size="10" fill="#484f58" text-anchor="middle" letter-spacing="2">SKILLS</text>
87
+ <text x="712" y="444" font-size="34" font-weight="800" fill="#ffffff" text-anchor="middle">97</text>
88
+ <text x="712" y="464" font-size="10" fill="#484f58" text-anchor="middle" letter-spacing="2">COMMANDS</text>
88
89
 
89
90
  <!-- Benefit pills -->
90
91
  <rect x="80" y="490" width="220" height="50" rx="8" fill="#161b22" stroke="#30363d" stroke-width="1"/>
@@ -0,0 +1,128 @@
1
+ <svg width="1200" height="628" viewBox="0 0 1200 628" xmlns="http://www.w3.org/2000/svg" font-family="'Segoe UI', system-ui, -apple-system, sans-serif">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#050811"/>
5
+ <stop offset="100%" style="stop-color:#0d1117"/>
6
+ </linearGradient>
7
+ <linearGradient id="accent" x1="0%" y1="0%" x2="100%" y2="0%">
8
+ <stop offset="0%" style="stop-color:#a78bfa"/>
9
+ <stop offset="100%" style="stop-color:#38bdf8"/>
10
+ </linearGradient>
11
+ <linearGradient id="ctaGrad" x1="0%" y1="0%" x2="100%" y2="0%">
12
+ <stop offset="0%" style="stop-color:#7c3aed"/>
13
+ <stop offset="100%" style="stop-color:#0891b2"/>
14
+ </linearGradient>
15
+ </defs>
16
+
17
+ <rect width="1200" height="628" fill="url(#bg)"/>
18
+
19
+ <!-- Ambient glow -->
20
+ <circle cx="1100" cy="80" r="280" fill="#a78bfa" opacity="0.04"/>
21
+ <circle cx="100" cy="550" r="220" fill="#38bdf8" opacity="0.03"/>
22
+
23
+ <!-- Ghost Depth Mark — right-side watermark, scale 0.55, op=0.17 -->
24
+ <g transform="translate(1044, 213) scale(0.55)" opacity="0.17">
25
+ <polyline points="-44,4 12,100 -44,196"
26
+ stroke="#ffffff" stroke-width="10"
27
+ stroke-linecap="round" stroke-linejoin="round"
28
+ fill="none"/>
29
+ <polyline points="0,0 88,100 0,200"
30
+ stroke="#ffffff" stroke-width="32"
31
+ stroke-linecap="round" stroke-linejoin="round"
32
+ fill="none"/>
33
+ </g>
34
+
35
+ <!-- Left vertical accent bar -->
36
+ <rect x="80" y="80" width="4" height="468" rx="2" fill="url(#accent)" opacity="0.6"/>
37
+
38
+ <!-- Category badge -->
39
+ <rect x="102" y="80" width="160" height="26" rx="13" fill="#a78bfa" fill-opacity="0.12" stroke="#a78bfa" stroke-opacity="0.4" stroke-width="1"/>
40
+ <text x="182" y="97" font-size="10" fill="#a78bfa" text-anchor="middle" font-weight="700" letter-spacing="1.5">AI CODING TOOLKIT</text>
41
+
42
+ <!-- Main headline -->
43
+ <text x="102" y="175" font-size="64" font-weight="900" letter-spacing="-2.5" fill="#ffffff">Build 10x faster</text>
44
+ <text x="102" y="250" font-size="64" font-weight="900" letter-spacing="-2.5" fill="url(#accent)">with every session.</text>
45
+
46
+ <!-- Sub-headline -->
47
+ <text x="102" y="305" font-size="20" font-weight="400" fill="#9ca3af">70 specialist AI agents that learn how you work.</text>
48
+ <text x="102" y="332" font-size="20" font-weight="400" fill="#9ca3af">Runs on 11 platforms. Completely free.</text>
49
+
50
+ <!-- Divider -->
51
+ <line x1="102" y1="365" x2="680" y2="365" stroke="#ffffff" stroke-width="1" opacity="0.10"/>
52
+
53
+ <!-- Feature bullets -->
54
+ <circle cx="112" cy="395" r="4" fill="#10b981"/>
55
+ <text x="126" y="400" font-size="15" fill="#e6edf3">Compound learning — improves with every correction you make</text>
56
+
57
+ <circle cx="112" cy="425" r="4" fill="#38bdf8"/>
58
+ <text x="126" y="430" font-size="15" fill="#e6edf3">Semantic routing — paste code or an error, the right agent fires</text>
59
+
60
+ <circle cx="112" cy="455" r="4" fill="#a78bfa"/>
61
+ <text x="126" y="460" font-size="15" fill="#e6edf3">Parallel agents — /team-review runs 4 specialists simultaneously</text>
62
+
63
+ <circle cx="112" cy="485" r="4" fill="#f59e0b"/>
64
+ <text x="126" y="490" font-size="15" fill="#e6edf3">Zero cloud — 100% local files, zero vendor lock-in</text>
65
+
66
+ <!-- CTA Section -->
67
+ <rect x="102" y="526" width="260" height="52" rx="26" fill="url(#ctaGrad)"/>
68
+ <text x="232" y="558" font-size="16" font-weight="700" text-anchor="middle" fill="#ffffff">Get it free — npm</text>
69
+
70
+ <text x="384" y="548" font-size="14" fill="#6b7280">npx kodelyth-ecc</text>
71
+ <text x="384" y="568" font-size="14" fill="#6b7280">github.com/sifxprime/kodelyth-ecc</text>
72
+
73
+ <!-- Right panel — terminal mockup -->
74
+ <rect x="720" y="60" width="440" height="508" rx="12" fill="#0d1117" stroke="#21262d" stroke-width="1.5"/>
75
+ <rect x="720" y="60" width="440" height="34" rx="12" fill="#161b22"/>
76
+ <rect x="720" y="84" width="440" height="10" fill="#161b22"/>
77
+ <circle cx="744" cy="77" r="5" fill="#ff5f57"/>
78
+ <circle cx="760" cy="77" r="5" fill="#febc2e"/>
79
+ <circle cx="776" cy="77" r="5" fill="#28c840"/>
80
+ <text x="940" y="81" font-size="11" fill="#484f58" text-anchor="middle">kodelyth-ecc — terminal</text>
81
+
82
+ <!-- Terminal content -->
83
+ <text x="748" y="122" font-family="'Courier New', Courier, monospace" font-size="12" fill="#10b981">$</text>
84
+ <text x="762" y="122" font-family="'Courier New', Courier, monospace" font-size="12" fill="#e6edf3"> npx kodelyth-ecc</text>
85
+
86
+ <text x="748" y="144" font-family="'Courier New', Courier, monospace" font-size="12" fill="#484f58">Installing v1.8.0...</text>
87
+
88
+ <text x="748" y="176" font-family="'Courier New', Courier, monospace" font-size="12" fill="#e6edf3"> Agents (70) </text>
89
+ <text x="890" y="176" font-family="'Courier New', Courier, monospace" font-size="12" fill="#484f58">→ installed</text>
90
+
91
+ <text x="748" y="198" font-family="'Courier New', Courier, monospace" font-size="12" fill="#e6edf3"> Skills (194) </text>
92
+ <text x="890" y="198" font-family="'Courier New', Courier, monospace" font-size="12" fill="#484f58">→ installed</text>
93
+
94
+ <text x="748" y="220" font-family="'Courier New', Courier, monospace" font-size="12" fill="#e6edf3"> Commands (97) </text>
95
+ <text x="890" y="220" font-family="'Courier New', Courier, monospace" font-size="12" fill="#484f58">→ installed</text>
96
+
97
+ <text x="748" y="242" font-family="'Courier New', Courier, monospace" font-size="12" fill="#e6edf3"> Hooks (22+) </text>
98
+ <text x="890" y="242" font-family="'Courier New', Courier, monospace" font-size="12" fill="#484f58">→ installed</text>
99
+
100
+ <line x1="748" y1="262" x2="1140" y2="262" stroke="#21262d" stroke-width="1"/>
101
+
102
+ <!-- Session simulation -->
103
+ <text x="748" y="286" font-family="'Courier New', Courier, monospace" font-size="11" fill="#10b981">$</text>
104
+ <text x="762" y="286" font-family="'Courier New', Courier, monospace" font-size="11" fill="#e6edf3"> [paste stack trace]</text>
105
+
106
+ <text x="748" y="306" font-family="'Courier New', Courier, monospace" font-size="11" fill="#a78bfa">→ Routing to debug-detective</text>
107
+
108
+ <text x="748" y="326" font-family="'Courier New', Courier, monospace" font-size="11" fill="#484f58">Root cause: null reference in</text>
109
+ <text x="748" y="342" font-family="'Courier New', Courier, monospace" font-size="11" fill="#484f58">getUserById() line 47.</text>
110
+ <text x="748" y="358" font-family="'Courier New', Courier, monospace" font-size="11" fill="#484f58">Fix: add optional chaining.</text>
111
+
112
+ <line x1="748" y1="376" x2="1140" y2="376" stroke="#21262d" stroke-width="1"/>
113
+
114
+ <text x="748" y="400" font-family="'Courier New', Courier, monospace" font-size="11" fill="#10b981">$</text>
115
+ <text x="762" y="400" font-family="'Courier New', Courier, monospace" font-size="11" fill="#e6edf3"> /team-review</text>
116
+
117
+ <text x="748" y="420" font-family="'Courier New', Courier, monospace" font-size="11" fill="#a78bfa">Spawning 4 agents in parallel...</text>
118
+ <text x="748" y="438" font-family="'Courier New', Courier, monospace" font-size="11" fill="#484f58"> code-reviewer · security-reviewer</text>
119
+ <text x="748" y="454" font-family="'Courier New', Courier, monospace" font-size="11" fill="#484f58"> performance-optimizer · api-guardian</text>
120
+
121
+ <rect x="748" y="470" width="396" height="34" rx="6" fill="#0d2b1d" stroke="#059669" stroke-opacity="0.35" stroke-width="1"/>
122
+ <text x="768" y="484" font-size="9" font-weight="700" fill="#10b981" letter-spacing="1">REVIEW COMPLETE — 0 critical · 1 high · 3 medium</text>
123
+ <text x="768" y="498" font-size="10" fill="#6b7280">15 min parallel vs 60 min sequential</text>
124
+
125
+ <!-- Bottom bar -->
126
+ <rect x="0" y="590" width="1200" height="38" fill="#0d1117" stroke="#21262d" stroke-width="0"/>
127
+ <text x="600" y="613" font-size="12" fill="#30363d" text-anchor="middle">kodelyth.com · Works with Claude Code · Windsurf · Cursor · Codex CLI · Antigravity · OpenCode · and 5 more</text>
128
+ </svg>