helixevo 0.2.39 → 0.2.41

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 CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  All notable changes to HelixEvo are documented here.
4
4
 
5
+ ## [0.2.41] - 2026-03-23
6
+
7
+ ### Added
8
+ - `helixevo dashboard` now checks npm for a newer HelixEvo release before launch and automatically updates itself before opening the dashboard
9
+ - New `helixevo dashboard --no-auto-update` flag for users who want to skip the automatic update check
10
+
11
+ ### Changed
12
+ - Dashboard launch messages now use the resolved current version more consistently after updates or relaunches
13
+ - Commands and Guide reference pages now document the dashboard auto-update behavior and opt-out flag
14
+
15
+ ## [0.2.40] - 2026-03-23
16
+
17
+ ### Added
18
+ - Premium dashboard shell with new shared UI primitives for sidebar navigation, page heroes, metric cards, section frames, and console panels
19
+ - Rebuilt Overview, Evolution, Frontier, Research, Projects, and Skill Network surfaces with stronger hierarchy, warmer premium styling, and more flow-oriented layouts
20
+ - Upgraded Project Setup into a full intake studio with improved local/GitHub setup, polished folder browser, richer streamed analysis console, and cleaner project portfolio cards
21
+ - Redesigned Commands and Changelog pages with richer release/command summaries and premium reference layouts
22
+
23
+ ### Changed
24
+ - Skill Network now has premium graph nodes, stronger graph framing, descriptive sub-view tabs, and a dedicated inspector sidebar for navigation and skill management
25
+ - Guide page hero and table of contents were refined for better scanning, quick routing, and visual consistency with the dashboard shell
26
+
27
+ ### Fixed
28
+ - Strict typing issues in dashboard network/data code paths uncovered during production build validation
29
+ - Cleaned and revalidated production dashboard build behavior after the large redesign pass
30
+
5
31
  ## [0.2.39] - 2026-03-23
6
32
 
7
33
  ### Added
@@ -1,6 +1,9 @@
1
1
  import { readFileSync, existsSync } from 'fs'
2
2
  import { join } from 'path'
3
3
  import { homedir } from 'os'
4
+ import { MetricCard } from '@/components/metric-card'
5
+ import { PageHero } from '@/components/page-hero'
6
+ import { SectionFrame } from '@/components/section-frame'
4
7
  import { VERSION } from '@/lib/version'
5
8
 
6
9
  export const dynamic = 'force-dynamic'
@@ -11,7 +14,6 @@ function findChangelog(): string {
11
14
  join(homedir(), 'Documents', 'GitHub', 'helixevo', 'CHANGELOG.md'),
12
15
  ]
13
16
 
