helixevo 0.2.8 → 0.2.9

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.
@@ -1,6 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import { useState, useEffect, useRef } from 'react'
4
+ import { VERSION } from '../../lib/version'
4
5
 
5
6
  // ─── Table of Contents ──────────────────────────────────────────
6
7
  const TOC = [
@@ -226,7 +227,7 @@ export default function GuidePage() {
226
227
  <nav className="guide-toc">
227
228
  <div className="guide-toc-header">
228
229
  <div className="guide-toc-title">Documentation</div>
229
- <div className="guide-toc-version">v0.2.6</div>
230
+ <div className="guide-toc-version">v{VERSION}</div>
230
231
  </div>
231
232
  {TOC.map(item => (
232
233
  <a
@@ -982,7 +983,7 @@ generation: 3
982
983
  {/* Footer */}
983
984
  <div className="guide-footer">
984
985
  <div className="guide-footer-content">
985
- <div style={{ fontSize: 13, fontWeight: 600 }}>HelixEvo v0.2.6</div>
986
+ <div style={{ fontSize: 13, fontWeight: 600 }}>HelixEvo v{VERSION}</div>
986
987
  <div style={{ fontSize: 12, color: 'var(--text-dim)', marginTop: 4 }}>
987
988
  Self-evolving skill ecosystem for AI agents · MIT License
988
989
  </div>
@@ -1,6 +1,8 @@
1
1
  import './globals.css'
2
2
  import type { Metadata } from 'next'
3
3
  import Link from 'next/link'
4
+ import { VERSION } from '../lib/version'
5
+ import { UpdateBanner } from '../components/update-banner'
4
6
 
5
7
  export const metadata: Metadata = {
6
8
  title: 'HelixEvo',
@@ -64,7 +66,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
64
66
  <div style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--green)' }} />
65
67
  <span>System Active</span>
66
68
  </div>
67
- <code style={{ fontSize: 10, background: 'var(--bg-section)', padding: '2px 6px', borderRadius: 4 }}>v0.2.0</code>
69
+ <code style={{ fontSize: 10, background: 'var(--bg-section)', padding: '2px 6px', borderRadius: 4 }}>v{VERSION}</code>
68
70
  </div>
69
71
  </nav>
70
72
 
@@ -73,6 +75,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
73
75
  {children}
74
76
  </main>
75
77
  </div>
78
+ <UpdateBanner currentVersion={VERSION} />
76
79
  </body>
77
80
  </html>
78
81
  )
@@ -0,0 +1,149 @@
1
+ 'use client'
2
+
3
+ import { useState, useEffect } from 'react'
4
+
5
+ const REGISTRY_URL = 'https://registry.npmjs.org/helixevo/latest'
6
+ const CHECK_INTERVAL_MS = 60 * 60 * 1000 // re-check every hour
7
+
8
+ function compareVersions(current: string, latest: string): boolean {
9
+ const c = current.split('.').map(Number)
10
+ const l = latest.split('.').map(Number)
11
+ for (let i = 0; i < 3; i++) {
12
+ if ((l[i] ?? 0) > (c[i] ?? 0)) return true
13
+ if ((l[i] ?? 0) < (c[i] ?? 0)) return false
14
+ }
15
+ return false
16
+ }
17
+
18
+ export function UpdateBanner({ currentVersion }: { currentVersion: string }) {
19
+ const [latestVersion, setLatestVersion] = useState<string | null>(null)
20
+ const [dismissed, setDismissed] = useState(false)
21
+ const [copied, setCopied] = useState(false)
22
+
23
+ useEffect(() => {
24
+ let mounted = true
25
+
26
+ async function check() {
27
+ try {
28
+ const res = await fetch(REGISTRY_URL)
29
+ if (!res.ok) return
30
+ const data = await res.json()
31
+ if (mounted && data.version && compareVersions(currentVersion, data.version)) {
32
+ setLatestVersion(data.version)
33
+ }
34
+ } catch {}
35
+ }
36
+
37
+ check()
38
+ const interval = setInterval(check, CHECK_INTERVAL_MS)
39
+ return () => { mounted = false; clearInterval(interval) }
40
+ }, [currentVersion])
41
+
42
+ if (!latestVersion || dismissed) return null
43
+
44
+ const command = 'npm install -g helixevo@latest'
45
+
46
+ const handleCopy = async () => {
47
+ try {
48
+ await navigator.clipboard.writeText(command)
49
+ setCopied(true)
50
+ setTimeout(() => setCopied(false), 2000)
51
+ } catch {}
52
+ }
53
+
54
+ return (
55
+ <div style={{
56
+ position: 'fixed',
57
+ bottom: 20,
58
+ right: 20,
59
+ width: 320,
60
+ background: 'var(--bg-card)',
61
+ border: '1px solid var(--purple-border)',
62
+ borderRadius: 'var(--radius-lg)',
63
+ boxShadow: 'var(--shadow-xl)',
64
+ padding: '16px 18px',
65
+ zIndex: 9999,
66
+ animation: 'updateSlideIn 0.4s ease-out',
67
+ }}>
68
+ {/* Close button */}
69
+ <button
70
+ onClick={() => setDismissed(true)}
71
+ style={{
72
+ position: 'absolute', top: 8, right: 10,
73
+ background: 'none', border: 'none', cursor: 'pointer',
74
+ color: 'var(--text-dim)', fontSize: 18, lineHeight: 1,
75
+ padding: '2px 4px',
76
+ }}
77
+ aria-label="Dismiss"
78
+ >
79
+ &times;
80
+ </button>
81
+
82
+ {/* Header */}
83
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
84
+ <div style={{
85
+ width: 28, height: 28, borderRadius: '50%',
86
+ background: 'var(--purple-light)', display: 'flex',
87
+ alignItems: 'center', justifyContent: 'center',
88
+ }}>
89
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--purple)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
90
+ <path d="M12 19V5m-7 7l7-7 7 7" />
91
+ </svg>
92
+ </div>
93
+ <div>
94
+ <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--text)', letterSpacing: -0.2 }}>
95
+ Update Available
96
+ </div>
97
+ <div style={{ fontSize: 11, color: 'var(--text-dim)' }}>
98
+ v{currentVersion} &rarr; <span style={{ color: 'var(--green)', fontWeight: 600 }}>v{latestVersion}</span>
99
+ </div>
100
+ </div>
101
+ </div>
102
+
103
+ {/* Command */}
104
+ <div
105
+ onClick={handleCopy}
106
+ style={{
107
+ background: 'var(--bg-section)',
108
+ border: '1px solid var(--border)',
109
+ borderRadius: 'var(--radius)',
110
+ padding: '8px 12px',
111
+ fontFamily: 'var(--font-mono)',
112
+ fontSize: 11,
113
+ color: 'var(--text-secondary)',
114
+ cursor: 'pointer',
115
+ display: 'flex',
116
+ alignItems: 'center',
117
+ justifyContent: 'space-between',
118
+ gap: 8,
119
+ transition: 'border-color 0.15s',
120
+ }}
121
+ title="Click to copy"
122
+ >
123
+ <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
124
+ $ {command}
125
+ </span>
126
+ <span style={{
127
+ fontSize: 10, fontFamily: 'var(--font)', fontWeight: 600,
128
+ color: copied ? 'var(--green)' : 'var(--purple)',
129
+ whiteSpace: 'nowrap',
130
+ flexShrink: 0,
131
+ }}>
132
+ {copied ? 'Copied!' : 'Copy'}
133
+ </span>
134
+ </div>
135
+
136
+ {/* Subtle hint */}
137
+ <div style={{ fontSize: 10, color: 'var(--text-muted)', marginTop: 8, textAlign: 'center' }}>
138
+ Run in terminal, then refresh dashboard
139
+ </div>
140
+
141
+ <style>{`
142
+ @keyframes updateSlideIn {
143
+ from { opacity: 0; transform: translateY(16px) scale(0.97); }
144
+ to { opacity: 1; transform: translateY(0) scale(1); }
145
+ }
146
+ `}</style>
147
+ </div>
148
+ )
149
+ }
@@ -0,0 +1 @@
1
+ export const VERSION = process.env.HELIXEVO_VERSION ?? '0.0.0'
@@ -0,0 +1,28 @@
1
+ import { readFileSync } from 'fs'
2
+ import { join, dirname } from 'path'
3
+ import { fileURLToPath } from 'url'
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url))
6
+
7
+ function getVersion() {
8
+ const candidates = [
9
+ join(__dirname, '..', 'package.json'), // dev: repo root
10
+ join(__dirname, 'package.json'), // fallback
11
+ ]
12
+ for (const p of candidates) {
13
+ try {
14
+ const pkg = JSON.parse(readFileSync(p, 'utf-8'))
15
+ if (pkg.name === 'helixevo' && pkg.version) return pkg.version
16
+ } catch {}
17
+ }
18
+ return '0.0.0'
19
+ }
20
+
21
+ /** @type {import('next').NextConfig} */
22
+ const nextConfig = {
23
+ env: {
24
+ HELIXEVO_VERSION: getVersion(),
25
+ },
26
+ }
27
+
28
+ export default nextConfig
package/dist/cli.js CHANGED
@@ -9576,7 +9576,7 @@ init_config();
9576
9576
  init_skills();
