summaryrefslogtreecommitdiffhomepage
path: root/packages/skills/src/tools-filter.ts
blob: 3f25e95d4ae59b623e6543e463094d3eb7a3cda3 (plain)
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
import { join, resolve } from "node:path";
import type { JsonSchemaProperty, ToolContract } from "@dispatch/kernel";
import type { ToolAssembly } from "@dispatch/session-orchestrator";
import { type SkillsDeps, scanSkillsDir } from "./load-skill.js";
import { mergeCatalog, renderDescription } from "./pure.js";

/**
 * Create a tools filter that rewrites the load_skill tool's description
 * and name parameter enum with the current skill catalog.
 */
export function makeSkillsToolFilter(deps: SkillsDeps) {
  const { homeDir, workdir } = deps;

  return async (asm: ToolAssembly): Promise<ToolAssembly> => {
    const effectiveBase = asm.cwd ? resolve(asm.cwd) : resolve(workdir);
    const cwdSkillsDir = join(effectiveBase, ".skills");
    const homeSkillsDir = join(resolve(homeDir), ".skills");

    let homeEntries: readonly import("./pure.js").SkillEntry[];
    let cwdEntries: readonly import("./pure.js").SkillEntry[];
    try {
      [homeEntries, cwdEntries] = await Promise.all([
        scanSkillsDir(homeSkillsDir),
        scanSkillsDir(cwdSkillsDir),
      ]);
    } catch {
      return asm;
    }

    const catalog = mergeCatalog(homeEntries, cwdEntries);
    const names = catalog.map((e) => e.name);
    const description = renderDescription(catalog);

    return {
      ...asm,
      tools: asm.tools.map((t: ToolContract) => {
        if (t.name !== "load_skill") return t;

        const nameProp = t.parameters.properties?.name;
        if (!nameProp) {
          return { ...t, description };
        }

        const updatedNameProp: JsonSchemaProperty =
          names.length > 0 ? { ...nameProp, enum: names } : { ...nameProp };

        const updatedProperties: Record<string, JsonSchemaProperty> = {
          ...t.parameters.properties,
          name: updatedNameProp,
        };

        return {
          ...t,
          description,
          parameters: {
            ...t.parameters,
            properties: updatedProperties,
          },
        };
      }),
    };
  };
}