store skill path, simplify metadata logic
skill loading in extras Revert "store skill path, simplify metadata logic" This reverts commit 691b3283ca40443e6f8e9d92c38dd1df7581d4ba. skill loading in extras
3clyp50 committed
Feb 5, 2026 at 16:03 UTC
ee6707df59ea718e2e8eaa465bdd551105a424ca
3 files changed
+103
-50
prompts/agent.system.skill.loaded.md
new
+4
@@ -0,0 +1,4 @@
1
+# Loaded skill
2
+- Explicitly loaded via skills_tool, persists each turn
3
+
4
+{{skill}}
python/extensions/message_loop_prompts_after/_65_include_loaded_skill.py
new
+91
@@ -0,0 +1,91 @@
1
+from pathlib import Path
2
+from python.helpers.extension import Extension
3
+from python.helpers import skills, files, file_tree, runtime
4
+from agent import LoopData
5
+
6
+
7
+DATA_NAME_LOADED_SKILL = "loaded_skill"
8
+
9
+
10
+class IncludeLoadedSkill(Extension):
11
+ async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
12
+ skill_name = self.agent.data.get(DATA_NAME_LOADED_SKILL)
13
+ if not skill_name:
14
+ return
15
+
16
+ # Load skill fresh each turn
17
+ skill = skills.find_skill(skill_name, include_content=True, agent=self.agent)
18
+ if not skill:
19
+ return
20
+
21
+ # Build skill block
22
+ skill_block = self._build_skill_block(skill)
23
+
24
+ # Add to persistent extras
25
+ loop_data.extras_persistent["loaded_skill"] = self.agent.read_prompt(
26
+ "agent.system.skill.loaded.md",
27
+ skill=skill_block,
28
+ )
29
+
30
+ def _build_skill_block(self, skill: skills.Skill) -> str:
31
+ """Build complete skill content with metadata, description, body, and files."""
32
+ runtime_path = (
33
+ files.normalize_a0_path(str(skill.path))
34
+ if self.agent.config.code_exec_ssh_enabled
35
+ else str(skill.path)
36
+ )
37
+
38
+ lines = [f"Skill: {skill.name}", f"Path: {runtime_path}"]
39
+
40
+ # Metadata
41
+ metadata = [
42
+ ("Version", skill.version),
43
+ ("Author", skill.author),
44
+ ("License", skill.license),
45
+ ("Compatibility", skill.compatibility),
46
+ ("Tags", ", ".join(skill.tags) if skill.tags else None),
47
+ ("Allowed tools", ", ".join(skill.allowed_tools) if skill.allowed_tools else None),
48
+ ("Triggers", ", ".join(skill.triggers) if skill.triggers else None),
49
+ ]
50
+ lines.extend(f"{label}: {value}" for label, value in metadata if value)
51
+
52
+ # Description and content
53
+ if skill.description:
54
+ lines.extend(["", "Description:", skill.description.strip()])
55
+
56
+ lines.extend(["", "Content (SKILL.md body):", skill.content.strip() or "(empty)"])
57
+
58
+ # File tree
59
+ files_tree = self._get_skill_files(skill.path)
60
+ lines.append("")
61
+ if files_tree:
62
+ lines.append("Files (use skills_tool method=read_file to open):")
63
+ lines.append(files_tree)
64
+ else:
65
+ lines.append("No additional files found.")
66
+
67
+ return "\n".join(lines)
68
+
69
+ def _get_skill_files(self, skill_dir: Path) -> str:
70
+ """Get file tree for skill directory."""
71
+ if not skill_dir.exists():
72
+ return ""
73
+
74
+ tree = str(
75
+ file_tree.file_tree(
76
+ str(skill_dir),
77
+ max_depth=10,
78
+ folders_first=True,
79
+ max_files=100,
80
+ max_folders=100,
81
+ output_mode="string",
82
+ max_lines=300,
83
+ ignore=files.read_file("conf/skill.default.gitignore"),
84
+ )
85
+ )
86
+
87
+ if tree and runtime.is_development():
88
+ runtime_path = files.normalize_a0_path(str(skill_dir))
89
+ tree = tree.replace(str(skill_dir), runtime_path)
90
+
91
+ return str(tree)
python/tools/skills_tool.py
+8
-50
@@ -8,6 +8,9 @@ from python.helpers import projects, files, file_tree
8
from python.helpers import skills as skills_helper, runtime
9
10
11
+DATA_NAME_LOADED_SKILL = "loaded_skill"
12
+
13
+
14
class SkillsTool(Tool):
15
"""
16
Manage and use SKILL.md-based Skills (Anthropic open standard).
@@ -106,70 +109,25 @@ class SkillsTool(Tool):
109
return "\n".join(lines)
110
111
def _load(self, skill_name: str) -> str:
109
-
112
skill_name = skill_name.strip()
113
if skill_name.startswith("**") and skill_name.endswith("**"):
112
- skill_name = skill_name[
113
- 2:-2
114
- ] # remove markdown bold markers if used by agent
114
+ skill_name = skill_name[2:-2]
115
116
if not skill_name:
117
return "Error: 'skill_name' is required for method=load."
118
119
skill = skills_helper.find_skill(
120
skill_name,
121
- include_content=True,
121
+ include_content=False,
122
agent=self.agent,
123
)
124
if not skill:
125
return f"Error: skill not found: {skill_name!r}. Try skills_tool method=list or method=search."
126
127
- # Enumerate files under the skill directory for progressive disclosure
128
- referenced_files = self._list_skill_files(skill.path, max_files=80)
129
- rel_skill_dir = Path(files.deabsolute_path(str(skill.path)))
130
- if self.agent.config.code_exec_ssh_enabled:
131
- runtime_path = files.normalize_a0_path(str(skill.path))
132
- else:
133
- runtime_path = str(skill.path)
127
+ # Store skill name for fresh loading each turn
128
+ self.agent.data[DATA_NAME_LOADED_SKILL] = skill.name
129
135
- lines: List[str] = []
136
- lines.append(f"Skill: {skill.name}")
137
- # lines.append(f"Path: {rel_skill_dir}")
138
- lines.append(f"Path: {runtime_path}")
139
- if skill.version:
140
- lines.append(f"Version: {skill.version}")
141
- if skill.author:
142
- lines.append(f"Author: {skill.author}")
143
- if skill.license:
144
- lines.append(f"License: {skill.license}")
145
- if skill.compatibility:
146
- lines.append(f"Compatibility: {skill.compatibility}")
147
- if skill.tags:
148
- lines.append(f"Tags: {', '.join(skill.tags)}")
149
- if skill.allowed_tools:
150
- lines.append(f"Allowed tools: {', '.join(skill.allowed_tools)}")
151
- if skill.triggers:
152
- lines.append(f"Triggers: {', '.join(skill.triggers)}")
153
-
154
- lines.append("")
155
- if skill.description:
156
- lines.append("Description:")
157
- lines.append(skill.description.strip())
158
- lines.append("")
159
-
160
- lines.append("Content (SKILL.md body):")
161
- lines.append(skill.content.strip() or "(empty)")
162
- lines.append("")
163
-
164
- if referenced_files:
165
- lines.append(
166
- "Files in skill directory (use skills_tool method=read_file to open):"
167
- )
168
- lines.append(referenced_files)
169
- else:
170
- lines.append("No additional files found in skill directory.")
171
-
172
- return "\n".join(lines)
130
+ return f"Loaded skill '{skill.name}' into persistent extras."
131
132
def _read_file(self, skill_name: str, file_path: str) -> str:
133
if not skill_name: