helixevo 0.2.40 → 0.3.0

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/dist/cli.js CHANGED
@@ -9174,9 +9174,240 @@ var init_skills = __esm(() => {
9174
9174
  FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
9175
9175
  });
9176
9176
 
9177
+ // src/ontology/entities.ts
9178
+ var ONTOLOGY_KERNEL_ENTITY_NAMES;
9179
+ var init_entities = __esm(() => {
9180
+ ONTOLOGY_KERNEL_ENTITY_NAMES = [
9181
+ "ProjectRecord",
9182
+ "SessionRecord",
9183
+ "TaskRecord",
9184
+ "FailureRecord",
9185
+ "PressureSignal",
9186
+ "Capability",
9187
+ "SkillNode",
9188
+ "SkillRelation",
9189
+ "ActivationEvent",
9190
+ "MutationProposal",
9191
+ "TopologyChange",
9192
+ "EvidenceArtifact",
9193
+ "EvaluationResult",
9194
+ "TransferEvent",
9195
+ "GovernanceMode",
9196
+ "HealthMetric",
9197
+ "LineageRecord",
9198
+ "OntologyConcept",
9199
+ "OntologyChangeEvent"
9200
+ ];
9201
+ });
9202
+
9203
+ // src/ontology/relations.ts
9204
+ var ONTOLOGY_RELATION_FAMILIES;
9205
+ var init_relations = __esm(() => {
9206
+ ONTOLOGY_RELATION_FAMILIES = [
9207
+ "belongs_to",
9208
+ "derived_from",
9209
+ "replaced_by",
9210
+ "parent_of",
9211
+ "child_of",
9212
+ "specializes",
9213
+ "generalizes_from",
9214
+ "depends_on",
9215
+ "enhances",
9216
+ "conflicts_with",
9217
+ "co_evolves_with",
9218
+ "covers",
9219
+ "activated_for",
9220
+ "suppressed_by",
9221
+ "contributed_to",
9222
+ "competed_with",
9223
+ "triggered_by",
9224
+ "validated_by",
9225
+ "contradicted_by",
9226
+ "survived_in",
9227
+ "requires_review",
9228
+ "proposed_due_to",
9229
+ "benefits",
9230
+ "transferred_to",
9231
+ "refined_by",
9232
+ "promoted_from",
9233
+ "executed_under",
9234
+ "deferred_by",
9235
+ "prioritized_by",
9236
+ "locked_by",
9237
+ "measures",
9238
+ "signals_weakness_in",
9239
+ "suggests_repair_for"
9240
+ ];
9241
+ });
9242
+
9243
+ // src/ontology/operations.ts
9244
+ var SKILL_OPERATIONS, TOPOLOGY_OPERATIONS, ONTOLOGY_OPERATIONS, GOVERNANCE_OPERATIONS, ONTOLOGY_MUTATION_OPERATIONS, ONTOLOGY_ALL_OPERATIONS;
9245
+ var init_operations = __esm(() => {
9246
+ SKILL_OPERATIONS = [
9247
+ "create_skill",
9248
+ "edit_skill",
9249
+ "specialize_skill",
9250
+ "generalize_skill",
9251
+ "promote_skill",
9252
+ "demote_skill",
9253
+ "retire_skill"
9254
+ ];
9255
+ TOPOLOGY_OPERATIONS = [
9256
+ "merge_skills",
9257
+ "split_skill",
9258
+ "rewire_relation",
9259
+ "prune_branch",
9260
+ "consolidate_cluster"
9261
+ ];
9262
+ ONTOLOGY_OPERATIONS = [
9263
+ "hypothesize_concept",
9264
+ "promote_concept",
9265
+ "merge_concepts",
9266
+ "split_concept",
9267
+ "deprecate_concept",
9268
+ "refine_relation_subtype",
9269
+ "add_invariant",
9270
+ "migrate_mapping"
9271
+ ];
9272
+ GOVERNANCE_OPERATIONS = [
9273
+ "change_mode",
9274
+ "adjust_plasticity_regime",
9275
+ "allocate_attention",
9276
+ "defer_change",
9277
+ "require_review"
9278
+ ];
9279
+ ONTOLOGY_MUTATION_OPERATIONS = [
9280
+ ...SKILL_OPERATIONS,
9281
+ ...TOPOLOGY_OPERATIONS
9282
+ ];
9283
+ ONTOLOGY_ALL_OPERATIONS = [
9284
+ ...SKILL_OPERATIONS,
9285
+ ...TOPOLOGY_OPERATIONS,
9286
+ ...ONTOLOGY_OPERATIONS,
9287
+ ...GOVERNANCE_OPERATIONS
9288
+ ];
9289
+ });
9290
+
9291
+ // src/ontology/invariants.ts
9292
+ var ONTOLOGY_INVARIANTS;
9293
+ var init_invariants = __esm(() => {
9294
+ ONTOLOGY_INVARIANTS = [
9295
+ {
9296
+ id: "accepted-change-requires-evidence",
9297
+ description: "Every accepted change must reference evidence."
9298
+ },
9299
+ {
9300
+ id: "specialist-requires-local-context",
9301
+ description: "Every specialist skill must be linked to a project or local context."
9302
+ },
9303
+ {
9304
+ id: "promotion-requires-repeated-evidence",
9305
+ description: "Every promotion toward generalist intelligence must cite repeated cross-project or repeated-context evidence."
9306
+ },
9307
+ {
9308
+ id: "relation-requires-family-and-confidence",
9309
+ description: "Every skill relation must declare a relation family and confidence source."
9310
+ },
9311
+ {
9312
+ id: "topology-change-preserves-lineage",
9313
+ description: "Every topology change must preserve or explicitly rewrite lineage."
9314
+ },
9315
+ {
9316
+ id: "deprecated-state-remains-recoverable",
9317
+ description: "Every deprecated skill or concept must remain recoverable through lineage."
9318
+ },
9319
+ {
9320
+ id: "ontology-promotion-requires-migration",
9321
+ description: "Every ontology promotion must include a migration mapping."
9322
+ },
9323
+ {
9324
+ id: "frontier-concepts-stay-provisional",
9325
+ description: "Every frontier concept remains provisional until promoted through review."
9326
+ },
9327
+ {
9328
+ id: "transfer-requires-source-and-target",
9329
+ description: "Every transfer claim must identify source and target contexts."
9330
+ },
9331
+ {
9332
+ id: "high-risk-topology-change-requires-validation",
9333
+ description: "Every accepted high-risk topology change must have replay or equivalent validation."
9334
+ }
9335
+ ];
9336
+ });
9337
+
9338
+ // src/ontology/events.ts
9339
+ var ONTOLOGY_EVENT_TYPES;
9340
+ var init_events = __esm(() => {
9341
+ ONTOLOGY_EVENT_TYPES = [
9342
+ "failure_captured",
9343
+ "pressure_detected",
9344
+ "skill_activated",
9345
+ "skill_suppressed",
9346
+ "capability_gap_detected",
9347
+ "mutation_proposed",
9348
+ "mutation_accepted",
9349
+ "mutation_rejected",
9350
+ "topology_changed",
9351
+ "transfer_realized",
9352
+ "replay_completed",
9353
+ "regression_completed",
9354
+ "canary_completed",
9355
+ "rollback_triggered",
9356
+ "concept_hypothesized",
9357
+ "concept_promoted",
9358
+ "concept_rejected",
9359
+ "concept_merged",
9360
+ "concept_split",
9361
+ "governance_changed"
9362
+ ];
9363
+ });
9364
+
9365
+ // src/ontology/spec.ts
9366
+ var ONTOLOGY_SPEC_VERSION = "0.1.0", ONTOLOGY_KERNEL_PILLARS, ONTOLOGY_LAYERS, ONTOLOGY_V0_SPEC;
9367
+ var init_spec = __esm(() => {
9368
+ init_entities();
9369
+ init_events();
9370
+ init_invariants();
9371
+ init_operations();
9372
+ init_relations();
9373
+ ONTOLOGY_KERNEL_PILLARS = [
9374
+ "Operational World",
9375
+ "Perception / Pressure",
9376
+ "Working Memory / Activation",
9377
+ "Long-Term Memory",
9378
+ "Plasticity / Topology",
9379
+ "Evidence / Immune System",
9380
+ "Executive / Governance",
9381
+ "Metacognition / Health",
9382
+ "Time / Lineage"
9383
+ ];
9384
+ ONTOLOGY_LAYERS = ["kernel", "extension", "frontier", "vocabulary"];
9385
+ ONTOLOGY_V0_SPEC = {
9386
+ version: ONTOLOGY_SPEC_VERSION,
9387
+ pillars: [...ONTOLOGY_KERNEL_PILLARS],
9388
+ layers: [...ONTOLOGY_LAYERS],
9389
+ entityNames: [...ONTOLOGY_KERNEL_ENTITY_NAMES],
9390
+ relationFamilies: [...ONTOLOGY_RELATION_FAMILIES],
9391
+ mutationOperations: [...ONTOLOGY_MUTATION_OPERATIONS],
9392
+ operations: [...ONTOLOGY_ALL_OPERATIONS],
9393
+ invariantIds: ONTOLOGY_INVARIANTS.map((invariant) => invariant.id),
9394
+ eventTypes: [...ONTOLOGY_EVENT_TYPES]
9395
+ };
9396
+ });
9397
+
9398
+ // src/ontology/index.ts
9399
+ var init_ontology = __esm(() => {
9400
+ init_entities();
9401
+ init_relations();
9402
+ init_operations();
9403
+ init_invariants();
9404
+ init_events();
9405
+ init_spec();
9406
+ });
9407
+
9177
9408
  // src/utils/data.ts
