rowboat/apps/x/packages/shared/src/skill.ts

37 lines
1.2 KiB
TypeScript
Raw Permalink Normal View History

feat(skills): single-source skill system with markdown SKILL.md + include directive Skills move out of packages/core/src/application/assistant/skills/*/skill.ts (TS string constants) into apps/skills/<id>/SKILL.md (Agent Skills spec format — YAML frontmatter + markdown body). One directory, one loader, one place to look at every skill the agent can load. Key change vs the old dev system: a `{{include:<skill-id>}}` directive lets one skill transclude another. This removes the parallel TS constant for the knowledge-note style guide — it now lives at apps/skills/knowledge-note-style/ (hidden from catalog) and is pulled into doc-collab + the live-note and background-task agents via the resolver instead of via a TS import. Infrastructure: - packages/core/src/skills/ — types, skill-md-parser, FS-backed official repo, SkillResolver with recursive {{include:<id>}} expansion + cycle detection - packages/shared/src/skill.ts — SkillFrontmatter, SkillCatalogEntry, ResolvedSkill schemas - DI: officialSkillsRepo + skillResolver registered; registerSkillsDir helper wires the path before any consumer resolves - IPC: skills:list / skills:get (read-only) for the Settings UI - Main: resolveSkillsDir picks Resources/skills (packaged) or repo apps/skills (dev). forge.config.cjs ships apps/skills/ as extraResource. Consumer refactor: - buildCopilotInstructions: catalog markdown built from resolver.getCatalog() - builtin-tools: loadSkill uses resolver, new listSkills tool - background-tasks/agent + live-note/agent: now async builders that load the knowledge-note-style skill content via resolver - runtime.loadAgent: awaits the now-async builders - Deleted: assistant/skills/ directory, knowledge-note-style.ts UI: - New SkillsSettings component (read-only list + detail view) wired into Settings dialog as the "Skills" tab.
2026-05-13 12:31:06 +05:30
import { z } from 'zod';
// SKILL.md frontmatter schema. `name` is the skill id (folder name) and
// `description` is the one-line catalog summary. `hidden: true` keeps a
// skill out of the public catalog while still allowing other skills to
// `{{include:<id>}}` it as content (e.g. shared style guides).
export const SkillFrontmatter = z.object({
name: z.string().max(64),
description: z.string().max(1024),
hidden: z.boolean().optional(),
license: z.string().optional(),
metadata: z.object({
title: z.string().optional(),
}).passthrough().optional(),
});
export type SkillFrontmatter = z.infer<typeof SkillFrontmatter>;
// Skill catalog entry seen by the agent and the renderer (no content body).
export const SkillCatalogEntry = z.object({
id: z.string(),
title: z.string(),
summary: z.string(),
});
export type SkillCatalogEntry = z.infer<typeof SkillCatalogEntry>;
// Fully-resolved skill: catalog metadata + body with all {{include:<id>}}
// directives expanded.
export const ResolvedSkill = z.object({
id: z.string(),
title: z.string(),
summary: z.string(),
content: z.string(),
});
export type ResolvedSkill = z.infer<typeof ResolvedSkill>;