← back to The Ai Factory
src/stages/memory_write.js
63 lines
// Stage 9 — memory_write. Appends one line to Steve's MEMORY.md index after a
// successful activation. We do NOT write a separate memory file per artifact —
// that would pollute the memory dir; the index entry is enough for future
// sessions to learn the artifact exists.
const fs = require('node:fs/promises');
const path = require('node:path');
const os = require('node:os');
const MEMORY_INDEX = path.join(
os.homedir(),
'.claude',
'projects',
'-Users-stevestudio2',
'memory',
'MEMORY.md'
);
function todayStamp() {
return new Date().toISOString().slice(0, 10); // YYYY-MM-DD
}
// SECURITY (P1 fix 2026-05-04): scope + triggers are LLM-generated. Without
// sanitization a poisoned response could inject newlines/markdown headers
// (e.g. `\n# OVERWRITE`) into MEMORY.md. Strip newlines, cap length.
function sanitize(s, max = 200) {
return String(s ?? '').replace(/[\r\n]+/g, ' ').replace(/\s+/g, ' ').trim().slice(0, max);
}
function buildLine({ runId, artifactType, artifactName, spec, dest }) {
const safeName = sanitize(artifactName, 60);
const safeType = sanitize(artifactType, 20);
const scope = sanitize(spec?.scope, 200);
const triggerArr = Array.isArray(spec?.triggers)
? spec.triggers.slice(0, 3).map(t => sanitize(t, 60)).filter(Boolean)
: [];
const triggers = triggerArr.length ? ` triggers: ${triggerArr.join(', ')};` : '';
const surface = safeType === 'subagent' ? '~/.claude/agents/' : '~/.claude/skills/';
return `- AI Factory shipped: \`${safeName}\` (${safeType}) — ${scope}${triggers} [run #${runId}, ${todayStamp()}, lives in ${surface}]`;
}
async function appendMemoryIndex(line) {
let existing = '';
try { existing = await fs.readFile(MEMORY_INDEX, 'utf8'); }
catch (e) {
if (e.code !== 'ENOENT') throw e;
}
// Avoid double-entry if activate is called twice for the same artifact.
if (existing.includes(line.split(' [run #')[0])) {
return { skipped: true, reason: 'already present' };
}
const trailing = existing.endsWith('\n') ? '' : (existing ? '\n' : '');
await fs.writeFile(MEMORY_INDEX, existing + trailing + line + '\n', 'utf8');
return { skipped: false };
}
async function runMemoryWrite({ runId, artifactType, artifactName, spec, dest }) {
const line = buildLine({ runId, artifactType, artifactName, spec, dest });
const result = await appendMemoryIndex(line);
return { line, ...result };
}
module.exports = { runMemoryWrite, MEMORY_INDEX };