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.
- package/CHANGELOG.md +31 -0
- package/dashboard/app/api/browse/route.ts +66 -0
- package/dashboard/app/changelog/page.tsx +179 -54
- package/dashboard/app/commands/page.tsx +232 -155
- package/dashboard/app/evolution/page.tsx +105 -102
- package/dashboard/app/frontier/page.tsx +103 -100
- package/dashboard/app/globals.css +1105 -403
- package/dashboard/app/guide/page.tsx +46 -2
- package/dashboard/app/layout.tsx +28 -57
- package/dashboard/app/network/client.tsx +453 -269
- package/dashboard/app/network/page.tsx +12 -2
- package/dashboard/app/page.tsx +166 -185
- package/dashboard/app/projects/client.tsx +923 -400
- package/dashboard/app/research/client.tsx +180 -248
- package/dashboard/components/SkillFlowNode.tsx +86 -128
- package/dashboard/components/console-panel.tsx +25 -0
- package/dashboard/components/metric-card.tsx +45 -0
- package/dashboard/components/overview-actions.tsx +29 -40
- package/dashboard/components/page-hero.tsx +44 -0
- package/dashboard/components/quick-actions.tsx +93 -167
- package/dashboard/components/section-frame.tsx +35 -0
- package/dashboard/components/sidebar-nav.tsx +75 -0
- package/dashboard/components/update-banner.tsx +101 -145
- package/dashboard/lib/data.ts +2 -2
- package/package.json +1 -1
|
@@ -20,170 +20,128 @@ interface SkillNodeData {
|
|
|
20
20
|
|
|
21
21
|
const CATEGORY_STYLES = {
|
|
22
22
|
generalized: {
|
|
23
|
-
border: '#
|
|
24
|
-
|
|
25
|
-
headerBg: '#ede9fe',
|
|
26
|
-
accent: '#7c3aed',
|
|
23
|
+
border: '#c7b8ff',
|
|
24
|
+
accent: '#6b49df',
|
|
27
25
|
label: 'GENERALIZED',
|
|
26
|
+
bg: 'linear-gradient(180deg, rgba(250,245,255,0.98) 0%, rgba(244,238,255,0.95) 100%)',
|
|
27
|
+
header: 'linear-gradient(180deg, rgba(237,230,255,0.98), rgba(247,242,255,0.88))',
|
|
28
28
|
},
|
|
29
29
|
evolved: {
|
|
30
|
-
border: '#
|
|
31
|
-
|
|
32
|
-
headerBg: '#dcfce7',
|
|
33
|
-
accent: '#16a34a',
|
|
30
|
+
border: '#b4dfd2',
|
|
31
|
+
accent: '#0f8363',
|
|
34
32
|
label: 'EVOLVED',
|
|
33
|
+
bg: 'linear-gradient(180deg, rgba(241,252,247,0.98) 0%, rgba(236,249,243,0.95) 100%)',
|
|
34
|
+
header: 'linear-gradient(180deg, rgba(223,245,236,0.98), rgba(243,252,248,0.9))',
|
|
35
35
|
},
|
|
36
36
|
original: {
|
|
37
|
-
border: '#
|
|
38
|
-
|
|
39
|
-
headerBg: '#f3f4f6',
|
|
40
|
-
accent: '#6b7280',
|
|
37
|
+
border: '#ddd6cd',
|
|
38
|
+
accent: '#676259',
|
|
41
39
|
label: 'ORIGINAL',
|
|
40
|
+
bg: 'linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(249,246,241,0.95) 100%)',
|
|
41
|
+
header: 'linear-gradient(180deg, rgba(244,240,235,0.96), rgba(255,255,255,0.92))',
|
|
42
42
|
},
|
|
43
43
|
project: {
|
|
44
|
-
border: '#
|
|
45
|
-
|
|
46
|
-
headerBg: '#fef3c7',
|
|
47
|
-
accent: '#b45309',
|
|
44
|
+
border: '#ecd28f',
|
|
45
|
+
accent: '#b67b12',
|
|
48
46
|
label: 'PROJECT',
|
|
47
|
+
bg: 'linear-gradient(180deg, rgba(255,252,242,0.98) 0%, rgba(255,247,228,0.95) 100%)',
|
|
48
|
+
header: 'linear-gradient(180deg, rgba(255,243,208,0.98), rgba(255,250,240,0.9))',
|
|
49
49
|
},
|
|
50
|
+
} as const
|
|
51
|
+
|
|
52
|
+
function metricPill(label: string, value: number, color: string) {
|
|
53
|
+
return (
|
|
54
|
+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, padding: '4px 8px', borderRadius: 999, background: 'rgba(255,255,255,0.76)', border: '1px solid rgba(123,109,91,0.12)', fontSize: 9.5, fontWeight: 700, color }}>
|
|
55
|
+
<span>{label}</span>
|
|
56
|
+
<span>{value}</span>
|
|
57
|
+
</span>
|
|
58
|
+
)
|
|
50
59
|
}
|
|
51
60
|
|
|
52
61
|
function SkillFlowNode({ data }: { data: SkillNodeData }) {
|
|
53
62
|
const style = CATEGORY_STYLES[data.category]
|
|
54
63
|
const scorePercent = Math.round(data.score * 100)
|
|
55
|
-
const scoreColor = data.score >= 0.8 ? '#
|
|
64
|
+
const scoreColor = data.score >= 0.8 ? '#0f8363' : data.score >= 0.6 ? '#c18a17' : '#cb4f40'
|
|
65
|
+
const summary = data.evolutionSummary[0] ?? data.description
|
|
66
|
+
const relationCount = data.inheritsFrom.length + data.children.length + data.enhances.length + data.conflicts.length
|
|
56
67
|
|
|
57
68
|
return (
|
|
58
|
-
<div
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
<div
|
|
70
|
+
style={{
|
|
71
|
+
width: 304,
|
|
72
|
+
borderRadius: 18,
|
|
73
|
+
overflow: 'hidden',
|
|
74
|
+
border: `1.5px solid ${style.border}`,
|
|
75
|
+
background: style.bg,
|
|
76
|
+
boxShadow: '0 18px 34px rgba(48, 30, 12, 0.12), 0 2px 8px rgba(48, 30, 12, 0.05)',
|
|
77
|
+
backdropFilter: 'blur(12px)',
|
|
78
|
+
color: '#171613',
|
|
79
|
+
fontFamily: 'Inter, system-ui, sans-serif',
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
68
82
|
<Handle type="target" position={Position.Top} style={{ background: style.border, width: 8, height: 8, border: '2px solid #fff' }} />
|
|
69
83
|
<Handle type="source" position={Position.Bottom} style={{ background: style.border, width: 8, height: 8, border: '2px solid #fff' }} />
|
|
70
84
|
<Handle type="target" position={Position.Left} id="left" style={{ background: style.border, width: 6, height: 6, border: '2px solid #fff' }} />
|
|
71
85
|
<Handle type="source" position={Position.Right} id="right" style={{ background: style.border, width: 6, height: 6, border: '2px solid #fff' }} />
|
|
72
86
|
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
fontSize:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
color: style.accent,
|
|
88
|
-
textTransform: 'uppercase',
|
|
89
|
-
marginBottom: 3,
|
|
90
|
-
}}>
|
|
91
|
-
{style.label}
|
|
92
|
-
</div>
|
|
93
|
-
<div style={{
|
|
94
|
-
fontSize: 14,
|
|
95
|
-
fontWeight: 700,
|
|
96
|
-
color: '#1a1d2e',
|
|
97
|
-
lineHeight: 1.3,
|
|
98
|
-
overflow: 'hidden',
|
|
99
|
-
textOverflow: 'ellipsis',
|
|
100
|
-
whiteSpace: 'nowrap',
|
|
101
|
-
}}>
|
|
102
|
-
{data.name}
|
|
87
|
+
<div style={{ padding: '14px 16px 12px', background: style.header, borderBottom: `1px solid ${style.border}` }}>
|
|
88
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, alignItems: 'flex-start' }}>
|
|
89
|
+
<div style={{ minWidth: 0, flex: 1 }}>
|
|
90
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 6, flexWrap: 'wrap' }}>
|
|
91
|
+
<span style={{ display: 'inline-flex', alignItems: 'center', padding: '3px 8px', borderRadius: 999, background: `${style.accent}14`, color: style.accent, border: `1px solid ${style.accent}22`, fontSize: 9.5, fontWeight: 800, letterSpacing: '0.1em' }}>
|
|
92
|
+
{style.label}
|
|
93
|
+
</span>
|
|
94
|
+
<span style={{ fontSize: 9.5, color: '#6d675f', fontWeight: 700, letterSpacing: '0.08em' }}>
|
|
95
|
+
{data.layer.toUpperCase()}
|
|
96
|
+
</span>
|
|
97
|
+
</div>
|
|
98
|
+
<div style={{ fontSize: 15, fontWeight: 800, lineHeight: 1.22, letterSpacing: '-0.03em', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
|
99
|
+
{data.name}
|
|
100
|
+
</div>
|
|
103
101
|
</div>
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
alignItems: 'center',
|
|
109
|
-
marginLeft: 10,
|
|
110
|
-
flexShrink: 0,
|
|
111
|
-
}}>
|
|
112
|
-
<div style={{
|
|
113
|
-
fontSize: 20,
|
|
114
|
-
fontWeight: 800,
|
|
115
|
-
color: scoreColor,
|
|
116
|
-
lineHeight: 1,
|
|
117
|
-
}}>
|
|
118
|
-
{scorePercent}
|
|
102
|
+
|
|
103
|
+
<div style={{ minWidth: 64, textAlign: 'right' }}>
|
|
104
|
+
<div style={{ fontSize: 24, fontWeight: 850, lineHeight: 1, color: scoreColor }}>{scorePercent}</div>
|
|
105
|
+
<div style={{ marginTop: 4, fontSize: 8.5, fontWeight: 800, letterSpacing: '0.16em', color: '#8f877d' }}>SCORE</div>
|
|
119
106
|
</div>
|
|
120
|
-
<div style={{ fontSize: 8, color: '#9ca3af', marginTop: 1, fontWeight: 500 }}>SCORE</div>
|
|
121
107
|
</div>
|
|
122
|
-
</div>
|
|
123
108
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
|
|
128
|
-
<div style={{ flex: 1, height: 4, background: '#e5e7eb', borderRadius: 2, overflow: 'hidden' }}>
|
|
129
|
-
<div style={{ width: `${scorePercent}%`, height: '100%', background: `linear-gradient(90deg, ${style.accent}80, ${scoreColor})`, borderRadius: 2, transition: 'width 0.5s ease' }} />
|
|
109
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 12 }}>
|
|
110
|
+
<div style={{ flex: 1, height: 6, borderRadius: 999, overflow: 'hidden', background: 'rgba(108,99,90,0.14)' }}>
|
|
111
|
+
<div style={{ width: `${scorePercent}%`, height: '100%', borderRadius: 999, background: `linear-gradient(90deg, ${style.accent}, ${scoreColor})` }} />
|
|
130
112
|
</div>
|
|
131
|
-
{data.generation > 0
|
|
132
|
-
<span style={{
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
)}
|
|
113
|
+
{data.generation > 0 ? (
|
|
114
|
+
<span style={{ flexShrink: 0, padding: '3px 8px', borderRadius: 999, background: 'rgba(15,131,99,0.12)', color: '#0f8363', fontSize: 9.5, fontWeight: 800 }}>
|
|
115
|
+
gen {data.generation}
|
|
116
|
+
</span>
|
|
117
|
+
) : null}
|
|
137
118
|
</div>
|
|
119
|
+
</div>
|
|
138
120
|
|
|
139
|
-
|
|
140
|
-
{
|
|
141
|
-
<div style={{ marginBottom:
|
|
142
|
-
{
|
|
143
|
-
<div key={i} style={{
|
|
144
|
-
fontSize: 10, color: '#4b5563', lineHeight: 1.4, marginBottom: 3,
|
|
145
|
-
paddingLeft: 8, borderLeft: `2px solid ${style.accent}40`,
|
|
146
|
-
}}>
|
|
147
|
-
{s.slice(0, 70)}{s.length > 70 ? '...' : ''}
|
|
148
|
-
</div>
|
|
149
|
-
))}
|
|
150
|
-
</div>
|
|
151
|
-
) : data.description ? (
|
|
152
|
-
<div style={{ fontSize: 10, color: '#6b7280', lineHeight: 1.4, marginBottom: 8 }}>
|
|
153
|
-
{data.description.slice(0, 90)}{data.description.length > 90 ? '...' : ''}
|
|
121
|
+
<div style={{ padding: '14px 16px 16px' }}>
|
|
122
|
+
{summary ? (
|
|
123
|
+
<div style={{ marginBottom: 12, padding: '10px 11px', borderRadius: 12, background: 'rgba(255,255,255,0.72)', border: '1px solid rgba(123,109,91,0.12)', fontSize: 10.5, lineHeight: 1.55, color: '#4d4942' }}>
|
|
124
|
+
{summary.slice(0, 118)}{summary.length > 118 ? '…' : ''}
|
|
154
125
|
</div>
|
|
155
126
|
) : null}
|
|
156
127
|
|
|
157
|
-
{
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
<span style={{ fontSize: 9, color: '#818cf8', fontWeight: 500 }}>↓ {data.children.length}</span>
|
|
165
|
-
)}
|
|
166
|
-
{data.enhances.length > 0 && (
|
|
167
|
-
<span style={{ fontSize: 9, color: '#22c55e', fontWeight: 500 }}>↔ {data.enhances.length}</span>
|
|
168
|
-
)}
|
|
169
|
-
{data.conflicts.length > 0 && (
|
|
170
|
-
<span style={{ fontSize: 9, color: '#ef4444', fontWeight: 500 }}>⚡ {data.conflicts.length}</span>
|
|
171
|
-
)}
|
|
172
|
-
</div>
|
|
173
|
-
)}
|
|
128
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: data.tags.length > 0 ? 10 : 0 }}>
|
|
129
|
+
{data.inheritsFrom.length > 0 ? metricPill('↑ parents', data.inheritsFrom.length, '#6b49df') : null}
|
|
130
|
+
{data.children.length > 0 ? metricPill('↓ children', data.children.length, '#2d66d8') : null}
|
|
131
|
+
{data.enhances.length > 0 ? metricPill('↔ enhances', data.enhances.length, '#0f8363') : null}
|
|
132
|
+
{data.conflicts.length > 0 ? metricPill('⚡ conflicts', data.conflicts.length, '#cb4f40') : null}
|
|
133
|
+
{relationCount === 0 ? metricPill('• isolated', 0, '#8c877f') : null}
|
|
134
|
+
</div>
|
|
174
135
|
|
|
175
|
-
{
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
background: `${style.accent}10`, color: style.accent, fontWeight: 500,
|
|
182
|
-
border: `1px solid ${style.accent}20`,
|
|
183
|
-
}}>{t}</span>
|
|
136
|
+
{data.tags.length > 0 ? (
|
|
137
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
|
|
138
|
+
{data.tags.slice(0, 4).map((tag) => (
|
|
139
|
+
<span key={tag} style={{ display: 'inline-flex', alignItems: 'center', padding: '3px 8px', borderRadius: 999, background: `${style.accent}10`, color: style.accent, border: `1px solid ${style.accent}20`, fontSize: 9.5, fontWeight: 700 }}>
|
|
140
|
+
{tag}
|
|
141
|
+
</span>
|
|
184
142
|
))}
|
|
185
143
|
</div>
|
|
186
|
-
)}
|
|
144
|
+
) : null}
|
|
187
145
|
</div>
|
|
188
146
|
</div>
|
|
189
147
|
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from 'react'
|
|
4
|
+
|
|
5
|
+
export function ConsolePanel({
|
|
6
|
+
title,
|
|
7
|
+
tone = 'neutral',
|
|
8
|
+
actions,
|
|
9
|
+
children,
|
|
10
|
+
}: {
|
|
11
|
+
title: ReactNode
|
|
12
|
+
tone?: 'neutral' | 'green' | 'red' | 'yellow' | 'blue' | 'purple'
|
|
13
|
+
actions?: ReactNode
|
|
14
|
+
children: ReactNode
|
|
15
|
+
}) {
|
|
16
|
+
return (
|
|
17
|
+
<div className={`console-panel console-panel-${tone}`}>
|
|
18
|
+
<div className="console-panel-header">
|
|
19
|
+
<div className="console-panel-title">{title}</div>
|
|
20
|
+
{actions ? <div className="console-panel-actions">{actions}</div> : null}
|
|
21
|
+
</div>
|
|
22
|
+
<div className="console-panel-body">{children}</div>
|
|
23
|
+
</div>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link'
|
|
4
|
+
import type { ReactNode } from 'react'
|
|
5
|
+
|
|
6
|
+
function toneClass(tone?: string) {
|
|
7
|
+
return tone ? `metric-card-${tone}` : 'metric-card-neutral'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function MetricCard({
|
|
11
|
+
label,
|
|
12
|
+
value,
|
|
13
|
+
sublabel,
|
|
14
|
+
icon,
|
|
15
|
+
tone = 'neutral',
|
|
16
|
+
href,
|
|
17
|
+
}: {
|
|
18
|
+
label: string
|
|
19
|
+
value: ReactNode
|
|
20
|
+
sublabel?: ReactNode
|
|
21
|
+
icon?: ReactNode
|
|
22
|
+
tone?: 'neutral' | 'purple' | 'green' | 'blue' | 'yellow' | 'red'
|
|
23
|
+
href?: string
|
|
24
|
+
}) {
|
|
25
|
+
const content = (
|
|
26
|
+
<div className={`metric-card ${toneClass(tone)} ${href ? 'metric-card-link' : ''}`}>
|
|
27
|
+
<div className="metric-card-header">
|
|
28
|
+
<div>
|
|
29
|
+
<div className="metric-card-label">{label}</div>
|
|
30
|
+
<div className="metric-card-value">{value}</div>
|
|
31
|
+
</div>
|
|
32
|
+
{icon ? <div className="metric-card-icon">{icon}</div> : null}
|
|
33
|
+
</div>
|
|
34
|
+
{sublabel ? <div className="metric-card-sublabel">{sublabel}</div> : null}
|
|
35
|
+
</div>
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if (!href) return content
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Link href={href} className="metric-card-anchor">
|
|
42
|
+
{content}
|
|
43
|
+
</Link>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
@@ -11,105 +11,94 @@ interface OverviewActionsProps {
|
|
|
11
11
|
projectList: string[]
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
// Small inline SVG flow diagrams for each action
|
|
15
14
|
function FlowDiagram({ steps, color }: { steps: string[]; color: string }) {
|
|
16
15
|
return (
|
|
17
|
-
<div
|
|
18
|
-
{steps.map((step,
|
|
19
|
-
<div key={
|
|
20
|
-
<
|
|
21
|
-
fontSize: 9, fontWeight: 600, padding: '2px 6px',
|
|
22
|
-
background: i === steps.length - 1 ? `${color}18` : 'var(--bg-section)',
|
|
23
|
-
color: i === steps.length - 1 ? color : 'var(--text-dim)',
|
|
24
|
-
borderRadius: 4, whiteSpace: 'nowrap',
|
|
25
|
-
border: i === steps.length - 1 ? `1px solid ${color}30` : '1px solid var(--border-subtle)',
|
|
26
|
-
}}>
|
|
16
|
+
<div className="flow-diagram">
|
|
17
|
+
{steps.map((step, index) => (
|
|
18
|
+
<div key={`${step}-${index}`} style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
19
|
+
<span className={`flow-step ${index === steps.length - 1 ? 'flow-step-terminal' : ''}`} style={{ color: index === steps.length - 1 ? color : 'var(--text-secondary)', background: index === steps.length - 1 ? `${color}16` : undefined }}>
|
|
27
20
|
{step}
|
|
28
|
-
</
|
|
29
|
-
{
|
|
30
|
-
<span style={{ fontSize: 9, color: 'var(--text-muted)', padding: '0 2px' }}>→</span>
|
|
31
|
-
)}
|
|
21
|
+
</span>
|
|
22
|
+
{index < steps.length - 1 ? <span className="flow-connector">→</span> : null}
|
|
32
23
|
</div>
|
|
33
24
|
))}
|
|
34
25
|
</div>
|
|
35
26
|
)
|
|
36
27
|
}
|
|
37
28
|
|
|
38
|
-
export function OverviewActions({ hasFailures, hasSkills, hasEdges, unresolvedCount, skillCount
|
|
29
|
+
export function OverviewActions({ hasFailures, hasSkills, hasEdges, unresolvedCount, skillCount }: OverviewActionsProps) {
|
|
39
30
|
const actions = [
|
|
40
31
|
{
|
|
41
32
|
id: 'graph-rebuild',
|
|
42
33
|
label: 'Organize Skills',
|
|
43
|
-
subtitle: `
|
|
34
|
+
subtitle: `Map how your ${skillCount} skills support, inherit from, or conflict with each other.`,
|
|
44
35
|
command: 'graph-rebuild',
|
|
45
36
|
icon: 'M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1',
|
|
46
37
|
color: 'var(--purple)',
|
|
47
|
-
description: 'Use LLM to infer relationships between skills',
|
|
38
|
+
description: 'Use LLM analysis to infer relationships between skills',
|
|
48
39
|
disabled: !hasSkills,
|
|
49
|
-
disabledReason: 'No skills imported yet — run helixevo init first',
|
|
50
|
-
flowSteps: [`${skillCount} skills`, '
|
|
40
|
+
disabledReason: 'No skills imported yet — run helixevo init first.',
|
|
41
|
+
flowSteps: [`${skillCount} skills`, 'Relationship scan', 'Network map'],
|
|
51
42
|
},
|
|
52
43
|
{
|
|
53
44
|
id: 'generalize',
|
|
54
45
|
label: 'Generalize',
|
|
55
|
-
subtitle: '
|
|
46
|
+
subtitle: 'Promote repeated patterns upward into clearer, reusable abstractions.',
|
|
56
47
|
command: 'generalize',
|
|
57
48
|
icon: 'M5 10l7-7m0 0l7 7m-7-7v18',
|
|
58
49
|
color: 'var(--blue)',
|
|
59
50
|
description: 'Promote cross-skill patterns to abstract parent skills',
|
|
60
51
|
disabled: !hasSkills,
|
|
61
|
-
disabledReason: 'No skills imported yet',
|
|
62
|
-
flowSteps: ['
|
|
52
|
+
disabledReason: 'No skills imported yet.',
|
|
53
|
+
flowSteps: ['Patterns', 'Abstraction', 'Higher-level skills'],
|
|
63
54
|
},
|
|
64
55
|
{
|
|
65
56
|
id: 'evolve',
|
|
66
|
-
label:
|
|
57
|
+
label: unresolvedCount > 0 ? `Evolve (${unresolvedCount})` : 'Evolve',
|
|
67
58
|
subtitle: hasFailures
|
|
68
|
-
? `
|
|
69
|
-
: 'Use
|
|
59
|
+
? `Turn ${unresolvedCount} unresolved correction${unresolvedCount === 1 ? '' : 's'} into proposed, tested skill improvements.`
|
|
60
|
+
: 'Use captured corrections to propose, judge, and apply improvements.',
|
|
70
61
|
command: 'evolve',
|
|
71
62
|
icon: 'M13 7h8m0 0v8m0-8l-8 8-4-4-6 6',
|
|
72
63
|
color: 'var(--green)',
|
|
73
64
|
description: 'Evolve skills based on captured corrections',
|
|
74
65
|
disabled: !hasFailures,
|
|
75
|
-
disabledReason: 'No corrections yet —
|
|
76
|
-
flowSteps: hasFailures
|
|
77
|
-
? [`${unresolvedCount} corrections`, 'Propose changes', 'Judge & test', 'Apply']
|
|
78
|
-
: ['Corrections', 'Propose', 'Judge', 'Apply'],
|
|
66
|
+
disabledReason: 'No corrections captured yet — once you correct Craft Agent output, they are recorded automatically.',
|
|
67
|
+
flowSteps: hasFailures ? [`${unresolvedCount} corrections`, 'Propose', 'Judge + regress', 'Apply'] : ['Corrections', 'Propose', 'Judge', 'Apply'],
|
|
79
68
|
},
|
|
80
69
|
{
|
|
81
70
|
id: 'health',
|
|
82
71
|
label: 'Health Check',
|
|
83
|
-
subtitle: 'Assess
|
|
72
|
+
subtitle: 'Assess coverage gaps, cohesion, balance, and transfer potential across the network.',
|
|
84
73
|
command: 'health',
|
|
85
74
|
icon: 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z',
|
|
86
75
|
color: 'var(--yellow)',
|
|
87
76
|
description: 'Assess network cohesion and coverage',
|
|
88
77
|
disabled: !hasSkills,
|
|
89
|
-
disabledReason: 'No skills imported yet',
|
|
90
|
-
flowSteps: ['
|
|
78
|
+
disabledReason: 'No skills imported yet.',
|
|
79
|
+
flowSteps: ['Network', 'Coverage scan', 'Health report'],
|
|
91
80
|
},
|
|
92
81
|
{
|
|
93
82
|
id: 'graph-optimize',
|
|
94
83
|
label: 'Optimize Network',
|
|
95
|
-
subtitle: '
|
|
84
|
+
subtitle: 'Detect skill overlaps, fragmentation, and merge/split opportunities.',
|
|
96
85
|
command: 'graph-optimize',
|
|
97
86
|
icon: 'M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15',
|
|
98
87
|
color: 'var(--text-secondary)',
|
|
99
|
-
description: 'Detect merge
|
|
88
|
+
description: 'Detect merge and split opportunities in the skill graph',
|
|
100
89
|
disabled: !hasEdges,
|
|
101
|
-
disabledReason: 'Run
|
|
102
|
-
flowSteps: ['Relationships', '
|
|
90
|
+
disabledReason: 'Run Organize Skills first to generate relationships.',
|
|
91
|
+
flowSteps: ['Relationships', 'Overlap scan', 'Merge / split / flag'],
|
|
103
92
|
},
|
|
104
93
|
{
|
|
105
94
|
id: 'research',
|
|
106
95
|
label: 'Discover New Skills',
|
|
107
|
-
subtitle: `Search
|
|
96
|
+
subtitle: `Search for techniques your current ${skillCount} skills still don’t cover.`,
|
|
108
97
|
command: 'research',
|
|
109
98
|
icon: 'M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z',
|
|
110
99
|
color: 'var(--purple)',
|
|
111
|
-
description: '
|
|
112
|
-
flowSteps: ['Find gaps', 'Web
|
|
100
|
+
description: 'Run proactive research to discover and draft new skills',
|
|
101
|
+
flowSteps: ['Find gaps', 'Web research', 'Draft skills', 'Score'],
|
|
113
102
|
},
|
|
114
103
|
]
|
|
115
104
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from 'react'
|
|
4
|
+
|
|
5
|
+
interface HeroChip {
|
|
6
|
+
label: string
|
|
7
|
+
tone?: 'neutral' | 'purple' | 'green' | 'blue' | 'yellow' | 'red'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function PageHero({
|
|
11
|
+
eyebrow,
|
|
12
|
+
title,
|
|
13
|
+
description,
|
|
14
|
+
chips,
|
|
15
|
+
actions,
|
|
16
|
+
}: {
|
|
17
|
+
eyebrow?: string
|
|
18
|
+
title: string
|
|
19
|
+
description: ReactNode
|
|
20
|
+
chips?: HeroChip[]
|
|
21
|
+
actions?: ReactNode
|
|
22
|
+
}) {
|
|
23
|
+
return (
|
|
24
|
+
<section className="page-hero-card">
|
|
25
|
+
<div className="page-hero-grid">
|
|
26
|
+
<div>
|
|
27
|
+
{eyebrow && <div className="page-eyebrow">{eyebrow}</div>}
|
|
28
|
+
<h1 className="page-title">{title}</h1>
|
|
29
|
+
<p className="page-desc">{description}</p>
|
|
30
|
+
{chips && chips.length > 0 && (
|
|
31
|
+
<div className="hero-chip-row">
|
|
32
|
+
{chips.map((chip, index) => (
|
|
33
|
+
<span key={`${chip.label}-${index}`} className={`hero-chip hero-chip-${chip.tone ?? 'neutral'}`}>
|
|
34
|
+
{chip.label}
|
|
35
|
+
</span>
|
|
36
|
+
))}
|
|
37
|
+
</div>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
{actions ? <div className="page-hero-actions">{actions}</div> : null}
|
|
41
|
+
</div>
|
|
42
|
+
</section>
|
|
43
|
+
)
|
|
44
|
+
}
|