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.
- package/dashboard/app/guide/page.tsx +3 -2
- package/dashboard/app/layout.tsx +4 -1
- package/dashboard/components/update-banner.tsx +149 -0
- package/dashboard/lib/version.ts +1 -0
- package/dashboard/next.config.mjs +28 -0
- package/dist/cli.js +235 -90
- package/dist/postinstall.js +21 -0
- package/package.json +5 -3
|
@@ -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">
|
|
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
|
|
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>
|
package/dashboard/app/layout.tsx
CHANGED
|
@@ -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 }}>
|
|
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
|
+
×
|
|
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} → <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
|
|
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(
|
|
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
|
-
|
|
9622
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
10127
|
-
var CANARY_DIR =
|
|
10128
|
-
var BACKUPS_DIR =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
10580
|
-
var BUFFER_PATH = () =>
|
|
10581
|
-
var DRAFTS_DIR = () =>
|
|
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 =
|
|
10670
|
+
const draftDir = join8(DRAFTS_DIR(), draft.skillName);
|
|
10639
10671
|
ensureDir(draftDir);
|
|
10640
|
-
writeFileSync5(
|
|
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 =
|
|
10686
|
+
const draftDir = join8(DRAFTS_DIR(), draft.skillName);
|
|
10655
10687
|
ensureDir(draftDir);
|
|
10656
|
-
writeFileSync5(
|
|
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
|
|
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 ??
|
|
10841
|
-
ensureDir(
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
11095
|
-
const skillDir =
|
|
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
|
|
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
|
|
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 =
|
|
11550
|
-
const reportsDir =
|
|
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 =
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
12318
|
-
import { existsSync as existsSync10, cpSync as cpSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync2,
|
|
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/
|
|
12322
|
-
var HELIX_DASHBOARD_DIR =
|
|
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(
|
|
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(
|
|
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
|
-
|
|
12408
|
+
join15(process.cwd(), "dashboard")
|
|
12377
12409
|
];
|
|
12378
12410
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
12379
|
-
candidates.push(
|
|
12380
|
-
candidates.push(
|
|
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(
|
|
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 :
|
|
12392
|
-
const pkgRoot =
|
|
12393
|
-
candidates.push(
|
|
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(
|
|
12398
|
-
candidates.push(
|
|
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(
|
|
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
|
|
12409
|
-
if (
|
|
12410
|
-
|
|
12411
|
-
|
|
12412
|
-
|
|
12413
|
-
|
|
12414
|
-
|
|
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 =
|
|
12425
|
-
const dest =
|
|
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
|
-
|
|
12429
|
-
|
|
12430
|
-
|
|
12431
|
-
|
|
12432
|
-
|
|
12433
|
-
|
|
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
|
|
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
|
|
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) ?
|
|
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 =
|
|
12595
|
+
const currentSize = statSync2(eventsPath).size;
|
|
12540
12596
|
if (currentSize <= lastSize)
|
|
12541
12597
|
return;
|
|
12542
|
-
const content =
|
|
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
|
|
12585
|
-
import { join as
|
|
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
|
|
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(
|
|
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
|
-
|
|
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 ??
|
|
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(
|
|
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.
|
|
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",
|