mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-06-15 20:05:16 +02:00
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.
This commit is contained in:
parent
b01af12148
commit
9a308cb7a9
38 changed files with 1217 additions and 1204 deletions
|
|
@ -16,4 +16,5 @@ export * as promptBlock from './prompt-block.js';
|
|||
export * as frontmatter from './frontmatter.js';
|
||||
export * as bases from './bases.js';
|
||||
export * as browserControl from './browser-control.js';
|
||||
export * as skill from './skill.js';
|
||||
export { PrefixLogger };
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { UserMessageContent } from './message.js';
|
|||
import { RowboatApiConfig } from './rowboat-account.js';
|
||||
import { ZListToolkitsResponse } from './composio.js';
|
||||
import { BrowserStateSchema } from './browser-control.js';
|
||||
import { ResolvedSkill, SkillCatalogEntry } from './skill.js';
|
||||
|
||||
// ============================================================================
|
||||
// Runtime Validation Schemas (Single Source of Truth)
|
||||
|
|
@ -871,6 +872,17 @@ const ipcSchemas = {
|
|||
req: BrowserStateSchema,
|
||||
res: z.null(),
|
||||
},
|
||||
// Skills channels (read-only)
|
||||
'skills:list': {
|
||||
req: z.null(),
|
||||
res: z.object({
|
||||
skills: z.array(SkillCatalogEntry),
|
||||
}),
|
||||
},
|
||||
'skills:get': {
|
||||
req: z.object({ id: z.string() }),
|
||||
res: ResolvedSkill.nullable(),
|
||||
},
|
||||
// Billing channels
|
||||
'billing:getInfo': {
|
||||
req: z.null(),
|
||||
|
|
|
|||
36
apps/x/packages/shared/src/skill.ts
Normal file
36
apps/x/packages/shared/src/skill.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
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>;
|
||||
Loading…
Add table
Add a link
Reference in a new issue