@tanstack/ai-skills
Version:
Portable Agent Skills (SKILL.md) as a first-class chat() middleware for TanStack AI.
68 lines (67 loc) • 2.85 kB
TypeScript
import { AnyChatMiddleware } from '@tanstack/ai';
import { HostPluginContext, PluginSetupContext } from '@tanstack/ai-harness';
import { SkillsOptions } from './middleware.js';
export interface SkillsPluginOptions extends SkillsOptions {
/**
* The skill folders. Each folder holds skill folders with a `SKILL.md`.
* When two folders have a skill with the same name, the first folder wins.
* A folder that does not exist yet is used once it exists.
*/
dirs: ReadonlyArray<string>;
/**
* Names that a UI handles itself, for example its own `/help`. A skill
* with one of these names gets the command `/skill:<name>`.
*/
reserved?: ReadonlyArray<string>;
}
/**
* A harness plugin that gives the model the skills of `dirs`, and makes each
* skill a command: `/<name> [task]` starts a turn that tells the model to use
* the skill. A skill whose name another command has is `/skill:<name>`.
* `/skills` lists every skill, its folder, and its command.
*
* The plugin watches each folder once for each host, and every session of
* the harness that uses this plugin object shares the watchers. Create it
* once, outside the `plugins` factory. It reads the folders again at the start
* of each turn. A skill that you add, change, or remove while a session runs
* changes the commands at once, with no restart. When a folder cannot be
* watched, the plugin warns once, to the host logger
* (`createHarnessHost({ logger })`), and its skills change at the next turn.
*
* The model's skill list has one line for each skill: its name and the first
* sentence of its description. When the list is longer than
* `maxCatalogTokens`, the skills of the earlier folders stay in it, and a
* skill that you called with its command always stays.
*
* @param options.dirs - The skill folders, the first one wins on a name.
* @param options.reserved - Command names a UI handles itself.
*
* @example
* ```ts
* import { homedir } from 'node:os'
* import { join } from 'node:path'
* import { skills } from '@tanstack/ai-skills/harness'
*
* const agentSkills = skills({
* dirs: ['./.agents/skills', join(homedir(), '.acme', 'skills')],
* })
*
* defineHarness({
* name: 'acme/agent',
* adapter,
* plugins: () => [agentSkills],
* })
* ```
*/
export declare function skills(options: SkillsPluginOptions): import('@tanstack/ai-harness').HarnessPlugin<NoInfer<{
readonly name: "tanstack/skills";
readonly lifetime: "host";
readonly setup: (host: HostPluginContext) => Promise<{
session: (ctx: PluginSetupContext<Record<string, unknown>>) => Promise<{
middleware: AnyChatMiddleware[];
commands: {
skills: import('@tanstack/ai-harness').CommandDefinition<undefined>;
};
}>;
}>;
}>, Record<string, unknown>>;