Makelore 2.0 initial clean snapshot
This commit is contained in:
80
electron/opencode/skill-registry.ts
Normal file
80
electron/opencode/skill-registry.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { readdir, readFile } from 'node:fs/promises';
|
||||
import { basename, dirname, join } from 'node:path';
|
||||
import type { OpencodeSkillInfo } from './client';
|
||||
|
||||
const SKILL_ROOT_NAMES = ['skill', 'skills'] as const;
|
||||
|
||||
async function collectSkillFiles(root: string): Promise<string[]> {
|
||||
let entries;
|
||||
try {
|
||||
entries = await readdir(root, { withFileTypes: true });
|
||||
} catch (error) {
|
||||
if (typeof error === 'object' && error && 'code' in error && error.code === 'ENOENT') {
|
||||
return [];
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const files: string[] = [];
|
||||
for (const entry of entries) {
|
||||
const entryPath = join(root, entry.name);
|
||||
if (entry.isFile() && entry.name === 'SKILL.md') {
|
||||
files.push(entryPath);
|
||||
continue;
|
||||
}
|
||||
if (entry.isDirectory()) {
|
||||
files.push(...await collectSkillFiles(entryPath));
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function unquoteYamlScalar(value: string): string {
|
||||
const trimmed = value.trim();
|
||||
if (
|
||||
(trimmed.startsWith('"') && trimmed.endsWith('"'))
|
||||
|| (trimmed.startsWith("'") && trimmed.endsWith("'"))
|
||||
) {
|
||||
return trimmed.slice(1, -1).trim();
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function readFrontmatterScalar(content: string, key: string): string | undefined {
|
||||
const match = content.match(/^---\s*\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
||||
if (!match) return undefined;
|
||||
const keyPattern = new RegExp(`^${key}:\\s*(.*)$`);
|
||||
for (const line of match[1].split(/\r?\n/)) {
|
||||
const scalar = line.match(keyPattern);
|
||||
if (!scalar) continue;
|
||||
const value = unquoteYamlScalar(scalar[1] ?? '');
|
||||
return value || undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function listInstalledOpencodeSkills(managedConfigDir?: string | null): Promise<OpencodeSkillInfo[]> {
|
||||
const configDir = managedConfigDir?.trim();
|
||||
if (!configDir) return [];
|
||||
|
||||
const skillFiles = (
|
||||
await Promise.all(SKILL_ROOT_NAMES.map((rootName) => collectSkillFiles(join(configDir, rootName))))
|
||||
).flat().sort((left, right) => left.localeCompare(right));
|
||||
|
||||
const seen = new Set<string>();
|
||||
const skills: OpencodeSkillInfo[] = [];
|
||||
for (const location of skillFiles) {
|
||||
const content = await readFile(location, 'utf8');
|
||||
const name = readFrontmatterScalar(content, 'name') ?? basename(dirname(location));
|
||||
if (!name || seen.has(name)) continue;
|
||||
seen.add(name);
|
||||
skills.push({
|
||||
name,
|
||||
description: readFrontmatterScalar(content, 'description'),
|
||||
location,
|
||||
content,
|
||||
});
|
||||
}
|
||||
|
||||
return skills;
|
||||
}
|
||||
Reference in New Issue
Block a user