Files
gnome-jarvis/daemon/agent-workspace.js
T
snxraven 4383763cb5
Rolling release / release (push) Successful in 7m29s
Updates
2026-09-12 16:46:49 -04:00

97 lines
3.2 KiB
JavaScript

import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
const TEMPLATE_DIR = path.resolve(new URL('../vendor/agent-harness/agent-workspace', import.meta.url).pathname);
const SEED_FILES = [
'SOUL.md',
'IDENTITY.md',
'AGENTS.md',
'USER.md',
'MEMORY.md',
'BOOTSTRAP.md',
'HEARTBEAT.md',
'TOOLS.md',
'PERSONA.md',
'skills/skill-creator/SKILL.md',
];
export function normalizeAssistantName(value) {
const text = String(value || '').replace(/[\r\n\t]/g, ' ').trim().replace(/\s+/g, ' ').slice(0, 32);
if (!text || /[<>&]/.test(text)) return 'Jarvis';
return text;
}
export function agentWorkspaceDir() {
return path.join(process.env.XDG_DATA_HOME || path.join(os.homedir(), '.local/share'), 'jarvis/workspace');
}
export function templateWorkspaceDir() {
return TEMPLATE_DIR;
}
function copyIfMissing(from, to) {
if (fs.existsSync(to)) return false;
fs.mkdirSync(path.dirname(to), { recursive: true });
fs.copyFileSync(from, to);
return true;
}
function escapeRegExp(value) {
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function renameAssistant(text, previous, next) {
if (!previous || previous === next) return text;
return String(text || '').replace(new RegExp(escapeRegExp(previous), 'g'), next);
}
export function applyAssistantName(dir, name) {
const who = normalizeAssistantName(name);
const identity = path.join(dir, 'IDENTITY.md');
let text = '';
try { text = fs.readFileSync(identity, 'utf8'); } catch { text = '# IDENTITY.md\n\n'; }
const previous = (text.match(/\*\*Name:\*\*\s*(.+)/) || [])[1]?.trim() || 'Jarvis';
text = renameAssistant(text, previous, who);
if (/\*\*Name:\*\*/.test(text)) text = text.replace(/\*\*Name:\*\*.*/, `**Name:** ${who}`);
else text += `\n- **Name:** ${who}\n`;
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(identity, text);
const soul = path.join(dir, 'SOUL.md');
try {
let soulText = renameAssistant(fs.readFileSync(soul, 'utf8'), previous, who);
soulText = soulText.replace(/^# SOUL\.md:.*/m, `# SOUL.md: ${who}`);
fs.writeFileSync(soul, soulText);
} catch {}
const memory = path.join(dir, 'MEMORY.md');
try {
let mem = renameAssistant(fs.readFileSync(memory, 'utf8'), previous, who);
if (/Assistant name:/.test(mem)) mem = mem.replace(/Assistant name:.*/, `Assistant name: ${who}`);
fs.writeFileSync(memory, mem);
} catch {}
return who;
}
export function applyAssistantPrompt(dir, prompt) {
const notes = String(prompt || '').replace(/\0/g, '').replace(/\s+$/g, '').slice(0, 4000);
const file = path.join(dir, 'PERSONA.md');
const body = notes
? `# PERSONA.md\n\n${notes}\n`
: '# PERSONA.md\n\n';
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(file, body);
return notes;
}
export function ensureAgentWorkspace({ name = 'Jarvis', prompt = '' } = {}) {
const dest = agentWorkspaceDir();
fs.mkdirSync(path.join(dest, 'memory'), { recursive: true });
fs.mkdirSync(path.join(dest, 'skills'), { recursive: true });
for (const rel of SEED_FILES) {
copyIfMissing(path.join(TEMPLATE_DIR, rel), path.join(dest, rel));
}
applyAssistantName(dest, name);
applyAssistantPrompt(dest, prompt);
return dest;
}