helixevo 0.2.38 → 0.2.40

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.
@@ -19,7 +19,17 @@ export default function SkillNetworkPage() {
19
19
  )
20
20
 
21
21
  // Build evolution history per skill
22
- const evolutionBySkill = {}
22
+ const evolutionBySkill: Record<string, Array<{
23
+ id: string
24
+ timestamp: string
25
+ action: string
26
+ description: string
27
+ outcome: string
28
+ outcomeReason: string
29
+ task: number
30
+ align: number
31
+ sideEffect: number
32
+ }>> = {}
23
33
  for (const iter of history.iterations) {
24
34
  for (const p of iter.proposals) {
25
35
  if (!evolutionBySkill[p.targetSkill]) evolutionBySkill[p.targetSkill] = []
@@ -33,7 +43,7 @@ export default function SkillNetworkPage() {
33
43
  }
34
44
 
35
45
  // Load skill contents
36
- const skillContents = {}
46
+ const skillContents: Record<string, string> = {}
37
47
  for (const n of graph.nodes) {
38
48
  const raw = loadSkillContent(n.id)
39
49
  if (raw) skillContents[n.id] = raw.match(/^---\n[\s\S]*?\n---\n([\s\S]*)$/)?.[1]?.trim() ?? raw
@@ -1,230 +1,211 @@
1
1
  import Link from 'next/link'
2
- import { getDashboardSummary, loadFrontier, loadHistory, loadGraph, loadFailures, listProjects } from '@/lib/data'
2
+ import { getDashboardSummary, loadFailures, loadFrontier, loadGraph, loadHistory, listProjects } from '@/lib/data'
3
3
  import { OverviewActions } from '@/components/overview-actions'
4
+ import { PageHero } from '@/components/page-hero'
5
+ import { MetricCard } from '@/components/metric-card'
6
+ import { SectionFrame } from '@/components/section-frame'
4
7
 
5
8
  export const dynamic = 'force-dynamic'
6
9
 
7
- function StatCard({ value, label, color, sub, accent, href }: {
8
- value: string | number; label: string; color: string; sub?: string; accent?: string; href: string
9
- }) {
10
- return (
11
- <Link href={href} style={{ textDecoration: 'none', color: 'inherit' }}>
12
- <div className="stat-card" style={{ cursor: 'pointer' }}>
13
- <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
14
- <div>
15
- <div className="stat-value" style={{ color }}>{value}</div>
16
- <div className="stat-label">{label}</div>
17
- {sub && <div style={{ fontSize: 10, color: 'var(--text-muted)', marginTop: 2 }}>{sub}</div>}
18
- </div>
19
- {accent && (
20
- <div style={{
21
- width: 36, height: 36, borderRadius: 10,
22
- background: `${color}12`, display: 'flex', alignItems: 'center', justifyContent: 'center',
23
- color, fontSize: 16,
24
- }}>{accent}</div>
25
- )}
26
- </div>
27
- </div>
28
- </Link>
29
- )
10
+ function scoreColor(score: number) {
11
+ if (score >= 0.8) return 'var(--green)'
12
+ if (score >= 0.6) return 'var(--yellow)'
13
+ return 'var(--red)'
30
14
  }
31
15
 
32
16
  export default function Overview() {
33
- const s = getDashboardSummary()
17
+ const summary = getDashboardSummary()
34
18
  const frontier = loadFrontier()
35
19
  const history = loadHistory()
36
20
  const graph = loadGraph()
37
21
  const failures = loadFailures()
38
- const recent = history.iterations.slice(-5).reverse()
22
+ const unresolved = failures.filter((failure) => !failure.resolved)
23
+ const recentRuns = history.iterations.slice(-4).reverse()
24
+ const topSkills = [...graph.nodes].sort((a, b) => b.score - a.score).slice(0, 10)
39
25
 
40
26
  return (
41
- <div>
42
- <div className="page-header">
43
- <h1 className="page-title">Dashboard</h1>
44
- <p className="page-desc">
45
- Overview of your self-evolving skill ecosystem
46
- </p>
47
- </div>
27
+ <div className="overview-grid">
28
+ <PageHero
29
+ eyebrow="System cockpit"
30
+ title="Dashboard"
31
+ description="Monitor the live state of your self-evolving skill ecosystem, launch high-value actions, and trace how failures turn into frontier-worthy improvements."
32
+ chips={[
33
+ { label: `${summary.skills.total} skills`, tone: 'purple' },
34
+ { label: `${summary.failures.unresolved} unresolved corrections`, tone: summary.failures.unresolved > 0 ? 'yellow' : 'green' },
35
+ { label: `${summary.evolution.runs} evolution runs`, tone: 'blue' },
36
+ { label: `${frontier.programs.length}/${frontier.capacity} frontier slots`, tone: 'green' },
37
+ ]}
38
+ actions={
39
+ <Link href="/projects" className="metric-card-anchor" style={{ minWidth: 240, display: 'block' }}>
40
+ <div className="metric-card metric-card-green metric-card-link">
41
+ <div className="metric-card-header">
42
+ <div>
43
+ <div className="metric-card-label">Next workflow</div>
44
+ <div className="metric-card-value" style={{ fontSize: 24 }}>Setup a project</div>
45
+ </div>
46
+ <div className="metric-card-icon">↗</div>
47
+ </div>
48
+ <div className="metric-card-sublabel">Analyze a folder or GitHub repo, match skills, and identify capability gaps.</div>
49
+ </div>
50
+ </Link>
51
+ }
52
+ />
48
53
 
49
- {/* Setup New Project */}
50
- <Link href="/projects" style={{ textDecoration: 'none', color: 'inherit' }}>
51
- <div className="card" style={{ marginBottom: 12, padding: '12px 18px', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 10, borderLeft: '3px solid var(--green)' }}>
52
- <div style={{ width: 28, height: 28, borderRadius: 8, background: 'var(--green-light)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 14, flexShrink: 0 }}>📁</div>
53
- <div style={{ flex: 1 }}>
54
- <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--text)' }}>Setup New Project</div>
55
- <div style={{ fontSize: 11, color: 'var(--text-dim)' }}>Analyze a folder or GitHub repo to match skills and find gaps</div>
56
- </div>
57
- <span style={{ fontSize: 16, color: 'var(--text-muted)' }}>&rarr;</span>
58
- </div>
59
- </Link>
54
+ <div className="grid-5">
55
+ <MetricCard label="Total skills" value={summary.skills.total} sublabel={`${summary.skills.evolved} evolved ${summary.skillTests} skill tests`} tone="purple" href="/network" icon="◆" />
56
+ <MetricCard label="Accepted proposals" value={summary.evolution.accepted} sublabel={`${summary.evolution.rejected} rejected`} tone="green" href="/evolution" icon="✓" />
57
+ <MetricCard label="Unresolved corrections" value={summary.failures.unresolved} sublabel={`out of ${summary.failures.total} captured failures`} tone={summary.failures.unresolved > 0 ? 'yellow' : 'green'} href={summary.failures.unresolved > 0 ? '#attention' : '/evolution'} icon="!" />
58
+ <MetricCard label="Discoveries" value={summary.buffer.discoveries} sublabel={`${summary.buffer.drafts} drafts in progress`} tone="blue" href="/research" icon="◎" />
59
+ <MetricCard label="Frontier candidates" value={frontier.programs.length} sublabel={`${summary.canaries} active canaries`} tone="neutral" href="/frontier" icon="▲" />
60
+ </div>
60
61
 
61
- {/* Quick Actions */}
62
- <div className="card" style={{ marginBottom: 20, padding: '14px 18px' }}>
63
- <div style={{ fontSize: 9, fontWeight: 600, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: 1.5, marginBottom: 10 }}>
64
- Quick Actions
65
- </div>
62
+ <SectionFrame
63
+ eyebrow="Execution layer"
64
+ title="Quick actions"
65
+ description="Run the highest-leverage HelixEvo workflows from one place. Each action flows from signal to judgment to applied change."
66
+ >
66
67
  <OverviewActions
67
68
  hasSkills={graph.nodes.length > 0}
68
69
  hasFailures={failures.length > 0}
69
70
  hasEdges={graph.edges.length > 0}
70
- unresolvedCount={s.failures.unresolved}
71
+ unresolvedCount={summary.failures.unresolved}
71
72
  skillCount={graph.nodes.length}
72
73
  projectList={listProjects()}
73
74
  />
74
- </div>
75
-
76
- {/* Stats — all clickable, linking to relevant pages */}
77
- <div className="grid-5" style={{ marginBottom: 28 }}>
78
- <StatCard value={s.skills.total} label="Total Skills" color="var(--purple)" sub={`${s.skills.evolved} evolved`} accent="◆" href="/network" />
79
- <StatCard value={s.evolution.accepted} label="Accepted" color="var(--green)" sub={`${s.evolution.rejected} rejected`} accent="✓" href="/evolution" />
80
- <StatCard value={s.failures.unresolved} label="Unresolved" color="var(--yellow)" sub={`of ${s.failures.total} total`} accent="!" href="#unresolved" />
81
- <StatCard value={s.buffer.discoveries} label="Discoveries" color="var(--blue)" sub={`${s.buffer.drafts} drafts`} accent="◎" href="/research" />
82
- <StatCard value={frontier.programs.length} label="Frontier" color="var(--text-secondary)" sub={`/${frontier.capacity} capacity`} accent="▲" href="/frontier" />
83
- </div>
75
+ </SectionFrame>
84
76
 
85
- {/* Unresolved Failures */}
86
- {failures.filter(f => !f.resolved).length > 0 && (
87
- <div id="unresolved" className="card" style={{ marginBottom: 20 }}>
88
- <div className="card-body">
89
- <div className="card-header-label">
90
- Unresolved Corrections ({failures.filter(f => !f.resolved).length})
91
- </div>
92
- <div style={{ fontSize: 12, color: 'var(--text-dim)', marginBottom: 14, lineHeight: 1.5 }}>
93
- These are corrections you made that haven&apos;t been incorporated into skills yet.
94
- Click <strong style={{ color: 'var(--text)' }}>Evolve</strong> above to improve skills based on these corrections.
95
- </div>
96
- <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
97
- {failures.filter(f => !f.resolved).slice(0, 5).map((f, i) => (
98
- <div key={`${f.id}-${i}`} style={{
99
- padding: '10px 14px', borderRadius: 'var(--radius)',
100
- background: 'var(--bg-section)',
101
- borderLeft: '3px solid var(--yellow)',
102
- }}>
103
- <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--text)', marginBottom: 3 }}>
104
- {f.userRequest.slice(0, 100)}{f.userRequest.length > 100 ? '...' : ''}
77
+ {unresolved.length > 0 ? (
78
+ <SectionFrame
79
+ eyebrow="Attention"
80
+ title="Unresolved corrections"
81
+ description="These user corrections have been captured but not yet folded back into the skill network."
82
+ tone="yellow"
83
+ className="anchor-target"
84
+ >
85
+ <div id="attention" className="signal-list">
86
+ {unresolved.slice(0, 5).map((failure, index) => (
87
+ <div key={`${failure.id}-${index}`} className="signal-row signal-row-attention">
88
+ <div className="signal-dot" />
89
+ <div style={{ flex: 1 }}>
90
+ <div className="signal-title">
91
+ {failure.userRequest.slice(0, 120)}
92
+ {failure.userRequest.length > 120 ? '…' : ''}
105
93
  </div>
106
- <div style={{ fontSize: 11, color: 'var(--text-dim)', lineHeight: 1.4 }}>
107
- Correction: {f.correction.slice(0, 120)}{f.correction.length > 120 ? '...' : ''}
94
+ <div className="signal-text">
95
+ Correction: {failure.correction.slice(0, 180)}
96
+ {failure.correction.length > 180 ? '…' : ''}
108
97
  </div>
109
- <div style={{ display: 'flex', gap: 6, marginTop: 6 }}>
110
- <span className="badge badge-yellow">{f.correctionType}</span>
111
- {f.project && <span className="badge badge-gray">{f.project}</span>}
112
- <span style={{ fontSize: 10, color: 'var(--text-muted)' }}>
113
- {new Date(f.timestamp).toLocaleDateString()}
114
- </span>
98
+ <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 10 }}>
99
+ <span className="badge badge-yellow">{failure.correctionType}</span>
100
+ {failure.project ? <span className="badge badge-gray">{failure.project}</span> : null}
101
+ <span className="badge badge-gray">{new Date(failure.timestamp).toLocaleDateString()}</span>
115
102
  </div>
116
103
  </div>
117
- ))}
118
- {failures.filter(f => !f.resolved).length > 5 && (
119
- <div style={{ fontSize: 11, color: 'var(--text-muted)', textAlign: 'center', padding: '6px 0' }}>
120
- +{failures.filter(f => !f.resolved).length - 5} more corrections
121
- </div>
122
- )}
123
- </div>
104
+ </div>
105
+ ))}
106
+ {unresolved.length > 5 ? (
107
+ <div className="signal-text" style={{ textAlign: 'center' }}>+{unresolved.length - 5} more unresolved corrections</div>
108
+ ) : null}
124
109
  </div>
125
- </div>
126
- )}
110
+ </SectionFrame>
111
+ ) : null}
127
112
 
128
113
  <div className="grid-2">
129
- {/* Frontier */}
130
- <Link href="/frontier" style={{ textDecoration: 'none', color: 'inherit' }}>
131
- <div className="card" style={{ cursor: 'pointer', height: '100%' }}>
132
- <div className="card-body">
133
- <div className="card-header-label">Pareto Frontier</div>
134
- {frontier.programs.map((p, i) => (
135
- <div key={`${p.id}-${i}`} style={{
136
- padding: '12px 0',
137
- borderBottom: i < frontier.programs.length - 1 ? '1px solid var(--border-subtle)' : 'none',
138
- }}>
139
- <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
140
- <span style={{ fontSize: 13, fontWeight: 600 }}>{p.id}</span>
141
- <span style={{
142
- fontSize: 20, fontWeight: 800, lineHeight: 1,
143
- color: p.score >= 0.8 ? 'var(--green)' : 'var(--yellow)',
144
- }}>
145
- {(p.score * 100).toFixed(0)}
146
- </span>
147
- </div>
148
- <div style={{ display: 'flex', gap: 6, fontSize: 11 }}>
149
- <span className="score-pill" style={{ color: 'var(--green)' }}>T:{(p.scores.taskCompletion * 10).toFixed(0)}</span>
150
- <span className="score-pill" style={{ color: 'var(--blue)' }}>A:{(p.scores.correctionAlignment * 10).toFixed(0)}</span>
151
- <span className="score-pill" style={{ color: 'var(--purple)' }}>S:{(p.scores.sideEffectFree * 10).toFixed(0)}</span>
152
- </div>
153
- <div style={{ fontSize: 11, color: 'var(--text-dim)', marginTop: 4, lineHeight: 1.4 }}>
154
- {p.changesDescription.slice(0, 80)}
114
+ <SectionFrame
115
+ eyebrow="Frontier"
116
+ title="Current leaders"
117
+ description="Top-performing configurations ranked by composite strength across task completion, correction alignment, and side-effect safety."
118
+ actions={<Link href="/frontier" className="badge badge-gray">Open frontier</Link>}
119
+ >
120
+ <div className="summary-list">
121
+ {frontier.programs.map((program, index) => (
122
+ <Link key={`${program.id}-${index}`} href="/frontier" className="summary-row">
123
+ <div className="summary-row-main">
124
+ <div className="summary-row-title">{program.id}</div>
125
+ <div className="summary-row-meta">{program.changesDescription.slice(0, 90)}{program.changesDescription.length > 90 ? '…' : ''}</div>
126
+ <div style={{ display: 'flex', gap: 6, marginTop: 8, flexWrap: 'wrap' }}>
127
+ <span className="score-pill" style={{ color: 'var(--green)' }}>T {(program.scores.taskCompletion * 10).toFixed(0)}</span>
128
+ <span className="score-pill" style={{ color: 'var(--blue)' }}>A {(program.scores.correctionAlignment * 10).toFixed(0)}</span>
129
+ <span className="score-pill" style={{ color: 'var(--purple)' }}>S {(program.scores.sideEffectFree * 10).toFixed(0)}</span>
155
130
  </div>
156
131
  </div>
157
- ))}
158
- </div>
132
+ <div style={{ fontSize: 26, fontWeight: 850, color: scoreColor(program.score), lineHeight: 1 }}>
133
+ {(program.score * 100).toFixed(0)}
134
+ </div>
135
+ </Link>
136
+ ))}
137
+ {frontier.programs.length === 0 ? (
138
+ <div className="empty-state" style={{ padding: 24 }}>
139
+ <div className="empty-state-title">Frontier is empty</div>
140
+ <div className="empty-state-desc">Run <code>helixevo evolve</code> to populate candidate configurations.</div>
141
+ </div>
142
+ ) : null}
159
143
  </div>
160
- </Link>
144
+ </SectionFrame>
161
145
 
162
- {/* Recent Evolution */}
163
- <Link href="/evolution" style={{ textDecoration: 'none', color: 'inherit' }}>
164
- <div className="card" style={{ cursor: 'pointer', height: '100%' }}>
165
- <div className="card-body">
166
- <div className="card-header-label">Recent Evolution</div>
167
- {recent.map((iter, idx) => (
168
- <div key={`${iter.id}-${idx}`} style={{
169
- padding: '10px 0',
170
- borderBottom: '1px solid var(--border-subtle)',
171
- }}>
172
- <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11, marginBottom: 8 }}>
173
- <span style={{ fontWeight: 600, color: 'var(--text)' }}>{iter.id}</span>
174
- <span style={{ color: 'var(--text-muted)' }}>{new Date(iter.timestamp).toLocaleDateString()}</span>
146
+ <SectionFrame
147
+ eyebrow="Evolution"
148
+ title="Recent runs"
149
+ description="A compact chronicle of the latest evolution loops and the proposals they generated."
150
+ actions={<Link href="/evolution" className="badge badge-gray">Open timeline</Link>}
151
+ >
152
+ <div className="summary-list">
153
+ {recentRuns.map((iteration, index) => (
154
+ <Link key={`${iteration.id}-${index}`} href="/evolution" className="summary-row" style={{ alignItems: 'flex-start' }}>
155
+ <div className="summary-row-main">
156
+ <div className="summary-row-title">{iteration.id}</div>
157
+ <div className="summary-row-meta">
158
+ {new Date(iteration.timestamp).toLocaleDateString()} • {iteration.trigger} • {iteration.failureCount} failures
159
+ </div>
160
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginTop: 10 }}>
161
+ {iteration.proposals.slice(0, 3).map((proposal) => (
162
+ <div key={proposal.id} style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
163
+ <span style={{ width: 8, height: 8, borderRadius: 999, background: proposal.outcome === 'accepted' ? 'var(--green)' : 'var(--red)', flexShrink: 0 }} />
164
+ <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--text)' }}>{proposal.targetSkill}</span>
165
+ <span className="badge badge-gray">{proposal.action}</span>
166
+ </div>
167
+ ))}
175
168
  </div>
176
- {iter.proposals.map(p => (
177
- <div key={p.id} style={{
178
- display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, marginBottom: 4,
179
- padding: '4px 0',
180
- }}>
181
- <span style={{
182
- width: 8, height: 8, borderRadius: '50%', flexShrink: 0,
183
- background: p.outcome === 'accepted' ? 'var(--green)' : 'var(--red)',
184
- }} />
185
- <span style={{ fontWeight: 500 }}>{p.targetSkill}</span>
186
- <span className="badge badge-gray" style={{ fontSize: 9 }}>{p.action}</span>
187
- </div>
188
- ))}
189
169
  </div>
190
- ))}
191
- </div>
170
+ </Link>
171
+ ))}
172
+ {recentRuns.length === 0 ? (
173
+ <div className="empty-state" style={{ padding: 24 }}>
174
+ <div className="empty-state-title">No evolution runs yet</div>
175
+ <div className="empty-state-desc">Start with <code>helixevo evolve</code> to create your first chronicle entry.</div>
176
+ </div>
177
+ ) : null}
192
178
  </div>
