1
0
Files
Toak/Core/Skills/SkillRegistry.cs

155 lines
6.3 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 = "Executes the spoken command in your shell.",
Hotwords = new[] { "System terminal", "System run", "System execute" },
Action = "script",
ScriptPath = "~/.config/toak/skills/terminal_action.sh",
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 = new[] { "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 = new[] { "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.
Text: {transcript}"
},
new SkillDefinition
{
Name = "Summary",
Description = "Provides a direct, crisp summary of the dictation.",
Hotwords = new[] { "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 can ignore those words.
Remove all fluff.
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);
}
// Create the default terminal wrapper script if it doesn't exist
string scriptPath = Path.Combine(SkillsDirectory, "terminal_action.sh");
if (!File.Exists(scriptPath))
{
string scriptContent = "#!/bin/bash\n" +
"export TOAK_PROPOSED_CMD=\"$1\"\n" +
"x-terminal-emulator -e bash -c 'echo -e \"\\033[1;32mToak Terminal Skill\\033[0m\"; " +
"echo \"Proposed command:\"; echo; " +
"echo -e \" \\033[33m$TOAK_PROPOSED_CMD\\033[0m\"; echo; " +
"read -p \"Execute command? [Y/n] \" resp; " +
"if [[ $resp =~ ^[Yy]$ ]] || [[ -z $resp ]]; then " +
"echo; echo -e \"\\033[1;36m>> Executing...\\033[0m\"; eval \"$TOAK_PROPOSED_CMD\"; " +
"else echo; echo \"Aborted.\"; fi; " +
"echo; echo \"Process finished. Press Enter to exit.\"; read;'\n";
File.WriteAllText(scriptPath, scriptContent);
// Try to make it executable
try {
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo {
FileName = "chmod",
Arguments = $"+x \"{scriptPath}\"",
CreateNoWindow = true,
UseShellExecute = false
})?.WaitForExit();
} catch { }
}
}
}