Files
mayatnikovandClaude Opus 4.7 86e5294bb8 chore: snapshot pre-v0.2.0 WIP (pathfinder/reflex/metrics/skills improvements)
Baseline for the v0.2.0 self-learning iteration. All 205 tests pass on this
state. Subsequent commits in this branch layer the knowledge base,
post-mortem coach, and persona narration on top.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 13:05:12 +03:00

77 lines
2.1 KiB
JavaScript

// Per-skill ok/fail counters. Persisted under state/<host>/ so the bot's
// next run and auto-improvement prompts can learn from prior attempts,
// not only the current process lifetime.
import fs from "node:fs";
import path from "node:path";
import { stateDir } from "./config.js";
const METRICS_PATH = path.join(stateDir, "skill-metrics.json");
function loadMetrics() {
const counts = new Map();
try {
const raw = fs.readFileSync(METRICS_PATH, "utf8");
const parsed = JSON.parse(raw);
for (const [id, m] of Object.entries(parsed ?? {})) {
counts.set(id, {
ok: Number(m.ok ?? 0),
fail: Number(m.fail ?? 0),
lastTs: Number(m.lastTs ?? 0),
lastCode: m.lastCode ?? null,
lastDurationMs: Number(m.lastDurationMs ?? 0),
totalDurationMs: Number(m.totalDurationMs ?? 0),
});
}
} catch {}
return counts;
}
function saveMetrics(counts) {
try {
fs.mkdirSync(stateDir, { recursive: true });
const out = {};
for (const [id, m] of counts) out[id] = { ...m };
const tmp = `${METRICS_PATH}.tmp`;
fs.writeFileSync(tmp, JSON.stringify(out, null, 2));
fs.renameSync(tmp, METRICS_PATH);
} catch {}
}
export function createSkillMetrics({ persist = true } = {}) {
const counts = persist ? loadMetrics() : new Map(); // id → { ok, fail, lastTs }
function record(id, ok, { code = null, durationMs = 0 } = {}) {
const cur = counts.get(id) ?? { ok: 0, fail: 0, lastTs: 0, lastCode: null, lastDurationMs: 0, totalDurationMs: 0 };
if (ok) cur.ok++;
else cur.fail++;
cur.lastTs = Date.now();
cur.lastCode = code ?? cur.lastCode ?? null;
cur.lastDurationMs = Math.max(0, Math.round(durationMs || 0));
cur.totalDurationMs += cur.lastDurationMs;
counts.set(id, cur);
if (persist) saveMetrics(counts);
}
function snapshot() {
const out = {};
for (const [id, m] of counts) {
const total = (m.ok ?? 0) + (m.fail ?? 0);
out[id] = {
...m,
avgDurationMs: total > 0 ? Math.round((m.totalDurationMs ?? 0) / total) : 0,
};
}
return out;
}
function reset() {
counts.clear();
if (persist) {
try { fs.unlinkSync(METRICS_PATH); } catch {}
}
}
return { record, snapshot, reset };
}