Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | 4x 4x 23x 30x 30x 49x 75x 75x 75x 75x 7x 7x 68x 68x 68x 68x 68x 23x 23x 2x 2x 2x 4x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x | import { createHash } from 'node:crypto';
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
import { basename, dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { SkillBuildAuthoringProvider } from './skill-contract.js';
const DEFAULT_AUTHORING_SKILL_NAME = 'skill-writer';
const AUTHORING_PROVIDER_ENV = 'WARDEN_SKILL_AUTHORING_ROOT';
function hashDirectory(rootDir: string): string {
const hash = createHash('sha256');
function visit(relativeDir: string): void {
const absoluteDir = join(rootDir, relativeDir);
for (const entry of readdirSync(absoluteDir, { withFileTypes: true })
.sort((a, b) => a.name.localeCompare(b.name))) {
Iif (entry.name === '.git' || entry.name === 'node_modules') {
continue;
}
const relativePath = relativeDir ? `${relativeDir}/${entry.name}` : entry.name;
const absolutePath = join(rootDir, relativePath);
if (entry.isDirectory()) {
visit(relativePath);
continue;
}
Iif (!entry.isFile()) {
continue;
}
hash.update(relativePath);
hash.update('\0');
hash.update(readFileSync(absolutePath));
hash.update('\0');
}
}
visit('');
return hash.digest('hex');
}
function candidateAuthoringSkillRoots(): string[] {
const fromEnv = process.env[AUTHORING_PROVIDER_ENV];
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
return [
fromEnv ? resolve(fromEnv) : undefined,
join(packageRoot, 'src', 'internal-skills', DEFAULT_AUTHORING_SKILL_NAME),
].filter((path): path is string => Boolean(path));
}
function authoringSkillName(rootDir: string): string {
const content = readFileSync(join(rootDir, 'SKILL.md'), 'utf-8');
const match = /^name:\s*["']?([^"'\n]+)["']?$/m.exec(content);
return match?.[1]?.trim() || basename(rootDir);
}
export function resolveAuthoringProvider(args: {
authoringSkillRoot?: string;
} = {}): SkillBuildAuthoringProvider {
const roots = args.authoringSkillRoot
? [resolve(args.authoringSkillRoot)]
: candidateAuthoringSkillRoots();
for (const rootDir of roots) {
const skillPath = join(rootDir, 'SKILL.md');
Iif (!existsSync(skillPath)) {
continue;
}
const stat = statSync(skillPath);
Iif (!stat.isFile()) {
continue;
}
return {
name: authoringSkillName(rootDir),
rootDir,
contentHash: hashDirectory(rootDir),
};
}
const searched = roots.map((root) => `- ${root}`).join('\n');
throw new Error(
`Unable to find generated-skill authoring provider "${DEFAULT_AUTHORING_SKILL_NAME}". ` +
`Set ${AUTHORING_PROVIDER_ENV} or ensure the internal provider is packaged in one of:\n${searched}`,
);
}
|