193
- </Link>
179
+ </SectionFrame>
194
180
  </div>
195
181
 
196
- {/* All Skills Summary */}
197
- <Link href="/network" style={{ textDecoration: 'none', color: 'inherit' }}>
198
- <div className="card" style={{ marginTop: 16, cursor: 'pointer' }}>
199
- <div className="card-body">
200
- <div className="card-header-label">All Skills</div>
201
- <div className="grid-2" style={{ gap: 6 }}>
202
- {graph.nodes.sort((a, b) => b.score - a.score).map(n => (
203
- <div key={n.id} style={{
204
- display: 'flex', alignItems: 'center', justifyContent: 'space-between',
205
- padding: '8px 12px', borderRadius: 8, background: 'var(--bg-section)',
206
- }}>
207
- <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>
208
- <span style={{ fontSize: 13, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{n.name}</span>
209
- {n.generation > 0 && <span className="badge badge-green" style={{ fontSize: 9 }}>gen {n.generation}</span>}
210
- </div>
211
- <div style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 90, flexShrink: 0 }}>
212
- <div className="score-track" style={{ width: 50 }}>
213
- <div className="score-fill" style={{
214
- width: `${n.score * 100}%`,
215
- background: n.score >= 0.8 ? 'var(--green)' : n.score >= 0.6 ? 'var(--yellow)' : 'var(--red)',
216
- }} />
217
- </div>
218
- <span style={{ fontSize: 12, fontWeight: 600, color: n.score >= 0.8 ? 'var(--green)' : 'var(--yellow)' }}>
219
- {(n.score * 100).toFixed(0)}
220
- </span>
221
- </div>
182
+ <SectionFrame
183
+ eyebrow="Network health"
184
+ title="Top skill signals"
185
+ description="Highest-scoring skills right now, ordered by current quality score and generation."
186
+ actions={<Link href="/network" className="badge badge-gray">Open skill network</Link>}
187
+ >
188
+ <div className="skill-score-grid">
189
+ {topSkills.map((skill) => (
190
+ <Link key={skill.id} href="/network" className="skill-score-row">
191
+ <div className="skill-score-main">
192
+ <div className="skill-score-name">
193
+ <span>{skill.name}</span>
194
+ {skill.generation > 0 ? <span className="badge badge-green">gen {skill.generation}</span> : null}
222
195
  </div>
223
- ))}
224
- </div>
225
- </div>
196
+ </div>
197
+ <div className="skill-score-value">
198
+ <div className="score-track" style={{ width: 72 }}>
199
+ <div className="score-fill" style={{ width: `${skill.score * 100}%`, background: scoreColor(skill.score) }} />
200
+ </div>
201
+ <span style={{ minWidth: 34, textAlign: 'right', fontWeight: 800, color: scoreColor(skill.score) }}>
202
+ {(skill.score * 100).toFixed(0)}
203
+ </span>
204
+ </div>
205
+ </Link>
206
+ ))}
226
207
  </div>
227
- </Link>
208
+ </SectionFrame>
228
209
  </div>
229
210
  )
230
211
  }