148 lines
5.1 KiB
JavaScript
148 lines
5.1 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { join } from 'node:path'
|
|
import test from 'node:test'
|
|
import {
|
|
buildBootstrapText,
|
|
isBootstrapVisible,
|
|
prepareBootstrap,
|
|
resolveBootstrapSkill,
|
|
stripFrontmatter,
|
|
truncateUtf8,
|
|
} from '../bundle/lib/bootstrap.js'
|
|
import { cleanup, tempDir, write } from './helpers.mjs'
|
|
|
|
test('stripFrontmatter removes a YAML header', () => {
|
|
assert.equal(stripFrontmatter('---\nname: using-superpowers\n---\nbody\n'), 'body')
|
|
assert.equal(stripFrontmatter('body\n'), 'body')
|
|
})
|
|
|
|
test('truncateUtf8 respects the byte budget', () => {
|
|
const out = truncateUtf8(`<!-- marker -->\n${'x'.repeat(5000)}`, 2000)
|
|
assert.ok(Buffer.byteLength(out, 'utf8') <= 2000)
|
|
assert.match(out, /bootstrap truncated/)
|
|
assert.match(out, /^<!-- marker -->/)
|
|
})
|
|
|
|
test('prepareBootstrap builds a marker-first message with the mapping', () => {
|
|
const dir = tempDir()
|
|
try {
|
|
const skillPath = join(dir, 'SKILL.md')
|
|
const mappingPath = join(dir, 'dsh-tools.md')
|
|
write(skillPath, '---\nname: using-superpowers\ndescription: test\n---\n# Use skills\nNever skip the skill check.\n')
|
|
write(mappingPath, '| Read a file | read |\n')
|
|
|
|
const prepared = prepareBootstrap({ skillPath, mappingPath, maxBytes: 65_536 })
|
|
assert.ok(prepared)
|
|
assert.match(prepared.marker, /^<!-- DSH-HARNESS-BOOTSTRAP:[0-9a-f]{16} -->$/)
|
|
assert.ok(prepared.text.startsWith(prepared.marker))
|
|
assert.match(prepared.text, /Never skip the skill check/)
|
|
assert.match(prepared.text, /\| Read a file \| read \|/)
|
|
assert.doesNotMatch(prepared.text, /^---/)
|
|
assert.match(prepared.text, /EXTREMELY_IMPORTANT/)
|
|
} finally {
|
|
cleanup(dir)
|
|
}
|
|
})
|
|
|
|
test('buildBootstrapText is deterministic for the same content', () => {
|
|
const args = {
|
|
skillBody: '# Skill',
|
|
mapping: '| Read | read |',
|
|
marker: '<!-- DSH-HARNESS-BOOTSTRAP:0000000000000000 -->',
|
|
}
|
|
assert.equal(buildBootstrapText(args), buildBootstrapText(args))
|
|
})
|
|
|
|
test('isBootstrapVisible sees the marker in messages and in the visible surface', () => {
|
|
const marker = '<!-- DSH-HARNESS-BOOTSTRAP:abc -->'
|
|
assert.equal(isBootstrapVisible({}, [{ content: [{ type: 'text', text: marker }] }], marker), true)
|
|
assert.equal(isBootstrapVisible({}, [{ content: [{ type: 'text', text: 'hello' }] }], marker), false)
|
|
|
|
const event = { type: 'user/message', data: { content: [{ type: 'text', text: `prefix ${marker}` }] } }
|
|
const visibleAgent = {
|
|
session: {
|
|
seq: 2,
|
|
surface: { nodes: new Set([1]) },
|
|
eventAt: (seq) => (seq === 1 ? event : undefined),
|
|
},
|
|
}
|
|
assert.equal(isBootstrapVisible(visibleAgent, [], marker), true)
|
|
|
|
const compactedAgent = {
|
|
session: { seq: 3, surface: { nodes: new Set() }, eventAt: () => undefined },
|
|
}
|
|
assert.equal(isBootstrapVisible(compactedAgent, [], marker), false)
|
|
})
|
|
|
|
test('resolveBootstrapSkill prefers an explicit path and finds the user root', () => {
|
|
const home = tempDir()
|
|
try {
|
|
const installed = join(home, '.dsh', 'skills', 'using-superpowers', 'SKILL.md')
|
|
write(installed, '---\nname: using-superpowers\n---\nbody\n')
|
|
assert.equal(resolveBootstrapSkill({ home, env: {} }), installed)
|
|
|
|
const explicit = join(home, 'explicit.md')
|
|
write(explicit, 'explicit\n')
|
|
assert.equal(resolveBootstrapSkill({ configuredPath: explicit, home, env: {} }), explicit)
|
|
} finally {
|
|
cleanup(home)
|
|
}
|
|
})
|
|
|
|
test('resolveBootstrapSkill prefers the DSH copy over the Claude cache', () => {
|
|
const home = tempDir()
|
|
try {
|
|
const dsh = join(home, '.dsh', 'skills', 'using-superpowers', 'SKILL.md')
|
|
const claude = join(
|
|
home,
|
|
'.claude',
|
|
'plugins',
|
|
'cache',
|
|
'claude-plugins-official',
|
|
'superpowers',
|
|
'9.9.9',
|
|
'skills',
|
|
'using-superpowers',
|
|
'SKILL.md',
|
|
)
|
|
write(dsh, 'dsh copy\n')
|
|
write(claude, 'claude copy\n')
|
|
assert.equal(resolveBootstrapSkill({ home, env: {} }), dsh)
|
|
} finally {
|
|
cleanup(home)
|
|
}
|
|
})
|
|
|
|
test('prepareBootstrap warns once when the legacy ~/.claude skill is used', () => {
|
|
const home = tempDir()
|
|
try {
|
|
const legacy = join(home, '.claude', 'skills', 'using-superpowers', 'SKILL.md')
|
|
write(legacy, '---\nname: using-superpowers\n---\nlegacy body\n')
|
|
const warnings = []
|
|
const logger = { warn: (...args) => warnings.push(args) }
|
|
|
|
const first = prepareBootstrap({ home, env: {}, logger })
|
|
const second = prepareBootstrap({ home, env: {}, logger })
|
|
|
|
assert.equal(first.path, legacy)
|
|
assert.ok(second)
|
|
assert.equal(warnings.length, 1, 'one warning per process, not per step')
|
|
assert.match(String(warnings[0][0]), /legacy Claude Code source/)
|
|
assert.deepEqual(warnings[0].slice(-3), ['bootstrap', legacy, 'skills'])
|
|
} finally {
|
|
cleanup(home)
|
|
}
|
|
})
|
|
|
|
test('prepareBootstrap stays silent for the DSH-owned copy', () => {
|
|
const home = tempDir()
|
|
try {
|
|
write(join(home, '.dsh', 'skills', 'using-superpowers', 'SKILL.md'), '---\nname: x\n---\nbody\n')
|
|
const warnings = []
|
|
prepareBootstrap({ home, env: {}, logger: { warn: (...args) => warnings.push(args) } })
|
|
assert.equal(warnings.length, 0)
|
|
} finally {
|
|
cleanup(home)
|
|
}
|
|
})
|