@evomap/evolver 1.89.0 → 1.89.2

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 (98) hide show
  1. package/CONTRIBUTING.md +19 -0
  2. package/README.ja-JP.md +9 -32
  3. package/README.ko-KR.md +9 -32
  4. package/README.md +530 -86
  5. package/README.zh-CN.md +4 -31
  6. package/SKILL.md +1 -1
  7. package/assets/cover.png +0 -0
  8. package/index.js +14 -1
  9. package/package.json +17 -6
  10. package/scripts/a2a_export.js +63 -0
  11. package/scripts/a2a_ingest.js +79 -0
  12. package/scripts/a2a_promote.js +118 -0
  13. package/scripts/analyze_by_skill.js +121 -0
  14. package/scripts/build_binaries.js +479 -0
  15. package/scripts/check-changelog.js +166 -0
  16. package/scripts/extract_log.js +85 -0
  17. package/scripts/generate_history.js +75 -0
  18. package/scripts/gep_append_event.js +96 -0
  19. package/scripts/gep_personality_report.js +234 -0
  20. package/scripts/human_report.js +147 -0
  21. package/scripts/recall-verify-report.js +234 -0
  22. package/scripts/recover_loop.js +61 -0
  23. package/scripts/seed-merchants.js +91 -0
  24. package/scripts/suggest_version.js +89 -0
  25. package/scripts/validate-modules.js +38 -0
  26. package/scripts/validate-suite.js +78 -0
  27. package/skills/index.json +14 -0
  28. package/src/evolve/guards.js +1 -721
  29. package/src/evolve/pipeline/collect.js +1 -1283
  30. package/src/evolve/pipeline/dispatch.js +1 -421
  31. package/src/evolve/pipeline/enrich.js +1 -434
  32. package/src/evolve/pipeline/hub.js +1 -319
  33. package/src/evolve/pipeline/select.js +1 -274
  34. package/src/evolve/pipeline/signals.js +1 -206
  35. package/src/evolve/utils.js +1 -264
  36. package/src/evolve.js +1 -350
  37. package/src/forceUpdate.js +105 -20
  38. package/src/gep/a2aProtocol.js +1 -4395
  39. package/src/gep/autoDistillConv.js +1 -205
  40. package/src/gep/autoDistillLlm.js +1 -315
  41. package/src/gep/candidateEval.js +1 -92
  42. package/src/gep/candidates.js +1 -198
  43. package/src/gep/contentHash.js +1 -30
  44. package/src/gep/conversationSniffer.js +1 -266
  45. package/src/gep/crypto.js +1 -89
  46. package/src/gep/curriculum.js +1 -163
  47. package/src/gep/deviceId.js +1 -218
  48. package/src/gep/envFingerprint.js +1 -118
  49. package/src/gep/epigenetics.js +1 -31
  50. package/src/gep/execBridge.js +1 -711
  51. package/src/gep/explore.js +1 -289
  52. package/src/gep/hash.js +1 -15
  53. package/src/gep/hubFetch.js +1 -359
  54. package/src/gep/hubReview.js +1 -207
  55. package/src/gep/hubSearch.js +1 -526
  56. package/src/gep/hubVerify.js +1 -306
  57. package/src/gep/learningSignals.js +1 -89
  58. package/src/gep/memoryGraph.js +1 -1374
  59. package/src/gep/memoryGraphAdapter.js +1 -203
  60. package/src/gep/mutation.js +1 -203
  61. package/src/gep/narrativeMemory.js +1 -108
  62. package/src/gep/oauthLogin.js +34 -0
  63. package/src/gep/openPRRegistry.js +1 -205
  64. package/src/gep/personality.js +1 -423
  65. package/src/gep/policyCheck.js +1 -599
  66. package/src/gep/prompt.js +1 -836
  67. package/src/gep/recallInject.js +1 -409
  68. package/src/gep/recallVerifier.js +1 -318
  69. package/src/gep/reflection.js +1 -177
  70. package/src/gep/selector.js +1 -602
  71. package/src/gep/skillDistiller.js +1 -1294
  72. package/src/gep/skillPublisher.js +1 -1
  73. package/src/gep/solidify.js +1 -1699
  74. package/src/gep/strategy.js +1 -136
  75. package/src/gep/tokenSavings.js +1 -88
  76. package/src/gep/workspaceKeychain.js +1 -174
  77. package/src/proxy/extensions/traceControl.js +1 -99
  78. package/src/proxy/inject.js +1 -52
  79. package/src/proxy/lifecycle/manager.js +2 -0
  80. package/src/proxy/trace/extractor.js +1 -534
  81. package/src/proxy/trace/usage.js +1 -105
  82. package/.cursor/BUGBOT.md +0 -182
  83. package/.env.example +0 -68
  84. package/.git-commit-guard-token +0 -1
  85. package/.github/CODEOWNERS +0 -63
  86. package/.github/ISSUE_TEMPLATE/good_first_issue.md +0 -23
  87. package/.github/pull_request_template.md +0 -45
  88. package/.github/workflows/test.yml +0 -75
  89. package/CHANGELOG.md +0 -1123
  90. package/README.public.md +0 -594
  91. package/SECURITY.md +0 -108
  92. package/assets/gep/events.jsonl +0 -3
  93. package/examples/atp-consumer-quickstart.md +0 -100
  94. package/examples/hello-world.md +0 -38
  95. package/proxy-package.json +0 -39
  96. package/public.manifest.json +0 -141
  97. /package/assets/gep/{genes.json → genes.seed.json} +0 -0
  98. /package/{bundled-skills → skills}/_meta/SKILL.md +0 -0
