import assert from 'node:assert/strict' import { join } from 'node:path' import test from 'node:test' import { resolveRolesDir } from '../bundle/index.js' import { isLegacyClaudePath } from '../bundle/lib/legacy.js' import { buildRoleText, listRoles, parseRole } from '../bundle/lib/role.js' import { cleanup, tempDir, write } from './helpers.mjs' const names = ['dev', 'admin', 'analyst', 'qa'] test('parseRole distinguishes list, role, and invalid input', () => { assert.deepEqual(parseRole('', names), { kind: 'list' }) assert.deepEqual(parseRole('dev', names), { kind: 'role', role: 'dev' }) assert.deepEqual(parseRole(' admin ', names), { kind: 'role', role: 'admin' }) assert.deepEqual(parseRole('nope', names), { kind: 'invalid', role: 'nope' }) }) test('buildRoleText reads the global layer and notes the missing project layer', () => { const root = tempDir() try { const rolesDir = join(root, 'roles') const project = join(root, 'project') write(join(rolesDir, 'dev.md'), '# Роль: dev\n\n## Процесс\n- TDD\n\n## Не делать\n- не писать в main\n') const built = buildRoleText({ role: 'dev', cwd: project, rolesDir, projectRolesDir: join(project, '.claude', 'roles'), roleNames: names, }) assert.match(built.text, /Роль сессии: dev/) assert.match(built.text, /# Роль: dev/) assert.match(built.text, /TDD/) assert.match(built.text, /Проектного файла роли dev нет/) assert.equal(built.projectMissing, true) } finally { cleanup(root) } }) test('buildRoleText includes the project layer when it exists', () => { const root = tempDir() try { const rolesDir = join(root, 'roles') const project = join(root, 'project') write(join(rolesDir, 'dev.md'), '# Роль: dev\n- общий\n') write(join(project, '.claude', 'roles', 'dev.md'), '# Роль: dev (проект)\n- проектный\n') const built = buildRoleText({ role: 'dev', cwd: project, rolesDir, projectRolesDir: join(project, '.claude', 'roles'), roleNames: names, }) assert.match(built.text, /- общий/) assert.match(built.text, /- проектный/) assert.equal(built.projectMissing, false) } finally { cleanup(root) } }) test('buildRoleText fails loudly without the global layer', () => { const root = tempDir() try { assert.throws( () => buildRoleText({ role: 'dev', cwd: root, rolesDir: join(root, 'roles'), roleNames: names }), /Нет общего файла роли/, ) } finally { cleanup(root) } }) test('resolveRolesDir prefers ~/.dsh/roles over ~/.claude/roles', () => { const home = tempDir() try { const dsh = join(home, '.dsh', 'roles') const claude = join(home, '.claude', 'roles') write(join(dsh, 'dev.md'), '# dsh\n') write(join(claude, 'dev.md'), '# claude\n') assert.equal(resolveRolesDir(join(home, 'bundle'), '', home), dsh) } finally { cleanup(home) } }) test('listRoles reports both layers', () => { const root = tempDir() try { const rolesDir = join(root, 'roles') const project = join(root, 'project') write(join(rolesDir, 'dev.md'), '# dev\n') write(join(project, '.claude', 'roles', 'qa.md'), '# qa\n') const text = listRoles({ cwd: project, rolesDir, projectRolesDir: join(project, '.claude', 'roles'), roleNames: names, }) assert.match(text, /dev: общий — есть; проектный — нет/) assert.match(text, /qa: общий — нет; проектный — есть/) } finally { cleanup(root) } }) test('isLegacyClaudePath detects ~/.claude paths only', () => { const home = tempDir() try { assert.equal(isLegacyClaudePath(join(home, '.claude', 'roles', 'dev.md'), home), true) assert.equal(isLegacyClaudePath(join(home, '.dsh', 'roles', 'dev.md'), home), false) assert.equal(isLegacyClaudePath(join(home, '.claude-other', 'x'), home), false) assert.equal(isLegacyClaudePath('', home), false) } finally { cleanup(home) } })