mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-10 08:05:14 +02:00
77 lines
3.6 KiB
TypeScript
77 lines
3.6 KiB
TypeScript
import type { MemoryFlowEvent, MemoryFlowReplayInput } from '@ktx/context/ingest/memory-flow';
|
|
import type { KtxDemoIo } from './demo.js';
|
|
|
|
function plural(n: number, one: string, many = `${one}s`): string {
|
|
return `${n} ${n === 1 ? one : many}`;
|
|
}
|
|
|
|
function formatDiff(added: number, modified: number, deleted: number, unchanged: number): string {
|
|
const parts: string[] = [];
|
|
if (added > 0) parts.push(`+${added} new`);
|
|
if (modified > 0) parts.push(`~${modified} changed`);
|
|
if (deleted > 0) parts.push(`-${deleted} removed`);
|
|
if (unchanged > 0) parts.push(`=${unchanged} unchanged`);
|
|
return parts.length > 0 ? parts.join(', ') : 'no changes';
|
|
}
|
|
|
|
export function formatMemoryFlowEventLine(event: MemoryFlowEvent): string | null {
|
|
switch (event.type) {
|
|
case 'source_acquired':
|
|
return `[connect] Connected ${event.adapter} - ${plural(event.fileCount, 'database file')} (${event.trigger})`;
|
|
case 'scope_detected':
|
|
return event.fingerprint
|
|
? `[scope] Scope locked: ${event.fingerprint}`
|
|
: '[scope] Reviewing the whole warehouse (no scope filter)';
|
|
case 'raw_snapshot_written':
|
|
return `[snapshot] Captured snapshot ${event.syncId} - ${plural(event.rawFileCount, 'file')}`;
|
|
case 'diff_computed':
|
|
return `[diff] Tables: ${formatDiff(event.added, event.modified, event.deleted, event.unchanged)}`;
|
|
case 'chunks_planned':
|
|
return event.evictionCount > 0
|
|
? `[plan] Grouped ${plural(event.workUnitCount, 'table')} into ${plural(event.chunkCount, 'business area')} (${plural(event.evictionCount, 'removal')})`
|
|
: `[plan] Grouped ${plural(event.workUnitCount, 'table')} into ${plural(event.chunkCount, 'business area')}`;
|
|
case 'stage_skipped':
|
|
return `[skip] ${event.stage} skipped: ${event.reason}`;
|
|
case 'work_unit_started':
|
|
return `[analyze] Reviewing "${event.unitKey}" - budget ${plural(event.stepBudget, 'agent step')}`;
|
|
case 'work_unit_step':
|
|
return null;
|
|
case 'candidate_action': {
|
|
const target = event.target === 'sl' ? 'semantic-layer' : 'wiki';
|
|
return `[draft] ${event.unitKey} -> ${target}: ${event.action} ${event.key}`;
|
|
}
|
|
case 'work_unit_finished':
|
|
if (event.status === 'success') {
|
|
return `[done] ${event.unitKey} reviewed`;
|
|
}
|
|
return `[fail] ${event.unitKey} needs attention${event.reason ? ` - ${event.reason}` : ''}`;
|
|
case 'reconciliation_finished': {
|
|
const conflicts = event.conflictCount === 0 ? 'no conflicts' : plural(event.conflictCount, 'conflict');
|
|
const fallbacks = event.fallbackCount === 0 ? 'nothing flagged for review' : `${plural(event.fallbackCount, 'item')} flagged for review`;
|
|
return `[validate] Reconciled drafts - ${conflicts}, ${fallbacks}`;
|
|
}
|
|
case 'saved': {
|
|
const total = event.wikiCount + event.slCount;
|
|
const commit = event.commitSha ? ` - commit ${event.commitSha.slice(0, 7)}` : '';
|
|
return `[memory] Saved ${plural(total, 'memory', 'memories')} (${event.wikiCount} wiki, ${event.slCount} semantic-layer)${commit}`;
|
|
}
|
|
case 'provenance_recorded':
|
|
return `[trace] Recorded provenance for ${plural(event.rowCount, 'row')}`;
|
|
case 'report_created':
|
|
return `[report] Run report ready: ${event.runId}`;
|
|
}
|
|
}
|
|
|
|
export function createPlainProgressEmitter(io: KtxDemoIo): (snapshot: MemoryFlowReplayInput) => void {
|
|
let printed = 0;
|
|
return (snapshot) => {
|
|
while (printed < snapshot.events.length) {
|
|
const event = snapshot.events[printed++];
|
|
if (!event) continue;
|
|
const line = formatMemoryFlowEventLine(event);
|
|
if (line !== null) {
|
|
io.stdout.write(`${line}\n`);
|
|
}
|
|
}
|
|
};
|
|
}
|