import test from 'node:test'; import assert from 'node:assert/strict'; import { mkdtempSync, readFileSync, existsSync, rmSync, mkdirSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import path from 'node:path'; import { ensureAgentWorkspace, applyAssistantName, applyAssistantPrompt, normalizeAssistantName, templateWorkspaceDir } from '../daemon/agent-workspace.js'; const previous = process.env.XDG_DATA_HOME; const data = mkdtempSync(path.join(tmpdir(), 'jarvis-workspace-')); process.env.XDG_DATA_HOME = data; test('workspace seed copies SOUL and bootstrap, then name writes IDENTITY.md', () => { const dir = ensureAgentWorkspace({ name: 'Jarvis' }); assert.equal(existsSync(path.join(dir, 'SOUL.md')), true); assert.equal(existsSync(path.join(dir, 'AGENTS.md')), true); assert.equal(existsSync(path.join(dir, 'skills/browser/SKILL.md')), true); assert.match(readFileSync(path.join(dir, 'AGENTS.md'), 'utf8'), /skills\/browser\/SKILL.md/); assert.match(readFileSync(path.join(dir, 'TOOLS.md'), 'utf8'), /headed Playwright Chromium window/); assert.equal(existsSync(path.join(dir, 'BOOTSTRAP.md')), true); assert.match(readFileSync(path.join(dir, 'SOUL.md'), 'utf8'), /You are \*\*Jarvis\*\*/); assert.match(readFileSync(path.join(dir, 'BOOTSTRAP.md'), 'utf8'), /first-run ritual/); assert.doesNotMatch(readFileSync(path.join(dir, 'AGENTS.md'), 'utf8'), /Pip/); applyAssistantName(dir, 'Ada'); assert.match(readFileSync(path.join(dir, 'IDENTITY.md'), 'utf8'), /\*\*Name:\*\* Ada/); assert.match(readFileSync(path.join(dir, 'IDENTITY.md'), 'utf8'), /Ada lives on this Ubuntu/); assert.match(readFileSync(path.join(dir, 'SOUL.md'), 'utf8'), /# SOUL\.md: Ada/); assert.match(readFileSync(path.join(dir, 'SOUL.md'), 'utf8'), /You are \*\*Ada\*\*/); assert.doesNotMatch(readFileSync(path.join(dir, 'SOUL.md'), 'utf8'), /You are \*\*Jarvis\*\*/); const again = ensureAgentWorkspace({ name: 'Ada' }); assert.equal(again, dir); assert.match(readFileSync(path.join(dir, 'IDENTITY.md'), 'utf8'), /\*\*Name:\*\* Ada/); applyAssistantPrompt(dir, 'Be a dry ship cook. Never say great question.'); assert.match(readFileSync(path.join(dir, 'PERSONA.md'), 'utf8'), /dry ship cook/); applyAssistantPrompt(dir, ''); assert.equal(readFileSync(path.join(dir, 'PERSONA.md'), 'utf8').trim(), '# PERSONA.md'); assert.match(templateWorkspaceDir(), /agent-workspace$/); }); test('workspace seed patches an old Playwright one-liner and adds the browser skill', () => { const dir = path.join(data, 'jarvis/workspace'); mkdirSync(path.join(dir, 'skills'), { recursive: true }); writeFileSync(path.join(dir, 'AGENTS.md'), [ '# AGENTS.md', '', '## Tools', '- `web_search` / `google_search` / `fetch_page` / `web_fetch` / `wiki_search` / `hn_search` / `code_search` — Playwright Chromium. For cookie walls or extra clicks, call `browser` with snapshot then click or type.', '', ].join('\n')); writeFileSync(path.join(dir, 'TOOLS.md'), [ '# TOOLS.md', '', '## Shell', '- Public HTTP via curl or wget is blocked. Use `web_search` / `web_fetch` in the Jarvis Chromium window. For cookie walls, call `browser` with snapshot then click or type.', '', ].join('\n')); const seeded = ensureAgentWorkspace({ name: 'Jarvis' }); assert.equal(seeded, dir); assert.match(readFileSync(path.join(dir, 'AGENTS.md'), 'utf8'), /skills\/browser\/SKILL.md/); assert.match(readFileSync(path.join(dir, 'TOOLS.md'), 'utf8'), /headed Playwright Chromium window/); assert.equal(existsSync(path.join(dir, 'skills/browser/SKILL.md')), true); }); test('cleanup', () => { if (previous === undefined) delete process.env.XDG_DATA_HOME; else process.env.XDG_DATA_HOME = previous; rmSync(data, { recursive: true, force: true }); });