9577
9577
  init_data();
9578
9578
  init_llm();
9579
- import { join as join3 } from "node:path";
9579
+ import { join as join4 } from "node:path";
9580
9580
  import { homedir as homedir2 } from "node:os";
9581
9581
  import { existsSync as existsSync4, cpSync } from "node:fs";
9582
9582
 
@@ -9605,21 +9605,29 @@ Return JSON:
9605
9605
  - Expected behavior should be concrete enough to verify against`;
9606
9606
  }
9607
9607
 
9608
+ // src/version.ts
9609
+ import { createRequire as createRequire2 } from "node:module";
9610
+ import { join as join3, dirname as dirname2 } from "node:path";
9611
+ import { fileURLToPath } from "node:url";
9612
+ var require2 = createRequire2(import.meta.url);
9613
+ var pkg = require2(join3(dirname2(fileURLToPath(import.meta.url)), "..", "package.json"));
9614
+ var VERSION = pkg.version;
9615
+
9608
9616
  // src/commands/init.ts
9609
9617
  async function initCommand(options) {
9610
- console.log(`\uD83E\uDDEC Initializing HelixEvo...
9618
+ console.log(`\uD83E\uDDEC Initializing HelixEvo v${VERSION}...
9611
9619
  `);
9612
9620
  const sgDir = getHelixDir();
9613
9621
  const generalDir = getGeneralSkillsPath();
9614
9622
  ensureDir(sgDir);
9615
9623
  ensureDir(generalDir);
