feat(core): per-workspace Agent Skills listed in the system prompt, /skills in Telegram

Skills live in <cwd>/.claude/skills/<name>/SKILL.md (frontmatter name + description).
SkillCatalog re-reads them on every run and the prompt lists name, description and
path; the model reads the full SKILL.md with the read tool when a task matches.
This commit is contained in:
EugeneTes
2026-10-10 14:13:42 +00:00
parent 891a644590
commit c3095c69ca
10 changed files with 284 additions and 8 deletions

View File

@@ -8,6 +8,7 @@ using Volcanic.AgenticCore.A2A.Server.Tasks;
using Volcanic.AgenticCore.Abstractions.Providers;
using Volcanic.AgenticCore.Extensions.A2A;
using Volcanic.AgenticCore.Extensions.Scheduler;
using Volcanic.AgenticCore.Features.SystemPrompt;
using Volcanic.AgenticCore.Hosting;
using Volcanic.AgenticCore.Telegram.Rendering;
using Volcanic.AgenticCore.Telegram.Scheduling;
@@ -29,6 +30,7 @@ public sealed class ChatCommands(TelegramSender sender, ChatTimeZoneService? zon
new() { Command = "compact", Description = "Summarise the conversation to free context" },
new() { Command = "thinking", Description = "Show or set the thinking level" },
new() { Command = "timezone", Description = "Show or set this chat's time zone" },
new() { Command = "skills", Description = "Skills in this chat's workspace" },
.. SchedulerSetup.Menu,
TriggersSetup.MenuCommand,
new() { Command = "a2a", Description = "A2A callers, remote agents and tasks of this chat" },
@@ -122,6 +124,10 @@ public sealed class ChatCommands(TelegramSender sender, ChatTimeZoneService? zon
await ReplyAsync(chat, triggers is null ? TriggersSetup.DisabledReply : "**Group triggers**\n" + triggers.Describe(chat.ChatId, chat.Session.TimeZone), cancellationToken).ConfigureAwait(false);
return true;
case "skills":
await ReplyAsync(chat, Skills(chat), cancellationToken).ConfigureAwait(false);
return true;
case "jobs":
await ReplyAsync(chat, Jobs(chat), cancellationToken).ConfigureAwait(false);
return true;
@@ -157,6 +163,7 @@ public sealed class ChatCommands(TelegramSender sender, ChatTimeZoneService? zon
/compact – summarise the conversation to free context
/thinking [level] – show or set the thinking level
/timezone [IANA name|reset] – this chat's time zone
/skills – skills in my workspace
""" + SchedulerSetup.HelpLines + "\n/a2a – A2A callers, remote agents and recent tasks of this chat" + TriggersSetup.HelpLines + GoogleCalendarSetup.HelpLines(chat.Session.Commands)
+ (chat.Session.Commands.Any(c => c.Name == "mcp_add") ? RemoteMcpSetup.HelpLines : "");
@@ -216,6 +223,14 @@ public sealed class ChatCommands(TelegramSender sender, ChatTimeZoneService? zon
: error;
}
private static string Skills(ChatContext chat)
{
var skills = SkillCatalog.Load(chat.Session.Cwd);
var where = $"`{Path.GetRelativePath(chat.Session.Cwd, SkillCatalog.Directory(chat.Session.Cwd))}/<name>/SKILL.md`";
if (skills.Count == 0) return $"No skills yet. Ask me to write one, or put it in {where}.";
return $"**Skills** ({where})\n" + string.Join('\n', skills.Select(s => $"• **{s.Name}** – {s.Description}"));
}
private string Jobs(ChatContext chat)
{
if (scheduler is not { IsStarted: true }) return SchedulerSetup.DisabledReply;