@sema-agent/core
Version:
Stateless, task-oriented AI agent core
47 lines (46 loc) • 1.85 kB
JavaScript
const SKILL_ROOT_PLACEHOLDERS = /\$\{(?:SKILL_ROOT|CLAUDE_PLUGIN_ROOT)\}/g;
export const SKILL_BASE_DIR_INVALID_CODE = "config.skill_base_dir_invalid";
export function skillBaseDirOf(value) {
if (value === undefined)
return undefined;
if (typeof value === "string" && value !== "")
return value;
const e = new Error(`SkillSpec.baseDir must be a non-empty string naming its own directory (got ${value === "" ? '""' : typeof value}) — ` +
`omit the key where no directory is stated; an empty one resolves the body's \${SKILL_ROOT} placeholders to nothing.`);
e.code = SKILL_BASE_DIR_INVALID_CODE;
throw e;
}
export function substituteSkillRoot(text, baseDir) {
return text.replace(SKILL_ROOT_PLACEHOLDERS, () => baseDir);
}
export function substitutedSkillLength(text, baseDir) {
const dir = skillBaseDirOf(baseDir);
if (dir === undefined)
return text.length;
return substituteSkillRootBounded(text, dir, 0).totalChars;
}
export function skillLoadReading(skill, limit) {
const delivered = substitutedSkillLength(skill.content, skill.baseDir);
return { loads: delivered <= limit, delivered };
}
export function substituteSkillRootBounded(text, baseDir, maxChars) {
const parts = [];
let totalChars = 0;
let built = 0;
const push = (piece) => {
totalChars += piece.length;
if (built >= maxChars)
return;
const kept = piece.length > maxChars - built ? piece.slice(0, maxChars - built) : piece;
parts.push(kept);
built += kept.length;
};
let last = 0;
for (const m of text.matchAll(SKILL_ROOT_PLACEHOLDERS)) {
push(text.slice(last, m.index));
push(baseDir);
last = m.index + m[0].length;
}
push(text.slice(last));
return { text: parts.join(""), totalChars };
}