@@ -0,0 +1,85 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const REPO_ROOT = path.resolve(__dirname, '..');
5
+ const LOG_FILE = path.join(REPO_ROOT, 'memory', 'mad_dog_evolution.log');
6
+ const OUT_FILE = path.join(REPO_ROOT, 'evolution_history.md');
7
+
8
+ function parseLog() {
9
+ if (!fs.existsSync(LOG_FILE)) {
10
+ console.log("Log file not found.");
11
+ return;
12
+ }
13
+
14
+ const content = fs.readFileSync(LOG_FILE, 'utf8');
15
+ const lines = content.split('\n');
16
+
17
+ const reports = [];
18
+ let currentTimestamp = null;
19
+
20
+ // Regex for Feishu command
21
+ // node skills/feishu-card/send.js --title "..." --color ... --text "..."
22
+ const cmdRegex = /node skills\/feishu-card\/send\.js --title "(.*?)" --color \w+ --text "(.*?)"/;
23
+
24
+ for (let i = 0; i < lines.length; i++) {
25
+ const line = lines[i];
26
+
27
+ // 1. Capture Timestamp
28
+ if (line.includes('Cycle Start:')) {
29
+ // Format: Cycle Start: Sun Feb 1 19:17:44 UTC 2026
30
+ const dateStr = line.split('Cycle Start: ')[1].trim();
31
+ try {
32
+ currentTimestamp = new Date(dateStr);
33
+ } catch (e) {
34
+ currentTimestamp = null;
35
+ }
36
+ }
37
+
38
+ const match = line.match(cmdRegex);
39
+ if (match) {
40
+ const title = match[1];
41
+ let text = match[2];
42
+
43
+ // Clean up text (unescape newlines)
44
+ text = text.replace(/\\n/g, '\n').replace(/\\"/g, '"');
45
+
46
+ if (currentTimestamp) {
47
+ reports.push({
48
+ ts: currentTimestamp,
49
+ title: title,
50
+ text: text,
51
+ id: title // Cycle ID is in title
52
+ });
53
+ }
54
+ }
55
+ }
56
+
57
+ // Deduplicate by ID (keep latest timestamp?)
58
+ const uniqueReports = {};
59
+ reports.forEach(r => {
60
+ uniqueReports[r.id] = r;
61
+ });
62
+
63
+ const sortedReports = Object.values(uniqueReports).sort((a, b) => a.ts - b.ts);
64
+
65
+ let md = "# Evolution History (Extracted)\n\n";
66
+ sortedReports.forEach(r => {
67
+ // Convert to CST (UTC+8)
68
+ const cstDate = r.ts.toLocaleString("zh-CN", {
69
+ timeZone: "Asia/Shanghai",
70
+ hour12: false,
71
+ year: 'numeric', month: '2-digit', day: '2-digit',
72
+ hour: '2-digit', minute: '2-digit', second: '2-digit'
73
+ });
74
+
75
+ md += `### ${r.title} (${cstDate})\n`;
76
+ md += `${r.text}\n\n`;
77
+ md += `---\n\n`;
78
+ });
79
+
80
+ fs.writeFileSync(OUT_FILE, md);
81
+ console.log(`Extracted ${sortedReports.length} reports to ${OUT_FILE}`);
82
+ }
83
+
84
+ parseLog();
85
+
@@ -0,0 +1,75 @@
1
+ const { execSync } = require('child_process');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ // Separator for git log parsing (something unlikely to be in commit messages)
6
+ const SEP = '|||';
7
+ const REPO_ROOT = path.resolve(__dirname, '..');
8
+
9
+ try {
10
+ // Git command:
11
+ // --reverse: Oldest to Newest (Time Sequence)
12
+ // --grep: Filter by keyword
13
+ // --format: Hash, Date (ISO), Author, Subject, Body
14
+ const cmd = `git log --reverse --grep="Evolution" --format="%H${SEP}%ai${SEP}%an${SEP}%s${SEP}%b"`;
15
+
16
+ console.log('Executing git log...');
17
+ const output = execSync(cmd, {
18
+ encoding: 'utf8',
19
+ cwd: REPO_ROOT,
20
+ maxBuffer: 1024 * 1024 * 10 // 10MB buffer just in case
21
+ });
22
+
23
+ const entries = output.split('\n').filter(line => line.trim().length > 0);
24
+
25
+ let markdown = '# Evolution History (Time Sequence)\n\n';
26
+ markdown += '> Filter: "Evolution"\n';
27
+ markdown += '> Timezone: CST (UTC+8)\n\n';
28
+
29
+ let count = 0;
30
+
31
+ entries.forEach(entry => {
32
+ const parts = entry.split(SEP);
33
+ if (parts.length < 4) return;
34
+
35
+ const hash = parts[0];
36
+ const dateStr = parts[1];
37
+ const author = parts[2];
38
+ const subject = parts[3];
39
+ const body = parts[4] || '';
40
+
41
+ // Parse Date and Convert to UTC+8
42
+ const date = new Date(dateStr);
43
+ // Add 8 hours (28800000 ms) to UTC timestamp to shift it
44
+ // Then formatting it as ISO will look like UTC but represent CST values
45
+ const cstDate = new Date(date.getTime() + 8 * 60 * 60 * 1000);
46
+
47
+ // Format: YYYY-MM-DD HH:mm:ss
48
+ const timeStr = cstDate.toISOString().replace('T', ' ').substring(0, 19);
49
+
50
+ markdown += `## ${timeStr}\n`;
51
+ markdown += `- Commit: \`${hash.substring(0, 7)}\`\n`;
52
+ markdown += `- Subject: ${subject}\n`;
53
+
54
+ if (body.trim()) {
55
+ // Indent body for better readability
56
+ const formattedBody = body.trim().split('\n').map(l => `> ${l}`).join('\n');
57
+ markdown += `- Details:\n${formattedBody}\n`;
58
+ }
59
+ markdown += '\n';
60
+ count++;
61
+ });
62
+
63
+ const outDir = path.join(REPO_ROOT, 'memory');
64
+ if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
65
+ const outPath = path.join(outDir, 'evolution_history.md');
66
+ fs.writeFileSync(outPath, markdown);
67
+
68
+ console.log(`Successfully generated report with ${count} entries.`);
69
+ console.log(`Saved to: ${outPath}`);
70
+
71
+ } catch (e) {
72
+ console.error('Error generating history:', e.message);
73
+ process.exit(1);
74
+ }
75
+
@@ -0,0 +1,96 @@
1
+ const fs = require('fs');
2
+ const { appendEventJsonl } = require('../src/gep/assetStore');
3
+
4
+ function readStdin() {
5
+ try {
6
+ return fs.readFileSync(0, 'utf8');
7
+ } catch {
8
+ return '';
9
+ }
10
+ }
11
+
12
+ function readTextIfExists(p) {
13
+ try {
14
+ if (!p) return '';
15
+ if (!fs.existsSync(p)) return '';
16
+ return fs.readFileSync(p, 'utf8');
17
+ } catch {
18
+ return '';
19
+ }
20
+ }
21
+
22
+ function parseInput(text) {
23
+ const raw = String(text || '').trim();
24
+ if (!raw) return [];
25
+
26
+ // Accept JSON array or single JSON.
27
+ try {
28
+ const maybe = JSON.parse(raw);
29
+ if (Array.isArray(maybe)) return maybe;
30
+ if (maybe && typeof maybe === 'object') return [maybe];
31
+ } catch (e) {}
32
+
33
+ // Fallback: JSONL.
34
+ const lines = raw.split('\n').map(l => l.trim()).filter(Boolean);
35
+ const out = [];
36
+ for (const line of lines) {
37
+ try {
38
+ const obj = JSON.parse(line);
39
+ out.push(obj);
40
+ } catch (e) {}
41
+ }
42
+ return out;
43
+ }
44
+
45
+ function isValidEvolutionEvent(ev) {
46
+ if (!ev || ev.type !== 'EvolutionEvent') return false;
47
+ if (!ev.id || typeof ev.id !== 'string') return false;
48
+ // parent may be null or string
49
+ if (!(ev.parent === null || typeof ev.parent === 'string')) return false;
50
+ if (!ev.intent || typeof ev.intent !== 'string') return false;
51
+ if (!Array.isArray(ev.signals)) return false;
52
+ if (!Array.isArray(ev.genes_used)) return false;
53
+ // GEP v1.4: mutation + personality are mandatory evolution dimensions
54
+ if (!ev.mutation_id || typeof ev.mutation_id !== 'string') return false;
55
+ if (!ev.personality_state || typeof ev.personality_state !== 'object') return false;
56
+ if (ev.personality_state.type !== 'PersonalityState') return false;
57
+ for (const k of ['rigor', 'creativity', 'verbosity', 'risk_tolerance', 'obedience']) {
58
+ const v = Number(ev.personality_state[k]);
59
+ if (!Number.isFinite(v) || v < 0 || v > 1) return false;
60
+ }
61
+ if (!ev.blast_radius || typeof ev.blast_radius !== 'object') return false;
62
+ if (!Number.isFinite(Number(ev.blast_radius.files))) return false;
63
+ if (!Number.isFinite(Number(ev.blast_radius.lines))) return false;
64
+ if (!ev.outcome || typeof ev.outcome !== 'object') return false;
65
+ if (!ev.outcome.status || typeof ev.outcome.status !== 'string') return false;
66
+ const score = Number(ev.outcome.score);
67
+ if (!Number.isFinite(score) || score < 0 || score > 1) return false;
68
+
69
+ // capsule_id is optional, but if present must be string or null.
70
+ if (!('capsule_id' in ev)) return true;
71
+ return ev.capsule_id === null || typeof ev.capsule_id === 'string';
72
+ }
73
+
74
+ function main() {
75
+ const args = process.argv.slice(2);
76
+ const inputPath = args.find(a => a && !a.startsWith('--')) || '';
77
+ const text = inputPath ? readTextIfExists(inputPath) : readStdin();
78
+ const items = parseInput(text);
79
+
80
+ let appended = 0;
81
+ for (const it of items) {
82
+ if (!isValidEvolutionEvent(it)) continue;
83
+ appendEventJsonl(it);
84
+ appended += 1;
85
+ }
86
+
87
+ process.stdout.write(`appended=${appended}\n`);
88
+ }
89
+
90
+ try {
91
+ main();
92
+ } catch (e) {
93
+ process.stderr.write(`${e && e.message ? e.message : String(e)}\n`);
94
+ process.exit(1);
95
+ }
96
+
@@ -0,0 +1,234 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { getRepoRoot, getMemoryDir, getGepAssetsDir } = require('../src/gep/paths');
4
+ const { normalizePersonalityState, personalityKey, defaultPersonalityState } = require('../src/gep/personality');
5
+
6
+ function readJsonIfExists(p, fallback) {
7
+ try {
8
+ if (!fs.existsSync(p)) return fallback;
9
+ const raw = fs.readFileSync(p, 'utf8');
10
+ if (!raw.trim()) return fallback;
11
+ return JSON.parse(raw);
12
+ } catch {
13
+ return fallback;
14
+ }
15
+ }
16
+
17
+ function readJsonlIfExists(p, limitLines = 5000) {
18
+ try {
19
+ if (!fs.existsSync(p)) return [];
20
+ const raw = fs.readFileSync(p, 'utf8');
21
+ const lines = raw
22
+ .split('\n')
23
+ .map(l => l.trim())
24
+ .filter(Boolean);
25
+ const recent = lines.slice(Math.max(0, lines.length - limitLines));
26
+ return recent
27
+ .map(l => {
28
+ try {
29
+ return JSON.parse(l);
30
+ } catch {
31
+ return null;
32
+ }
33
+ })
34
+ .filter(Boolean);
35
+ } catch {
36
+ return [];
37
+ }
38
+ }
39
+
40
+ function clamp01(x) {
41
+ const n = Number(x);
42
+ if (!Number.isFinite(n)) return 0;
43
+ return Math.max(0, Math.min(1, n));
44
+ }
45
+
46
+ function pct(x) {
47
+ const n = Number(x);
48
+ if (!Number.isFinite(n)) return '0.0%';
49
+ return `${(n * 100).toFixed(1)}%`;
50
+ }
51
+
52
+ function pad(s, n) {
53
+ const str = String(s == null ? '' : s);
54
+ if (str.length >= n) return str.slice(0, n);
55
+ return str + ' '.repeat(n - str.length);
56
+ }
57
+
58
+ function scoreFromCounts(success, fail, avgScore) {
59
+ const succ = Number(success) || 0;
60
+ const fl = Number(fail) || 0;
61
+ const total = succ + fl;
62
+ const p = (succ + 1) / (total + 2); // Laplace smoothing
63
+ const sampleWeight = Math.min(1, total / 8);
64
+ const q = avgScore == null ? 0.5 : clamp01(avgScore);
65
+ return p * 0.75 + q * 0.25 * sampleWeight;
66
+ }
67
+
68
+ function aggregateFromEvents(events) {
69
+ const map = new Map();
70
+ for (const ev of Array.isArray(events) ? events : []) {
71
+ if (!ev || ev.type !== 'EvolutionEvent') continue;
72
+ const ps = ev.personality_state && typeof ev.personality_state === 'object' ? ev.personality_state : null;
73
+ if (!ps) continue;
74
+ const key = personalityKey(normalizePersonalityState(ps));
75
+ const cur = map.get(key) || {
76
+ key,
77
+ success: 0,
78
+ fail: 0,
79
+ n: 0,
80
+ avg_score: 0.5,
81
+ last_event_id: null,
82
+ last_at: null,
83
+ mutation: { repair: 0, optimize: 0, innovate: 0 },
84
+ mutation_success: { repair: 0, optimize: 0, innovate: 0 },
85
+ };
86
+ const st = ev.outcome && ev.outcome.status ? String(ev.outcome.status) : 'unknown';
87
+ if (st === 'success') cur.success += 1;
88
+ else if (st === 'failed') cur.fail += 1;
89
+
90
+ const sc = ev.outcome && Number.isFinite(Number(ev.outcome.score)) ? clamp01(Number(ev.outcome.score)) : null;
91
+ if (sc != null) {
92
+ cur.n += 1;
93
+ cur.avg_score = cur.avg_score + (sc - cur.avg_score) / cur.n;
94
+ }
95
+
96
+ const cat = ev.intent ? String(ev.intent) : null;
97
+ if (cat && cur.mutation[cat] != null) {
98
+ cur.mutation[cat] += 1;
99
+ if (st === 'success') cur.mutation_success[cat] += 1;
100
+ }
101
+
102
+ cur.last_event_id = ev.id || cur.last_event_id;
103
+ const at = ev.meta && ev.meta.at ? String(ev.meta.at) : null;
104
+ cur.last_at = at || cur.last_at;
105
+ map.set(key, cur);
106
+ }
107
+ return Array.from(map.values());
108
+ }
109
+
110
+ function main() {
111
+ const repoRoot = getRepoRoot();
112
+ const memoryDir = getMemoryDir();
113
+ const assetsDir = getGepAssetsDir();
114
+
115
+ const personalityPath = path.join(memoryDir, 'personality_state.json');
116
+ const model = readJsonIfExists(personalityPath, null);
117
+ const current = model && model.current ? normalizePersonalityState(model.current) : defaultPersonalityState();
118
+ const currentKey = personalityKey(current);
119
+
120
+ const eventsPath = path.join(assetsDir, 'events.jsonl');
121
+ const events = readJsonlIfExists(eventsPath, 10000);
122
+ const evs = events.filter(e => e && e.type === 'EvolutionEvent');
123
+ const agg = aggregateFromEvents(evs);
124
+
125
+ // Prefer model.stats if present, but still show event-derived aggregation (ground truth).
126
+ const stats = model && model.stats && typeof model.stats === 'object' ? model.stats : {};
127
+ const statRows = Object.entries(stats).map(([key, e]) => {
128
+ const entry = e && typeof e === 'object' ? e : {};
129
+ const success = Number(entry.success) || 0;
130
+ const fail = Number(entry.fail) || 0;
131
+ const total = success + fail;
132
+ const avg = Number.isFinite(Number(entry.avg_score)) ? clamp01(Number(entry.avg_score)) : null;
133
+ const score = scoreFromCounts(success, fail, avg);
134
+ return { key, success, fail, total, avg_score: avg, score, updated_at: entry.updated_at || null, source: 'model' };
135
+ });
136
+
137
+ const evRows = agg.map(e => {
138
+ const success = Number(e.success) || 0;
139
+ const fail = Number(e.fail) || 0;
140
+ const total = success + fail;
141
+ const avg = Number.isFinite(Number(e.avg_score)) ? clamp01(Number(e.avg_score)) : null;
142
+ const score = scoreFromCounts(success, fail, avg);
143
+ return { key: e.key, success, fail, total, avg_score: avg, score, updated_at: e.last_at || null, source: 'events', _ev: e };
144
+ });
145
+
146
+ // Merge rows by key (events take precedence for total/success/fail; model provides updated_at if events missing).
147
+ const byKey = new Map();
148
+ for (const r of [...statRows, ...evRows]) {
149
+ const prev = byKey.get(r.key);
150
+ if (!prev) {
151
+ byKey.set(r.key, r);
152
+ continue;
153
+ }
154
+ // Prefer events for counts and avg_score
155
+ if (r.source === 'events') byKey.set(r.key, { ...prev, ...r });
156
+ else byKey.set(r.key, { ...r, ...prev });
157
+ }
158
+
159
+ const merged = Array.from(byKey.values()).sort((a, b) => b.score - a.score);
160
+
161
+ process.stdout.write(`Repo: ${repoRoot}\n`);
162
+ process.stdout.write(`MemoryDir: ${memoryDir}\n`);
163
+ process.stdout.write(`AssetsDir: ${assetsDir}\n\n`);
164
+
165
+ process.stdout.write(`[Current Personality]\n`);
166
+ process.stdout.write(`${currentKey}\n`);
167
+ process.stdout.write(`${JSON.stringify(current, null, 2)}\n\n`);
168
+
169
+ process.stdout.write(`[Personality Stats] (ranked by score)\n`);
170
+ if (merged.length === 0) {
171
+ process.stdout.write('(no stats yet; run a few cycles and solidify)\n');
172
+ return;
173
+ }
174
+
175
+ const header =
176
+ pad('rank', 5) +
177
+ pad('total', 8) +
178
+ pad('succ', 8) +
179
+ pad('fail', 8) +
180
+ pad('succ_rate', 11) +
181
+ pad('avg', 7) +
182
+ pad('score', 8) +
183
+ 'key';
184
+ process.stdout.write(header + '\n');
185
+ process.stdout.write('-'.repeat(Math.min(140, header.length + 40)) + '\n');
186
+
187
+ const topN = Math.min(25, merged.length);
188
+ for (let i = 0; i < topN; i++) {
189
+ const r = merged[i];
190
+ const succ = Number(r.success) || 0;
191
+ const fail = Number(r.fail) || 0;
192
+ const total = Number(r.total) || succ + fail;
193
+ const succRate = total > 0 ? succ / total : 0;
194
+ const avg = r.avg_score == null ? '-' : Number(r.avg_score).toFixed(2);
195
+ const line =
196
+ pad(String(i + 1), 5) +
197
+ pad(String(total), 8) +
198
+ pad(String(succ), 8) +
199
+ pad(String(fail), 8) +
200
+ pad(pct(succRate), 11) +
201
+ pad(String(avg), 7) +
202
+ pad(Number(r.score).toFixed(3), 8) +
203
+ String(r.key);
204
+ process.stdout.write(line + '\n');
205
+
206
+ if (r._ev) {
207
+ const ev = r._ev;
208
+ const ms = ev.mutation || {};
209
+ const mSucc = ev.mutation_success || {};
210
+ const parts = [];
211
+ for (const cat of ['repair', 'optimize', 'innovate']) {
212
+ const n = Number(ms[cat]) || 0;
213
+ if (n <= 0) continue;
214
+ const s = Number(mSucc[cat]) || 0;
215
+ parts.push(`${cat}:${s}/${n}`);
216
+ }
217
+ if (parts.length) process.stdout.write(` mutation_success: ${parts.join(' | ')}\n`);
218
+ }
219
+ }
220
+
221
+ process.stdout.write('\n');
222
+ process.stdout.write(`[Notes]\n`);
223
+ process.stdout.write(`- score is a smoothed composite of success_rate + avg_score (sample-weighted)\n`);
224
+ process.stdout.write(`- current_key appears in the ranking once enough data accumulates\n`);
225
+ }
226
+
227
+ try {
228
+ main();
229
+ } catch (e) {
230
+ process.stderr.write((e && e.message) || String(e));
231
+ process.stderr.write('\n');
232
+ process.exit(1);
233
+ }
234
+
@@ -0,0 +1,147 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const REPO_ROOT = path.resolve(__dirname, '..');
5
+ const IN_FILE = path.join(REPO_ROOT, 'evolution_history_full.md');
6
+ const OUT_FILE = path.join(REPO_ROOT, 'evolution_human_summary.md');
7
+
8
+ function generateHumanReport() {
9
+ if (!fs.existsSync(IN_FILE)) return console.error("No input file");
10
+
11
+ const content = fs.readFileSync(IN_FILE, 'utf8');
12
+ const entries = content.split('---').map(e => e.trim()).filter(e => e.length > 0);
13
+
14
+ const categories = {
15
+ 'Security & Stability': [],
16
+ 'Performance & Optimization': [],
17
+ 'Tooling & Features': [],
18
+ 'Documentation & Process': []
19
+ };
20
+
21
+ const componentMap = {}; // Component -> Change List
22
+
23
+ entries.forEach(entry => {
24
+ // Extract basic info
25
+ const lines = entry.split('\n');
26
+ const header = lines[0]; // ### Title (Date)
27
+ const body = lines.slice(1).join('\n');
28
+
29
+ const dateMatch = header.match(/\((.*?)\)/);
30
+ const dateStr = dateMatch ? dateMatch[1] : '';
31
+ const time = dateStr.split(' ')[1] || ''; // HH:mm:ss
32
+
33
+ // Classify
34
+ let category = 'Tooling & Features';
35
+ let component = 'System';
36
+ let summary = '';
37
+
38
+ const lowerBody = body.toLowerCase();
39
+
40
+ // Detect Component
41
+ if (lowerBody.includes('feishu-card')) component = 'feishu-card';
42
+ else if (lowerBody.includes('feishu-sticker')) component = 'feishu-sticker';
43
+ else if (lowerBody.includes('git-sync')) component = 'git-sync';
44
+ else if (lowerBody.includes('capability-evolver') || lowerBody.includes('evolve.js')) component = 'capability-evolver';
45
+ else if (lowerBody.includes('interaction-logger')) component = 'interaction-logger';
46
+ else if (lowerBody.includes('chat-to-image')) component = 'chat-to-image';
47
+ else if (lowerBody.includes('safe_publish')) component = 'capability-evolver';
48
+
49
+ // Detect Category
50
+ if (lowerBody.includes('security') || lowerBody.includes('permission') || lowerBody.includes('auth') || lowerBody.includes('harden')) {
51
+ category = 'Security & Stability';
52
+ } else if (lowerBody.includes('optimiz') || lowerBody.includes('performance') || lowerBody.includes('memory') || lowerBody.includes('fast')) {
53
+ category = 'Performance & Optimization';
54
+ } else if (lowerBody.includes('doc') || lowerBody.includes('readme')) {
55
+ category = 'Documentation & Process';
56
+ }
57
+
58
+ // Extract Human Summary (First meaningful line that isn't Status/Action/Date)
59
+ const summaryLines = lines.filter(l =>
60
+ !l.startsWith('###') &&
61
+ !l.startsWith('Status:') &&
62
+ !l.startsWith('Action:') &&
63
+ l.trim().length > 10
64
+ );
65
+
66
+ if (summaryLines.length > 0) {
67
+ // Clean up the line
68
+ summary = summaryLines[0]
69
+ .replace(/^-\s*/, '') // Remove bullets
70
+ .replace(/\*\*/g, '') // Remove bold
71
+ .replace(/`/, '')
72
+ .trim();
73
+
74
+ // Deduplicate
75
+ const key = `${component}:${summary.substring(0, 20)}`;
76
+ const exists = categories[category].some(i => i.key === key);
77
+
78
+ if (!exists && !summary.includes("Stability Scan OK") && !summary.includes("Workspace Sync")) {
79
+ categories[category].push({ time, component, summary, key });
80
+
81
+ if (!componentMap[component]) componentMap[component] = [];
82
+ componentMap[component].push(summary);
83
+ }
84
+ }
85
+ });
86
+
87
+ // --- Generate Markdown ---
88
+ const today = new Date().toISOString().slice(0, 10);
89
+ let md = `# Evolution Summary: The Day in Review (${today})\n\n`;
90
+ md += `> Overview: Grouped summary of changes extracted from evolution history.\n\n`;
91
+
92
+ // Section 1: By Theme (Evolution Direction)
93
+ md += `## 1. Evolution Direction\n`;
94
+
95
+ for (const [cat, items] of Object.entries(categories)) {
96
+ if (items.length === 0) continue;
97
+ md += `### ${cat}\n`;
98
+ // Group by component within theme
99
+ const compGroup = {};
100
+ items.forEach(i => {
101
+ if (!compGroup[i.component]) compGroup[i.component] = [];
102
+ compGroup[i.component].push(i.summary);
103
+ });
104
+
105
+ for (const [comp, sums] of Object.entries(compGroup)) {
106
+ // Unique summaries only
107
+ const uniqueSums = [...new Set(sums)];
108
+ uniqueSums.forEach(s => {
109
+ md += `- **${comp}**: ${s}\n`;
110
+ });
111
+ }
112
+ md += `\n`;
113
+ }
114
+
115
+ // Section 2: By Timeline (High Level)
116
+ md += `## 2. Timeline of Critical Events\n`;
117
+ // Flatten and sort all items by time
118
+ const allItems = [];
119
+ Object.values(categories).forEach(list => allItems.push(...list));
120
+ allItems.sort((a, b) => a.time.localeCompare(b.time));
121
+
122
+ // Filter for "Critical" keywords
123
+ const criticalItems = allItems.filter(i =>
124
+ i.summary.toLowerCase().includes('fix') ||
125
+ i.summary.toLowerCase().includes('patch') ||
126
+ i.summary.toLowerCase().includes('create') ||
127
+ i.summary.toLowerCase().includes('optimiz')
128
+ );
129
+
130
+ criticalItems.forEach(i => {
131
+ md += `- \`${i.time}\` (${i.component}): ${i.summary}\n`;
132
+ });
133
+
134
+ // Section 3: Package Adjustments
135
+ md += `\n## 3. Package & Documentation Adjustments\n`;
136
+ const comps = Object.keys(componentMap).sort();
137
+ comps.forEach(comp => {
138
+ const count = new Set(componentMap[comp]).size;
139
+ md += `- **${comp}**: Received ${count} significant updates.\n`;
140
+ });
141
+
142
+ fs.writeFileSync(OUT_FILE, md);
143
+ console.log("Human report generated.");
144
+ }
145
+
146
+ generateHumanReport();
147
+