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 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. Provides a visual interface for monitoring skills, evolution, frontier, and network health.',
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: 'Open the dashboard in your browser' },
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. Shows overview, skill network, evolution timeline, research, and frontier.',
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 join15, dirname as dirname3 } from "node:path";
12447
- 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";
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 homedir3 } from "node:os";
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 = join15(homedir3(), ".helix", "dashboard");
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 (!existsSync10(join15(dir, "node_modules"))) {
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 = join15(homedir3(), ".helix", "dashboard.log");
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 currentVersion = VERSION;
12591
+ let currentVersion2 = VERSION;
12481
12592
  try {
12482
- currentVersion = execSync2("helixevo --version 2>/dev/null", { encoding: "utf-8" }).trim() || VERSION;
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: currentVersion },
12598
+ env: { ...process.env, HELIXEVO_VERSION: currentVersion2 },
12488
12599
  detached: true
12489
12600
  });
12490
12601
  child.unref();
12491
- writeFileSync9(join15(homedir3(), ".helix", "dashboard.pid"), String(child.pid));
12492
- console.log(` \uD83C\uDF10 HelixEvo Dashboard v${VERSION} running in background`);
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
- console.log(` \uD83C\uDF10 Starting HelixEvo Dashboard v${VERSION} at http://localhost:3847
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 = join15(dir, ".next");
12537
- if (existsSync10(nextCache)) {
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 (existsSync10(join15(HELIX_DASHBOARD_DIR, "package.json"))) {
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
- join15(process.cwd(), "dashboard")
12725
+ join16(process.cwd(), "dashboard")
12571
12726
  ];
12572
12727
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
12573
- candidates.push(join15(home, "Documents", "GitHub", "helixevo", "dashboard"));
12574
- candidates.push(join15(home, "Documents", "GitHub", "skillgraph", "dashboard"));
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 (existsSync10(join15(dir, "package.json")) && !dir.includes("node_modules")) {
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(join15(pkgRoot, "dashboard"));
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(join15(globalPrefix, "lib", "node_modules", "helixevo", "dashboard"));
12592
- candidates.push(join15(globalPrefix, "node_modules", "helixevo", "dashboard"));
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 (existsSync10(join15(dir, "package.json"))) {
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 = join15(HELIX_DASHBOARD_DIR, ".helixevo-version");
12604
- if (!existsSync10(versionFile))
12758
+ const versionFile = join16(HELIX_DASHBOARD_DIR, ".helixevo-version");
12759
+ if (!existsSync11(versionFile))
12605
12760
  return null;
12606
- return readFileSync8(versionFile, "utf-8").trim();
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 = join15(sourceDir, "..", "package.json");
12615
- const pkg2 = JSON.parse(readFileSync8(srcRoot, "utf-8"));
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
- mkdirSync4(HELIX_DASHBOARD_DIR, { recursive: true });
12783
+ mkdirSync5(HELIX_DASHBOARD_DIR, { recursive: true });
12629
12784
  let depsChanged = true;
12630
12785
  try {
12631
- const srcDeps = readFileSync8(join15(sourceDir, "package.json"), "utf-8");
12632
- const dstDeps = readFileSync8(join15(HELIX_DASHBOARD_DIR, "package.json"), "utf-8");
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 = join15(sourceDir, item.name);
12644
- const dest = join15(HELIX_DASHBOARD_DIR, item.name);
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 = join15(HELIX_DASHBOARD_DIR, "node_modules");
12649
- if (existsSync10(oldModules))
12803
+ const oldModules = join16(HELIX_DASHBOARD_DIR, "node_modules");
12804
+ if (existsSync11(oldModules))
12650
12805
  rmSync2(oldModules, { recursive: true });
12651
- const oldLock = join15(HELIX_DASHBOARD_DIR, "package-lock.json");
12652
- if (existsSync10(oldLock))
12806
+ const oldLock = join16(HELIX_DASHBOARD_DIR, "package-lock.json");
12807
+ if (existsSync11(oldLock))
12653
12808
  rmSync2(oldLock);
12654
12809
  }
12655
- const nextCache = join15(HELIX_DASHBOARD_DIR, ".next");
12656
- if (existsSync10(nextCache))
12810
+ const nextCache = join16(HELIX_DASHBOARD_DIR, ".next");
12811
+ if (existsSync11(nextCache))
12657
12812
  rmSync2(nextCache, { recursive: true });
12658
- writeFileSync9(join15(HELIX_DASHBOARD_DIR, ".helixevo-version"), sourceVersion);
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 join17 } from "node:path";
12701
- import { existsSync as existsSync13 } from "node:fs";
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 readFileSync9, existsSync as existsSync11, statSync as statSync2 } from "node:fs";
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 = existsSync11(eventsPath) ? statSync2(eventsPath).size : 0;
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 (!existsSync11(eventsPath))
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 = readFileSync9(eventsPath, "utf-8");
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 readFileSync10, writeFileSync as writeFileSync10, existsSync as existsSync12 } from "node:fs";
12847
- import { join as join16 } from "node:path";
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 join16(getHelixDir(), "metrics.json");
13004
+ return join17(getHelixDir(), "metrics.json");
12850
13005
  }
12851
13006
  function loadMetrics() {
12852
13007
  const path = getMetricsPath();
12853
- if (!existsSync12(path)) {
13008
+ if (!existsSync13(path)) {
12854
13009
  return { updated: new Date().toISOString(), skills: [], globalCorrectionRate: [], evolutionImpact: [] };
12855
13010
  }
12856
- return JSON.parse(readFileSync10(path, "utf-8"));
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
- writeFileSync10(getMetricsPath(), JSON.stringify(store, null, 2));
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 ?? join17(process.cwd(), "events.jsonl");
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 (!existsSync13(eventsPath)) {
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 join18 } from "node:path";
13194
- import { existsSync as existsSync14, readFileSync as readFileSync11, writeFileSync as writeFileSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync3 } from "node:fs";
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 = join18(projectPath, f);
13276
- if (existsSync14(p)) {
13430
+ const p = join19(projectPath, f);
13431
+ if (existsSync15(p)) {
13277
13432
  try {
13278
- const content = readFileSync11(p, "utf-8");
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 = join18(projectPath, "src");
13291
- if (existsSync14(srcDir)) {
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(join18(dir, item.name), prefix + " ", depth + 1));
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 join18(getHelixDir(), "projects");
13490
+ return join19(getHelixDir(), "projects");
13336
13491
  }
13337
13492
  function saveProjectProfile(profile) {
13338
- const dir = join18(getProjectsDir(), profile.name);
13339
- mkdirSync5(dir, { recursive: true });
13340
- writeFileSync11(join18(dir, "profile.json"), JSON.stringify(profile, null, 2));
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 = join18(process.cwd(), projectPath).replace(/\/$/, "");
13346
- if (!existsSync14(resolvedPath)) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helixevo",
3
- "version": "0.2.40",
3
+ "version": "0.2.41",
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": {