Parent directory

loader.ts

2104 bytes
 1import { dirname } from "node:path";
 2import type { FileSystem } from "../ports/fs.ts";
 3import type { FrontmatterCodec } from "../frontmatter/parser.ts";
 4import type { SkillLocator } from "../discovery/skill-locator.ts";
 5import type { SkillRecord } from "../types.ts";
 6import { deriveSkillMetadata } from "../frontmatter/validation.ts";
 7import { classifyInvocationMode } from "./classifier.ts";
 8
 9export interface SkillInventory {
10  load(cwd: string): Promise<SkillRecord[]>;
11}
12
13export class DefaultSkillInventory implements SkillInventory {
14  constructor(
15    private readonly locator: SkillLocator,
16    private readonly fs: FileSystem,
17    private readonly codec: FrontmatterCodec,
18  ) {}
19
20  async load(cwd: string): Promise<SkillRecord[]> {
21    const located = await this.locator.findSkillFiles(cwd);
22    const records: SkillRecord[] = [];
23
24    for (const file of located) {
25      try {
26        const raw = await this.fs.readFile(file.filePath);
27        const doc = this.codec.parse(raw);
28        const metadata = deriveSkillMetadata(file.filePath, doc);
29        records.push({
30          id: file.filePath,
31          name: metadata.name,
32          description: metadata.description,
33          filePath: file.filePath,
34          baseDir: dirname(file.filePath),
35          source: file.source,
36          editable: file.editable && doc.hasFrontmatter && !metadata.diagnostics.some((d) => d.severity === "error"),
37          mode: classifyInvocationMode(doc),
38          diagnostics: metadata.diagnostics,
39        });
40      } catch (error) {
41        records.push({
42          id: file.filePath,
43          name: file.filePath.split("/").at(-2) ?? file.filePath,
44          description: "",
45          filePath: file.filePath,
46          baseDir: dirname(file.filePath),
47          source: file.source,
48          editable: false,
49          mode: "agent-invocable",
50          diagnostics: [{ severity: "error", message: error instanceof Error ? error.message : String(error) }],
51        });
52      }
53    }
54
55    return records.sort((a, b) => a.name.localeCompare(b.name) || a.filePath.localeCompare(b.filePath));
56  }
57}