9616
- if (!existsSync4(join3(sgDir, "config.json"))) {
9624
+ if (!existsSync4(join4(sgDir, "config.json"))) {
9617
9625
  saveConfig(DEFAULT_CONFIG);
9618
9626
  console.log(" ✓ Created config.json");
9619
9627
  }
9620
9628
  const defaultPaths = [
9621
- join3(homedir2(), ".agents", "skills"),
9622
- join3(homedir2(), ".craft-agent", "workspaces")
9629
+ join4(homedir2(), ".agents", "skills"),
9630
+ join4(homedir2(), ".craft-agent", "workspaces")
9623
9631
  ];
9624
9632
  const scanPaths = options.skillsPaths ?? defaultPaths;
9625
9633
  const expandedPaths = [];
@@ -9628,7 +9636,7 @@ async function initCommand(options) {
9628
9636
  const { readdirSync: readdirSync2 } = await import("node:fs");
9629
9637
  const workspaces = readdirSync2(p, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("."));
9630
9638
  for (const ws of workspaces) {
9631
- const skillsDir = join3(p, ws.name, "skills");
9639
+ const skillsDir = join4(p, ws.name, "skills");
9632
9640
  if (existsSync4(skillsDir))
9633
9641
  expandedPaths.push(skillsDir);
9634
9642
  }
@@ -9641,7 +9649,7 @@ async function initCommand(options) {
9641
9649
  `);
9642
9650
  let imported = 0;
9643
9651
  for (const skill of existingSkills) {
9644
- const targetDir = join3(generalDir, skill.slug);
9652
+ const targetDir = join4(generalDir, skill.slug);
9645
9653
  if (existsSync4(targetDir)) {
9646
9654
  console.log(` → ${skill.slug}: already exists, skipping`);
9647
9655
  continue;
@@ -9653,6 +9661,30 @@ async function initCommand(options) {
9653
9661
  console.log(`
9654
9662
  Imported ${imported} new skills
9655
9663
  `);
9664
+ const allSkills = loadAllGeneralSkills();
9665
+ if (allSkills.length > 0) {
9666
+ const nodes = allSkills.map((s) => ({
9667
+ id: s.slug,
9668
+ name: s.slug,
9669
+ layer: "system",
9670
+ score: 0.7,
9671
+ generation: 0,
9672
+ status: "active",
9673
+ tags: [],
9674
+ failureCount: 0,
9675
+ lastEvolved: new Date().toISOString()
9676
+ }));
9677
+ const graph = {
9678
+ updated: new Date().toISOString(),
9679
+ nodes,
9680
+ edges: [],
9681
+ clusters: []
9682
+ };
9683
+ saveSkillGraph(graph);
9684
+ console.log(` ✓ Generated skill graph (${nodes.length} nodes)`);
9685
+ console.log(` Run "helixevo graph --rebuild" later for full relationship inference
9686
+ `);
9687
+ }
9656
9688
  if (!options.skipTests) {
9657
9689
  const generalSkills = loadAllGeneralSkills();
9658
9690
  console.log(` Generating skill tests...
@@ -9773,7 +9805,7 @@ init_config();
9773
9805
  init_data();
9774
9806
  init_skills();
9775
9807
  init_llm();
9776
- import { join as join6 } from "node:path";
9808
+ import { join as join7 } from "node:path";
9777
9809
 
9778
9810
  // src/prompts/cluster.ts
9779
9811
  function buildClusterPrompt(failures, skills) {
@@ -10123,11 +10155,11 @@ Return JSON: { "score": <number>, "reason": "<one sentence>" }`;
10123
10155
  // src/core/canary.ts
10124
10156
  init_config();
10125
10157
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync6, cpSync as cpSync2, rmSync } from "node:fs";
10126
- import { join as join4 } from "node:path";
10127
- var CANARY_DIR = join4(getHelixDir(), "canary");
10128
- var BACKUPS_DIR = join4(getHelixDir(), "backups");
10158
+ import { join as join5 } from "node:path";
10159
+ var CANARY_DIR = join5(getHelixDir(), "canary");
10160
+ var BACKUPS_DIR = join5(getHelixDir(), "backups");
10129
10161
  function getRegistryPath() {
10130
- return join4(getHelixDir(), "canary-registry.json");
10162
+ return join5(getHelixDir(), "canary-registry.json");
10131
10163
  }
10132
10164
  function loadRegistry() {
10133
10165
  const path = getRegistryPath();
@@ -10140,7 +10172,7 @@ function saveRegistry(registry) {
10140
10172
  }
10141
10173
  function backupSkill(skillPath, slug, version) {
10142
10174
  ensureDir(BACKUPS_DIR);
10143
- const backupPath = join4(BACKUPS_DIR, `${slug}_${version}_${Date.now()}`);
10175
+ const backupPath = join5(BACKUPS_DIR, `${slug}_${version}_${Date.now()}`);
10144
10176
  if (existsSync6(skillPath)) {
10145
10177
  cpSync2(skillPath, backupPath, { recursive: true });
10146
10178
  }
@@ -10222,7 +10254,7 @@ init_config();
10222
10254
  init_skills();
10223
10255
  init_data();
10224
10256
  init_llm();
10225
- import { join as join5 } from "node:path";
10257
+ import { join as join6 } from "node:path";
10226
10258
  async function autoGeneralize(candidates, verbose = false, dryRun = false) {
10227
10259
  const created = [];
10228
10260
  const skipped = [];
@@ -10291,7 +10323,7 @@ Keep it focused and actionable. No filler.`
10291
10323
  meta.lastEvolved = new Date().toISOString();
10292
10324
  meta.tags = [...meta.tags ?? [], "auto-generalized"];
10293
10325
  meta.enhances = candidate.sourceSkills;
10294
- const skillDir = join5(getGeneralSkillsPath(), candidate.suggestedName);
10326
+ const skillDir = join6(getGeneralSkillsPath(), candidate.suggestedName);
10295
10327
  writeSkill(skillDir, meta, content);
10296
10328
  created.push(candidate.suggestedName);
10297
10329
  console.log(` ✓ Created: ${candidate.suggestedName} (domain layer)`);
@@ -10488,7 +10520,7 @@ async function evolveCommand(options) {
10488
10520
  if (finalAccepted && !dryRun) {
10489
10521
  const { meta, content } = parseSkillMd(proposalOutput.proposedSkillMd);
10490
10522
  const skillSlug2 = proposalOutput.targetSkill ?? proposal.targetSkill;
10491
- const skillDir = join6(getGeneralSkillsPath(), skillSlug2);
10523
+ const skillDir = join7(getGeneralSkillsPath(), skillSlug2);
10492
10524
  meta.generation = generation;
10493
10525
  meta.score = avgScore;
10494
10526
  meta.lastEvolved = new Date().toISOString();
@@ -10576,9 +10608,9 @@ init_skills();
10576
10608
  // src/core/knowledge-buffer.ts
10577
10609
  init_config();
10578
10610
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync7 } from "node:fs";
10579
- import { join as join7 } from "node:path";
10580
- var BUFFER_PATH = () => join7(getHelixDir(), "knowledge-buffer.json");
10581
- var DRAFTS_DIR = () => join7(getHelixDir(), "drafts");
10611
+ import { join as join8 } from "node:path";
10612
+ var BUFFER_PATH = () => join8(getHelixDir(), "knowledge-buffer.json");
10613
+ var DRAFTS_DIR = () => join8(getHelixDir(), "drafts");
10582
10614
  var MAX_DISCOVERIES = 50;
10583
10615
  var MAX_DRAFTS = 10;
10584
10616
  var DECAY_RATE = 0.1;
@@ -10635,9 +10667,9 @@ function saveDraft(draft) {
10635
10667
  existing.avgScore = draft.avgScore;
10636
10668
  existing.iteration++;
10637
10669
  existing.lastIterated = new Date().toISOString();
10638
- const draftDir = join7(DRAFTS_DIR(), draft.skillName);
10670
+ const draftDir = join8(DRAFTS_DIR(), draft.skillName);
10639
10671
  ensureDir(draftDir);
10640
- writeFileSync5(join7(draftDir, "SKILL.md"), draft.skillMd);
10672
+ writeFileSync5(join8(draftDir, "SKILL.md"), draft.skillMd);
10641
10673
  }
10642
10674
  } else {
10643
10675
  buffer.drafts.push({
@@ -10651,9 +10683,9 @@ function saveDraft(draft) {
10651
10683
  iteration: 1,
10652
10684
  lastIterated: new Date().toISOString()
10653
10685
  });
10654
- const draftDir = join7(DRAFTS_DIR(), draft.skillName);
10686
+ const draftDir = join8(DRAFTS_DIR(), draft.skillName);
10655
10687
  ensureDir(draftDir);
10656
- writeFileSync5(join7(draftDir, "SKILL.md"), draft.skillMd);
10688
+ writeFileSync5(join8(draftDir, "SKILL.md"), draft.skillMd);
10657
10689
  }
10658
10690
  trimAndSave(buffer);
10659
10691
  }
@@ -10753,7 +10785,7 @@ async function statusCommand() {
10753
10785
  init_data();
10754
10786
  init_skills();
10755
10787
  import { writeFileSync as writeFileSync6 } from "node:fs";
10756
- import { join as join8 } from "node:path";
10788
+ import { join as join9 } from "node:path";
10757
10789
  init_config();
10758
10790
  async function reportCommand(options) {
10759
10791
  const days = parseInt(options.days ?? "1");
@@ -10837,8 +10869,8 @@ async function reportCommand(options) {
10837
10869
  report += `- **${s.slug}**${evolved}
10838
10870
  `;
10839
10871
  }
10840
- const outputPath = options.output ?? join8(getHelixDir(), "reports", `${date}.md`);
10841
- ensureDir(join8(getHelixDir(), "reports"));
10872
+ const outputPath = options.output ?? join9(getHelixDir(), "reports", `${date}.md`);
10873
+ ensureDir(join9(getHelixDir(), "reports"));
10842
10874
  writeFileSync6(outputPath, report);
10843
10875
  console.log(report);
10844
10876
  console.log(`
@@ -10849,7 +10881,7 @@ async function reportCommand(options) {
10849
10881
  init_config();
10850
10882
  init_skills();
10851
10883
  init_llm();
10852
- import { join as join9 } from "node:path";
10884
+ import { join as join10 } from "node:path";
10853
10885
  async function generalizeCommand(options) {
10854
10886
  const verbose = options.verbose ?? false;
10855
10887
  const dryRun = options.dryRun ?? false;
@@ -10912,7 +10944,7 @@ async function generalizeCommand(options) {
10912
10944
  meta.generation = 1;
10913
10945
  meta.score = candidate.confidence;
10914
10946
  meta.lastEvolved = new Date().toISOString();
10915
- const skillDir = join9(getGeneralSkillsPath(), candidate.suggestedName);
10947
+ const skillDir = join10(getGeneralSkillsPath(), candidate.suggestedName);
10916
10948
  writeSkill(skillDir, meta, content);
10917
10949
  console.log(` ✓ Created: ${candidate.suggestedName} (${candidate.suggestedLayer} layer)
10918
10950
  `);
@@ -11020,7 +11052,7 @@ Return JSON:
11020
11052
  init_data();
11021
11053
  init_skills();
11022
11054
  init_llm();
11023
- import { join as join10 } from "node:path";
11055
+ import { join as join11 } from "node:path";
11024
11056
  async function specializeCommand(options) {
11025
11057
  const verbose = options.verbose ?? false;
11026
11058
  const dryRun = options.dryRun ?? false;
@@ -11091,8 +11123,8 @@ async function specializeCommand(options) {
11091
11123
  meta.generation = 1;
11092
11124
  meta.score = candidate.confidence;
11093
11125
  meta.lastEvolved = new Date().toISOString();
11094
- const projectSkillsDir = join10(process.cwd(), ".helix", "skills");
11095
- const skillDir = join10(projectSkillsDir, candidate.suggestedName);
11126
+ const projectSkillsDir = join11(process.cwd(), ".helix", "skills");
11127
+ const skillDir = join11(projectSkillsDir, candidate.suggestedName);
11096
11128
  writeSkill(skillDir, meta, content);
11097
11129
  console.log(` ✓ Created: ${candidate.suggestedName} (project layer, parent: ${candidate.domainSkill})
11098
11130
  `);
@@ -11189,7 +11221,7 @@ Return JSON:
11189
11221
  // src/commands/graph.ts
11190
11222
  import { writeFileSync as writeFileSync8 } from "node:fs";
11191
11223
  import { execSync } from "node:child_process";
11192
- import { join as join12 } from "node:path";
11224
+ import { join as join13 } from "node:path";
11193
11225
  import { tmpdir } from "node:os";
11194
11226
 
11195
11227
  // src/core/network.ts
@@ -11542,29 +11574,29 @@ function detectMergeCandidates(enhances, coEvolves, skills) {
11542
11574
  init_data();
11543
11575
  init_skills();
11544
11576
  import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync3 } from "node:fs";
11545
- import { join as join11 } from "node:path";
11577
+ import { join as join12 } from "node:path";
11546
11578
  function syncToObsidian(vaultPath, verbose = false) {
11547
11579
  const graph = loadSkillGraph();
11548
11580
  const skills = loadAllGeneralSkills();
11549
- const skillsDir = join11(vaultPath, "Skills");
11550
- const reportsDir = join11(vaultPath, "Reports");
11581
+ const skillsDir = join12(vaultPath, "Skills");
11582
+ const reportsDir = join12(vaultPath, "Reports");
11551
11583
  mkdirSync3(skillsDir, { recursive: true });
11552
11584
  mkdirSync3(reportsDir, { recursive: true });
11553
11585
  for (const node of graph.nodes) {
11554
11586
  const skill = skills.find((s) => s.slug === node.id);
11555
11587
  const note = generateSkillNote(node, graph.edges, skill ?? undefined);
11556
- const notePath = join11(skillsDir, `${node.id}.md`);
11588
+ const notePath = join12(skillsDir, `${node.id}.md`);
11557
11589
  writeFileSync7(notePath, note);
11558
11590
  if (verbose)
11559
11591
  console.log(` ✓ ${node.id}.md`);
11560
11592
  }
11561
11593
  const indexNote = generateIndexNote(graph);
11562
- writeFileSync7(join11(vaultPath, "HelixEvo Index.md"), indexNote);
11594
+ writeFileSync7(join12(vaultPath, "HelixEvo Index.md"), indexNote);
11563
11595
  const recent = getRecentIterations(7);
11564
11596
  if (recent.length > 0) {
11565
11597
  const report = generateEvolutionReport(recent);
11566
11598
  const date = new Date().toISOString().slice(0, 10);
11567
- writeFileSync7(join11(reportsDir, `Evolution ${date}.md`), report);
11599
+ writeFileSync7(join12(reportsDir, `Evolution ${date}.md`), report);
11568
11600
  }
11569
11601
  console.log(` ✓ Synced ${graph.nodes.length} skills to ${vaultPath}`);
11570
11602
  }
@@ -11923,7 +11955,7 @@ ${mermaidCode}
11923
11955
  });
11924
11956
  </script>
11925
11957
  </body></html>`;
11926
- const htmlPath = join12(tmpdir(), "helix-network.html");
11958
+ const htmlPath = join13(tmpdir(), "helix-network.html");
11927
11959
  writeFileSync8(htmlPath, html);
11928
11960
  execSync(`open "${htmlPath}"`);
11929
11961
  console.log(` ✓ Opened in browser`);
@@ -11988,7 +12020,7 @@ function renderScoreBar(score) {
11988
12020
  init_config();
11989
12021
  init_skills();
11990
12022
  init_llm();
11991
- import { join as join13 } from "node:path";
12023
+ import { join as join14 } from "node:path";
11992
12024
  import { readFileSync as readFileSync7, existsSync as existsSync9 } from "node:fs";
11993
12025
  async function researchCommand(options) {
11994
12026
  const verbose = options.verbose ?? false;
@@ -12104,7 +12136,7 @@ Only include discoveries that are NOT already covered by current skills.`
12104
12136
  meta.score = result.avgScore / 10;
12105
12137
  meta.lastEvolved = new Date().toISOString();
12106
12138
  meta.tags = [...meta.tags ?? [], "research-discovered"];
12107
- const skillDir = join13(getGeneralSkillsPath(), hypothesis.skillName);
12139
+ const skillDir = join14(getGeneralSkillsPath(), hypothesis.skillName);
12108
12140
  writeSkill(skillDir, meta, content);
12109
12141
  console.log(` ✓ Created: ${hypothesis.skillName} (from research)
12110
12142
  `);
@@ -12152,7 +12184,7 @@ async function understandGoals(projectPath, skills) {
12152
12184
  const paths = projectPath ? [projectPath] : [process.cwd()];
12153
12185
  for (const p of paths) {
12154
12186
  for (const file of ["README.md", "CLAUDE.md", "package.json", ".agents/AGENTS.md"]) {
12155
- const fullPath = join13(p, file);
12187
+ const fullPath = join14(p, file);
12156
12188
  if (existsSync9(fullPath)) {
12157
12189
  const content = readFileSync7(fullPath, "utf-8").slice(0, 2000);
12158
12190
  context.push(`## ${file}
@@ -12314,12 +12346,12 @@ ${replay.slice(0, 800)}`
12314
12346
 
12315
12347
  // src/commands/dashboard.ts
12316
12348
  import { execSync as execSync2, spawn as spawn2 } from "node:child_process";
12317
- import { join as join14, dirname as dirname2 } from "node:path";
12318
- import { existsSync as existsSync10, cpSync as cpSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync2, statSync as statSync2, rmSync as rmSync2 } from "node:fs";
12319
- import { fileURLToPath } from "node:url";
12349
+ import { join as join15, dirname as dirname3 } from "node:path";
12350
+ import { existsSync as existsSync10, cpSync as cpSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync8, rmSync as rmSync2, writeFileSync as writeFileSync9 } from "node:fs";
12351
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
12320
12352
  import { homedir as homedir3 } from "node:os";
12321
- var __filename = "/Users/tianchichen/Documents/GitHub/skillgraph/src/commands/dashboard.ts";
12322
- var HELIX_DASHBOARD_DIR = join14(homedir3(), ".helix", "dashboard");
12353
+ var __filename = "/Users/tianchichen/Documents/GitHub/helixevo/src/commands/dashboard.ts";
12354
+ var HELIX_DASHBOARD_DIR = join15(homedir3(), ".helix", "dashboard");
12323
12355
  async function dashboardCommand() {
12324
12356
  const dir = prepareDashboard();
12325
12357
  if (!dir) {
@@ -12330,7 +12362,7 @@ async function dashboardCommand() {
12330
12362
  console.error(" cd helixevo/dashboard && npm install && npx next dev --port 3847");
12331
12363
  process.exit(1);
12332
12364
  }
12333
- if (!existsSync10(join14(dir, "node_modules"))) {
12365
+ if (!existsSync10(join15(dir, "node_modules"))) {
12334
12366
  console.log(" Installing dashboard dependencies...");
12335
12367
  try {
12336
12368
  execSync2("npm install --no-audit --no-fund", { cwd: dir, stdio: "inherit" });
@@ -12341,7 +12373,7 @@ async function dashboardCommand() {
12341
12373
  process.exit(1);
12342
12374
  }
12343
12375
  }
12344
- console.log(` \uD83C\uDF10 Starting HelixEvo Dashboard at http://localhost:3847
12376
+ console.log(` \uD83C\uDF10 Starting HelixEvo Dashboard v${VERSION} at http://localhost:3847
12345
12377
  `);
12346
12378
  const child = spawn2("npx", ["next", "dev", "--port", "3847"], {
12347
12379
  cwd: dir,
@@ -12366,20 +12398,20 @@ function prepareDashboard() {
12366
12398
  const npmSource = findNpmDashboard();
12367
12399
  if (npmSource)
12368
12400
  return copyToHelix(npmSource);
12369
- if (existsSync10(join14(HELIX_DASHBOARD_DIR, "package.json"))) {
12401
+ if (existsSync10(join15(HELIX_DASHBOARD_DIR, "package.json"))) {
12370
12402
  return HELIX_DASHBOARD_DIR;
12371
12403
  }
12372
12404
  return null;
12373
12405
  }
12374
12406
  function findDevDashboard() {
12375
12407
  const candidates = [
12376
- join14(process.cwd(), "dashboard")
12408
+ join15(process.cwd(), "dashboard")
12377
12409
  ];
12378
12410
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
12379
- candidates.push(join14(home, "Documents", "GitHub", "helixevo", "dashboard"));
12380
- candidates.push(join14(home, "Documents", "GitHub", "skillgraph", "dashboard"));
12411
+ candidates.push(join15(home, "Documents", "GitHub", "helixevo", "dashboard"));
12412
+ candidates.push(join15(home, "Documents", "GitHub", "skillgraph", "dashboard"));
12381
12413
  for (const dir of candidates) {
12382
- if (existsSync10(join14(dir, "package.json")) && !dir.includes("node_modules")) {
12414
+ if (existsSync10(join15(dir, "package.json")) && !dir.includes("node_modules")) {
12383
12415
  return dir;
12384
12416
  }
12385
12417
  }
@@ -12388,60 +12420,84 @@ function findDevDashboard() {
12388
12420
  function findNpmDashboard() {
12389
12421
  const candidates = [];
12390
12422
  try {
12391
- const thisFile = typeof __filename !== "undefined" ? __filename : fileURLToPath(import.meta.url);
12392
- const pkgRoot = dirname2(dirname2(thisFile));
12393
- candidates.push(join14(pkgRoot, "dashboard"));
12423
+ const thisFile = typeof __filename !== "undefined" ? __filename : fileURLToPath2(import.meta.url);
12424
+ const pkgRoot = dirname3(dirname3(thisFile));
12425
+ candidates.push(join15(pkgRoot, "dashboard"));
12394
12426
  } catch {}
12395
12427
  try {
12396
12428
  const globalPrefix = execSync2("npm prefix -g", { encoding: "utf-8" }).trim();
12397
- candidates.push(join14(globalPrefix, "lib", "node_modules", "helixevo", "dashboard"));
12398
- candidates.push(join14(globalPrefix, "node_modules", "helixevo", "dashboard"));
12429
+ candidates.push(join15(globalPrefix, "lib", "node_modules", "helixevo", "dashboard"));
12430
+ candidates.push(join15(globalPrefix, "node_modules", "helixevo", "dashboard"));
12399
12431
  } catch {}
12400
12432
  for (const dir of candidates) {
12401
- if (existsSync10(join14(dir, "package.json"))) {
12433
+ if (existsSync10(join15(dir, "package.json"))) {
12402
12434
  return dir;
12403
12435
  }
12404
12436
  }
12405
12437
  return null;
12406
12438
  }
12439
+ function getInstalledDashboardVersion() {
12440
+ try {
12441
+ const versionFile = join15(HELIX_DASHBOARD_DIR, ".helixevo-version");
12442
+ if (!existsSync10(versionFile))
12443
+ return null;
12444
+ return readFileSync8(versionFile, "utf-8").trim();
12445
+ } catch {
12446
+ return null;
12447
+ }
12448
+ }
12407
12449
  function copyToHelix(sourceDir) {
12408
- const destPkg = join14(HELIX_DASHBOARD_DIR, "package.json");
12409
- if (existsSync10(destPkg)) {
12410
- try {
12411
- const srcTime = statSync2(join14(sourceDir, "package.json")).mtimeMs;
12412
- const dstTime = statSync2(destPkg).mtimeMs;
12413
- if (srcTime <= dstTime) {
12414
- return HELIX_DASHBOARD_DIR;
12415
- }
12416
- } catch {}
12450
+ const installedVersion = getInstalledDashboardVersion();
12451
+ if (installedVersion === VERSION) {
12452
+ return HELIX_DASHBOARD_DIR;
12453
+ }
12454
+ if (installedVersion) {
12455
+ console.log(` Updating dashboard: v${installedVersion} → v${VERSION}`);
12456
+ } else {
12457
+ console.log(" Setting up dashboard at ~/.helix/dashboard...");
12417
12458
  }
12418
- console.log(" Setting up dashboard at ~/.helix/dashboard...");
12419
12459
  mkdirSync4(HELIX_DASHBOARD_DIR, { recursive: true });
12460
+ let depsChanged = true;
12461
+ try {
12462
+ const srcDeps = readFileSync8(join15(sourceDir, "package.json"), "utf-8");
12463
+ const dstDeps = readFileSync8(join15(HELIX_DASHBOARD_DIR, "package.json"), "utf-8");
12464
+ const srcPkg = JSON.parse(srcDeps);
12465
+ const dstPkg = JSON.parse(dstDeps);
12466
+ depsChanged = JSON.stringify(srcPkg.dependencies) !== JSON.stringify(dstPkg.dependencies) || JSON.stringify(srcPkg.devDependencies) !== JSON.stringify(dstPkg.devDependencies);
12467
+ } catch {
12468
+ depsChanged = true;
12469
+ }
12420
12470
  const items = readdirSync2(sourceDir, { withFileTypes: true });
12421
12471
  for (const item of items) {
12422
12472
  if (item.name === "node_modules" || item.name === ".next" || item.name === "package-lock.json")
12423
12473
  continue;
12424
- const src = join14(sourceDir, item.name);
12425
- const dest = join14(HELIX_DASHBOARD_DIR, item.name);
12474
+ const src = join15(sourceDir, item.name);
12475
+ const dest = join15(HELIX_DASHBOARD_DIR, item.name);
12426
12476
  cpSync3(src, dest, { recursive: true });
12427
12477
  }
12428
- const oldModules = join14(HELIX_DASHBOARD_DIR, "node_modules");
12429
- if (existsSync10(oldModules))
12430
- rmSync2(oldModules, { recursive: true });
12431
- const oldLock = join14(HELIX_DASHBOARD_DIR, "package-lock.json");
12432
- if (existsSync10(oldLock))
12433
- rmSync2(oldLock);
12478
+ if (depsChanged) {
12479
+ const oldModules = join15(HELIX_DASHBOARD_DIR, "node_modules");
12480
+ if (existsSync10(oldModules))
12481
+ rmSync2(oldModules, { recursive: true });
12482
+ const oldLock = join15(HELIX_DASHBOARD_DIR, "package-lock.json");
12483
+ if (existsSync10(oldLock))
12484
+ rmSync2(oldLock);
12485
+ }
12486
+ const nextCache = join15(HELIX_DASHBOARD_DIR, ".next");
12487
+ if (existsSync10(nextCache))
12488
+ rmSync2(nextCache, { recursive: true });
12489
+ writeFileSync9(join15(HELIX_DASHBOARD_DIR, ".helixevo-version"), VERSION);
12434
12490
  return HELIX_DASHBOARD_DIR;
12435
12491
  }
12436
12492
 
12437
12493
  // src/commands/watch.ts
12438
- import { join as join16 } from "node:path";
12494
+ import { join as join17 } from "node:path";
12439
12495
  import { existsSync as existsSync13 } from "node:fs";
12440
12496
 
12441
12497
  // src/core/auto-capture.ts
12442
12498
  init_data();
12443
12499
  init_llm();
12444
- import { readFileSync as readFileSync8, existsSync as existsSync11, statSync as statSync3 } from "node:fs";
12500
+ import { readFileSync as readFileSync9, existsSync as existsSync11, statSync as statSync2 } from "node:fs";
12445
12501
  var CORRECTION_SIGNALS = [
12446
12502
  /\bno[, ]+(?:that's|thats|that is)\s+(?:wrong|incorrect|not right)/i,
12447
12503
  /\bdon'?t\s+do\s+(?:that|it\s+like\s+that)/i,
@@ -12529,17 +12585,17 @@ If no corrections found, return: { "corrections": [] }`
12529
12585
  }
12530
12586
  function watchEvents(options) {
12531
12587
  const { eventsPath, project, onCapture, onError, pollIntervalMs = 5000 } = options;
12532
- let lastSize = existsSync11(eventsPath) ? statSync3(eventsPath).size : 0;
12588
+ let lastSize = existsSync11(eventsPath) ? statSync2(eventsPath).size : 0;
12533
12589
  let messageBuffer = [];
12534
12590
  let pendingExtraction = false;
12535
12591
  const interval = setInterval(async () => {
12536
12592
  try {
12537
12593
  if (!existsSync11(eventsPath))
12538
12594
  return;
12539
- const currentSize = statSync3(eventsPath).size;
12595
+ const currentSize = statSync2(eventsPath).size;
12540
12596
  if (currentSize <= lastSize)
12541
12597
  return;
12542
- const content = readFileSync8(eventsPath, "utf-8");
12598
+ const content = readFileSync9(eventsPath, "utf-8");
12543
12599
  const lines = content.trim().split(`
12544
12600
  `).filter(Boolean);
12545
12601
  const newLines = lines.slice(messageBuffer.length);
@@ -12581,22 +12637,22 @@ init_data();
12581
12637
  // src/core/metrics.ts
12582
12638
  init_config();
12583
12639
  init_data();
12584
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync9, existsSync as existsSync12 } from "node:fs";
12585
- import { join as join15 } from "node:path";
12640
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync10, existsSync as existsSync12 } from "node:fs";
12641
+ import { join as join16 } from "node:path";
12586
12642
  function getMetricsPath() {
12587
- return join15(getHelixDir(), "metrics.json");
12643
+ return join16(getHelixDir(), "metrics.json");
12588
12644
  }
12589
12645
  function loadMetrics() {
12590
12646
  const path = getMetricsPath();
12591
12647
  if (!existsSync12(path)) {
12592
12648
  return { updated: new Date().toISOString(), skills: [], globalCorrectionRate: [], evolutionImpact: [] };
12593
12649
  }
12594
- return JSON.parse(readFileSync9(path, "utf-8"));
12650
+ return JSON.parse(readFileSync10(path, "utf-8"));
12595
12651
  }
12596
12652
  function saveMetrics(store) {
12597
12653
  ensureDir(getHelixDir());
12598
12654
  store.updated = new Date().toISOString();
12599
- writeFileSync9(getMetricsPath(), JSON.stringify(store, null, 2));
12655
+ writeFileSync10(getMetricsPath(), JSON.stringify(store, null, 2));
12600
12656
  }
12601
12657
  function updateMetrics() {
12602
12658
  const failures = loadFailures();
@@ -12771,7 +12827,7 @@ async function watchCommand(options) {
12771
12827
  const verbose = options.verbose ?? false;
12772
12828
  const autoEvolve = options.evolve !== false;
12773
12829
  const project = options.project ?? null;
12774
- const eventsPath = options.events ?? join16(process.cwd(), "events.jsonl");
12830
+ const eventsPath = options.events ?? join17(process.cwd(), "events.jsonl");
12775
12831
  console.log(`\uD83E\uDDEC HelixEvo Watch Mode — Always-On Learning
12776
12832
  `);
12777
12833
  console.log(` Events: ${eventsPath}`);
@@ -12925,9 +12981,95 @@ async function metricsCommand(options) {
12925
12981
  }
12926
12982
  }
12927
12983
 
12984
+ // src/utils/update-check.ts
12985
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync11, existsSync as existsSync14, mkdirSync as mkdirSync5 } from "node:fs";
12986
+ import { join as join18 } from "node:path";
12987
+ import { homedir as homedir4 } from "node:os";
12988
+ var HELIX_DIR2 = join18(homedir4(), ".helix");
12989
+ var CACHE_PATH = join18(HELIX_DIR2, "update-check.json");
12990
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
12991
+ var REGISTRY_URL = "https://registry.npmjs.org/helixevo/latest";
12992
+ function readCache() {
12993
+ try {
12994
+ if (!existsSync14(CACHE_PATH))
12995
+ return null;
12996
+ return JSON.parse(readFileSync11(CACHE_PATH, "utf-8"));
12997
+ } catch {
12998
+ return null;
12999
+ }
13000
+ }
13001
+ function writeCache(cache) {
13002
+ try {
13003
+ if (!existsSync14(HELIX_DIR2))
13004
+ mkdirSync5(HELIX_DIR2, { recursive: true });
13005
+ writeFileSync11(CACHE_PATH, JSON.stringify(cache));
13006
+ } catch {}
13007
+ }
13008
+ function compareVersions(current, latest) {
13009
+ const c = current.split(".").map(Number);
13010
+ const l = latest.split(".").map(Number);
13011
+ for (let i = 0;i < 3; i++) {
13012
+ if ((l[i] ?? 0) > (c[i] ?? 0))
13013
+ return true;
13014
+ if ((l[i] ?? 0) < (c[i] ?? 0))
13015
+ return false;
13016
+ }
13017
+ return false;
13018
+ }
13019
+ async function fetchLatestVersion() {
13020
+ try {
13021
+ const controller = new AbortController;
13022
+ const timeout = setTimeout(() => controller.abort(), 3000);
13023
+ const res = await fetch(REGISTRY_URL, { signal: controller.signal });
13024
+ clearTimeout(timeout);
13025
+ if (!res.ok)
13026
+ return null;
13027
+ const data = await res.json();
13028
+ return data.version ?? null;
13029
+ } catch {
13030
+ return null;
13031
+ }
13032
+ }
13033
+ async function checkForUpdates() {
13034
+ try {
13035
+ const cache = readCache();
13036
+ if (cache && Date.now() - cache.lastCheck < CHECK_INTERVAL_MS) {
13037
+ if (compareVersions(VERSION, cache.latestVersion)) {
13038
+ printUpdateBanner(cache.latestVersion);
13039
+ }
13040
+ return;
13041
+ }
13042
+ const latest = await fetchLatestVersion();
13043
+ if (!latest)
13044
+ return;
13045
+ writeCache({ lastCheck: Date.now(), latestVersion: latest });
13046
+ if (compareVersions(VERSION, latest)) {
13047
+ printUpdateBanner(latest);
13048
+ }
13049
+ } catch {}
13050
+ }
13051
+ function printUpdateBanner(latestVersion) {
13052
+ const yellow = "\x1B[33m";
13053
+ const green = "\x1B[32m";
13054
+ const cyan = "\x1B[36m";
13055
+ const bold = "\x1B[1m";
13056
+ const dim = "\x1B[2m";
13057
+ const reset = "\x1B[0m";
13058
+ console.log();
13059
+ console.log(`${yellow} ╭──────────────────────────────────────────────╮${reset}`);
13060
+ console.log(`${yellow} │ │${reset}`);
13061
+ console.log(`${yellow} │ ${bold}Update available!${reset}${yellow} ${dim}v${VERSION}${reset}${yellow} → ${green}${bold}v${latestVersion}${reset}${yellow} │${reset}`);
13062
+ console.log(`${yellow} │ │${reset}`);
13063
+ console.log(`${yellow} │ ${reset}Run ${cyan}npm install -g helixevo@latest${reset}${yellow} │${reset}`);
13064
+ console.log(`${yellow} │ ${reset}to update${yellow} │${reset}`);
13065
+ console.log(`${yellow} │ │${reset}`);
13066
+ console.log(`${yellow} ╰──────────────────────────────────────────────╯${reset}`);
13067
+ console.log();
13068
+ }
13069
+
12928
13070
  // src/cli.ts
12929
13071
  var program2 = new Command;
12930
- program2.name("helixevo").description("Self-evolving skill ecosystem for AI agents").version("0.2.8").addHelpText("after", `
13072
+ program2.name("helixevo").description("Self-evolving skill ecosystem for AI agents").version(VERSION).addHelpText("after", `
12931
13073
  Examples:
12932
13074
  $ helixevo watch Always-on learning (auto-capture + auto-evolve)
12933
13075
  $ helixevo watch --project myapp Watch with project context
@@ -12963,4 +13105,7 @@ program2.command("health").description("Assess network health: cohesion, coverag
12963
13105
  printHealthReport2(health);
12964
13106
  });
12965
13107
  program2.command("metrics").description("Show correction rates, skill trends, and evolution impact").option("--verbose", "Show detailed per-skill breakdown").action(metricsCommand);
13108
+ program2.hook("postAction", () => {
13109
+ checkForUpdates().catch(() => {});
13110
+ });
12966
13111
  program2.parse();
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/postinstall.ts
4
+ import { createRequire } from "node:module";
5
+ import { join, dirname } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ var require2 = createRequire(import.meta.url);
8
+ var pkg = require2(join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"));
9
+ var green = "\x1B[32m";
10
+ var cyan = "\x1B[36m";
11
+ var bold = "\x1B[1m";
12
+ var dim = "\x1B[2m";
13
+ var reset = "\x1B[0m";
14
+ console.log();
15
+ console.log(` ${green}✓${reset} ${bold}HelixEvo v${pkg.version}${reset} installed successfully`);
16
+ console.log();
17
+ console.log(` ${dim}Get started:${reset}`);
18
+ console.log(` ${cyan}helixevo init${reset} Import skills + generate skill tests`);
19
+ console.log(` ${cyan}helixevo dashboard${reset} Open web dashboard`);
20
+ console.log(` ${cyan}helixevo watch${reset} Always-on learning mode`);
21
+ console.log();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helixevo",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Self-evolving skill ecosystem for AI agents. Skills and projects co-evolve through multi-judge evaluation and a Pareto frontier.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,15 +14,17 @@
14
14
  "dashboard/package.json",
15
15
  "dashboard/tsconfig.json",
16
16
  "dashboard/next-env.d.ts",
17
+ "dashboard/next.config.mjs",
17
18
  "README.md",
18
19
  "LICENSE"
19
20
  ],
20
21
  "scripts": {
21
- "build": "bun build src/cli.ts --outdir dist --target node",
22
+ "build": "bun build src/cli.ts --outdir dist --target node && bun build src/postinstall.ts --outdir dist --target node --entry-naming postinstall.js",
22
23
  "dev": "bun run src/cli.ts",
23
24
  "typecheck": "tsc --noEmit",
24
25
  "prepare": "npm run build",
25
- "prepublishOnly": "npm run typecheck && npm run build"
26
+ "prepublishOnly": "npm run typecheck && npm run build",
27
+ "postinstall": "node dist/postinstall.js 2>/dev/null || true"
26
28
  },
27
29
  "dependencies": {
28
30
  "commander": "^13.1.0",