[READ-ONLY] Mirror of https://github.com/bombshell-dev/tools. Internal CLI to standardize tooling across all Bombshell projects
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183import { lstat, readlink, realpath, symlink } from 'node:fs/promises';import { fileURLToPath } from 'node:url';import { describe, it, expect } from 'vitest';import { createFixture, createMocks } from '../test-utils/index.ts';import { copySkills, findParentPackage, resolveSkillsSource, updateAgentsMd } from './sync.ts';
describe('copySkills', () => { it('symlinks each skill into the destination', async () => { const fixture = await createFixture({ 'source-skills': { build: { 'SKILL.md': '---\nname: build\ndescription: Build the project.\n---\nbody', }, }, });
const source = new URL('source-skills/', fixture.root); const dest = new URL('project/skills/', fixture.root);
const skills = await copySkills({ source, dest });
expect(skills).toEqual([{ name: 'build', description: 'Build the project.' }]);
// The destination entry must be a symlink, not a copy. const linkPath = fileURLToPath(new URL('build', dest)); expect((await lstat(linkPath)).isSymbolicLink()).toBe(true);
// And it must resolve back to the source skill. expect(await readlink(linkPath)).toBe('../../source-skills/build');
// Reading through the link reaches the real file. expect(await fixture.text('project/skills/build/SKILL.md')).toContain('name: build'); });
it('is idempotent and never touches the source on re-sync', async () => { const fixture = await createFixture({ 'source-skills': { build: { 'SKILL.md': '---\nname: build\ndescription: Build the project.\n---\nbody', }, }, });
const source = new URL('source-skills/', fixture.root); const dest = new URL('project/skills/', fixture.root);
const first = await copySkills({ source, dest }); // Re-running must not throw and must yield the same result. const second = await copySkills({ source, dest });
expect(second).toEqual(first); // The source skill files must survive a re-sync. expect(await fixture.text('source-skills/build/SKILL.md')).toContain('name: build'); expect(await fixture.text('project/skills/build/SKILL.md')).toContain('name: build'); });
it('reads block scalar descriptions without the indicator', async () => { const fixture = await createFixture({ 'source-skills': { test: { 'SKILL.md': '---\nname: test\ndescription: >\n Vitest test runner with colocated .test.ts files.\n Use when writing tests.\nmetadata:\n type: core\n---\nbody', }, lint: { 'SKILL.md': '---\nname: lint\ndescription: |-\n Lint the project.\n---\nbody', }, }, });
const skills = await copySkills({ source: new URL('source-skills/', fixture.root), dest: new URL('project/skills/', fixture.root), });
expect(skills).toEqual( expect.arrayContaining([ { name: 'test', description: 'Vitest test runner with colocated .test.ts files. Use when writing tests.', }, { name: 'lint', description: 'Lint the project.' }, ]), ); });
it('prunes stale links that target the real path of the source', async () => { const fixture = await createFixture({ store: { skills: { build: { 'SKILL.md': '---\nname: build\ndescription: Build.\n---\n' }, }, }, project: { tools: ({ symlink }) => symlink('../store'), skills: {}, }, }); // Left behind by an older sync that linked through the real path. await symlink( `${await realpath(fileURLToPath(new URL('store/skills/', fixture.root)))}/removed`, fileURLToPath(new URL('project/skills/removed', fixture.root)), );
await copySkills({ source: new URL('project/tools/skills/', fixture.root), dest: new URL('project/skills/', fixture.root), });
await expect( lstat(fileURLToPath(new URL('project/skills/removed', fixture.root))), ).rejects.toThrow(); });});
describe('updateAgentsMd', () => { it('summarizes each skill with its first full sentence', async () => { const fixture = await createFixture({ 'AGENTS.md': '# Project\n' });
await updateAgentsMd({ root: fixture.root, skills: [ { name: 'test', description: 'Vitest test runner with colocated .test.ts files. Use when writing tests.', }, ], });
expect(await fixture.text('AGENTS.md')).toContain( '- **test** — [skills/test/SKILL.md](skills/test/SKILL.md) - Vitest test runner with colocated .test.ts files\n', ); });});
describe('resolveSkillsSource', () => { it('prefers the project node_modules path over the real store path', async () => { const fixture = await createFixture({ store: { tools: { skills: { build: { 'SKILL.md': '' } } } }, project: { 'node_modules/@bomb.sh/tools': ({ symlink }) => symlink('../../../store/tools'), }, }); const root = new URL('project/', fixture.root); const fallback = new URL('store/tools/skills/', fixture.root);
expect((await resolveSkillsSource(root, fallback)).href).toBe( new URL('node_modules/@bomb.sh/tools/skills/', root).href, ); });
it('falls back when the project has no linked @bomb.sh/tools', async () => { const fixture = await createFixture({ project: {} }); const fallback = new URL('store/tools/skills/', fixture.root);
expect(await resolveSkillsSource(new URL('project/', fixture.root), fallback)).toBe(fallback); });});
describe('findParentPackage', () => { it('resolves the project root from INIT_CWD, not from this package', async () => { const fixture = await createFixture({ project: { 'package.json': '{ "name": "my-app" }', nested: {}, }, }); createMocks({ env: { INIT_CWD: fileURLToPath(new URL('project/nested/', fixture.root)) } });
const found = await findParentPackage();
expect(found).toBe(fileURLToPath(new URL('project/package.json', fixture.root))); });
it('returns null when invoked inside @bomb.sh/tools itself', async () => { const fixture = await createFixture({ 'package.json': '{ "name": "@bomb.sh/tools" }', }); createMocks({ env: { INIT_CWD: fileURLToPath(fixture.root) } });
expect(await findParentPackage()).toBe(null); });});