14
- // Also check npm global install
15
17
  try {
16
18
  const { execSync } = require('child_process')
17
19
  const globalPrefix = execSync('npm prefix -g', { encoding: 'utf-8' }).trim()
@@ -19,10 +21,8 @@ function findChangelog(): string {
19
21
  candidates.push(join(globalPrefix, 'node_modules', 'helixevo', 'CHANGELOG.md'))
20
22
  } catch {}
21
23
 
22
- for (const p of candidates) {
23
- if (existsSync(p)) {
24
- return readFileSync(p, 'utf-8')
25
- }
24
+ for (const path of candidates) {
25
+ if (existsSync(path)) return readFileSync(path, 'utf-8')
26
26
  }
27
27
  return ''
28
28
  }
@@ -47,12 +47,14 @@ function parseChangelog(md: string): ChangelogEntry[] {
47
47
  currentSection = null
48
48
  continue
49
49
  }
50
+
50
51
  const sectionMatch = line.match(/^### (.+)/)
51
52
  if (sectionMatch && current) {
52
53
  currentSection = { title: sectionMatch[1], items: [] }
53
54
  current.sections.push(currentSection)
54
55
  continue
55
56
  }
57
+
56
58
  if (line.startsWith('- ') && current) {
57
59
  if (!currentSection) {
58
60
  currentSection = { title: 'Changes', items: [] }
@@ -61,82 +63,205 @@ function parseChangelog(md: string): ChangelogEntry[] {
61
63
  currentSection.items.push(line.slice(2))
62
64
  }
63
65
  }
66
+
64
67
  if (current) entries.push(current)
65
68
  return entries
66
69
  }
67
70
 
68
71
  export default function ChangelogPage() {
69
- const md = findChangelog()
70
- const entries = parseChangelog(md)
72
+ const markdown = findChangelog()
73
+ const entries = parseChangelog(markdown)
74
+ const latestEntry = entries[0]
75
+ const installedEntry = entries.find(entry => entry.version === VERSION)
76
+ const totalItems = entries.reduce((sum, entry) => sum + entry.sections.reduce((sectionSum, section) => sectionSum + section.items.length, 0), 0)
71
77
 
72
78
  return (
73
79
  <div>
74
- <div className="page-header">
75
- <h1 className="page-title">Changelog</h1>
76
- <p className="page-desc">
77
- What changed in each version of HelixEvo currently on v{VERSION}
78
- </p>
80
+ <PageHero
81
+ eyebrow="Release history"
82
+ title="Changelog"
83
+ description={`A version-by-version ledger of what changed in HelixEvo. You are currently running v${VERSION}.`}
84
+ chips={[
85
+ { label: `${entries.length} release${entries.length === 1 ? '' : 's'}`, tone: 'blue' },
86
+ { label: `${totalItems} logged changes`, tone: 'purple' },
87
+ { label: `installed v${VERSION}`, tone: 'green' },
88
+ latestEntry ? { label: `latest v${latestEntry.version}`, tone: latestEntry.version === VERSION ? 'green' : 'yellow' } : { label: 'changelog unavailable', tone: 'red' },
89
+ ]}
90
+ actions={
91
+ latestEntry ? (
92
+ <div className="hero-note-card">
93
+ <div className="hero-note-label">Current release lens</div>
94
+ <div className="hero-note-title">Latest published version: v{latestEntry.version}</div>
95
+ <div className="hero-note-copy">Use this page to compare the installed version against recent releases and scan the release narrative over time.</div>
96
+ </div>
97
+ ) : null
98
+ }
99
+ />
100
+
101
+ <div className="grid-4" style={{ marginBottom: 24 }}>
102
+ <MetricCard label="Release entries" value={entries.length} sublabel="Parsed from CHANGELOG.md across the local package or repository." tone="blue" />
103
+ <MetricCard label="Installed version" value={`v${VERSION}`} sublabel={installedEntry ? `Found in changelog dated ${installedEntry.date || 'undated'}.` : 'Current version is not present in the parsed changelog.'} tone="green" />
104
+ <MetricCard label="Latest release" value={latestEntry ? `v${latestEntry.version}` : '—'} sublabel={latestEntry?.date || 'No changelog date available'} tone={latestEntry?.version === VERSION ? 'green' : 'yellow'} />
105
+ <MetricCard label="Tracked changes" value={totalItems} sublabel="Bulleted items parsed across every changelog section." tone="purple" />
79
106
  </div>
80
107
 
81
108
  {entries.length === 0 ? (
82
- <div className="card">
109
+ <SectionFrame
110
+ eyebrow="Missing release log"
111
+ title="No changelog found"
112
+ description="CHANGELOG.md was not found in the current package installation or repository checkout."
113
+ tone="red"
114
+ >
83
115
  <div className="empty-state">
84
116
  <div className="empty-state-title">No changelog found</div>
85
117
  <div className="empty-state-desc">
86
- CHANGELOG.md was not found in the package installation.
118
+ Ensure <code>CHANGELOG.md</code> is bundled with the package or available in the working repository.
87
119
  </div>
88
120
  </div>
89
- </div>
121
+ </SectionFrame>
90
122
  ) : (
91
- <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
92
- {entries.map((entry, i) => (
93
- <div key={entry.version} className="card" style={{
94
- borderLeft: i === 0 ? '3px solid var(--green)' : undefined,
95
- }}>
96
- <div className="card-body">
97
- <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
98
- <span style={{
99
- fontSize: 15, fontWeight: 700, color: 'var(--text)',
100
- fontFamily: 'var(--font-mono)',
101
- }}>
102
- v{entry.version}
103
- </span>
104
- {i === 0 && (
105
- <span className="badge badge-green">latest</span>
106
- )}
107
- {entry.version === VERSION && (
108
- <span className="badge badge-blue">installed</span>
109
- )}
110
- {entry.date && (
111
- <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>{entry.date}</span>
112
- )}
123
+ <>
124
+ {latestEntry ? (
125
+ <SectionFrame
126
+ eyebrow="Release spotlight"
127
+ title={`v${latestEntry.version}`}
128
+ description={latestEntry.date || 'Current top entry in the parsed release history.'}
129
+ tone={latestEntry.version === VERSION ? 'green' : 'yellow'}
130
+ >
131
+ <div className="grid-2" style={{ alignItems: 'start' }}>
132
+ <div style={{ display: 'grid', gap: 12 }}>
133
+ {latestEntry.sections.map((section, index) => (
134
+ <div key={`${latestEntry.version}-${section.title}-${index}`} style={{
135
+ padding: '16px 18px',
136
+ borderRadius: 20,
137
+ border: '1px solid var(--border)',
138
+ background: 'rgba(255,255,255,0.76)',
139
+ }}>
140
+ <div style={{ display: 'flex', justifyContent: 'space-between', gap: 10, alignItems: 'center', marginBottom: 10, flexWrap: 'wrap' }}>
141
+ <div style={{ fontSize: 12, fontWeight: 800, color: 'var(--text)', textTransform: 'uppercase', letterSpacing: 0.7 }}>
142
+ {section.title}
143
+ </div>
144
+ <span className="hero-chip hero-chip-neutral">{section.items.length} item{section.items.length === 1 ? '' : 's'}</span>
145
+ </div>
146
+ <div style={{ display: 'grid', gap: 8 }}>
147
+ {section.items.map((item, itemIndex) => (
148
+ <div key={`${latestEntry.version}-${section.title}-${itemIndex}`} style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
149
+ <span style={{ color: 'var(--green)', fontWeight: 800, fontSize: 14 }}>•</span>
150
+ <span style={{ fontSize: 12.5, color: 'var(--text-secondary)', lineHeight: 1.65 }}>{item}</span>
151
+ </div>
152
+ ))}
153
+ </div>
154
+ </div>
155
+ ))}
156
+ </div>
157
+
158
+ <div style={{
159
+ padding: '18px 20px',
160
+ borderRadius: 22,
161
+ border: '1px solid var(--border)',
162
+ background: 'linear-gradient(180deg, rgba(255,255,255,0.92), rgba(246,242,236,0.96))',
163
+ boxShadow: 'var(--shadow-sm)',
164
+ }}>
165
+ <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: 0.7, marginBottom: 8 }}>
166
+ Release context
167
+ </div>
168
+ <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginBottom: 12 }}>
169
+ <span className={`hero-chip hero-chip-${latestEntry.version === VERSION ? 'green' : 'yellow'}`}>{latestEntry.version === VERSION ? 'Installed now' : 'Ahead of installed version'}</span>
170
+ {latestEntry.date ? <span className="hero-chip hero-chip-blue">{latestEntry.date}</span> : null}
171
+ </div>
172
+ <div style={{ fontSize: 13, color: 'var(--text-secondary)', lineHeight: 1.7, marginBottom: 12 }}>
173
+ The spotlight release gives you the fastest read on the current product narrative: what changed most recently, how many grouped sections shipped, and whether your installed build matches the latest logged release.
174
+ </div>
175
+ <div className="grid-2" style={{ gap: 10 }}>
176
+ <div style={{ padding: '12px 14px', borderRadius: 16, background: 'rgba(16,185,129,0.08)', border: '1px solid rgba(16,185,129,0.16)' }}>
177
+ <div style={{ fontSize: 10, color: 'var(--green)', textTransform: 'uppercase', fontWeight: 700, letterSpacing: 0.5, marginBottom: 4 }}>Sections</div>
178
+ <div style={{ fontSize: 24, fontWeight: 800, color: 'var(--green)' }}>{latestEntry.sections.length}</div>
179
+ </div>
180
+ <div style={{ padding: '12px 14px', borderRadius: 16, background: 'rgba(107,73,223,0.08)', border: '1px solid rgba(107,73,223,0.16)' }}>
181
+ <div style={{ fontSize: 10, color: 'var(--purple)', textTransform: 'uppercase', fontWeight: 700, letterSpacing: 0.5, marginBottom: 4 }}>Items</div>
182
+ <div style={{ fontSize: 24, fontWeight: 800, color: 'var(--purple)' }}>{latestEntry.sections.reduce((sum, section) => sum + section.items.length, 0)}</div>
183
+ </div>
184
+ </div>
113
185
  </div>
186
+ </div>
187
+ </SectionFrame>
188
+ ) : null}
189
+
190
+ <SectionFrame
191
+ eyebrow="Release ledger"
192
+ title="Version history"
193
+ description="A clean timeline of every parsed release entry, with the newest releases first."
194
+ tone="blue"
195
+ >
196
+ <div style={{ display: 'grid', gap: 14 }}>
197
+ {entries.map((entry, index) => {
198
+ const isLatest = index === 0
199
+ const isInstalled = entry.version === VERSION
200
+ const itemCount = entry.sections.reduce((sum, section) => sum + section.items.length, 0)
114
201
 
115
- {entry.sections.map((section, j) => (
116
- <div key={j} style={{ marginBottom: j < entry.sections.length - 1 ? 12 : 0 }}>
202
+ return (
203
+ <article key={entry.version} className="card" style={{
204
+ overflow: 'hidden',
205
+ borderColor: isLatest ? 'rgba(16,185,129,0.22)' : isInstalled ? 'rgba(59,130,246,0.22)' : undefined,
206
+ }}>
117
207
  <div style={{
118
- fontSize: 10, fontWeight: 700, color: 'var(--text-muted)',
119
- textTransform: 'uppercase', letterSpacing: 1.2, marginBottom: 6,
208
+ padding: '16px 18px',
209
+ borderBottom: '1px solid var(--border-subtle)',
210
+ background: isLatest
211
+ ? 'linear-gradient(135deg, rgba(16,185,129,0.08), rgba(255,255,255,0.88))'
212
+ : isInstalled
213
+ ? 'linear-gradient(135deg, rgba(59,130,246,0.08), rgba(255,255,255,0.88))'
214
+ : 'linear-gradient(180deg, rgba(255,255,255,0.86), rgba(245,241,236,0.82))',
120
215
  }}>
121
- {section.title}
216
+ <div style={{ display: 'flex', justifyContent: 'space-between', gap: 14, alignItems: 'flex-start', flexWrap: 'wrap' }}>
217
+ <div>
218
+ <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap', marginBottom: 8 }}>
219
+ <code style={{ fontFamily: 'var(--font-mono)', fontSize: 15, fontWeight: 800, color: 'var(--text)' }}>v{entry.version}</code>
220
+ {isLatest ? <span className="hero-chip hero-chip-green">latest</span> : null}
221
+ {isInstalled ? <span className="hero-chip hero-chip-blue">installed</span> : null}
222
+ </div>
223
+ <div style={{ fontSize: 12.5, color: 'var(--text-dim)' }}>{entry.date || 'Undated release entry'}</div>
224
+ </div>
225
+ <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
226
+ <span className="hero-chip hero-chip-neutral">{entry.sections.length} section{entry.sections.length === 1 ? '' : 's'}</span>
227
+ <span className="hero-chip hero-chip-purple">{itemCount} item{itemCount === 1 ? '' : 's'}</span>
228
+ </div>
229
+ </div>
122
230
  </div>
123
- <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
124
- {section.items.map((item, k) => (
125
- <div key={k} style={{
126
- fontSize: 12, color: 'var(--text-secondary)', lineHeight: 1.5,
127
- paddingLeft: 12,
128
- borderLeft: '2px solid var(--border)',
231
+
232
+ <div className="card-body" style={{ display: 'grid', gap: 14 }}>
233
+ {entry.sections.map((section, sectionIndex) => (
234
+ <div key={`${entry.version}-${section.title}-${sectionIndex}`} style={{
235
+ padding: '14px 16px',
236
+ borderRadius: 18,
237
+ background: 'rgba(255,255,255,0.72)',
238
+ border: '1px solid var(--border)',
129
239
  }}>
130
- {item}
240
+ <div style={{ fontSize: 11, fontWeight: 800, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: 0.7, marginBottom: 8 }}>
241
+ {section.title}
242
+ </div>
243
+ <div style={{ display: 'grid', gap: 8 }}>
244
+ {section.items.map((item, itemIndex) => (
245
+ <div key={`${entry.version}-${section.title}-${itemIndex}`} style={{
246
+ paddingLeft: 12,
247
+ borderLeft: '2px solid var(--border)',
248
+ fontSize: 12.5,
249
+ color: 'var(--text-secondary)',
250
+ lineHeight: 1.65,
251
+ }}>
252
+ {item}
253
+ </div>
254
+ ))}
255
+ </div>
131
256
  </div>
132
257
  ))}
133
258
  </div>
134
- </div>
135
- ))}
136
- </div>
259
+ </article>
260
+ )
261
+ })}
137
262
  </div>
138
- ))}
139
- </div>
263
+ </SectionFrame>
264
+ </>
140
265
  )}
141
266
  </div>
142
267
  )