Move skills into apps/skills/, drop override + sync layers

Skills now ship with the app under /apps/skills/ (sibling of /apps/x).
Forge bundles the directory into Resources/skills/; main resolves it via
process.resourcesPath in production and a workspace-relative path in dev,
then registers it in the DI container. The runtime reads SKILL.md files
directly from the bundle — no copy to ~/.rowboat/skills/, no GitHub
tarball sync.

Drop the override layer (FSSkillsRepo, SkillOverride, edit/diff UI,
skill-update notification) since skills are now read-only and only ship
with app updates. Resolver simplifies to a single source.

Add a placeholder substitution layer so skills that need live data
(currently `tracks`, with {{TRACK_BLOCK_SCHEMA}}) keep dynamic content
without depending on TS-module evaluation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
tusharmagar 2026-04-30 08:35:09 +05:30
parent 66c0bc5fa7
commit e23f4ad6d2
32 changed files with 5999 additions and 1446 deletions

View file

@ -8,7 +8,7 @@ import { AgentScheduleState } from './agent-schedule-state.js';
import { ServiceEvent } from './service-events.js';
import { TrackEvent } from './track-block.js';
import { UserMessageContent } from './message.js';
import { ResolvedSkill, SkillOverride } from './skill.js';
import { ResolvedSkill } from './skill.js';
import { RowboatApiConfig } from './rowboat-account.js';
import { ZListToolkitsResponse } from './composio.js';
import { BrowserStateSchema } from './browser-control.js';
@ -735,30 +735,6 @@ const ipcSchemas = {
}),
res: ResolvedSkill.nullable(),
},
'skills:getOfficial': {
req: z.object({
id: z.string(),
}),
res: ResolvedSkill.nullable(),
},
'skills:saveOverride': {
req: z.object({
skillId: z.string(),
meta: SkillOverride,
content: z.string(),
}),
res: z.object({
success: z.literal(true),
}),
},
'skills:deleteOverride': {
req: z.object({
skillId: z.string(),
}),
res: z.object({
success: z.literal(true),
}),
},
'billing:getInfo': {
req: z.null(),
res: z.object({

View file

@ -1,8 +1,7 @@
import { z } from 'zod';
// SKILL.md frontmatter schema (Agent Skills spec compliant)
// Top-level: name, description, license, compatibility, allowed-tools, metadata
// Custom Rowboat fields go under metadata
// https://agentskills.io/specification
export const SkillFrontmatter = z.object({
name: z.string().max(64),
description: z.string().max(1024),
@ -19,43 +18,12 @@ export const SkillFrontmatter = z.object({
export type SkillFrontmatter = z.infer<typeof SkillFrontmatter>;
// Official skill metadata (bundled with app)
export const OfficialSkillMeta = z.object({
id: z.string(),
title: z.string(),
summary: z.string(),
version: z.string(),
source: z.literal("official"),
});
// User override metadata (stored on disk as YAML frontmatter)
export const SkillOverride = z.object({
base_skill_id: z.string(),
base_version: z.string(),
title: z.string().optional(),
summary: z.string().optional(),
});
// Parsed override entry (metadata + content)
export const SkillOverrideEntry = z.object({
skillId: z.string(),
meta: SkillOverride,
content: z.string(),
});
// Resolved skill seen by the agent (source-agnostic)
// Skill seen by the agent and the renderer (read-only).
export const ResolvedSkill = z.object({
id: z.string(),
title: z.string(),
summary: z.string(),
version: z.string(),
source: z.enum(["official", "override", "installed"]),
content: z.string(),
hasUpdate: z.boolean().optional(),
baseVersion: z.string().optional(),
});
export type OfficialSkillMeta = z.infer<typeof OfficialSkillMeta>;
export type SkillOverride = z.infer<typeof SkillOverride>;
export type SkillOverrideEntry = z.infer<typeof SkillOverrideEntry>;
export type ResolvedSkill = z.infer<typeof ResolvedSkill>;