Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/EvoMap/evolver/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The lifecycle module provides commands to manage the Evolver loop process. It supports starting, stopping, restarting, checking status, viewing logs, and performing health checks. Location: src/ops/lifecycle.js

CLI Usage

node src/ops/lifecycle.js [start|stop|restart|status|log|check]

Functions

start()

Start the Evolver loop in detached mode. Location: src/ops/lifecycle.js:58
function start(options)
options.delayMs
number
default:0
Delay before starting (in milliseconds)
status
string
started or already_running
pid
number
Process ID of started loop
pids
number[]
Array of existing PIDs (if already running)
Example:
const { start } = require('./src/ops/lifecycle');

const result = start({ delayMs: 2000 });
console.log(result);
// { status: 'started', pid: 12345 }

stop()

Stop all running Evolver loop processes. Location: src/ops/lifecycle.js:88
function stop()
status
string
stopped or not_running
killed
number[]
Array of PIDs that were stopped
Example:
const { stop } = require('./src/ops/lifecycle');

const result = stop();
console.log(result);
// { status: 'stopped', killed: [12345, 12346] }

restart()

Restart the Evolver loop (stop + start). Location: src/ops/lifecycle.js:116
function restart(options)
options.delayMs
number
default:2000
Delay before restarting (in milliseconds)
status
string
started
pid
number
Process ID of restarted loop
Example:
node src/ops/lifecycle.js restart

status()

Check if Evolver loop is running. Location: src/ops/lifecycle.js:121
function status()
running
boolean
Whether the loop is running
pids
object[]
Array of running processes with pid and cmd
log
string
Path to log file (relative to workspace)
Example:
const { status } = require('./src/ops/lifecycle');

const result = status();
console.log(result);
// {
//   running: true,
//   pids: [{ pid: 12345, cmd: 'node skills/feishu-evolver-wrapper/index.js --loop' }],
//   log: 'memory/evolver.log'
// }

tailLog()

View recent log entries. Location: src/ops/lifecycle.js:129
function tailLog(lines)
lines
number
default:20
Number of lines to display
file
string
Path to log file
content
string
Log content
error
string
Error message (if no log file exists)
Example:
node src/ops/lifecycle.js log

checkHealth()

Check if the loop is healthy and responsive. Location: src/ops/lifecycle.js:138
function checkHealth()
healthy
boolean
Whether the loop is healthy
reason
string
Reason if unhealthy: not_running or stagnation
silenceMinutes
number
Minutes since last log update (if stagnant)
pids
number[]
Array of running PIDs (if healthy)
Stagnation Detection: Location: src/ops/lifecycle.js:13
const MAX_SILENCE_MS = 30 * 60 * 1000; // 30 minutes

if (fs.existsSync(LOG_FILE)) {
  const silenceMs = Date.now() - fs.statSync(LOG_FILE).mtimeMs;
  if (silenceMs > MAX_SILENCE_MS) {
    return {
      healthy: false,
      reason: 'stagnation',
      silenceMinutes: Math.round(silenceMs / 60000)
    };
  }
}
Example:
node src/ops/lifecycle.js check
# Automatically restarts if unhealthy

Process Discovery

Location: src/ops/lifecycle.js:25
function getRunningPids() {
  const out = execSync('ps -e -o pid,args', { encoding: 'utf8' });
  const pids = [];
  
  for (const line of out.split('\n')) {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith('PID')) continue;
    
    const parts = trimmed.split(/\s+/);
    const pid = parseInt(parts[0], 10);
    const cmd = parts.slice(1).join(' ');
    
    if (pid === process.pid) continue; // Skip self
    
    // Match evolver loop processes
    if (cmd.includes('node') && cmd.includes('index.js') && cmd.includes('--loop')) {
      if (cmd.includes('feishu-evolver-wrapper') || cmd.includes('skills/evolver')) {
        pids.push(pid);
      }
    }
  }
  
  return [...new Set(pids)].filter(isPidRunning);
}

Loop Script Selection

Location: src/ops/lifecycle.js:15
function getLoopScript() {
  // Prefer wrapper if exists, fallback to core evolver
  if (process.env.EVOLVER_LOOP_SCRIPT) {
    return process.env.EVOLVER_LOOP_SCRIPT;
  }
  
  const wrapper = path.join(WORKSPACE_ROOT, 'skills/feishu-evolver-wrapper/index.js');
  if (fs.existsSync(wrapper)) {
    return wrapper;
  }
  
  return path.join(getRepoRoot(), 'index.js');
}

Environment Setup

Location: src/ops/lifecycle.js:73
const env = Object.assign({}, process.env);
const npmGlobal = path.join(process.env.HOME || '', '.npm-global/bin');
if (env.PATH && !env.PATH.includes(npmGlobal)) {
  env.PATH = npmGlobal + ':' + env.PATH;
}

const child = spawn('node', [script, '--loop'], {
  detached: true,
  stdio: ['ignore', out, err],
  cwd: WORKSPACE_ROOT,
  env: env
});

child.unref();

Complete Example

const lifecycle = require('./src/ops/lifecycle');

// Start the loop
const startResult = lifecycle.start();
if (startResult.status === 'started') {
  console.log('Evolver loop started:', startResult.pid);
}

// Check status
const statusResult = lifecycle.status();
if (statusResult.running) {
  console.log('Running processes:', statusResult.pids);
  console.log('Log file:', statusResult.log);
}

// Check health
const healthResult = lifecycle.checkHealth();
if (!healthResult.healthy) {
  console.error('Unhealthy:', healthResult.reason);
  if (healthResult.reason === 'stagnation') {
    console.log('Silence:', healthResult.silenceMinutes, 'minutes');
  }
  
  // Auto-restart
  lifecycle.restart();
}

// View logs
const logResult = lifecycle.tailLog(50);
if (logResult.content) {
  console.log(logResult.content);
}

// Stop the loop
const stopResult = lifecycle.stop();
if (stopResult.status === 'stopped') {
  console.log('Stopped PIDs:', stopResult.killed);
}