erosolar-cli
Version:
Unified AI agent framework for the command line - Multi-provider support with schema-driven tools, code intelligence, and transparent reasoning
136 lines • 5.1 kB
JavaScript
import { readFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
const DEFAULT_RULEBOOK_ROOT = resolve(MODULE_DIR, '..', '..');
const manifestCache = new Map();
const promptCache = new Map();
export function loadAgentRulebook(profile, options = {}) {
// If inline manifest is provided, use it directly
if (options.inline) {
const manifest = options.inline;
if (manifest.profile !== profile) {
throw new Error(`Rulebook profile mismatch for ${profile}. Expected \"${profile}\" but inline manifest declares \"${manifest.profile}\".`);
}
return manifest;
}
// Otherwise load from file
const root = options.root ?? DEFAULT_RULEBOOK_ROOT;
const filePath = resolveRulebookPath(profile, root, options.file);
const cacheKey = filePath;
const cached = manifestCache.get(cacheKey);
if (cached) {
return cached;
}
const raw = readFileSync(filePath, 'utf8');
const manifest = JSON.parse(raw);
if (manifest.profile !== profile) {
throw new Error(`Rulebook profile mismatch for ${profile}. Expected \"${profile}\" but file declares \"${manifest.profile}\".`);
}
manifestCache.set(cacheKey, manifest);
return manifest;
}
export function buildAgentRulebookPrompt(profile, options = {}) {
// If inline, don't cache (since it might change)
if (options.inline) {
const manifest = loadAgentRulebook(profile, { inline: options.inline });
return formatAgentRulebook(manifest);
}
const root = options.root ?? DEFAULT_RULEBOOK_ROOT;
const filePath = resolveRulebookPath(profile, root, options.file);
const cacheKey = filePath;
const cached = promptCache.get(cacheKey);
if (cached) {
return cached;
}
const manifest = loadAgentRulebook(profile, { root, file: options.file });
const prompt = formatAgentRulebook(manifest);
promptCache.set(cacheKey, prompt);
return prompt;
}
export function formatAgentRulebook(manifest) {
const lines = [];
const headerLabel = manifest.label || manifest.profile;
lines.push(`${headerLabel} — rulebook version ${manifest.version} (contract ${manifest.contractVersion}).`);
if (manifest.description) {
lines.push(manifest.description.trim());
}
if (manifest.globalPrinciples?.length) {
lines.push('\nGLOBAL PRINCIPLES:');
for (const rule of manifest.globalPrinciples) {
lines.push(formatRuleLine(rule));
}
}
for (const phase of manifest.phases) {
const phaseSummary = [phase.label || phase.id];
if (phase.description) {
phaseSummary.push(`– ${phase.description}`);
}
lines.push(`\nPHASE ${phase.id}: ${phaseSummary.join(' ')}`);
if (phase.trigger) {
lines.push(` Trigger: ${phase.trigger}`);
}
for (const step of phase.steps) {
lines.push(formatStepLine(step));
}
}
return lines.filter(Boolean).join('\n');
}
function resolveRulebookPath(profile, root, fileOverride) {
const trimmedOverride = fileOverride?.trim();
if (trimmedOverride) {
return resolve(root, trimmedOverride);
}
return resolve(root, 'agents', `${profile}.rules.json`);
}
function formatRuleLine(rule, indent = ' ') {
const severity = rule.severity?.toUpperCase() ?? 'INFO';
const parts = [`[${severity}] (${rule.id}) ${rule.summary}`];
if (rule.detail) {
parts.push(`— ${rule.detail}`);
}
if (rule.evidenceRequired) {
parts.push(`Evidence: ${rule.evidenceRequired}`);
}
return `${indent}${parts.join(' ')}`;
}
function formatStepLine(step) {
const details = [];
if (step.intent) {
details.push(step.intent);
}
if (step.description && step.description !== step.intent) {
details.push(step.description);
}
const header = [` STEP ${step.id}: ${step.title}`];
if (details.length) {
header.push(`(${details.join(' — ')})`);
}
const lines = [header.join(' ')];
if (step.entryCriteria?.length) {
lines.push(` Entry: ${step.entryCriteria.join('; ')}`);
}
if (step.exitCriteria?.length) {
lines.push(` Exit: ${step.exitCriteria.join('; ')}`);
}
if (step.allowedTools?.length) {
lines.push(` Allowed tools: ${step.allowedTools.join(', ')}`);
}
if (step.blockedTools?.length) {
lines.push(` Blocked tools: ${step.blockedTools.join(', ')}`);
}
if (step.notes?.length) {
lines.push(` Notes: ${step.notes.join(' ')}`);
}
for (const rule of step.rules) {
lines.push(formatRuleLine(rule, ' - '));
}
if (step.subSteps?.length) {
lines.push(' Sub-steps:');
for (const subStep of step.subSteps) {
lines.push(formatStepLine(subStep).replace(/^ {2}/gm, ' '));
}
}
return lines.join('\n');
}
//# sourceMappingURL=agentRulebook.js.map