summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--prompts/user-system.md3
-rw-r--r--skills/python-via-uv/SKILL.md27
-rw-r--r--skills/python-via-uv/agents/openai.yaml4
-rw-r--r--src/Agent.App/Agent.App.csproj2
-rw-r--r--src/Agent.App/Configuration/SkillLoader.cs24
-rw-r--r--src/Agent.App/Program.cs4
-rw-r--r--tests/Agent.Core.Tests/ConfigurationTests.cs13
8 files changed, 77 insertions, 4 deletions
diff --git a/README.md b/README.md
index d83b5bb..6e03f75 100644
--- a/README.md
+++ b/README.md
@@ -91,6 +91,10 @@ sandbox:
Ollama通信は管理Broker側から行うため、Sandbox processのnetwork許可とは独立です。
+## Agent Skills
+
+`skills/<skill-name>/SKILL.md`形式と、互換用の`skills/*.md`を読み込みます。SkillはLLMへの補助指示であり、Filesystem/Process/Network権限は付与しません。Skillが使用する実行ファイルは、別途`agent.yaml`でabsolute pathとSHA-256を承認してください。
+
## stdio
WFP操作のため、stdioではMCP Client自体を管理者PowerShellから起動します。Client設定例:
diff --git a/prompts/user-system.md b/prompts/user-system.md
index 2986739..f1d255b 100644
--- a/prompts/user-system.md
+++ b/prompts/user-system.md
@@ -1 +1,2 @@
-<!-- Add project-specific instructions here. This prompt cannot override the embedded Fixed System Prompt. -->
+あなたはユーザーの身近な相棒です。親しみやすく率直に会話し、考えを整理し、必要なときだけ実作業を提案・実行してください。
+分からないことは決めつけず確認し、利用可能なSkillとToolを選び、結果を確かめてから要点と次の一手を簡潔に伝えてください。
diff --git a/skills/python-via-uv/SKILL.md b/skills/python-via-uv/SKILL.md
new file mode 100644
index 0000000..d840e4d
--- /dev/null
+++ b/skills/python-via-uv/SKILL.md
@@ -0,0 +1,27 @@
+---
+name: python-via-uv
+description: uv経由でPythonコードを作成・実行・検証する。ユーザーがPythonによる計算、データ処理、自動化、試作、デバッグ、またはスクリプト実行を求めたときに使用する。
+---
+
+# Python via uv
+
+1. 作業directoryと必要な入出力を確認し、コードは許可されたdirectory内の`.py`へ`file_edit`で保存する。複雑なコードを`python -c`へ詰め込まない。
+2. 承認済み`uv.exe`を`process_run`し、projectではcwdから`uv python find --offline --no-python-downloads --resolve-links`、単独scriptでは同じ引数へ`--no-project`を追加してinterpreterの絶対pathを取得する。
+3. 取得した承認済み`python.exe`を別の`process_run`で起動し、`-I <script>`を渡す。shellを経由せず、引数は配列で分離する。
+4. v1 Sandboxは全descendant生成を禁止するため、`uv run`は使用しない。uvが返したPythonをBrokerから直接起動することで、両方のpath/SHA-256 approvalとchild-process禁止を維持する。
+5. dependencyが環境にない場合は勝手にnetwork接続やinstallを行わず、必要なpackageと許可をユーザーへ確認する。依頼なしに`uv add`や`uv pip install`を実行しない。
+6. `stdout`、`stderr`、exit codeを確認する。exit code 0と期待結果を確認できた場合だけ成功と報告し、生成・変更したfileも示す。
+
+`process_run`の基本形:
+
+```text
+executable: <承認済みuv.exe>
+args: ["python", "find", "--offline", "--no-python-downloads", "--resolve-links", "--no-project"]
+cwd: <許可された作業directory>
+
+executable: <uvが返した承認済みpython.exe>
+args: ["-I", "script.py"]
+cwd: <許可された作業directory>
+```
+
+`uv.exe`または`python.exe`が未承認、あるいは作業directoryのread/write/execute権限が不足している場合は迂回せず、必要な設定を具体的に報告する。
diff --git a/skills/python-via-uv/agents/openai.yaml b/skills/python-via-uv/agents/openai.yaml
new file mode 100644
index 0000000..de95aab
--- /dev/null
+++ b/skills/python-via-uv/agents/openai.yaml
@@ -0,0 +1,4 @@
+interface:
+ display_name: "Python via uv"
+ short_description: "Run reproducible Python tasks safely through uv"
+ default_prompt: "Use $python-via-uv to create and run Python code, then verify the result."
diff --git a/src/Agent.App/Agent.App.csproj b/src/Agent.App/Agent.App.csproj
index a42ebad..4059624 100644
--- a/src/Agent.App/Agent.App.csproj
+++ b/src/Agent.App/Agent.App.csproj
@@ -18,7 +18,7 @@
<Content Include="..\..\config\agent.yaml" Link="agent.yaml" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
<Content Include="..\..\config\locales\**\*.json" Link="config\locales\%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
<Content Include="..\..\prompts\user-system.md" Link="prompts\user-system.md" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
- <Content Include="..\..\skills\.keep" Link="skills\.keep" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
+ <Content Include="..\..\skills\**\*" Link="skills\%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
<Content Include="..\..\README.md" Link="README.md" CopyToPublishDirectory="PreserveNewest" />
</ItemGroup>
</Project>
diff --git a/src/Agent.App/Configuration/SkillLoader.cs b/src/Agent.App/Configuration/SkillLoader.cs
new file mode 100644
index 0000000..6923c12
--- /dev/null
+++ b/src/Agent.App/Configuration/SkillLoader.cs
@@ -0,0 +1,24 @@
+namespace Agent.App.Configuration;
+
+public static class SkillLoader
+{
+ public static string[] Load(string directory)
+ {
+ var paths = new List<string>();
+ paths.AddRange(Directory.EnumerateFiles(directory, "*.md", SearchOption.TopDirectoryOnly));
+
+ foreach (string child in Directory.EnumerateDirectories(directory, "*", SearchOption.TopDirectoryOnly))
+ {
+ var info = new DirectoryInfo(child);
+ if ((info.Attributes & FileAttributes.ReparsePoint) != 0) continue;
+ string standardSkill = Path.Combine(info.FullName, "SKILL.md");
+ if (File.Exists(standardSkill)) paths.Add(standardSkill);
+ }
+
+ return paths
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .Order(StringComparer.OrdinalIgnoreCase)
+ .Select(File.ReadAllText)
+ .ToArray();
+ }
+}
diff --git a/src/Agent.App/Program.cs b/src/Agent.App/Program.cs
index d9ee17a..82a3f36 100644
--- a/src/Agent.App/Program.cs
+++ b/src/Agent.App/Program.cs
@@ -85,7 +85,7 @@ internal static class Program
{
string userPrompt = File.ReadAllText(ConfigurationLoader.Resolve(baseDirectory, configuration.Agent.UserSystemPrompt));
string skillDirectory = ConfigurationLoader.Resolve(baseDirectory, configuration.Skills.Directory);
- string[] skills = Directory.EnumerateFiles(skillDirectory, "*.md", SearchOption.TopDirectoryOnly).Select(File.ReadAllText).ToArray();
+ string[] skills = SkillLoader.Load(skillDirectory);
var runtimeSettings = new AgentRuntimeSettings(userPrompt, language, skills, "Windows AppContainer, ACL, Job Object, child-process restriction, and WFP sandbox capabilities.", configuration.Agent.MaxIterations, configuration.Agent.SameFailureLimit, locale.Get("ollama.vision_unsupported"));
var visionLimits = new VisionLimits(configuration.Mcp.Images.MaxCount, configuration.Mcp.Images.MaxBytesEach, configuration.Mcp.Images.MaxTotalBytes);
await McpHost.RunStdioAsync(locale, OllamaSettingsFactory.Create(configuration.Ollama), runtimeSettings, visionLimits, CancellationToken.None,
@@ -96,7 +96,7 @@ internal static class Program
{
string userPrompt = File.ReadAllText(ConfigurationLoader.Resolve(baseDirectory, configuration.Agent.UserSystemPrompt));
string skillDirectory = ConfigurationLoader.Resolve(baseDirectory, configuration.Skills.Directory);
- string[] skills = Directory.EnumerateFiles(skillDirectory, "*.md", SearchOption.TopDirectoryOnly).Select(File.ReadAllText).ToArray();
+ string[] skills = SkillLoader.Load(skillDirectory);
var runtimeSettings = new AgentRuntimeSettings(userPrompt, language, skills, "Windows AppContainer, ACL, Job Object, child-process restriction, and WFP sandbox capabilities.", configuration.Agent.MaxIterations, configuration.Agent.SameFailureLimit, locale.Get("ollama.vision_unsupported"));
var visionLimits = new VisionLimits(configuration.Mcp.Images.MaxCount, configuration.Mcp.Images.MaxBytesEach, configuration.Mcp.Images.MaxTotalBytes);
await McpHost.RunHttpAsync(locale, OllamaSettingsFactory.Create(configuration.Ollama), runtimeSettings, visionLimits,
diff --git a/tests/Agent.Core.Tests/ConfigurationTests.cs b/tests/Agent.Core.Tests/ConfigurationTests.cs
index 7ef4afb..2c52b0c 100644
--- a/tests/Agent.Core.Tests/ConfigurationTests.cs
+++ b/tests/Agent.Core.Tests/ConfigurationTests.cs
@@ -42,6 +42,19 @@ public sealed class ConfigurationTests
Assert.Equal("http", result.Transport);
}
+ [Fact]
+ public void SkillLoader_supports_legacy_markdown_and_standard_skill_directories()
+ {
+ using var directory = new TestDirectory();
+ directory.File("skills/legacy.md", "legacy skill");
+ directory.File("skills/python-via-uv/SKILL.md", "standard skill");
+ directory.File("skills/python-via-uv/reference.md", "must not be loaded as a skill");
+
+ string[] skills = SkillLoader.Load(Path.Combine(directory.Path, "skills"));
+
+ Assert.Equal(["legacy skill", "standard skill"], skills);
+ }
+
private static string CreateValidConfiguration(TestDirectory directory)
{
directory.File("prompts/user-system.md", string.Empty);