helixevo 0.2.40 → 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 +10 -0
- package/dashboard/app/commands/page.tsx +11 -4
- package/dashboard/app/guide/page.tsx +2 -2
- package/dist/cli.js +225 -156
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
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
|
+
|
|
5
15
|
## [0.2.40] - 2026-03-23
|
|
6
16
|
|
|
7
17
|
### Added
|
|
@@ -225,14 +225,21 @@ const COMMANDS: CommandInfo[] = [
|
|
|
225
225
|
},
|
|
226
226
|
{
|
|
227
227
|
name: 'dashboard',
|
|
228
|
-
description: 'Launch the web dashboard at http://localhost:3847.
|
|
229
|
-
usage: 'helixevo dashboard',
|
|
228
|
+
description: 'Launch the web dashboard at http://localhost:3847. When a newer npm version is available, HelixEvo now auto-updates itself before opening the dashboard unless you opt out.',
|
|
229
|
+
usage: 'helixevo dashboard [options]',
|
|
230
230
|
examples: [
|
|
231
|
-
{ cmd: 'helixevo dashboard', desc: '
|
|
231
|
+
{ cmd: 'helixevo dashboard', desc: 'Auto-update to the latest version if needed, then open the dashboard' },
|
|
232
|
+
{ cmd: 'helixevo dashboard --background', desc: 'Run the dashboard in the background after the same update check' },
|
|
233
|
+
{ cmd: 'helixevo dashboard --no-auto-update', desc: 'Open the dashboard immediately without checking npm for updates' },
|
|
234
|
+
],
|
|
235
|
+
options: [
|
|
236
|
+
{ flag: '--background', desc: 'Run the dashboard detached from the terminal' },
|
|
237
|
+
{ flag: '--stop', desc: 'Stop a background dashboard process' },
|
|
238
|
+
{ flag: '--no-auto-update', desc: 'Skip the automatic update check before launch' },
|
|
232
239
|
],
|
|
233
240
|
category: 'system',
|
|
234
241
|
needsLLM: false,
|
|
235
|
-
note: 'You are currently using the dashboard
|
|
242
|
+
note: 'You are currently using the dashboard. By default it now upgrades itself first when a newer release is available.',
|
|
236
243
|
},
|
|
237
244
|
]
|
|
238
245
|
|
|
@@ -450,8 +450,8 @@ helixevo status`}</Code>
|
|
|
450
450
|
},
|
|
451
451
|
{
|
|
452
452
|
cmd: 'helixevo dashboard',
|
|
453
|
-
desc: 'Open the interactive web dashboard at localhost:3847.
|
|
454
|
-
flags: [],
|
|
453
|
+
desc: 'Open the interactive web dashboard at localhost:3847. Before launch, HelixEvo checks npm for a newer version and auto-updates itself unless you opt out.',
|
|
454
|
+
flags: ['--background', '--stop', '--no-auto-update'],
|
|
455
455
|
},
|
|
456
456
|
{
|
|
457
457
|
cmd: 'helixevo status',
|
package/dist/cli.js
CHANGED
|
@@ -12443,15 +12443,126 @@ ${replay.slice(0, 800)}`
|
|
|
12443
12443
|
|
|
12444
12444
|
// src/commands/dashboard.ts
|
|
12445
12445
|
import { execSync as execSync2, spawn as spawn2 } from "node:child_process";
|
|
12446
|
-
import { join as
|
|
12447
|
-
import { existsSync as
|
|
12446
|
+
import { join as join16, dirname as dirname3 } from "node:path";
|
|
12447
|
+
import { existsSync as existsSync11, cpSync as cpSync3, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync9, rmSync as rmSync2, writeFileSync as writeFileSync10 } from "node:fs";
|
|
12448
12448
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
12449
|
-
import { homedir as
|
|
12449
|
+
import { homedir as homedir4 } from "node:os";
|
|
12450
12450
|
init_skills();
|
|
12451
12451
|
init_data();
|
|
12452
|
+
|
|
12453
|
+
// src/utils/update-check.ts
|
|
12454
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync9, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "node:fs";
|
|
12455
|
+
import { join as join15 } from "node:path";
|
|
12456
|
+
import { homedir as homedir3 } from "node:os";
|
|
12457
|
+
var HELIX_DIR2 = join15(homedir3(), ".helix");
|
|
12458
|
+
var CACHE_PATH = join15(HELIX_DIR2, "update-check.json");
|
|
12459
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
12460
|
+
var REGISTRY_URL = "https://registry.npmjs.org/helixevo/latest";
|
|
12461
|
+
function readCache() {
|
|
12462
|
+
try {
|
|
12463
|
+
if (!existsSync10(CACHE_PATH))
|
|
12464
|
+
return null;
|
|
12465
|
+
return JSON.parse(readFileSync8(CACHE_PATH, "utf-8"));
|
|
12466
|
+
} catch {
|
|
12467
|
+
return null;
|
|
12468
|
+
}
|
|
12469
|
+
}
|
|
12470
|
+
function writeCache(cache) {
|
|
12471
|
+
try {
|
|
12472
|
+
if (!existsSync10(HELIX_DIR2))
|
|
12473
|
+
mkdirSync4(HELIX_DIR2, { recursive: true });
|
|
12474
|
+
writeFileSync9(CACHE_PATH, JSON.stringify(cache));
|
|
12475
|
+
} catch {}
|
|
12476
|
+
}
|
|
12477
|
+
function hasNewerVersion(current, latest) {
|
|
12478
|
+
const c = current.split(".").map(Number);
|
|
12479
|
+
const l = latest.split(".").map(Number);
|
|
12480
|
+
for (let i = 0;i < 3; i++) {
|
|
12481
|
+
if ((l[i] ?? 0) > (c[i] ?? 0))
|
|
12482
|
+
return true;
|
|
12483
|
+
if ((l[i] ?? 0) < (c[i] ?? 0))
|
|
12484
|
+
return false;
|
|
12485
|
+
}
|
|
12486
|
+
return false;
|
|
12487
|
+
}
|
|
12488
|
+
async function fetchLatestVersion() {
|
|
12489
|
+
try {
|
|
12490
|
+
const controller = new AbortController;
|
|
12491
|
+
const timeout = setTimeout(() => controller.abort(), 3000);
|
|
12492
|
+
const res = await fetch(REGISTRY_URL, { signal: controller.signal });
|
|
12493
|
+
clearTimeout(timeout);
|
|
12494
|
+
if (!res.ok)
|
|
12495
|
+
return null;
|
|
12496
|
+
const data = await res.json();
|
|
12497
|
+
return data.version ?? null;
|
|
12498
|
+
} catch {
|
|
12499
|
+
return null;
|
|
12500
|
+
}
|
|
12501
|
+
}
|
|
12502
|
+
async function getUpdateInfo(currentVersion = VERSION, forceRefresh = false) {
|
|
12503
|
+
const cache = forceRefresh ? null : readCache();
|
|
12504
|
+
if (cache && Date.now() - cache.lastCheck < CHECK_INTERVAL_MS) {
|
|
12505
|
+
return {
|
|
12506
|
+
currentVersion,
|
|
12507
|
+
latestVersion: cache.latestVersion,
|
|
12508
|
+
hasUpdate: hasNewerVersion(currentVersion, cache.latestVersion),
|
|
12509
|
+
source: "cache"
|
|
12510
|
+
};
|
|
12511
|
+
}
|
|
12512
|
+
const latest = await fetchLatestVersion();
|
|
12513
|
+
if (!latest) {
|
|
12514
|
+
return {
|
|
12515
|
+
currentVersion,
|
|
12516
|
+
latestVersion: cache?.latestVersion ?? null,
|
|
12517
|
+
hasUpdate: cache ? hasNewerVersion(currentVersion, cache.latestVersion) : false,
|
|
12518
|
+
source: cache ? "cache" : "none"
|
|
12519
|
+
};
|
|
12520
|
+
}
|
|
12521
|
+
writeCache({ lastCheck: Date.now(), latestVersion: latest });
|
|
12522
|
+
return {
|
|
12523
|
+
currentVersion,
|
|
12524
|
+
latestVersion: latest,
|
|
12525
|
+
hasUpdate: hasNewerVersion(currentVersion, latest),
|
|
12526
|
+
source: "network"
|
|
12527
|
+
};
|
|
12528
|
+
}
|
|
12529
|
+
async function checkForUpdates() {
|
|
12530
|
+
try {
|
|
12531
|
+
const update = await getUpdateInfo(VERSION);
|
|
12532
|
+
if (update.hasUpdate && update.latestVersion) {
|
|
12533
|
+
printUpdateBanner(update.currentVersion, update.latestVersion);
|
|
12534
|
+
}
|
|
12535
|
+
} catch {}
|
|
12536
|
+
}
|
|
12537
|
+
function printUpdateBanner(currentVersion, latestVersion) {
|
|
12538
|
+
const yellow = "\x1B[33m";
|
|
12539
|
+
const green = "\x1B[32m";
|
|
12540
|
+
const cyan = "\x1B[36m";
|
|
12541
|
+
const bold = "\x1B[1m";
|
|
12542
|
+
const dim = "\x1B[2m";
|
|
12543
|
+
const reset = "\x1B[0m";
|
|
12544
|
+
console.log();
|
|
12545
|
+
console.log(`${yellow} ╭──────────────────────────────────────────────╮${reset}`);
|
|
12546
|
+
console.log(`${yellow} │ │${reset}`);
|
|
12547
|
+
console.log(`${yellow} │ ${bold}Update available!${reset}${yellow} ${dim}v${currentVersion}${reset}${yellow} → ${green}${bold}v${latestVersion}${reset}${yellow} │${reset}`);
|
|
12548
|
+
console.log(`${yellow} │ │${reset}`);
|
|
12549
|
+
console.log(`${yellow} │ ${reset}Run ${cyan}npm install -g helixevo@latest${reset}${yellow} │${reset}`);
|
|
12550
|
+
console.log(`${yellow} │ ${reset}to update${yellow} │${reset}`);
|
|
12551
|
+
console.log(`${yellow} │ │${reset}`);
|
|
12552
|
+
console.log(`${yellow} ╰──────────────────────────────────────────────╯${reset}`);
|
|
12553
|
+
console.log();
|
|
12554
|
+
}
|
|
12555
|
+
|
|
12556
|
+
// src/commands/dashboard.ts
|
|
12452
12557
|
var __filename = "/Users/tianchichen/Documents/GitHub/helixevo/src/commands/dashboard.ts";
|
|
12453
|
-
var HELIX_DASHBOARD_DIR =
|
|
12558
|
+
var HELIX_DASHBOARD_DIR = join16(homedir4(), ".helix", "dashboard");
|
|
12559
|
+
var DASHBOARD_SKIP_AUTO_UPDATE_ENV = "HELIXEVO_DASHBOARD_SKIP_AUTO_UPDATE";
|
|
12454
12560
|
async function dashboardCommand(options) {
|
|
12561
|
+
if (options.autoUpdate !== false && process.env[DASHBOARD_SKIP_AUTO_UPDATE_ENV] !== "1" && !findDevDashboard()) {
|
|
12562
|
+
const didRelaunch = await maybeAutoUpdateAndRelaunch(options);
|
|
12563
|
+
if (didRelaunch)
|
|
12564
|
+
return;
|
|
12565
|
+
}
|
|
12455
12566
|
const dir = prepareDashboard();
|
|
12456
12567
|
if (!dir) {
|
|
12457
12568
|
console.error(` Dashboard not found.
|
|
@@ -12461,7 +12572,7 @@ async function dashboardCommand(options) {
|
|
|
12461
12572
|
console.error(" cd helixevo/dashboard && npm install && npx next dev --port 3847");
|
|
12462
12573
|
process.exit(1);
|
|
12463
12574
|
}
|
|
12464
|
-
if (!
|
|
12575
|
+
if (!existsSync11(join16(dir, "node_modules"))) {
|
|
12465
12576
|
console.log(" Installing dashboard dependencies...");
|
|
12466
12577
|
try {
|
|
12467
12578
|
execSync2("npm install --no-audit --no-fund", { cwd: dir, stdio: "inherit" });
|
|
@@ -12474,22 +12585,22 @@ async function dashboardCommand(options) {
|
|
|
12474
12585
|
}
|
|
12475
12586
|
ensureSkillGraph();
|
|
12476
12587
|
if (options.background) {
|
|
12477
|
-
const logFile =
|
|
12588
|
+
const logFile = join16(homedir4(), ".helix", "dashboard.log");
|
|
12478
12589
|
const out = __require("fs").openSync(logFile, "a");
|
|
12479
12590
|
const err = __require("fs").openSync(logFile, "a");
|
|
12480
|
-
let
|
|
12591
|
+
let currentVersion2 = VERSION;
|
|
12481
12592
|
try {
|
|
12482
|
-
|
|
12593
|
+
currentVersion2 = execSync2("helixevo --version 2>/dev/null", { encoding: "utf-8" }).trim() || VERSION;
|
|
12483
12594
|
} catch {}
|
|
12484
12595
|
const child = spawn2("npx", ["next", "dev", "--port", "3847"], {
|
|
12485
12596
|
cwd: dir,
|
|
12486
12597
|
stdio: ["ignore", out, err],
|
|
12487
|
-
env: { ...process.env, HELIXEVO_VERSION:
|
|
12598
|
+
env: { ...process.env, HELIXEVO_VERSION: currentVersion2 },
|
|
12488
12599
|
detached: true
|
|
12489
12600
|
});
|
|
12490
12601
|
child.unref();
|
|
12491
|
-
|
|
12492
|
-
console.log(` \uD83C\uDF10 HelixEvo Dashboard v${
|
|
12602
|
+
writeFileSync10(join16(homedir4(), ".helix", "dashboard.pid"), String(child.pid));
|
|
12603
|
+
console.log(` \uD83C\uDF10 HelixEvo Dashboard v${currentVersion2} running in background`);
|
|
12493
12604
|
console.log(` http://localhost:3847`);
|
|
12494
12605
|
console.log(` Logs: ${logFile}`);
|
|
12495
12606
|
console.log(` Stop: helixevo dashboard --stop
|
|
@@ -12503,10 +12614,54 @@ async function dashboardCommand(options) {
|
|
|
12503
12614
|
}, 2000);
|
|
12504
12615
|
process.exit(0);
|
|
12505
12616
|
}
|
|
12506
|
-
|
|
12617
|
+
let currentVersion = VERSION;
|
|
12618
|
+
try {
|
|
12619
|
+
currentVersion = execSync2("helixevo --version 2>/dev/null", { encoding: "utf-8" }).trim() || VERSION;
|
|
12620
|
+
} catch {}
|
|
12621
|
+
console.log(` \uD83C\uDF10 Starting HelixEvo Dashboard v${currentVersion} at http://localhost:3847
|
|
12507
12622
|
`);
|
|
12508
12623
|
launchDashboard(dir, true);
|
|
12509
12624
|
}
|
|
12625
|
+
async function maybeAutoUpdateAndRelaunch(options) {
|
|
12626
|
+
const update = await getUpdateInfo(VERSION);
|
|
12627
|
+
if (!update.hasUpdate || !update.latestVersion)
|
|
12628
|
+
return false;
|
|
12629
|
+
console.log(` ⟳ New HelixEvo version detected: v${VERSION} → v${update.latestVersion}`);
|
|
12630
|
+
console.log(` Updating before launching dashboard...
|
|
12631
|
+
`);
|
|
12632
|
+
try {
|
|
12633
|
+
execSync2("npm install -g helixevo@latest", {
|
|
12634
|
+
stdio: "inherit",
|
|
12635
|
+
timeout: 120000,
|
|
12636
|
+
env: { ...process.env }
|
|
12637
|
+
});
|
|
12638
|
+
} catch {
|
|
12639
|
+
console.warn(`
|
|
12640
|
+
Auto-update failed. Continuing with the current installed version.
|
|
12641
|
+
`);
|
|
12642
|
+
return false;
|
|
12643
|
+
}
|
|
12644
|
+
console.log(`
|
|
12645
|
+
✓ Updated to v${update.latestVersion}. Relaunching dashboard...
|
|
12646
|
+
`);
|
|
12647
|
+
const executable = process.platform === "win32" ? "helixevo.cmd" : "helixevo";
|
|
12648
|
+
const args = ["dashboard"];
|
|
12649
|
+
if (options.background)
|
|
12650
|
+
args.push("--background");
|
|
12651
|
+
if (options.autoUpdate === false)
|
|
12652
|
+
args.push("--no-auto-update");
|
|
12653
|
+
await new Promise((resolve, reject) => {
|
|
12654
|
+
const child = spawn2(executable, args, {
|
|
12655
|
+
stdio: "inherit",
|
|
12656
|
+
env: { ...process.env, [DASHBOARD_SKIP_AUTO_UPDATE_ENV]: "1" }
|
|
12657
|
+
});
|
|
12658
|
+
child.on("error", reject);
|
|
12659
|
+
child.on("close", (code) => {
|
|
12660
|
+
process.exit(code ?? 0);
|
|
12661
|
+
});
|
|
12662
|
+
});
|
|
12663
|
+
return true;
|
|
12664
|
+
}
|
|
12510
12665
|
var RESTART_EXIT_CODE = 75;
|
|
12511
12666
|
function launchDashboard(dir, openBrowser) {
|
|
12512
12667
|
let currentVersion = VERSION;
|
|
@@ -12533,8 +12688,8 @@ function launchDashboard(dir, openBrowser) {
|
|
|
12533
12688
|
\uD83D\uDD04 Restarting dashboard after update...
|
|
12534
12689
|
`);
|
|
12535
12690
|
setTimeout(() => {
|
|
12536
|
-
const nextCache =
|
|
12537
|
-
if (
|
|
12691
|
+
const nextCache = join16(dir, ".next");
|
|
12692
|
+
if (existsSync11(nextCache)) {
|
|
12538
12693
|
try {
|
|
12539
12694
|
rmSync2(nextCache, { recursive: true });
|
|
12540
12695
|
} catch {}
|
|
@@ -12560,20 +12715,20 @@ function prepareDashboard() {
|
|
|
12560
12715
|
const npmSource = findNpmDashboard();
|
|
12561
12716
|
if (npmSource)
|
|
12562
12717
|
return copyToHelix(npmSource);
|
|
12563
|
-
if (
|
|
12718
|
+
if (existsSync11(join16(HELIX_DASHBOARD_DIR, "package.json"))) {
|
|
12564
12719
|
return HELIX_DASHBOARD_DIR;
|
|
12565
12720
|
}
|
|
12566
12721
|
return null;
|
|
12567
12722
|
}
|
|
12568
12723
|
function findDevDashboard() {
|
|
12569
12724
|
const candidates = [
|
|
12570
|
-
|
|
12725
|
+
join16(process.cwd(), "dashboard")
|
|
12571
12726
|
];
|
|
12572
12727
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
12573
|
-
candidates.push(
|
|
12574
|
-
candidates.push(
|
|
12728
|
+
candidates.push(join16(home, "Documents", "GitHub", "helixevo", "dashboard"));
|
|
12729
|
+
candidates.push(join16(home, "Documents", "GitHub", "skillgraph", "dashboard"));
|
|
12575
12730
|
for (const dir of candidates) {
|
|
12576
|
-
if (
|
|
12731
|
+
if (existsSync11(join16(dir, "package.json")) && !dir.includes("node_modules")) {
|
|
12577
12732
|
return dir;
|
|
12578
12733
|
}
|
|
12579
12734
|
}
|
|
@@ -12584,15 +12739,15 @@ function findNpmDashboard() {
|
|
|
12584
12739
|
try {
|
|
12585
12740
|
const thisFile = typeof __filename !== "undefined" ? __filename : fileURLToPath2(import.meta.url);
|
|
12586
12741
|
const pkgRoot = dirname3(dirname3(thisFile));
|
|
12587
|
-
candidates.push(
|
|
12742
|
+
candidates.push(join16(pkgRoot, "dashboard"));
|
|
12588
12743
|
} catch {}
|
|
12589
12744
|
try {
|
|
12590
12745
|
const globalPrefix = execSync2("npm prefix -g", { encoding: "utf-8" }).trim();
|
|
12591
|
-
candidates.push(
|
|
12592
|
-
candidates.push(
|
|
12746
|
+
candidates.push(join16(globalPrefix, "lib", "node_modules", "helixevo", "dashboard"));
|
|
12747
|
+
candidates.push(join16(globalPrefix, "node_modules", "helixevo", "dashboard"));
|
|
12593
12748
|
} catch {}
|
|
12594
12749
|
for (const dir of candidates) {
|
|
12595
|
-
if (
|
|
12750
|
+
if (existsSync11(join16(dir, "package.json"))) {
|
|
12596
12751
|
return dir;
|
|
12597
12752
|
}
|
|
12598
12753
|
}
|
|
@@ -12600,10 +12755,10 @@ function findNpmDashboard() {
|
|
|
12600
12755
|
}
|
|
12601
12756
|
function getInstalledDashboardVersion() {
|
|
12602
12757
|
try {
|
|
12603
|
-
const versionFile =
|
|
12604
|
-
if (!
|
|
12758
|
+
const versionFile = join16(HELIX_DASHBOARD_DIR, ".helixevo-version");
|
|
12759
|
+
if (!existsSync11(versionFile))
|
|
12605
12760
|
return null;
|
|
12606
|
-
return
|
|
12761
|
+
return readFileSync9(versionFile, "utf-8").trim();
|
|
12607
12762
|
} catch {
|
|
12608
12763
|
return null;
|
|
12609
12764
|
}
|
|
@@ -12611,8 +12766,8 @@ function getInstalledDashboardVersion() {
|
|
|
12611
12766
|
function copyToHelix(sourceDir) {
|
|
12612
12767
|
let sourceVersion = VERSION;
|
|
12613
12768
|
try {
|
|
12614
|
-
const srcRoot =
|
|
12615
|
-
const pkg2 = JSON.parse(
|
|
12769
|
+
const srcRoot = join16(sourceDir, "..", "package.json");
|
|
12770
|
+
const pkg2 = JSON.parse(readFileSync9(srcRoot, "utf-8"));
|
|
12616
12771
|
if (pkg2.name === "helixevo" && pkg2.version)
|
|
12617
12772
|
sourceVersion = pkg2.version;
|
|
12618
12773
|
} catch {}
|
|
@@ -12625,11 +12780,11 @@ function copyToHelix(sourceDir) {
|
|
|
12625
12780
|
} else {
|
|
12626
12781
|
console.log(" Setting up dashboard at ~/.helix/dashboard...");
|
|
12627
12782
|
}
|
|
12628
|
-
|
|
12783
|
+
mkdirSync5(HELIX_DASHBOARD_DIR, { recursive: true });
|
|
12629
12784
|
let depsChanged = true;
|
|
12630
12785
|
try {
|
|
12631
|
-
const srcDeps =
|
|
12632
|
-
const dstDeps =
|
|
12786
|
+
const srcDeps = readFileSync9(join16(sourceDir, "package.json"), "utf-8");
|
|
12787
|
+
const dstDeps = readFileSync9(join16(HELIX_DASHBOARD_DIR, "package.json"), "utf-8");
|
|
12633
12788
|
const srcPkg = JSON.parse(srcDeps);
|
|
12634
12789
|
const dstPkg = JSON.parse(dstDeps);
|
|
12635
12790
|
depsChanged = JSON.stringify(srcPkg.dependencies) !== JSON.stringify(dstPkg.dependencies) || JSON.stringify(srcPkg.devDependencies) !== JSON.stringify(dstPkg.devDependencies);
|
|
@@ -12640,22 +12795,22 @@ function copyToHelix(sourceDir) {
|
|
|
12640
12795
|
for (const item of items) {
|
|
12641
12796
|
if (item.name === "node_modules" || item.name === ".next" || item.name === "package-lock.json" || item.name === ".env.local")
|
|
12642
12797
|
continue;
|
|
12643
|
-
const src =
|
|
12644
|
-
const dest =
|
|
12798
|
+
const src = join16(sourceDir, item.name);
|
|
12799
|
+
const dest = join16(HELIX_DASHBOARD_DIR, item.name);
|
|
12645
12800
|
cpSync3(src, dest, { recursive: true });
|
|
12646
12801
|
}
|
|
12647
12802
|
if (depsChanged) {
|
|
12648
|
-
const oldModules =
|
|
12649
|
-
if (
|
|
12803
|
+
const oldModules = join16(HELIX_DASHBOARD_DIR, "node_modules");
|
|
12804
|
+
if (existsSync11(oldModules))
|
|
12650
12805
|
rmSync2(oldModules, { recursive: true });
|
|
12651
|
-
const oldLock =
|
|
12652
|
-
if (
|
|
12806
|
+
const oldLock = join16(HELIX_DASHBOARD_DIR, "package-lock.json");
|
|
12807
|
+
if (existsSync11(oldLock))
|
|
12653
12808
|
rmSync2(oldLock);
|
|
12654
12809
|
}
|
|
12655
|
-
const nextCache =
|
|
12656
|
-
if (
|
|
12810
|
+
const nextCache = join16(HELIX_DASHBOARD_DIR, ".next");
|
|
12811
|
+
if (existsSync11(nextCache))
|
|
12657
12812
|
rmSync2(nextCache, { recursive: true });
|
|
12658
|
-
|
|
12813
|
+
writeFileSync10(join16(HELIX_DASHBOARD_DIR, ".helixevo-version"), sourceVersion);
|
|
12659
12814
|
return HELIX_DASHBOARD_DIR;
|
|
12660
12815
|
}
|
|
12661
12816
|
function ensureSkillGraph() {
|
|
@@ -12697,13 +12852,13 @@ function ensureSkillGraph() {
|
|
|
12697
12852
|
}
|
|
12698
12853
|
|
|
12699
12854
|
// src/commands/watch.ts
|
|
12700
|
-
import { join as
|
|
12701
|
-
import { existsSync as
|
|
12855
|
+
import { join as join18 } from "node:path";
|
|
12856
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
12702
12857
|
|
|
12703
12858
|
// src/core/auto-capture.ts
|
|
12704
12859
|
init_data();
|
|
12705
12860
|
init_llm();
|
|
12706
|
-
import { readFileSync as
|
|
12861
|
+
import { readFileSync as readFileSync10, existsSync as existsSync12, statSync as statSync2 } from "node:fs";
|
|
12707
12862
|
var CORRECTION_SIGNALS = [
|
|
12708
12863
|
/\bno[, ]+(?:that's|thats|that is)\s+(?:wrong|incorrect|not right)/i,
|
|
12709
12864
|
/\bdon'?t\s+do\s+(?:that|it\s+like\s+that)/i,
|
|
@@ -12791,17 +12946,17 @@ If no corrections found, return: { "corrections": [] }`
|
|
|
12791
12946
|
}
|
|
12792
12947
|
function watchEvents(options) {
|
|
12793
12948
|
const { eventsPath, project, onCapture, onError, pollIntervalMs = 5000 } = options;
|
|
12794
|
-
let lastSize =
|
|
12949
|
+
let lastSize = existsSync12(eventsPath) ? statSync2(eventsPath).size : 0;
|
|
12795
12950
|
let messageBuffer = [];
|
|
12796
12951
|
let pendingExtraction = false;
|
|
12797
12952
|
const interval = setInterval(async () => {
|
|
12798
12953
|
try {
|
|
12799
|
-
if (!
|
|
12954
|
+
if (!existsSync12(eventsPath))
|
|
12800
12955
|
return;
|
|
12801
12956
|
const currentSize = statSync2(eventsPath).size;
|
|
12802
12957
|
if (currentSize <= lastSize)
|
|
12803
12958
|
return;
|
|
12804
|
-
const content =
|
|
12959
|
+
const content = readFileSync10(eventsPath, "utf-8");
|
|
12805
12960
|
const lines = content.trim().split(`
|
|
12806
12961
|
`).filter(Boolean);
|
|
12807
12962
|
const newLines = lines.slice(messageBuffer.length);
|
|
@@ -12843,22 +12998,22 @@ init_data();
|
|
|
12843
12998
|
// src/core/metrics.ts
|
|
12844
12999
|
init_config();
|
|
12845
13000
|
init_data();
|
|
12846
|
-
import { readFileSync as
|
|
12847
|
-
import { join as
|
|
13001
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync11, existsSync as existsSync13 } from "node:fs";
|
|
13002
|
+
import { join as join17 } from "node:path";
|
|
12848
13003
|
function getMetricsPath() {
|
|
12849
|
-
return
|
|
13004
|
+
return join17(getHelixDir(), "metrics.json");
|
|
12850
13005
|
}
|
|
12851
13006
|
function loadMetrics() {
|
|
12852
13007
|
const path = getMetricsPath();
|
|
12853
|
-
if (!
|
|
13008
|
+
if (!existsSync13(path)) {
|
|
12854
13009
|
return { updated: new Date().toISOString(), skills: [], globalCorrectionRate: [], evolutionImpact: [] };
|
|
12855
13010
|
}
|
|
12856
|
-
return JSON.parse(
|
|
13011
|
+
return JSON.parse(readFileSync11(path, "utf-8"));
|
|
12857
13012
|
}
|
|
12858
13013
|
function saveMetrics(store) {
|
|
12859
13014
|
ensureDir(getHelixDir());
|
|
12860
13015
|
store.updated = new Date().toISOString();
|
|
12861
|
-
|
|
13016
|
+
writeFileSync11(getMetricsPath(), JSON.stringify(store, null, 2));
|
|
12862
13017
|
}
|
|
12863
13018
|
function updateMetrics() {
|
|
12864
13019
|
const failures = loadFailures();
|
|
@@ -13033,14 +13188,14 @@ async function watchCommand(options) {
|
|
|
13033
13188
|
const verbose = options.verbose ?? false;
|
|
13034
13189
|
const autoEvolve = options.evolve !== false;
|
|
13035
13190
|
const project = options.project ?? null;
|
|
13036
|
-
const eventsPath = options.events ??
|
|
13191
|
+
const eventsPath = options.events ?? join18(process.cwd(), "events.jsonl");
|
|
13037
13192
|
console.log(`\uD83E\uDDEC HelixEvo Watch Mode — Always-On Learning
|
|
13038
13193
|
`);
|
|
13039
13194
|
console.log(` Events: ${eventsPath}`);
|
|
13040
13195
|
console.log(` Project: ${project ?? "(auto-detect)"}`);
|
|
13041
13196
|
console.log(` Auto-evolve: ${autoEvolve ? "ON" : "OFF"}`);
|
|
13042
13197
|
console.log();
|
|
13043
|
-
if (!
|
|
13198
|
+
if (!existsSync14(eventsPath)) {
|
|
13044
13199
|
console.log(` ⚠ Events file not found yet. Will start watching when it appears.`);
|
|
13045
13200
|
console.log(` Expected at: ${eventsPath}
|
|
13046
13201
|
`);
|
|
@@ -13190,8 +13345,8 @@ async function metricsCommand(options) {
|
|
|
13190
13345
|
// src/commands/project-setup.ts
|
|
13191
13346
|
init_skills();
|
|
13192
13347
|
init_llm();
|
|
13193
|
-
import { join as
|
|
13194
|
-
import { existsSync as
|
|
13348
|
+
import { join as join19 } from "node:path";
|
|
13349
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12, writeFileSync as writeFileSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync3 } from "node:fs";
|
|
13195
13350
|
|
|
13196
13351
|
// src/prompts/project-analysis.ts
|
|
13197
13352
|
function buildProjectAnalysisPrompt(projectContext, skills) {
|
|
@@ -13272,10 +13427,10 @@ function readProjectContext(projectPath) {
|
|
|
13272
13427
|
"Dockerfile"
|
|
13273
13428
|
];
|
|
13274
13429
|
for (const f of filesToRead) {
|
|
13275
|
-
const p =
|
|
13276
|
-
if (
|
|
13430
|
+
const p = join19(projectPath, f);
|
|
13431
|
+
if (existsSync15(p)) {
|
|
13277
13432
|
try {
|
|
13278
|
-
const content =
|
|
13433
|
+
const content = readFileSync12(p, "utf-8");
|
|
13279
13434
|
contextFiles.push({ name: f, content: content.slice(0, 2000) });
|
|
13280
13435
|
} catch {}
|
|
13281
13436
|
}
|
|
@@ -13287,8 +13442,8 @@ function readProjectContext(projectPath) {
|
|
|
13287
13442
|
`);
|
|
13288
13443
|
} catch {}
|
|
13289
13444
|
let srcStructure = "";
|
|
13290
|
-
const srcDir =
|
|
13291
|
-
if (
|
|
13445
|
+
const srcDir = join19(projectPath, "src");
|
|
13446
|
+
if (existsSync15(srcDir)) {
|
|
13292
13447
|
try {
|
|
13293
13448
|
const scanDir = (dir, prefix, depth) => {
|
|
13294
13449
|
if (depth > 2)
|
|
@@ -13298,7 +13453,7 @@ function readProjectContext(projectPath) {
|
|
|
13298
13453
|
for (const item of items) {
|
|
13299
13454
|
lines.push(`${prefix}${item.isDirectory() ? "\uD83D\uDCC1" : "\uD83D\uDCC4"} ${item.name}`);
|
|
13300
13455
|
if (item.isDirectory()) {
|
|
13301
|
-
lines.push(...scanDir(
|
|
13456
|
+
lines.push(...scanDir(join19(dir, item.name), prefix + " ", depth + 1));
|
|
13302
13457
|
}
|
|
13303
13458
|
}
|
|
13304
13459
|
return lines;
|
|
@@ -13332,18 +13487,18 @@ ${f.content}
|
|
|
13332
13487
|
return context;
|
|
13333
13488
|
}
|
|
13334
13489
|
function getProjectsDir() {
|
|
13335
|
-
return
|
|
13490
|
+
return join19(getHelixDir(), "projects");
|
|
13336
13491
|
}
|
|
13337
13492
|
function saveProjectProfile(profile) {
|
|
13338
|
-
const dir =
|
|
13339
|
-
|
|
13340
|
-
|
|
13493
|
+
const dir = join19(getProjectsDir(), profile.name);
|
|
13494
|
+
mkdirSync6(dir, { recursive: true });
|
|
13495
|
+
writeFileSync12(join19(dir, "profile.json"), JSON.stringify(profile, null, 2));
|
|
13341
13496
|
}
|
|
13342
13497
|
async function projectSetupCommand(projectPath, options) {
|
|
13343
13498
|
const verbose = options.verbose ?? false;
|
|
13344
13499
|
const dryRun = options.dryRun ?? false;
|
|
13345
|
-
const resolvedPath =
|
|
13346
|
-
if (!
|
|
13500
|
+
const resolvedPath = join19(process.cwd(), projectPath).replace(/\/$/, "");
|
|
13501
|
+
if (!existsSync15(resolvedPath)) {
|
|
13347
13502
|
console.error(` ✗ Path not found: ${resolvedPath}`);
|
|
13348
13503
|
process.exit(1);
|
|
13349
13504
|
}
|
|
@@ -13428,92 +13583,6 @@ async function projectSetupCommand(projectPath, options) {
|
|
|
13428
13583
|
console.log();
|
|
13429
13584
|
}
|
|
13430
13585
|
|
|
13431
|
-
// src/utils/update-check.ts
|
|
13432
|
-
import { readFileSync as readFileSync12, writeFileSync as writeFileSync12, existsSync as existsSync15, mkdirSync as mkdirSync6 } from "node:fs";
|
|
13433
|
-
import { join as join19 } from "node:path";
|
|
13434
|
-
import { homedir as homedir4 } from "node:os";
|
|
13435
|
-
var HELIX_DIR2 = join19(homedir4(), ".helix");
|
|
13436
|
-
var CACHE_PATH = join19(HELIX_DIR2, "update-check.json");
|
|
13437
|
-
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
13438
|
-
var REGISTRY_URL = "https://registry.npmjs.org/helixevo/latest";
|
|
13439
|
-
function readCache() {
|
|
13440
|
-
try {
|
|
13441
|
-
if (!existsSync15(CACHE_PATH))
|
|
13442
|
-
return null;
|
|
13443
|
-
return JSON.parse(readFileSync12(CACHE_PATH, "utf-8"));
|
|
13444
|
-
} catch {
|
|
13445
|
-
return null;
|
|
13446
|
-
}
|
|
13447
|
-
}
|
|
13448
|
-
function writeCache(cache) {
|
|
13449
|
-
try {
|
|
13450
|
-
if (!existsSync15(HELIX_DIR2))
|
|
13451
|
-
mkdirSync6(HELIX_DIR2, { recursive: true });
|
|
13452
|
-
writeFileSync12(CACHE_PATH, JSON.stringify(cache));
|
|
13453
|
-
} catch {}
|
|
13454
|
-
}
|
|
13455
|
-
function compareVersions(current, latest) {
|
|
13456
|
-
const c = current.split(".").map(Number);
|
|
13457
|
-
const l = latest.split(".").map(Number);
|
|
13458
|
-
for (let i = 0;i < 3; i++) {
|
|
13459
|
-
if ((l[i] ?? 0) > (c[i] ?? 0))
|
|
13460
|
-
return true;
|
|
13461
|
-
if ((l[i] ?? 0) < (c[i] ?? 0))
|
|
13462
|
-
return false;
|
|
13463
|
-
}
|
|
13464
|
-
return false;
|
|
13465
|
-
}
|
|
13466
|
-
async function fetchLatestVersion() {
|
|
13467
|
-
try {
|
|
13468
|
-
const controller = new AbortController;
|
|
13469
|
-
const timeout = setTimeout(() => controller.abort(), 3000);
|
|
13470
|
-
const res = await fetch(REGISTRY_URL, { signal: controller.signal });
|
|
13471
|
-
clearTimeout(timeout);
|
|
13472
|
-
if (!res.ok)
|
|
13473
|
-
return null;
|
|
13474
|
-
const data = await res.json();
|
|
13475
|
-
return data.version ?? null;
|
|
13476
|
-
} catch {
|
|
13477
|
-
return null;
|
|
13478
|
-
}
|
|
13479
|
-
}
|
|
13480
|
-
async function checkForUpdates() {
|
|
13481
|
-
try {
|
|
13482
|
-
const cache = readCache();
|
|
13483
|
-
if (cache && Date.now() - cache.lastCheck < CHECK_INTERVAL_MS) {
|
|
13484
|
-
if (compareVersions(VERSION, cache.latestVersion)) {
|
|
13485
|
-
printUpdateBanner(cache.latestVersion);
|
|
13486
|
-
}
|
|
13487
|
-
return;
|
|
13488
|
-
}
|
|
13489
|
-
const latest = await fetchLatestVersion();
|
|
13490
|
-
if (!latest)
|
|
13491
|
-
return;
|
|
13492
|
-
writeCache({ lastCheck: Date.now(), latestVersion: latest });
|
|
13493
|
-
if (compareVersions(VERSION, latest)) {
|
|
13494
|
-
printUpdateBanner(latest);
|
|
13495
|
-
}
|
|
13496
|
-
} catch {}
|
|
13497
|
-
}
|
|
13498
|
-
function printUpdateBanner(latestVersion) {
|
|
13499
|
-
const yellow = "\x1B[33m";
|
|
13500
|
-
const green = "\x1B[32m";
|
|
13501
|
-
const cyan = "\x1B[36m";
|
|
13502
|
-
const bold = "\x1B[1m";
|
|
13503
|
-
const dim = "\x1B[2m";
|
|
13504
|
-
const reset = "\x1B[0m";
|
|
13505
|
-
console.log();
|
|
13506
|
-
console.log(`${yellow} ╭──────────────────────────────────────────────╮${reset}`);
|
|
13507
|
-
console.log(`${yellow} │ │${reset}`);
|
|
13508
|
-
console.log(`${yellow} │ ${bold}Update available!${reset}${yellow} ${dim}v${VERSION}${reset}${yellow} → ${green}${bold}v${latestVersion}${reset}${yellow} │${reset}`);
|
|
13509
|
-
console.log(`${yellow} │ │${reset}`);
|
|
13510
|
-
console.log(`${yellow} │ ${reset}Run ${cyan}npm install -g helixevo@latest${reset}${yellow} │${reset}`);
|
|
13511
|
-
console.log(`${yellow} │ ${reset}to update${yellow} │${reset}`);
|
|
13512
|
-
console.log(`${yellow} │ │${reset}`);
|
|
13513
|
-
console.log(`${yellow} ╰──────────────────────────────────────────────╯${reset}`);
|
|
13514
|
-
console.log();
|
|
13515
|
-
}
|
|
13516
|
-
|
|
13517
13586
|
// src/cli.ts
|
|
13518
13587
|
import { join as join20 } from "node:path";
|
|
13519
13588
|
import { homedir as homedir5 } from "node:os";
|
|
@@ -13545,7 +13614,7 @@ program2.command("generalize").description("Promote cross-skill patterns to high
|
|
|
13545
13614
|
program2.command("specialize").description("Create project-specific skills ↓ --project <name> [--dry-run] [--verbose]").requiredOption("--project <name>", "Project name").option("--dry-run", "Show candidates without applying").option("--verbose", "Show detailed analysis").action(specializeCommand);
|
|
13546
13615
|
program2.command("graph").description("Skill network [--mermaid] [--obsidian <path>] [--rebuild] [--optimize]").option("--mermaid", "Render as Mermaid diagram in browser").option("--rebuild", "Force rebuild (re-infer relationships via LLM)").option("--obsidian <path>", "Sync to Obsidian vault at path").option("--optimize", "Run network optimization (merge/split/conflict detection)").option("--verbose", "Show detailed analysis").action(graphCommand);
|
|
13547
13616
|
program2.command("research").description("Proactive research via web [--project <path>] [--dry-run] [--verbose]").option("--project <path>", "Project path for goal extraction").option("--dry-run", "Show discoveries without creating skills").option("--verbose", "Show detailed research steps").option("--max-hypotheses <n>", "Max hypotheses to test", "3").action(researchCommand);
|
|
13548
|
-
program2.command("dashboard").description("Open web dashboard at http://localhost:3847").option("--background", "Run in background (detach from terminal)").option("--stop", "Stop a background dashboard").action(async (options) => {
|
|
13617
|
+
program2.command("dashboard").description("Open web dashboard at http://localhost:3847").option("--background", "Run in background (detach from terminal)").option("--stop", "Stop a background dashboard").option("--no-auto-update", "Skip automatic update check before launching the dashboard").action(async (options) => {
|
|
13549
13618
|
if (options.stop) {
|
|
13550
13619
|
const pidFile = join20(homedir5(), ".helix", "dashboard.pid");
|
|
13551
13620
|
if (existsSync16(pidFile)) {
|
|
@@ -13560,7 +13629,7 @@ program2.command("dashboard").description("Open web dashboard at http://localhos
|
|
|
13560
13629
|
}
|
|
13561
13630
|
return;
|
|
13562
13631
|
}
|
|
13563
|
-
dashboardCommand(options);
|
|
13632
|
+
await dashboardCommand(options);
|
|
13564
13633
|
});
|
|
13565
13634
|
program2.command("status").description("Show frontier, skills, failures, and network health").action(statusCommand);
|
|
13566
13635
|
program2.command("report").description("Evolution report [--days <n>] [--output <path>]").option("--days <n>", "Report period in days", "1").option("--output <path>", "Output path for report").action(reportCommand);
|
package/package.json
CHANGED