129 lines
4.8 KiB
C#
129 lines
4.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using Toak.Serialization;
|
|
|
|
namespace Toak.Core.Skills;
|
|
|
|
public static class SkillRegistry
|
|
{
|
|
public static List<ISkill> AllSkills = new List<ISkill>();
|
|
|
|
public static string SkillsDirectory => Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
|
".config", "toak", "skills");
|
|
|
|
public static void Initialize()
|
|
{
|
|
if (!Directory.Exists(SkillsDirectory))
|
|
{
|
|
Directory.CreateDirectory(SkillsDirectory);
|
|
CreateDefaultSkills();
|
|
}
|
|
|
|
AllSkills.Clear();
|
|
foreach (var file in Directory.GetFiles(SkillsDirectory, "*.json"))
|
|
{
|
|
try
|
|
{
|
|
string json = File.ReadAllText(file);
|
|
var def = JsonSerializer.Deserialize(json, AppJsonSerializerContext.Default.SkillDefinition);
|
|
if (def != null)
|
|
{
|
|
AllSkills.Add(new DynamicSkill(def));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogDebug($"Failed to load skill from {file}: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
public static ISkill? DetectSkill(string transcript, IEnumerable<string> activeSkillNames)
|
|
{
|
|
if (AllSkills.Count == 0) Initialize();
|
|
|
|
var activeSkills = AllSkills.Where(s => activeSkillNames.Contains(s.Name, StringComparer.OrdinalIgnoreCase)).ToList();
|
|
string normalizedTranscript = transcript.Trim();
|
|
|
|
foreach (var skill in activeSkills)
|
|
{
|
|
foreach (var hotword in skill.Hotwords)
|
|
{
|
|
if (normalizedTranscript.StartsWith(hotword, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return skill;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static void CreateDefaultSkills()
|
|
{
|
|
var defaults = new List<SkillDefinition>
|
|
{
|
|
new SkillDefinition
|
|
{
|
|
Name = "Terminal",
|
|
Description = "Translates the spoken command into a bash command and types it.",
|
|
Hotwords = ["System terminal", "System run", "System execute"],
|
|
Action = "type",
|
|
SystemPrompt = @"You are a Linux terminal expert.
|
|
Translate the user's request into a single, valid bash command.
|
|
Output ONLY the raw command, no formatting, no markdown."
|
|
},
|
|
new SkillDefinition
|
|
{
|
|
Name = "Translate",
|
|
Description = "Translates the spoken text into another language on the fly.",
|
|
Hotwords = ["System translate to", "System translate into"],
|
|
Action = "type",
|
|
SystemPrompt = @"You are an expert translator. The user wants to translate the following text.
|
|
The first few words identify the target language (e.g. 'Translate to Spanish:', 'Translate into Hungarian:').
|
|
Translate the REST of the transcript into that target language.
|
|
Output ONLY the final translated text. Do not include markdown, explanations, or quotes."
|
|
},
|
|
new SkillDefinition
|
|
{
|
|
Name = "Professional",
|
|
Description = "Rewrites text into a formal, articulate tone.",
|
|
Hotwords = ["System professional", "System formalize", "System formal"],
|
|
Action = "type",
|
|
SystemPrompt = @"Rewrite the following text to be articulate and formal.
|
|
The text will start with 'System professional', 'System formalize', or 'System formal',
|
|
or something along the lines of that. You can ignore those words.
|
|
Do not add any conversational filler.
|
|
Make sure to preserve the meaning of the original text.
|
|
Output ONLY the final professional text.
|
|
Text: {transcript}"
|
|
},
|
|
new SkillDefinition
|
|
{
|
|
Name = "Summary",
|
|
Description = "Provides a direct, crisp summary of the dictation.",
|
|
Hotwords = ["System summary", "System concise", "System summarize"],
|
|
Action = "type",
|
|
SystemPrompt = @"Summarize the following text to be as concise
|
|
and direct as possible.
|
|
The text will start with 'System summary', 'System concise', or 'System summarize',
|
|
and you shoul ignore that part of the text.
|
|
Output ONLY the final summary text.
|
|
Text: {transcript}"
|
|
}
|
|
};
|
|
|
|
foreach (var def in defaults)
|
|
{
|
|
string filename = Path.Combine(SkillsDirectory, $"{def.Name.ToLowerInvariant()}.json");
|
|
string json = JsonSerializer.Serialize(def, AppJsonSerializerContext.Default.SkillDefinition);
|
|
File.WriteAllText(filename, json);
|
|
}
|
|
|
|
// No longer generating terminal_action.sh as Terminal skill now uses "type" action
|
|
}
|
|
}
|