aios-core
Version:
Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework
559 lines (468 loc) • 18 kB
JavaScript
/**
* AIOS IDS CLI — Incremental Development System
* Standalone (no external dependencies for npx compatibility)
*
* Commands:
* aios ids:query {intent} — Query registry for matching artifacts
* aios ids:create-review — Review CREATE decisions for promotion/deprecation
* aios ids:health — Run registry health check (self-healing)
* aios ids:check {intent} — Pre-check registry (FrameworkGovernor advisory)
* aios ids:impact {entity-id} — Impact analysis for modifications
* aios ids:stats — Registry statistics
* aios ids:register {path} — Register entity after creation
*
* Flags:
* --json — Machine-readable JSON output
* --type {type} — Filter by entity type
* --category {category} — Filter by category
* --fix — Auto-heal fixable issues (ids:health)
*
* Stories: IDS-2 (Decision Engine), IDS-4a (Self-Healing), IDS-7 (Framework Governor)
*/
;
const path = require('path');
const { RegistryLoader } = require(path.resolve(__dirname, '..', '.aios-core', 'core', 'ids', 'registry-loader'));
const { IncrementalDecisionEngine } = require(path.resolve(__dirname, '..', '.aios-core', 'core', 'ids', 'incremental-decision-engine'));
const { RegistryUpdater } = require(path.resolve(__dirname, '..', '.aios-core', 'core', 'ids', 'registry-updater'));
const { FrameworkGovernor } = require(path.resolve(__dirname, '..', '.aios-core', 'core', 'ids', 'framework-governor'));
// Optional: RegistryHealer (IDS-4a — may not exist yet)
let RegistryHealer = null;
try {
RegistryHealer = require(path.resolve(__dirname, '..', '.aios-core', 'core', 'ids', 'registry-healer')).RegistryHealer;
} catch (_err) {
// RegistryHealer not available — health commands degrade gracefully
}
// Parse arguments
const args = process.argv.slice(2);
const command = args[0];
const flags = args.filter((a) => a.startsWith('--'));
const jsonOutput = flags.includes('--json');
const fixFlag = flags.includes('--fix');
function showHelp() {
console.log(`
AIOS IDS — Incremental Development System
Commands:
ids:query {intent} Query registry for matching artifacts
ids:create-review Review CREATE decisions (30-day review)
ids:health Run registry health check (self-healing)
ids:check {intent} Pre-check registry (advisory REUSE/ADAPT/CREATE)
ids:impact {entity-id} Impact analysis for entity modifications
ids:stats Registry statistics (entity counts, health score)
ids:register {path} Register entity after creation
Flags:
--json Output as JSON
--type {type} Filter by entity type (task, script, agent, etc.)
--category {category} Filter by category
--fix Auto-heal fixable issues (ids:health only)
Examples:
aios ids:query "validate story drafts"
aios ids:query "template rendering engine" --json
aios ids:query "database migration" --type script
aios ids:create-review
aios ids:create-review --json
aios ids:health
aios ids:health --fix
aios ids:check "validate yaml schema" --type task
aios ids:impact create-doc
aios ids:stats
aios ids:stats --json
aios ids:register .aios-core/development/tasks/my-new-task.md
`);
}
function getFlag(name) {
const idx = args.indexOf(`--${name}`);
if (idx === -1 || idx + 1 >= args.length) return undefined;
return args[idx + 1];
}
/**
* Collect positional args (skipping flags and their values).
*/
function collectPositionalArgs(startIdx) {
const flagNames = new Set(['--json', '--type', '--category', '--fix']);
const parts = [];
let skipNext = false;
for (let i = startIdx; i < args.length; i++) {
if (skipNext) {
skipNext = false;
continue;
}
if (args[i] === '--type' || args[i] === '--category') {
skipNext = true;
continue;
}
if (flagNames.has(args[i])) {
continue;
}
parts.push(args[i]);
}
return parts.join(' ').trim();
}
function formatRecommendation(rec, index) {
const lines = [];
const icon = rec.decision === 'REUSE' ? '\u2705' : rec.decision === 'ADAPT' ? '\u{1F504}' : '\u2728';
lines.push(` ${index + 1}. ${icon} ${rec.decision} (${rec.confidence}) — ${rec.entityId}`);
lines.push(` Path: ${rec.entityPath}`);
lines.push(` Type: ${rec.entityType} | Relevance: ${(rec.relevanceScore * 100).toFixed(1)}%`);
lines.push(` ${rec.rationale}`);
if (rec.adaptationImpact) {
lines.push(` Impact: ${rec.adaptationImpact.directCount} direct, ${rec.adaptationImpact.indirectCount} indirect consumers`);
}
return lines.join('\n');
}
function formatCreateReviewEntry(entry) {
const statusIcon = {
'promotion-candidate': '\u{1F31F}',
'monitoring': '\u{1F50D}',
'deprecation-review': '\u26A0\uFE0F',
};
const icon = statusIcon[entry.status] || '\u2753';
return ` ${icon} ${entry.entityId} — reused ${entry.reusageCount}x — status: ${entry.status}`;
}
// ─── Helper: create a FrameworkGovernor instance ─────────────────────────────
function createGovernor() {
const loader = new RegistryLoader();
try {
loader.load();
} catch (err) {
console.error(`Error: Failed to load registry — ${err.message}`);
process.exit(1);
}
const engine = new IncrementalDecisionEngine(loader);
const updater = new RegistryUpdater();
const healer = RegistryHealer ? new RegistryHealer() : null;
return new FrameworkGovernor(loader, engine, updater, healer);
}
// ─── Command: ids:query ──────────────────────────────────────────────────────
function runQuery() {
const intent = collectPositionalArgs(1);
if (!intent) {
console.error('Error: Intent is required. Usage: aios ids:query "your intent here"');
process.exit(1);
}
const loader = new RegistryLoader();
try {
loader.load();
} catch (err) {
console.error(`Error: Failed to load registry — ${err.message}`);
process.exit(1);
}
const engine = new IncrementalDecisionEngine(loader);
const context = {};
const typeFilter = getFlag('type');
const categoryFilter = getFlag('category');
if (typeFilter) context.type = typeFilter;
if (categoryFilter) context.category = categoryFilter;
const result = engine.analyze(intent, context);
if (jsonOutput) {
console.log(JSON.stringify(result, null, 2));
return;
}
// Formatted output
console.log(`\nIDS Analysis: "${intent}"`);
console.log(`${'─'.repeat(60)}`);
console.log(`Registry: ${result.summary.totalEntities} entities | Matches: ${result.summary.matchesFound}`);
console.log(`Decision: ${result.summary.decision} (${result.summary.confidence} confidence)`);
if (result.warnings && result.warnings.length > 0) {
for (const w of result.warnings) {
console.log(`\u26A0\uFE0F ${w}`);
}
}
console.log(`\nRationale: ${result.rationale}`);
if (result.recommendations.length > 0) {
console.log('\nRecommendations:');
for (let i = 0; i < result.recommendations.length; i++) {
console.log(formatRecommendation(result.recommendations[i], i));
}
}
if (result.justification) {
console.log('\nCREATE Justification:');
console.log(` Evaluated: ${result.justification.evaluated_patterns.join(', ') || 'none'}`);
if (Object.keys(result.justification.rejection_reasons).length > 0) {
console.log(' Rejections:');
for (const [id, reason] of Object.entries(result.justification.rejection_reasons)) {
console.log(` - ${id}: ${reason}`);
}
}
console.log(` New capability: ${result.justification.new_capability}`);
console.log(` Review scheduled: ${result.justification.review_scheduled}`);
}
console.log('');
}
// ─── Command: ids:create-review ──────────────────────────────────────────────
function runCreateReview() {
const loader = new RegistryLoader();
try {
loader.load();
} catch (err) {
console.error(`Error: Failed to load registry — ${err.message}`);
process.exit(1);
}
const engine = new IncrementalDecisionEngine(loader);
const report = engine.reviewCreateDecisions();
if (jsonOutput) {
console.log(JSON.stringify(report, null, 2));
return;
}
console.log('\nIDS CREATE Decision Review');
console.log(`${'─'.repeat(60)}`);
console.log(`Total entities with CREATE justification: ${report.totalReviewed}`);
if (report.totalReviewed === 0) {
console.log('\nNo entities with CREATE justification metadata found.');
console.log('CREATE justifications are stored when new entities are registered via IDS.');
console.log('');
return;
}
if (report.pendingReview.length > 0) {
console.log(`\nPending Review (${report.pendingReview.length}):`);
for (const entry of report.pendingReview) {
console.log(formatCreateReviewEntry(entry));
}
}
if (report.promotionCandidates.length > 0) {
console.log(`\nPromotion Candidates (${report.promotionCandidates.length}):`);
for (const entry of report.promotionCandidates) {
console.log(formatCreateReviewEntry(entry));
}
}
if (report.deprecationReview.length > 0) {
console.log(`\nDeprecation Review (${report.deprecationReview.length}):`);
for (const entry of report.deprecationReview) {
console.log(formatCreateReviewEntry(entry));
}
}
if (report.monitoring.length > 0) {
console.log(`\nMonitoring (${report.monitoring.length}):`);
for (const entry of report.monitoring) {
console.log(formatCreateReviewEntry(entry));
}
}
console.log('');
}
// ─── Command: ids:health ─────────────────────────────────────────────────────
function runHealth() {
if (!RegistryHealer) {
// Degrade gracefully — use FrameworkGovernor.healthCheck() instead
const governor = createGovernor();
governor.healthCheck().then((result) => {
if (jsonOutput) {
console.log(JSON.stringify(result, null, 2));
} else {
console.log('\nIDS Registry Health Check');
console.log(`${'─'.repeat(60)}`);
console.log(`Healer Status: ${result.healerStatus}`);
console.log(`Message: ${result.message}`);
if (result.basicStats) {
console.log(`Entity Count: ${result.basicStats.entityCount}`);
}
}
});
return;
}
const healer = new RegistryHealer();
const healthResult = healer.runHealthCheck();
if (fixFlag && healthResult.issues.length > 0) {
const healResult = healer.heal(healthResult.issues, { autoOnly: true });
if (jsonOutput) {
console.log(JSON.stringify({ health: healthResult, healing: healResult }, null, 2));
} else {
formatHealthReport(healthResult);
console.log('');
formatHealingReport(healResult);
}
// Emit warnings for non-auto-healable issues
const manualIssues = healthResult.issues.filter((i) => !i.autoHealable);
if (manualIssues.length > 0) {
healer.emitWarnings(manualIssues).catch(() => {});
}
// Exit code 1 if critical issues remain unhealed
const unresolvedCritical = healResult.skipped.filter((i) => i.severity === 'critical');
if (unresolvedCritical.length > 0) {
process.exit(1);
}
return;
}
if (jsonOutput) {
console.log(JSON.stringify(healthResult, null, 2));
} else {
formatHealthReport(healthResult);
}
// Exit code 1 if critical issues found
if (healthResult.summary.bySeverity.critical > 0) {
process.exit(1);
}
}
function formatHealthReport(result) {
const { summary } = result;
console.log('\nIDS Registry Health Check');
console.log(`${'─'.repeat(60)}`);
console.log(`Timestamp: ${result.timestamp}`);
console.log(`Total Issues: ${summary.total}`);
console.log(` Critical: ${summary.bySeverity.critical}`);
console.log(` High: ${summary.bySeverity.high}`);
console.log(` Medium: ${summary.bySeverity.medium}`);
console.log(` Low: ${summary.bySeverity.low}`);
console.log(`Auto-healable: ${summary.autoHealable} (${summary.autoHealableRate}%)`);
console.log(`Needs manual: ${summary.needsManual}`);
if (result.issues.length === 0) {
console.log('\nRegistry is healthy. No issues detected.');
return;
}
console.log('\nIssues:');
for (const issue of result.issues) {
const severityIcon = {
critical: '\u274C',
high: '\u26A0\uFE0F',
medium: '\u{1F7E1}',
low: '\u{1F535}',
};
const icon = severityIcon[issue.severity] || '\u2753';
const healable = issue.autoHealable ? ' [auto-fixable]' : ' [manual]';
console.log(` ${icon} ${issue.severity.toUpperCase()} ${issue.ruleId}: ${issue.entityId}${healable}`);
console.log(` ${issue.description}`);
}
if (summary.autoHealable > 0) {
console.log(`\nRun with --fix to auto-heal ${summary.autoHealable} fixable issue(s).`);
}
}
function formatHealingReport(result) {
console.log('IDS Self-Healing Report');
console.log(`${'─'.repeat(60)}`);
console.log(`Batch ID: ${result.batchId}`);
console.log(`Healed: ${result.healed.length}`);
console.log(`Skipped: ${result.skipped.length}`);
console.log(`Errors: ${result.errors.length}`);
if (result.backupPath) {
console.log(`Backup: ${result.backupPath}`);
}
if (result.healed.length > 0) {
console.log('\nHealed:');
for (const item of result.healed) {
console.log(` \u2705 ${item.ruleId}: ${item.entityId}`);
}
}
if (result.skipped.length > 0) {
console.log('\nSkipped (manual intervention required):');
for (const item of result.skipped) {
console.log(` \u23ED ${item.ruleId}: ${item.entityId} — ${item.reason}`);
}
}
if (result.errors.length > 0) {
console.log('\nErrors:');
for (const item of result.errors) {
console.log(` \u274C ${item.ruleId || 'unknown'}: ${item.entityId || 'batch'} — ${item.error}`);
}
}
}
// ─── Command: ids:check (IDS-7) ─────────────────────────────────────────────
async function runCheck() {
const intent = collectPositionalArgs(1);
if (!intent) {
console.error('Error: Intent is required. Usage: aios ids:check "your intent here"');
process.exit(1);
}
const typeFilter = getFlag('type');
const governor = createGovernor();
const result = await governor.preCheck(intent, typeFilter);
if (jsonOutput) {
console.log(JSON.stringify(result, null, 2));
return;
}
console.log(FrameworkGovernor.formatPreCheckOutput(result));
}
// ─── Command: ids:impact (IDS-7) ────────────────────────────────────────────
async function runImpact() {
const entityId = collectPositionalArgs(1);
if (!entityId) {
console.error('Error: Entity ID is required. Usage: aios ids:impact {entity-id}');
process.exit(1);
}
const governor = createGovernor();
const result = await governor.impactAnalysis(entityId);
if (jsonOutput) {
console.log(JSON.stringify(result, null, 2));
return;
}
console.log(FrameworkGovernor.formatImpactOutput(result));
}
// ─── Command: ids:stats (IDS-7) ─────────────────────────────────────────────
async function runStats() {
const governor = createGovernor();
const result = await governor.getStats();
if (jsonOutput) {
console.log(JSON.stringify(result, null, 2));
return;
}
console.log(FrameworkGovernor.formatStatsOutput(result));
}
// ─── Command: ids:register (IDS-7) ──────────────────────────────────────────
async function runRegister() {
const filePath = collectPositionalArgs(1);
if (!filePath) {
console.error('Error: File path is required. Usage: aios ids:register {file-path}');
process.exit(1);
}
const typeFilter = getFlag('type');
const governor = createGovernor();
const result = await governor.postRegister(filePath, {
type: typeFilter || 'unknown',
agent: 'cli',
});
if (jsonOutput) {
console.log(JSON.stringify(result, null, 2));
return;
}
if (result.registered) {
console.log(`\nRegistered: ${filePath}`);
console.log(`Updated: ${result.updated} entities`);
} else {
console.log(`\nRegistration failed for: ${filePath}`);
if (result.errors.length > 0) {
for (const err of result.errors) {
console.log(` Error: ${err}`);
}
}
}
console.log('');
}
// ─── Main dispatch ──────────────────────────────────────────────────────────
switch (command) {
case 'ids:query':
case 'query':
runQuery();
break;
case 'ids:create-review':
case 'create-review':
runCreateReview();
break;
case 'ids:health':
case 'health':
runHealth();
break;
case 'ids:check':
case 'check':
runCheck();
break;
case 'ids:impact':
case 'impact':
runImpact();
break;
case 'ids:stats':
case 'stats':
runStats();
break;
case 'ids:register':
case 'register':
runRegister();
break;
case '--help':
case '-h':
case 'help':
case undefined:
showHelp();
break;
default:
console.error(`Unknown command: ${command}`);
showHelp();
process.exit(1);
}