9178
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, appendFileSync, existsSync as existsSync3 } from "node:fs";
9179
- import { dirname } from "node:path";
9409
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, appendFileSync, existsSync as existsSync3, readdirSync as readdirSync2 } from "node:fs";
9410
+ import { dirname, join as join3 } from "node:path";
9180
9411
  function readJsonl(filename) {
9181
9412
  const path = getDataPath(filename);
9182
9413
  if (!existsSync3(path))
@@ -9246,19 +9477,45 @@ function loadSkillGraph() {
9246
9477
  function saveSkillGraph(graph) {
9247
9478
  writeJson("skill-graph.json", graph);
9248
9479
  }
9480
+ function appendEvolutionArtifact(artifact) {
9481
+ appendJsonl("evolution-artifacts.jsonl", artifact);
9482
+ }
9483
+ function appendActivationTrace(trace) {
9484
+ appendJsonl("activation-traces.jsonl", trace);
9485
+ }
9486
+ function appendPressureSignal(signal) {
9487
+ appendJsonl("pressure-signals.jsonl", signal);
9488
+ }
9249
9489
  var init_data = __esm(() => {
9490
+ init_ontology();
9250
9491
  init_config();
9251
9492
  });
9252
9493
 
9253
9494
  // src/utils/llm.ts
9254
9495
  import { spawn } from "node:child_process";
9255
- function runClaude(prompt, args) {
9496
+ function buildClaudeEnv({ dropOauthToken }) {
9497
+ const env = { ...process.env };
9498
+ if (dropOauthToken) {
9499
+ delete env.CLAUDE_CODE_OAUTH_TOKEN;
9500
+ }
9501
+ return env;
9502
+ }
9503
+ function runClaudeOnce(prompt, args, env) {
9256
9504
  return new Promise((resolve, reject) => {
9257
9505
  const proc = spawn("claude", args, {
9258
- stdio: ["pipe", "pipe", "pipe"]
9506
+ stdio: ["pipe", "pipe", "pipe"],
9507
+ env
9259
9508
  });
9260
9509
  let stdout = "";
9261
9510
  let stderr = "";
9511
+ let settled = false;
9512
+ const timeout = setTimeout(() => {
9513
+ proc.kill();
9514
+ if (!settled) {
9515
+ settled = true;
9516
+ reject(new Error("claude call timed out after 180s"));
9517
+ }
9518
+ }, 180000);
9262
9519
  proc.stdout.on("data", (data) => {
9263
9520
  stdout += data.toString();
9264
9521
  });
@@ -9266,23 +9523,42 @@ function runClaude(prompt, args) {
9266
9523
  stderr += data.toString();
9267
9524
  });
9268
9525
  proc.on("close", (code) => {
9526
+ clearTimeout(timeout);
9527
+ if (settled)
9528
+ return;
9529
+ settled = true;
9269
9530
  if (code !== 0) {
9270
- reject(new Error(`claude exited with code ${code}: ${stderr.slice(0, 500)}`));
9531
+ const output = (stderr || stdout).slice(0, 500);
9532
+ reject(new Error(`claude exited with code ${code}: ${output}`));
9271
9533
  } else {
9272
9534
  resolve(stdout.trim());
9273
9535
  }
9274
9536
  });
9275
9537
  proc.on("error", (err) => {
9538
+ clearTimeout(timeout);
9539
+ if (settled)
9540
+ return;
9541
+ settled = true;
9276
9542
  reject(new Error(`Failed to spawn claude: ${err.message}`));
9277
9543
  });
9278
9544
  proc.stdin.write(prompt);
9279
9545
  proc.stdin.end();
9280
- setTimeout(() => {
9281
- proc.kill();
9282
- reject(new Error("claude call timed out after 180s"));
9283
- }, 180000);
9284
9546
  });
9285
9547
  }
9548
+ function isRetryableClaudeAuthError(error) {
9549
+ const message = error instanceof Error ? error.message : String(error);
9550
+ return CLAUDE_AUTH_ERROR_PATTERNS.some((pattern) => message.includes(pattern));
9551
+ }
9552
+ async function runClaude(prompt, args) {
9553
+ try {
9554
+ return await runClaudeOnce(prompt, args, buildClaudeEnv({ dropOauthToken: false }));
9555
+ } catch (error) {
9556
+ if (!process.env.CLAUDE_CODE_OAUTH_TOKEN || !isRetryableClaudeAuthError(error)) {
9557
+ throw error;
9558
+ }
9559
+ return runClaudeOnce(prompt, args, buildClaudeEnv({ dropOauthToken: true }));
9560
+ }
9561
+ }
9286
9562
  async function chat(options) {
9287
9563
  const config = loadConfig();
9288
9564
  const model = options.model ?? config.model;
@@ -9334,8 +9610,14 @@ async function searchWeb(query) {
9334
9610
  ];
9335
9611
  return runClaude(query, args);
9336
9612
  }
9613
+ var CLAUDE_AUTH_ERROR_PATTERNS;
9337
9614
  var init_llm = __esm(() => {
9338
9615
  init_config();
9616
+ CLAUDE_AUTH_ERROR_PATTERNS = [
9617
+ "OAuth token has expired",
9618
+ "authentication_error",
9619
+ "Failed to authenticate"
9620
+ ];
9339
9621
  });
9340
9622
 
9341
9623
  // src/core/network-health.ts
@@ -9576,7 +9858,7 @@ init_config();
9576
9858
  init_skills();
9577
9859
  init_data();
9578
9860
  init_llm();
9579
- import { join as join4 } from "node:path";
9861
+ import { join as join5 } from "node:path";
9580
9862
  import { homedir as homedir2 } from "node:os";
9581
9863
  import { existsSync as existsSync4, cpSync } from "node:fs";
9582
9864
 
@@ -9607,10 +9889,10 @@ Return JSON:
9607
9889
 
9608
9890
  // src/version.ts
9609
9891
  import { createRequire as createRequire2 } from "node:module";
9610
- import { join as join3, dirname as dirname2 } from "node:path";
9892
+ import { join as join4, dirname as dirname2 } from "node:path";
9611
9893
  import { fileURLToPath } from "node:url";
9612
9894
  var require2 = createRequire2(import.meta.url);
9613
- var pkg = require2(join3(dirname2(fileURLToPath(import.meta.url)), "..", "package.json"));
9895
+ var pkg = require2(join4(dirname2(fileURLToPath(import.meta.url)), "..", "package.json"));
9614
9896
  var VERSION = pkg.version;
9615
9897
 
9616
9898
  // src/commands/init.ts
@@ -9621,22 +9903,22 @@ async function initCommand(options) {
9621
9903
  const generalDir = getGeneralSkillsPath();
9622
9904
  ensureDir(sgDir);
9623
9905
  ensureDir(generalDir);
9624
- if (!existsSync4(join4(sgDir, "config.json"))) {
9906
+ if (!existsSync4(join5(sgDir, "config.json"))) {
9625
9907
  saveConfig(DEFAULT_CONFIG);
9626
9908
  console.log(" ✓ Created config.json");
9627
9909
  }
9628
9910
  const defaultPaths = [
9629
- join4(homedir2(), ".agents", "skills"),
9630
- join4(homedir2(), ".craft-agent", "workspaces")
9911
+ join5(homedir2(), ".agents", "skills"),
9912
+ join5(homedir2(), ".craft-agent", "workspaces")
9631
9913
  ];
9632
9914
  const scanPaths = options.skillsPaths ?? defaultPaths;
9633
9915
  const expandedPaths = [];
9634
9916
  for (const p of scanPaths) {
9635
9917
  if (p.includes("workspaces") && existsSync4(p)) {
9636
- const { readdirSync: readdirSync2 } = await import("node:fs");
9637
- const workspaces = readdirSync2(p, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("."));
9918
+ const { readdirSync: readdirSync3 } = await import("node:fs");
9919
+ const workspaces = readdirSync3(p, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("."));
9638
9920
  for (const ws of workspaces) {
9639
- const skillsDir = join4(p, ws.name, "skills");
9921
+ const skillsDir = join5(p, ws.name, "skills");
9640
9922
  if (existsSync4(skillsDir))
9641
9923
  expandedPaths.push(skillsDir);
9642
9924
  }
@@ -9649,7 +9931,7 @@ async function initCommand(options) {
9649
9931
  `);
9650
9932
  let imported = 0;
9651
9933
  for (const skill of existingSkills) {
9652
- const targetDir = join4(generalDir, skill.slug);
9934
+ const targetDir = join5(generalDir, skill.slug);
9653
9935
  if (existsSync4(targetDir)) {
9654
9936
  console.log(` → ${skill.slug}: already exists, skipping`);
9655
9937
  continue;
@@ -9747,19 +10029,57 @@ async function captureCommand(sessionPath, options) {
9747
10029
  }
9748
10030
  const project = options.project ?? output.project;
9749
10031
  for (const f of output.failures) {
10032
+ const timestamp = new Date().toISOString();
10033
+ const failureId = `f_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
10034
+ const activationTraceId = `activation_${failureId}`;
10035
+ const pressureSignalId = `pressure_${failureId}`;
9750
10036
  const record = {
9751
- id: `f_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
10037
+ id: failureId,
9752
10038
  sessionId,
9753
- timestamp: new Date().toISOString(),
10039
+ timestamp,
9754
10040
  project,
9755
10041
  userRequest: f.userRequest,
9756
10042
  agentAction: f.agentAction,
9757
10043
  correction: f.correction,
9758
10044
  correctionType: f.correctionType,
9759
10045
  skillsActive: f.skillsActive,
9760
- skillsRelevant: f.skillsActive
10046
+ skillsRelevant: f.skillsActive,
10047
+ activationTraceId,
10048
+ pressureSignals: [pressureSignalId]
10049
+ };
10050
+ const activationTrace = {
10051
+ id: activationTraceId,
10052
+ timestamp,
10053
+ provenance: "capture-derived",
10054
+ sourceId: failureId,
10055
+ projectId: project ?? undefined,
10056
+ sessionId,
10057
+ activatedSkillIds: f.skillsActive,
10058
+ relevantSkillIds: f.skillsActive,
10059
+ reasonSummary: `${f.correctionType} correction captured from session review`,
10060
+ contextSummary: f.correction,
10061
+ relatedFailureId: failureId,
10062
+ traceVersion: "0.1.0"
10063
+ };
10064
+ const pressureSignal = {
10065
+ id: pressureSignalId,
10066
+ kind: `correction:${f.correctionType}`,
10067
+ provenance: "failure-native",
10068
+ sourceType: "failure",
10069
+ sourceId: failureId,
10070
+ projectId: project ?? undefined,
10071
+ detectedAt: timestamp,
10072
+ severity: f.correctionType === "manual_edit" ? 0.95 : f.correctionType === "mode_switch" ? 0.9 : f.correctionType === "retry" ? 0.72 : 0.65,
10073
+ priority: f.correctionType === "manual_edit" || f.correctionType === "mode_switch" ? "high" : f.correctionType === "retry" ? "medium" : "low",
10074
+ description: f.correction,
10075
+ relatedFailureId: failureId,
10076
+ relatedActivationTraceId: activationTraceId,
10077
+ skillIds: f.skillsActive,
10078
+ status: "open"
9761
10079
  };
9762
10080
  appendFailure(record);
10081
+ appendActivationTrace(activationTrace);
10082
+ appendPressureSignal(pressureSignal);
9763
10083
  console.log(` ✓ Captured: "${f.correction.slice(0, 60)}..."`);
9764
10084
  }
9765
10085
  console.log(`
@@ -9805,7 +10125,7 @@ init_config();
9805
10125
  init_data();
9806
10126
  init_skills();
9807
10127
  init_llm();
9808
- import { join as join7 } from "node:path";
10128
+ import { join as join8 } from "node:path";
9809
10129
 
9810
10130
  // src/prompts/cluster.ts
9811
10131
  function buildClusterPrompt(failures, skills, graph) {
@@ -10216,11 +10536,11 @@ Return JSON: { "score": <number>, "reason": "<one sentence>" }`;
10216
10536
  // src/core/canary.ts
10217
10537
  init_config();
10218
10538
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync6, cpSync as cpSync2, rmSync } from "node:fs";
10219
- import { join as join5 } from "node:path";
10220
- var CANARY_DIR = join5(getHelixDir(), "canary");
10221
- var BACKUPS_DIR = join5(getHelixDir(), "backups");
10539
+ import { join as join6 } from "node:path";
10540
+ var CANARY_DIR = join6(getHelixDir(), "canary");
10541
+ var BACKUPS_DIR = join6(getHelixDir(), "backups");
10222
10542
  function getRegistryPath() {
10223
- return join5(getHelixDir(), "canary-registry.json");
10543
+ return join6(getHelixDir(), "canary-registry.json");
10224
10544
  }
10225
10545
  function loadRegistry() {
10226
10546
  const path = getRegistryPath();
@@ -10233,7 +10553,7 @@ function saveRegistry(registry) {
10233
10553
  }
10234
10554
  function backupSkill(skillPath, slug, version) {
10235
10555
  ensureDir(BACKUPS_DIR);
10236
- const backupPath = join5(BACKUPS_DIR, `${slug}_${version}_${Date.now()}`);
10556
+ const backupPath = join6(BACKUPS_DIR, `${slug}_${version}_${Date.now()}`);
10237
10557
  if (existsSync6(skillPath)) {
10238
10558
  cpSync2(skillPath, backupPath, { recursive: true });
10239
10559
  }
@@ -10315,7 +10635,7 @@ init_config();
10315
10635
  init_skills();
10316
10636
  init_data();
10317
10637
  init_llm();
10318
- import { join as join6 } from "node:path";
10638
+ import { join as join7 } from "node:path";
10319
10639
  async function autoGeneralize(candidates, verbose = false, dryRun = false) {
10320
10640
  const created = [];
10321
10641
  const skipped = [];
@@ -10384,7 +10704,7 @@ Keep it focused and actionable. No filler.`
10384
10704
  meta.lastEvolved = new Date().toISOString();
10385
10705
  meta.tags = [...meta.tags ?? [], "auto-generalized"];
10386
10706
  meta.enhances = candidate.sourceSkills;
10387
- const skillDir = join6(getGeneralSkillsPath(), candidate.suggestedName);
10707
+ const skillDir = join7(getGeneralSkillsPath(), candidate.suggestedName);
10388
10708
  writeSkill(skillDir, meta, content);
10389
10709
  created.push(candidate.suggestedName);
10390
10710
  console.log(` ✓ Created: ${candidate.suggestedName} (domain layer)`);
@@ -10489,8 +10809,10 @@ async function evolveCommand(options) {
10489
10809
  console.log();
10490
10810
  }
10491
10811
  const generation = getCurrentGeneration() + 1;
10812
+ const iterationId = generateIterationId();
10492
10813
  const proposals = [];
10493
10814
  const failureClusters = [];
10815
+ const artifacts = [];
10494
10816
  let proposalCount = 0;
10495
10817
  for (const cluster of clusters.clusters) {
10496
10818
  if (proposalCount >= maxProposals)
@@ -10589,10 +10911,40 @@ async function evolveCommand(options) {
10589
10911
  outcomeReason: !consensus ? `Rejected: only ${passCount}/3 judges passed` : !passedRegression ? `Rejected: regression ${(regressionResult.passRate * 100).toFixed(0)}% < ${(config.quality.regressionPassRate * 100).toFixed(0)}% threshold` : `Accepted: ${passCount}/3 judges + ${(regressionResult.passRate * 100).toFixed(0)}% regression`,
10590
10912
  relatedProposals: proposalOutput.relatedIterations
10591
10913
  };
10914
+ const artifactCreatedAt = new Date().toISOString();
10915
+ artifacts.push({
10916
+ id: `artifact_${proposalId}`,
10917
+ createdAt: artifactCreatedAt,
10918
+ artifactType: "evolution",
10919
+ provenance: "native-evolution",
10920
+ sourceId: proposalId,
10921
+ iterationId,
10922
+ proposalId,
10923
+ title: `${skillSlug} · ${proposalOutput.action}`,
10924
+ summary: proposalOutput.diff,
10925
+ targetSkill: skillSlug,
10926
+ operation: proposalOutput.action,
10927
+ outcome: proposal.outcome,
10928
+ clusterType: cluster.type,
10929
+ trigger: "manual",
10930
+ judgeScores: {
10931
+ taskCompletion: judge1.score,
10932
+ correctionAlignment: judge2.score,
10933
+ sideEffectCheck: judge3.score
10934
+ },
10935
+ regression: regressionResult,
10936
+ metrics: {
10937
+ averageJudgeScore: avgScore,
10938
+ taskCompletion: judge1.score,
10939
+ correctionAlignment: judge2.score,
10940
+ sideEffectCheck: judge3.score,
10941
+ regressionPassRate: regressionResult.passRate
10942
+ }
10943
+ });
10592
10944
  if (finalAccepted && !dryRun) {
10593
10945
  const { meta, content } = parseSkillMd(proposalOutput.proposedSkillMd);
10594
10946
  const skillSlug2 = proposalOutput.targetSkill ?? proposal.targetSkill;
10595
- const skillDir = join7(getGeneralSkillsPath(), skillSlug2);
10947
+ const skillDir = join8(getGeneralSkillsPath(), skillSlug2);
10596
10948
  meta.generation = generation;
10597
10949
  meta.score = avgScore;
10598
10950
  meta.lastEvolved = new Date().toISOString();
@@ -10639,15 +10991,21 @@ async function evolveCommand(options) {
10639
10991
  proposals.push(proposal);
10640
10992
  proposalCount++;
10641
10993
  }
10994
+ const iterationTimestamp = new Date().toISOString();
10642
10995
  const iteration = {
10643
- id: generateIterationId(),
10644
- timestamp: new Date().toISOString(),
10996
+ id: iterationId,
10997
+ timestamp: iterationTimestamp,
10645
10998
  trigger: "manual",
10646
10999
  failureCount: batch.length,
10647
11000
  failureClusters,
10648
11001
  proposals
10649
11002
  };
10650
11003
  addIteration(iteration);
11004
+ if (!dryRun) {
11005
+ for (const artifact of artifacts) {
11006
+ appendEvolutionArtifact(artifact);
11007
+ }
11008
+ }
10651
11009
  if (hasGraph) {
10652
11010
  const accepted2 = proposals.filter((p) => p.outcome === "accepted");
10653
11011
  if (accepted2.length > 0) {
@@ -10705,9 +11063,9 @@ init_skills();
10705
11063
  // src/core/knowledge-buffer.ts
10706
11064
  init_config();
10707
11065
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync7 } from "node:fs";
10708
- import { join as join8 } from "node:path";
10709
- var BUFFER_PATH = () => join8(getHelixDir(), "knowledge-buffer.json");
10710
- var DRAFTS_DIR = () => join8(getHelixDir(), "drafts");
11066
+ import { join as join9 } from "node:path";
11067
+ var BUFFER_PATH = () => join9(getHelixDir(), "knowledge-buffer.json");
11068
+ var DRAFTS_DIR = () => join9(getHelixDir(), "drafts");
10711
11069
  var MAX_DISCOVERIES = 50;
10712
11070
  var MAX_DRAFTS = 10;
10713
11071
  var DECAY_RATE = 0.1;
@@ -10764,9 +11122,9 @@ function saveDraft(draft) {
10764
11122
  existing.avgScore = draft.avgScore;
10765
11123
  existing.iteration++;
10766
11124
  existing.lastIterated = new Date().toISOString();
10767
- const draftDir = join8(DRAFTS_DIR(), draft.skillName);
11125
+ const draftDir = join9(DRAFTS_DIR(), draft.skillName);
10768
11126
  ensureDir(draftDir);
10769
- writeFileSync5(join8(draftDir, "SKILL.md"), draft.skillMd);
11127
+ writeFileSync5(join9(draftDir, "SKILL.md"), draft.skillMd);
10770
11128
  }
10771
11129
  } else {
10772
11130
  buffer.drafts.push({
@@ -10780,9 +11138,9 @@ function saveDraft(draft) {
10780
11138
  iteration: 1,
10781
11139
  lastIterated: new Date().toISOString()
10782
11140
  });
10783
- const draftDir = join8(DRAFTS_DIR(), draft.skillName);
11141
+ const draftDir = join9(DRAFTS_DIR(), draft.skillName);
10784
11142
  ensureDir(draftDir);
10785
- writeFileSync5(join8(draftDir, "SKILL.md"), draft.skillMd);
11143
+ writeFileSync5(join9(draftDir, "SKILL.md"), draft.skillMd);
10786
11144
  }
10787
11145
  trimAndSave(buffer);
10788
11146
  }
@@ -10882,7 +11240,7 @@ async function statusCommand() {
10882
11240
  init_data();
10883
11241
  init_skills();
10884
11242
  import { writeFileSync as writeFileSync6 } from "node:fs";
10885
- import { join as join9 } from "node:path";
11243
+ import { join as join10 } from "node:path";
10886
11244
  init_config();
10887
11245
  async function reportCommand(options) {
10888
11246
  const days = parseInt(options.days ?? "1");
@@ -10966,8 +11324,8 @@ async function reportCommand(options) {
10966
11324
  report += `- **${s.slug}**${evolved}
10967
11325
  `;
10968
11326
  }
10969
- const outputPath = options.output ?? join9(getHelixDir(), "reports", `${date}.md`);
10970
- ensureDir(join9(getHelixDir(), "reports"));
11327
+ const outputPath = options.output ?? join10(getHelixDir(), "reports", `${date}.md`);
11328
+ ensureDir(join10(getHelixDir(), "reports"));
10971
11329
  writeFileSync6(outputPath, report);
10972
11330
  console.log(report);
10973
11331
  console.log(`
@@ -10978,7 +11336,7 @@ async function reportCommand(options) {
10978
11336
  init_config();
10979
11337
  init_skills();
10980
11338
  init_llm();
10981
- import { join as join10 } from "node:path";
11339
+ import { join as join11 } from "node:path";
10982
11340
  async function generalizeCommand(options) {
10983
11341
  const verbose = options.verbose ?? false;
10984
11342
  const dryRun = options.dryRun ?? false;
@@ -11041,7 +11399,7 @@ async function generalizeCommand(options) {
11041
11399
  meta.generation = 1;
11042
11400
  meta.score = candidate.confidence;
11043
11401
  meta.lastEvolved = new Date().toISOString();
11044
- const skillDir = join10(getGeneralSkillsPath(), candidate.suggestedName);
11402
+ const skillDir = join11(getGeneralSkillsPath(), candidate.suggestedName);
11045
11403
  writeSkill(skillDir, meta, content);
11046
11404
  console.log(` ✓ Created: ${candidate.suggestedName} (${candidate.suggestedLayer} layer)
11047
11405
  `);
@@ -11149,7 +11507,7 @@ Return JSON:
11149
11507
  init_data();
11150
11508
  init_skills();
11151
11509
  init_llm();
11152
- import { join as join11 } from "node:path";
11510
+ import { join as join12 } from "node:path";
11153
11511
  async function specializeCommand(options) {
11154
11512
  const verbose = options.verbose ?? false;
11155
11513
  const dryRun = options.dryRun ?? false;
@@ -11220,8 +11578,8 @@ async function specializeCommand(options) {
11220
11578
  meta.generation = 1;
11221
11579
  meta.score = candidate.confidence;
11222
11580
  meta.lastEvolved = new Date().toISOString();
11223
- const projectSkillsDir = join11(process.cwd(), ".helix", "skills");
11224
- const skillDir = join11(projectSkillsDir, candidate.suggestedName);
11581
+ const projectSkillsDir = join12(process.cwd(), ".helix", "skills");
11582
+ const skillDir = join12(projectSkillsDir, candidate.suggestedName);
11225
11583
  writeSkill(skillDir, meta, content);
11226
11584
  console.log(` ✓ Created: ${candidate.suggestedName} (project layer, parent: ${candidate.domainSkill})
11227
11585
  `);
@@ -11318,10 +11676,11 @@ Return JSON:
11318
11676
  // src/commands/graph.ts
11319
11677
  import { writeFileSync as writeFileSync8 } from "node:fs";
11320
11678
  import { execSync } from "node:child_process";
11321
- import { join as join13 } from "node:path";
11679
+ import { join as join14 } from "node:path";
11322
11680
  import { tmpdir } from "node:os";
11323
11681
 
11324
11682
  // src/core/network.ts
11683
+ init_ontology();
11325
11684
  init_skills();
11326
11685
  init_data();
11327
11686
  init_config();
@@ -11371,7 +11730,12 @@ async function fullRebuild(verbose) {
11371
11730
  status: "active",
11372
11731
  tags: s.meta.tags ?? [],
11373
11732
  failureCount: s.meta.failureCount ?? 0,
11374
- lastEvolved: s.meta.lastEvolved ?? ""
11733
+ lastEvolved: s.meta.lastEvolved ?? "",
11734
+ cognitiveRole: s.meta.cognitiveRole,
11735
+ stabilityState: s.meta.stabilityState,
11736
+ plasticityState: s.meta.plasticityState,
11737
+ capabilities: s.meta.capabilities,
11738
+ lineageId: s.meta.lineageId
11375
11739
  }));
11376
11740
  const edges = [];
11377
11741
  for (const s of skills) {
@@ -11399,6 +11763,7 @@ async function fullRebuild(verbose) {
11399
11763
  const clusters = clusterSkills(nodes, edges);
11400
11764
  const graph = {
11401
11765
  updated: new Date().toISOString(),
11766
+ ontologyVersion: ONTOLOGY_SPEC_VERSION,
11402
11767
  nodes,
11403
11768
  edges: deduplicateEdges(edges),
11404
11769
  clusters
@@ -11671,29 +12036,29 @@ function detectMergeCandidates(enhances, coEvolves, skills) {
11671
12036
  init_data();
11672
12037
  init_skills();
11673
12038
  import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync3 } from "node:fs";
11674
- import { join as join12 } from "node:path";
12039
+ import { join as join13 } from "node:path";
11675
12040
  function syncToObsidian(vaultPath, verbose = false) {
11676
12041
  const graph = loadSkillGraph();
11677
12042
  const skills = loadAllGeneralSkills();
11678
- const skillsDir = join12(vaultPath, "Skills");
11679
- const reportsDir = join12(vaultPath, "Reports");
12043
+ const skillsDir = join13(vaultPath, "Skills");
12044
+ const reportsDir = join13(vaultPath, "Reports");
11680
12045
  mkdirSync3(skillsDir, { recursive: true });
11681
12046
  mkdirSync3(reportsDir, { recursive: true });
11682
12047
  for (const node of graph.nodes) {
11683
12048
  const skill = skills.find((s) => s.slug === node.id);
11684
12049
  const note = generateSkillNote(node, graph.edges, skill ?? undefined);
11685
- const notePath = join12(skillsDir, `${node.id}.md`);
12050
+ const notePath = join13(skillsDir, `${node.id}.md`);
11686
12051
  writeFileSync7(notePath, note);
11687
12052
  if (verbose)
11688
12053
  console.log(` ✓ ${node.id}.md`);
11689
12054
  }
11690
12055
  const indexNote = generateIndexNote(graph);
11691
- writeFileSync7(join12(vaultPath, "HelixEvo Index.md"), indexNote);
12056
+ writeFileSync7(join13(vaultPath, "HelixEvo Index.md"), indexNote);
11692
12057
  const recent = getRecentIterations(7);
11693
12058
  if (recent.length > 0) {
11694
12059
  const report = generateEvolutionReport(recent);
11695
12060
  const date = new Date().toISOString().slice(0, 10);
11696
- writeFileSync7(join12(reportsDir, `Evolution ${date}.md`), report);
12061
+ writeFileSync7(join13(reportsDir, `Evolution ${date}.md`), report);
11697
12062
  }
11698
12063
  console.log(` ✓ Synced ${graph.nodes.length} skills to ${vaultPath}`);
11699
12064
  }
@@ -12052,7 +12417,7 @@ ${mermaidCode}
12052
12417
  });
12053
12418
  </script>
12054
12419
  </body></html>`;
12055
- const htmlPath = join13(tmpdir(), "helix-network.html");
12420
+ const htmlPath = join14(tmpdir(), "helix-network.html");
12056
12421
  writeFileSync8(htmlPath, html);
12057
12422
  execSync(`open "${htmlPath}"`);
12058
12423
  console.log(` ✓ Opened in browser`);
@@ -12117,7 +12482,7 @@ function renderScoreBar(score) {
12117
12482
  init_config();
12118
12483
  init_skills();
12119
12484
  init_llm();
12120
- import { join as join14 } from "node:path";
12485
+ import { join as join15 } from "node:path";
12121
12486
  import { readFileSync as readFileSync7, existsSync as existsSync9 } from "node:fs";
12122
12487
  async function researchCommand(options) {
12123
12488
  const verbose = options.verbose ?? false;
@@ -12233,7 +12598,7 @@ Only include discoveries that are NOT already covered by current skills.`
12233
12598
  meta.score = result.avgScore / 10;
12234
12599
  meta.lastEvolved = new Date().toISOString();
12235
12600
  meta.tags = [...meta.tags ?? [], "research-discovered"];
12236
- const skillDir = join14(getGeneralSkillsPath(), hypothesis.skillName);
12601
+ const skillDir = join15(getGeneralSkillsPath(), hypothesis.skillName);
12237
12602
  writeSkill(skillDir, meta, content);
12238
12603
  console.log(` ✓ Created: ${hypothesis.skillName} (from research)
12239
12604
  `);
@@ -12281,7 +12646,7 @@ async function understandGoals(projectPath, skills) {
12281
12646
  const paths = projectPath ? [projectPath] : [process.cwd()];
12282
12647
  for (const p of paths) {
12283
12648
  for (const file of ["README.md", "CLAUDE.md", "package.json", ".agents/AGENTS.md"]) {
12284
- const fullPath = join14(p, file);
12649
+ const fullPath = join15(p, file);
12285
12650
  if (existsSync9(fullPath)) {
12286
12651
  const content = readFileSync7(fullPath, "utf-8").slice(0, 2000);
12287
12652
  context.push(`## ${file}
@@ -12443,15 +12808,126 @@ ${replay.slice(0, 800)}`
12443
12808
 
12444
12809
  // src/commands/dashboard.ts
12445
12810
  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";
12811
+ import { join as join17, dirname as dirname3 } from "node:path";
12812
+ import { existsSync as existsSync11, cpSync as cpSync3, mkdirSync as mkdirSync5, readdirSync as readdirSync3, readFileSync as readFileSync9, rmSync as rmSync2, writeFileSync as writeFileSync10 } from "node:fs";
12448
12813
  import { fileURLToPath as fileURLToPath2 } from "node:url";
12449
- import { homedir as homedir3 } from "node:os";
12814
+ import { homedir as homedir4 } from "node:os";
12450
12815
  init_skills();
12451
12816
  init_data();
12817
+
12818
+ // src/utils/update-check.ts
12819
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync9, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "node:fs";
12820
+ import { join as join16 } from "node:path";
12821
+ import { homedir as homedir3 } from "node:os";
12822
+ var HELIX_DIR2 = join16(homedir3(), ".helix");
12823
+ var CACHE_PATH = join16(HELIX_DIR2, "update-check.json");
12824
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
12825
+ var REGISTRY_URL = "https://registry.npmjs.org/helixevo/latest";
12826
+ function readCache() {
12827
+ try {
12828
+ if (!existsSync10(CACHE_PATH))
12829
+ return null;
12830
+ return JSON.parse(readFileSync8(CACHE_PATH, "utf-8"));
12831
+ } catch {
12832
+ return null;
12833
+ }
12834
+ }
12835
+ function writeCache(cache) {
12836
+ try {
12837
+ if (!existsSync10(HELIX_DIR2))
12838
+ mkdirSync4(HELIX_DIR2, { recursive: true });
12839
+ writeFileSync9(CACHE_PATH, JSON.stringify(cache));
12840
+ } catch {}
12841
+ }
12842
+ function hasNewerVersion(current, latest) {
12843
+ const c = current.split(".").map(Number);
12844
+ const l = latest.split(".").map(Number);
12845
+ for (let i = 0;i < 3; i++) {
12846
+ if ((l[i] ?? 0) > (c[i] ?? 0))
12847
+ return true;
12848
+ if ((l[i] ?? 0) < (c[i] ?? 0))
12849
+ return false;
12850
+ }
12851
+ return false;
12852
+ }
12853
+ async function fetchLatestVersion() {
12854
+ try {
12855
+ const controller = new AbortController;
12856
+ const timeout = setTimeout(() => controller.abort(), 3000);
12857
+ const res = await fetch(REGISTRY_URL, { signal: controller.signal });
12858
+ clearTimeout(timeout);
12859
+ if (!res.ok)
12860
+ return null;
12861
+ const data = await res.json();
12862
+ return data.version ?? null;
12863
+ } catch {
12864
+ return null;
12865
+ }
12866
+ }
12867
+ async function getUpdateInfo(currentVersion = VERSION, forceRefresh = false) {
12868
+ const cache = forceRefresh ? null : readCache();
12869
+ if (cache && Date.now() - cache.lastCheck < CHECK_INTERVAL_MS) {
12870
+ return {
12871
+ currentVersion,
12872
+ latestVersion: cache.latestVersion,
12873
+ hasUpdate: hasNewerVersion(currentVersion, cache.latestVersion),
12874
+ source: "cache"
12875
+ };
12876
+ }
12877
+ const latest = await fetchLatestVersion();
12878
+ if (!latest) {
12879
+ return {
12880
+ currentVersion,
12881
+ latestVersion: cache?.latestVersion ?? null,
12882
+ hasUpdate: cache ? hasNewerVersion(currentVersion, cache.latestVersion) : false,
12883
+ source: cache ? "cache" : "none"
12884
+ };
12885
+ }
12886
+ writeCache({ lastCheck: Date.now(), latestVersion: latest });
12887
+ return {
12888
+ currentVersion,
12889
+ latestVersion: latest,
12890
+ hasUpdate: hasNewerVersion(currentVersion, latest),
12891
+ source: "network"
12892
+ };
12893
+ }
12894
+ async function checkForUpdates() {
12895
+ try {
12896
+ const update = await getUpdateInfo(VERSION);
12897
+ if (update.hasUpdate && update.latestVersion) {
12898
+ printUpdateBanner(update.currentVersion, update.latestVersion);
12899
+ }
12900
+ } catch {}
12901
+ }
12902
+ function printUpdateBanner(currentVersion, latestVersion) {
12903
+ const yellow = "\x1B[33m";
12904
+ const green = "\x1B[32m";
12905
+ const cyan = "\x1B[36m";
12906
+ const bold = "\x1B[1m";
12907
+ const dim = "\x1B[2m";
12908
+ const reset = "\x1B[0m";
12909
+ console.log();
12910
+ console.log(`${yellow} ╭──────────────────────────────────────────────╮${reset}`);
12911
+ console.log(`${yellow} │ │${reset}`);
12912
+ console.log(`${yellow} │ ${bold}Update available!${reset}${yellow} ${dim}v${currentVersion}${reset}${yellow} → ${green}${bold}v${latestVersion}${reset}${yellow} │${reset}`);
12913
+ console.log(`${yellow} │ │${reset}`);
12914
+ console.log(`${yellow} │ ${reset}Run ${cyan}npm install -g helixevo@latest${reset}${yellow} │${reset}`);
12915
+ console.log(`${yellow} │ ${reset}to update${yellow} │${reset}`);
12916
+ console.log(`${yellow} │ │${reset}`);
12917
+ console.log(`${yellow} ╰──────────────────────────────────────────────╯${reset}`);
12918
+ console.log();
12919
+ }
12920
+
12921
+ // src/commands/dashboard.ts
12452
12922
  var __filename = "/Users/tianchichen/Documents/GitHub/helixevo/src/commands/dashboard.ts";
12453
- var HELIX_DASHBOARD_DIR = join15(homedir3(), ".helix", "dashboard");
12923
+ var HELIX_DASHBOARD_DIR = join17(homedir4(), ".helix", "dashboard");
12924
+ var DASHBOARD_SKIP_AUTO_UPDATE_ENV = "HELIXEVO_DASHBOARD_SKIP_AUTO_UPDATE";
12454
12925
  async function dashboardCommand(options) {
12926
+ if (options.autoUpdate !== false && process.env[DASHBOARD_SKIP_AUTO_UPDATE_ENV] !== "1" && !findDevDashboard()) {
12927
+ const didRelaunch = await maybeAutoUpdateAndRelaunch(options);
12928
+ if (didRelaunch)
12929
+ return;
12930
+ }
12455
12931
  const dir = prepareDashboard();
12456
12932
  if (!dir) {
12457
12933
  console.error(` Dashboard not found.
@@ -12461,7 +12937,7 @@ async function dashboardCommand(options) {
12461
12937
  console.error(" cd helixevo/dashboard && npm install && npx next dev --port 3847");
12462
12938
  process.exit(1);
12463
12939
  }
12464
- if (!existsSync10(join15(dir, "node_modules"))) {
12940
+ if (!existsSync11(join17(dir, "node_modules"))) {
12465
12941
  console.log(" Installing dashboard dependencies...");
12466
12942
  try {
12467
12943
  execSync2("npm install --no-audit --no-fund", { cwd: dir, stdio: "inherit" });
@@ -12474,22 +12950,22 @@ async function dashboardCommand(options) {
12474
12950
  }
12475
12951
  ensureSkillGraph();
12476
12952
  if (options.background) {
12477
- const logFile = join15(homedir3(), ".helix", "dashboard.log");
12953
+ const logFile = join17(homedir4(), ".helix", "dashboard.log");
12478
12954
  const out = __require("fs").openSync(logFile, "a");
12479
12955
  const err = __require("fs").openSync(logFile, "a");
12480
- let currentVersion = VERSION;
12956
+ let currentVersion2 = VERSION;
12481
12957
  try {
12482
- currentVersion = execSync2("helixevo --version 2>/dev/null", { encoding: "utf-8" }).trim() || VERSION;
12958
+ currentVersion2 = execSync2("helixevo --version 2>/dev/null", { encoding: "utf-8" }).trim() || VERSION;
12483
12959
  } catch {}
12484
12960
  const child = spawn2("npx", ["next", "dev", "--port", "3847"], {
12485
12961
  cwd: dir,
12486
12962
  stdio: ["ignore", out, err],
12487
- env: { ...process.env, HELIXEVO_VERSION: currentVersion },
12963
+ env: { ...process.env, HELIXEVO_VERSION: currentVersion2 },
12488
12964
  detached: true
12489
12965
  });
12490
12966
  child.unref();
12491
- writeFileSync9(join15(homedir3(), ".helix", "dashboard.pid"), String(child.pid));
12492
- console.log(` \uD83C\uDF10 HelixEvo Dashboard v${VERSION} running in background`);
12967
+ writeFileSync10(join17(homedir4(), ".helix", "dashboard.pid"), String(child.pid));
12968
+ console.log(` \uD83C\uDF10 HelixEvo Dashboard v${currentVersion2} running in background`);
12493
12969
  console.log(` http://localhost:3847`);
12494
12970
  console.log(` Logs: ${logFile}`);
12495
12971
  console.log(` Stop: helixevo dashboard --stop
@@ -12503,10 +12979,54 @@ async function dashboardCommand(options) {
12503
12979
  }, 2000);
12504
12980
  process.exit(0);
12505
12981
  }
12506
- console.log(` \uD83C\uDF10 Starting HelixEvo Dashboard v${VERSION} at http://localhost:3847
12982
+ let currentVersion = VERSION;
12983
+ try {
12984
+ currentVersion = execSync2("helixevo --version 2>/dev/null", { encoding: "utf-8" }).trim() || VERSION;
12985
+ } catch {}
12986
+ console.log(` \uD83C\uDF10 Starting HelixEvo Dashboard v${currentVersion} at http://localhost:3847
12507
12987
  `);
12508
12988
  launchDashboard(dir, true);
12509
12989
  }
12990
+ async function maybeAutoUpdateAndRelaunch(options) {
12991
+ const update = await getUpdateInfo(VERSION);
12992
+ if (!update.hasUpdate || !update.latestVersion)
12993
+ return false;
12994
+ console.log(` ⟳ New HelixEvo version detected: v${VERSION} → v${update.latestVersion}`);
12995
+ console.log(` Updating before launching dashboard...
12996
+ `);
12997
+ try {
12998
+ execSync2("npm install -g helixevo@latest", {
12999
+ stdio: "inherit",
13000
+ timeout: 120000,
13001
+ env: { ...process.env }
13002
+ });
13003
+ } catch {
13004
+ console.warn(`
13005
+ Auto-update failed. Continuing with the current installed version.
13006
+ `);
13007
+ return false;
13008
+ }
13009
+ console.log(`
13010
+ ✓ Updated to v${update.latestVersion}. Relaunching dashboard...
13011
+ `);
13012
+ const executable = process.platform === "win32" ? "helixevo.cmd" : "helixevo";
13013
+ const args = ["dashboard"];
13014
+ if (options.background)
13015
+ args.push("--background");
13016
+ if (options.autoUpdate === false)
13017
+ args.push("--no-auto-update");
13018
+ await new Promise((resolve, reject) => {
13019
+ const child = spawn2(executable, args, {
13020
+ stdio: "inherit",
13021
+ env: { ...process.env, [DASHBOARD_SKIP_AUTO_UPDATE_ENV]: "1" }
13022
+ });
13023
+ child.on("error", reject);
13024
+ child.on("close", (code) => {
13025
+ process.exit(code ?? 0);
13026
+ });
13027
+ });
13028
+ return true;
13029
+ }
12510
13030
  var RESTART_EXIT_CODE = 75;
12511
13031
  function launchDashboard(dir, openBrowser) {
12512
13032
  let currentVersion = VERSION;
@@ -12533,8 +13053,8 @@ function launchDashboard(dir, openBrowser) {
12533
13053
  \uD83D\uDD04 Restarting dashboard after update...
12534
13054
  `);
12535
13055
  setTimeout(() => {
12536
- const nextCache = join15(dir, ".next");
12537
- if (existsSync10(nextCache)) {
13056
+ const nextCache = join17(dir, ".next");
13057
+ if (existsSync11(nextCache)) {
12538
13058
  try {
12539
13059
  rmSync2(nextCache, { recursive: true });
12540
13060
  } catch {}
@@ -12560,20 +13080,20 @@ function prepareDashboard() {
12560
13080
  const npmSource = findNpmDashboard();
12561
13081
  if (npmSource)
12562
13082
  return copyToHelix(npmSource);
12563
- if (existsSync10(join15(HELIX_DASHBOARD_DIR, "package.json"))) {
13083
+ if (existsSync11(join17(HELIX_DASHBOARD_DIR, "package.json"))) {
12564
13084
  return HELIX_DASHBOARD_DIR;
12565
13085
  }
12566
13086
  return null;
12567
13087
  }
12568
13088
  function findDevDashboard() {
12569
13089
  const candidates = [
12570
- join15(process.cwd(), "dashboard")
13090
+ join17(process.cwd(), "dashboard")
12571
13091
  ];
12572
13092
  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"));
13093
+ candidates.push(join17(home, "Documents", "GitHub", "helixevo", "dashboard"));
13094
+ candidates.push(join17(home, "Documents", "GitHub", "skillgraph", "dashboard"));
12575
13095
  for (const dir of candidates) {
12576
- if (existsSync10(join15(dir, "package.json")) && !dir.includes("node_modules")) {
13096
+ if (existsSync11(join17(dir, "package.json")) && !dir.includes("node_modules")) {
12577
13097
  return dir;
12578
13098
  }
12579
13099
  }
@@ -12584,15 +13104,15 @@ function findNpmDashboard() {
12584
13104
  try {
12585
13105
  const thisFile = typeof __filename !== "undefined" ? __filename : fileURLToPath2(import.meta.url);
12586
13106
  const pkgRoot = dirname3(dirname3(thisFile));
12587
- candidates.push(join15(pkgRoot, "dashboard"));
13107
+ candidates.push(join17(pkgRoot, "dashboard"));
12588
13108
  } catch {}
12589
13109
  try {
12590
13110
  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"));
13111
+ candidates.push(join17(globalPrefix, "lib", "node_modules", "helixevo", "dashboard"));
13112
+ candidates.push(join17(globalPrefix, "node_modules", "helixevo", "dashboard"));
12593
13113
  } catch {}
12594
13114
  for (const dir of candidates) {
12595
- if (existsSync10(join15(dir, "package.json"))) {
13115
+ if (existsSync11(join17(dir, "package.json"))) {
12596
13116
  return dir;
12597
13117
  }
12598
13118
  }
@@ -12600,10 +13120,10 @@ function findNpmDashboard() {
12600
13120
  }
12601
13121
  function getInstalledDashboardVersion() {
12602
13122
  try {
12603
- const versionFile = join15(HELIX_DASHBOARD_DIR, ".helixevo-version");
12604
- if (!existsSync10(versionFile))
13123
+ const versionFile = join17(HELIX_DASHBOARD_DIR, ".helixevo-version");
13124
+ if (!existsSync11(versionFile))
12605
13125
  return null;
12606
- return readFileSync8(versionFile, "utf-8").trim();
13126
+ return readFileSync9(versionFile, "utf-8").trim();
12607
13127
  } catch {
12608
13128
  return null;
12609
13129
  }
@@ -12611,8 +13131,8 @@ function getInstalledDashboardVersion() {
12611
13131
  function copyToHelix(sourceDir) {
12612
13132
  let sourceVersion = VERSION;
12613
13133
  try {
12614
- const srcRoot = join15(sourceDir, "..", "package.json");
12615
- const pkg2 = JSON.parse(readFileSync8(srcRoot, "utf-8"));
13134
+ const srcRoot = join17(sourceDir, "..", "package.json");
13135
+ const pkg2 = JSON.parse(readFileSync9(srcRoot, "utf-8"));
12616
13136
  if (pkg2.name === "helixevo" && pkg2.version)
12617
13137
  sourceVersion = pkg2.version;
12618
13138
  } catch {}
@@ -12625,37 +13145,37 @@ function copyToHelix(sourceDir) {
12625
13145
  } else {
12626
13146
  console.log(" Setting up dashboard at ~/.helix/dashboard...");
12627
13147
  }
12628
- mkdirSync4(HELIX_DASHBOARD_DIR, { recursive: true });
13148
+ mkdirSync5(HELIX_DASHBOARD_DIR, { recursive: true });
12629
13149
  let depsChanged = true;
12630
13150
  try {
12631
- const srcDeps = readFileSync8(join15(sourceDir, "package.json"), "utf-8");
12632
- const dstDeps = readFileSync8(join15(HELIX_DASHBOARD_DIR, "package.json"), "utf-8");
13151
+ const srcDeps = readFileSync9(join17(sourceDir, "package.json"), "utf-8");
13152
+ const dstDeps = readFileSync9(join17(HELIX_DASHBOARD_DIR, "package.json"), "utf-8");
12633
13153
  const srcPkg = JSON.parse(srcDeps);
12634
13154
  const dstPkg = JSON.parse(dstDeps);
12635
13155
  depsChanged = JSON.stringify(srcPkg.dependencies) !== JSON.stringify(dstPkg.dependencies) || JSON.stringify(srcPkg.devDependencies) !== JSON.stringify(dstPkg.devDependencies);
12636
13156
  } catch {
12637
13157
  depsChanged = true;
12638
13158
  }
12639
- const items = readdirSync2(sourceDir, { withFileTypes: true });
13159
+ const items = readdirSync3(sourceDir, { withFileTypes: true });
12640
13160
  for (const item of items) {
12641
13161
  if (item.name === "node_modules" || item.name === ".next" || item.name === "package-lock.json" || item.name === ".env.local")
12642
13162
  continue;
12643
- const src = join15(sourceDir, item.name);
12644
- const dest = join15(HELIX_DASHBOARD_DIR, item.name);
13163
+ const src = join17(sourceDir, item.name);
13164
+ const dest = join17(HELIX_DASHBOARD_DIR, item.name);
12645
13165
  cpSync3(src, dest, { recursive: true });
12646
13166
  }
12647
13167
  if (depsChanged) {
12648
- const oldModules = join15(HELIX_DASHBOARD_DIR, "node_modules");
12649
- if (existsSync10(oldModules))
13168
+ const oldModules = join17(HELIX_DASHBOARD_DIR, "node_modules");
13169
+ if (existsSync11(oldModules))
12650
13170
  rmSync2(oldModules, { recursive: true });
12651
- const oldLock = join15(HELIX_DASHBOARD_DIR, "package-lock.json");
12652
- if (existsSync10(oldLock))
13171
+ const oldLock = join17(HELIX_DASHBOARD_DIR, "package-lock.json");
13172
+ if (existsSync11(oldLock))
12653
13173
  rmSync2(oldLock);
12654
13174
  }
12655
- const nextCache = join15(HELIX_DASHBOARD_DIR, ".next");
12656
- if (existsSync10(nextCache))
13175
+ const nextCache = join17(HELIX_DASHBOARD_DIR, ".next");
13176
+ if (existsSync11(nextCache))
12657
13177
  rmSync2(nextCache, { recursive: true });
12658
- writeFileSync9(join15(HELIX_DASHBOARD_DIR, ".helixevo-version"), sourceVersion);
13178
+ writeFileSync10(join17(HELIX_DASHBOARD_DIR, ".helixevo-version"), sourceVersion);
12659
13179
  return HELIX_DASHBOARD_DIR;
12660
13180
  }
12661
13181
  function ensureSkillGraph() {
@@ -12697,13 +13217,13 @@ function ensureSkillGraph() {
12697
13217
  }
12698
13218
 
12699
13219
  // src/commands/watch.ts
12700
- import { join as join17 } from "node:path";
12701
- import { existsSync as existsSync13 } from "node:fs";
13220
+ import { join as join19 } from "node:path";
13221
+ import { existsSync as existsSync14 } from "node:fs";
12702
13222
 
12703
13223
  // src/core/auto-capture.ts
12704
13224
  init_data();
12705
13225
  init_llm();
12706
- import { readFileSync as readFileSync9, existsSync as existsSync11, statSync as statSync2 } from "node:fs";
13226
+ import { readFileSync as readFileSync10, existsSync as existsSync12, statSync as statSync2 } from "node:fs";
12707
13227
  var CORRECTION_SIGNALS = [
12708
13228
  /\bno[, ]+(?:that's|thats|that is)\s+(?:wrong|incorrect|not right)/i,
12709
13229
  /\bdon'?t\s+do\s+(?:that|it\s+like\s+that)/i,
@@ -12791,17 +13311,17 @@ If no corrections found, return: { "corrections": [] }`
12791
13311
  }
12792
13312
  function watchEvents(options) {
12793
13313
  const { eventsPath, project, onCapture, onError, pollIntervalMs = 5000 } = options;
12794
- let lastSize = existsSync11(eventsPath) ? statSync2(eventsPath).size : 0;
13314
+ let lastSize = existsSync12(eventsPath) ? statSync2(eventsPath).size : 0;
12795
13315
  let messageBuffer = [];
12796
13316
  let pendingExtraction = false;
12797
13317
  const interval = setInterval(async () => {
12798
13318
  try {
12799
- if (!existsSync11(eventsPath))
13319
+ if (!existsSync12(eventsPath))
12800
13320
  return;
12801
13321
  const currentSize = statSync2(eventsPath).size;
12802
13322
  if (currentSize <= lastSize)
12803
13323
  return;
12804
- const content = readFileSync9(eventsPath, "utf-8");
13324
+ const content = readFileSync10(eventsPath, "utf-8");
12805
13325
  const lines = content.trim().split(`
12806
13326
  `).filter(Boolean);
12807
13327
  const newLines = lines.slice(messageBuffer.length);
@@ -12843,22 +13363,22 @@ init_data();
12843
13363
  // src/core/metrics.ts
12844
13364
  init_config();
12845
13365
  init_data();
12846
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync10, existsSync as existsSync12 } from "node:fs";
12847
- import { join as join16 } from "node:path";
13366
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync11, existsSync as existsSync13 } from "node:fs";
13367
+ import { join as join18 } from "node:path";
12848
13368
  function getMetricsPath() {
12849
- return join16(getHelixDir(), "metrics.json");
13369
+ return join18(getHelixDir(), "metrics.json");
12850
13370
  }
12851
13371
  function loadMetrics() {
12852
13372
  const path = getMetricsPath();
12853
- if (!existsSync12(path)) {
13373
+ if (!existsSync13(path)) {
12854
13374
  return { updated: new Date().toISOString(), skills: [], globalCorrectionRate: [], evolutionImpact: [] };
12855
13375
  }
12856
- return JSON.parse(readFileSync10(path, "utf-8"));
13376
+ return JSON.parse(readFileSync11(path, "utf-8"));
12857
13377
  }
12858
13378
  function saveMetrics(store) {
12859
13379
  ensureDir(getHelixDir());
12860
13380
  store.updated = new Date().toISOString();
12861
- writeFileSync10(getMetricsPath(), JSON.stringify(store, null, 2));
13381
+ writeFileSync11(getMetricsPath(), JSON.stringify(store, null, 2));
12862
13382
  }
12863
13383
  function updateMetrics() {
12864
13384
  const failures = loadFailures();
@@ -13033,14 +13553,14 @@ async function watchCommand(options) {
13033
13553
  const verbose = options.verbose ?? false;
13034
13554
  const autoEvolve = options.evolve !== false;
13035
13555
  const project = options.project ?? null;
13036
- const eventsPath = options.events ?? join17(process.cwd(), "events.jsonl");
13556
+ const eventsPath = options.events ?? join19(process.cwd(), "events.jsonl");
13037
13557
  console.log(`\uD83E\uDDEC HelixEvo Watch Mode — Always-On Learning
13038
13558
  `);
13039
13559
  console.log(` Events: ${eventsPath}`);
13040
13560
  console.log(` Project: ${project ?? "(auto-detect)"}`);
13041
13561
  console.log(` Auto-evolve: ${autoEvolve ? "ON" : "OFF"}`);
13042
13562
  console.log();
13043
- if (!existsSync13(eventsPath)) {
13563
+ if (!existsSync14(eventsPath)) {
13044
13564
  console.log(` ⚠ Events file not found yet. Will start watching when it appears.`);
13045
13565
  console.log(` Expected at: ${eventsPath}
13046
13566
  `);
@@ -13188,10 +13708,12 @@ async function metricsCommand(options) {
13188
13708
  }
13189
13709
 
13190
13710
  // src/commands/project-setup.ts
13711
+ init_data();
13191
13712
  init_skills();
13192
13713
  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";
13714
+ import { isAbsolute, join as join20, normalize, resolve } from "node:path";
13715
+ import { existsSync as existsSync15, readFileSync as readFileSync12, writeFileSync as writeFileSync12, mkdirSync as mkdirSync6, readdirSync as readdirSync4 } from "node:fs";
13716
+ import { homedir as homedir5 } from "node:os";
13195
13717
 
13196
13718
  // src/prompts/project-analysis.ts
13197
13719
  function buildProjectAnalysisPrompt(projectContext, skills) {
@@ -13255,6 +13777,23 @@ Return JSON:
13255
13777
 
13256
13778
  // src/commands/project-setup.ts
13257
13779
  init_config();
13780
+ function stripTrailingSeparator(path) {
13781
+ if (path.length <= 1)
13782
+ return path;
13783
+ return path.replace(/[\\/]+$/, "") || path;
13784
+ }
13785
+ function expandHomePath(path) {
13786
+ if (path === "~")
13787
+ return homedir5();
13788
+ if (path.startsWith("~/"))
13789
+ return join20(homedir5(), path.slice(2));
13790
+ return path;
13791
+ }
13792
+ function normalizeProjectPath(projectPath) {
13793
+ const expanded = expandHomePath(projectPath.trim());
13794
+ const resolvedPath = isAbsolute(expanded) ? normalize(expanded) : resolve(process.cwd(), expanded);
13795
+ return stripTrailingSeparator(resolvedPath);
13796
+ }
13258
13797
  function readProjectContext(projectPath) {
13259
13798
  const contextFiles = [];
13260
13799
  const filesToRead = [
@@ -13272,33 +13811,33 @@ function readProjectContext(projectPath) {
13272
13811
  "Dockerfile"
13273
13812
  ];
13274
13813
  for (const f of filesToRead) {
13275
- const p = join18(projectPath, f);
13276
- if (existsSync14(p)) {
13814
+ const p = join20(projectPath, f);
13815
+ if (existsSync15(p)) {
13277
13816
  try {
13278
- const content = readFileSync11(p, "utf-8");
13817
+ const content = readFileSync12(p, "utf-8");
13279
13818
  contextFiles.push({ name: f, content: content.slice(0, 2000) });
13280
13819
  } catch {}
13281
13820
  }
13282
13821
  }
13283
13822
  let dirStructure = "";
13284
13823
  try {
13285
- const items = readdirSync3(projectPath, { withFileTypes: true }).filter((d) => !d.name.startsWith(".") && d.name !== "node_modules" && d.name !== "__pycache__").slice(0, 30);
13824
+ const items = readdirSync4(projectPath, { withFileTypes: true }).filter((d) => !d.name.startsWith(".") && d.name !== "node_modules" && d.name !== "__pycache__").slice(0, 30);
13286
13825
  dirStructure = items.map((d) => `${d.isDirectory() ? "\uD83D\uDCC1" : "\uD83D\uDCC4"} ${d.name}`).join(`
13287
13826
  `);
13288
13827
  } catch {}
13289
13828
  let srcStructure = "";
13290
- const srcDir = join18(projectPath, "src");
13291
- if (existsSync14(srcDir)) {
13829
+ const srcDir = join20(projectPath, "src");
13830
+ if (existsSync15(srcDir)) {
13292
13831
  try {
13293
13832
  const scanDir = (dir, prefix, depth) => {
13294
13833
  if (depth > 2)
13295
13834
  return [];
13296
13835
  const lines = [];
13297
- const items = readdirSync3(dir, { withFileTypes: true }).filter((d) => !d.name.startsWith(".")).slice(0, 20);
13836
+ const items = readdirSync4(dir, { withFileTypes: true }).filter((d) => !d.name.startsWith(".")).slice(0, 20);
13298
13837
  for (const item of items) {
13299
13838
  lines.push(`${prefix}${item.isDirectory() ? "\uD83D\uDCC1" : "\uD83D\uDCC4"} ${item.name}`);
13300
13839
  if (item.isDirectory()) {
13301
- lines.push(...scanDir(join18(dir, item.name), prefix + " ", depth + 1));
13840
+ lines.push(...scanDir(join20(dir, item.name), prefix + " ", depth + 1));
13302
13841
  }
13303
13842
  }
13304
13843
  return lines;
@@ -13332,18 +13871,18 @@ ${f.content}
13332
13871
  return context;
13333
13872
  }
13334
13873
  function getProjectsDir() {
13335
- return join18(getHelixDir(), "projects");
13874
+ return join20(getHelixDir(), "projects");
13336
13875
  }
13337
13876
  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));
13877
+ const dir = join20(getProjectsDir(), profile.name);
13878
+ mkdirSync6(dir, { recursive: true });
13879
+ writeFileSync12(join20(dir, "profile.json"), JSON.stringify(profile, null, 2));
13341
13880
  }
13342
13881
  async function projectSetupCommand(projectPath, options) {
13343
13882
  const verbose = options.verbose ?? false;
13344
13883
  const dryRun = options.dryRun ?? false;
13345
- const resolvedPath = join18(process.cwd(), projectPath).replace(/\/$/, "");
13346
- if (!existsSync14(resolvedPath)) {
13884
+ const resolvedPath = normalizeProjectPath(projectPath);
13885
+ if (!existsSync15(resolvedPath)) {
13347
13886
  console.error(` ✗ Path not found: ${resolvedPath}`);
13348
13887
  process.exit(1);
13349
13888
  }
@@ -13402,19 +13941,58 @@ async function projectSetupCommand(projectPath, options) {
13402
13941
  }
13403
13942
  }
13404
13943
  if (!dryRun) {
13944
+ const analyzedAt = new Date().toISOString();
13405
13945
  const profile = {
13406
13946
  name: analysis.name,
13407
13947
  path: resolvedPath,
13408
13948
  description: analysis.description,
13409
13949
  techStack: analysis.techStack,
13410
13950
  domains: analysis.domains,
13411
- analyzedAt: new Date().toISOString(),
13951
+ analyzedAt,
13412
13952
  matchedSkills: analysis.matchedSkills,
13413
13953
  gaps: analysis.gaps,
13414
13954
  recommendations: analysis.recommendations,
13415
13955
  status: "analyzed"
13416
13956
  };
13417
13957
  saveProjectProfile(profile);
13958
+ const activationTraceId = `activation_project_${analysis.name}_${Date.now()}`;
13959
+ const activationTrace = {
13960
+ id: activationTraceId,
13961
+ timestamp: analyzedAt,
13962
+ provenance: "project-analysis",
13963
+ sourceId: resolvedPath,
13964
+ projectId: analysis.name,
13965
+ projectPath: resolvedPath,
13966
+ activatedSkillIds: analysis.matchedSkills.map((skill) => skill.slug),
13967
+ relevantSkillIds: analysis.matchedSkills.map((skill) => skill.slug),
13968
+ reasonSummary: "project-analysis skill match and capability scan",
13969
+ contextSummary: analysis.description,
13970
+ gapAreas: analysis.gaps.map((gap) => gap.area),
13971
+ recommendations: analysis.recommendations,
13972
+ traceVersion: "0.1.0"
13973
+ };
13974
+ appendActivationTrace(activationTrace);
13975
+ analysis.gaps.forEach((gap, index) => {
13976
+ const signal = {
13977
+ id: `pressure_project_${analysis.name}_${index}_${Date.now()}`,
13978
+ kind: `gap:${gap.area}`,
13979
+ provenance: "project-analysis-native",
13980
+ sourceType: "project-analysis",
13981
+ sourceId: resolvedPath,
13982
+ projectId: analysis.name,
13983
+ projectPath: resolvedPath,
13984
+ detectedAt: analyzedAt,
13985
+ severity: gap.priority === "high" ? 0.95 : gap.priority === "medium" ? 0.75 : 0.55,
13986
+ priority: gap.priority,
13987
+ capability: gap.area,
13988
+ description: gap.description,
13989
+ suggestedAction: gap.suggestedAction,
13990
+ relatedActivationTraceId: activationTraceId,
13991
+ skillIds: analysis.matchedSkills.map((skill) => skill.slug),
13992
+ status: "open"
13993
+ };
13994
+ appendPressureSignal(signal);
13995
+ });
13418
13996
  console.log(`
13419
13997
  ✓ Project profile saved to ~/.helix/projects/${analysis.name}/`);
13420
13998
  console.log(` Next: Run "helixevo specialize --project ${analysis.name}" to create project-specific skills`);
@@ -13428,95 +14006,9 @@ async function projectSetupCommand(projectPath, options) {
13428
14006
  console.log();
13429
14007
  }
13430
14008
 
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
14009
  // src/cli.ts
13518
- import { join as join20 } from "node:path";
13519
- import { homedir as homedir5 } from "node:os";
14010
+ import { join as join21 } from "node:path";
14011
+ import { homedir as homedir6 } from "node:os";
13520
14012
  import { existsSync as existsSync16, readFileSync as readFileSync13, rmSync as rmSync3 } from "node:fs";
13521
14013
  var program2 = new Command;
13522
14014
  program2.name("helixevo").description("Self-evolving skill ecosystem for AI agents").version(VERSION).addHelpText("after", `
@@ -13545,9 +14037,9 @@ program2.command("generalize").description("Promote cross-skill patterns to high
13545
14037
  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
14038
  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
14039
  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) => {
14040
+ 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
14041
  if (options.stop) {
13550
- const pidFile = join20(homedir5(), ".helix", "dashboard.pid");
14042
+ const pidFile = join21(homedir6(), ".helix", "dashboard.pid");
13551
14043
  if (existsSync16(pidFile)) {
13552
14044
  const pid = parseInt(readFileSync13(pidFile, "utf-8").trim());
13553
14045
  try {
@@ -13560,7 +14052,7 @@ program2.command("dashboard").description("Open web dashboard at http://localhos
13560
14052
  }
13561
14053
  return;
13562
14054
  }
13563
- dashboardCommand(options);
14055
+ await dashboardCommand(options);
13564
14056
  });
13565
14057
  program2.command("status").description("Show frontier, skills, failures, and network health").action(statusCommand);
13566
14058
  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);