skills extras preparation
frdel committed
Feb 5, 2026 at 16:59 UTC
6fafba67d7d5150259a74ce00da009937151ed9b
5 files changed
+51
-134
prompts/agent.system.skills.loaded.md
+2
-3
@@ -1,5 +1,4 @@
1
-# Loaded skill
2
-- The following skill was explicitly loaded via skills_tool
3
-- Full content persists here each turn to survive history compression
1
+# loaded skills
2
+- the following skills were explicitly loaded via skills_tool
3
4
{{skills}}
python/extensions/message_loop_prompts_after/_65_include_loaded_skill.py
deleted
-91
@@ -1,91 +0,0 @@
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/extensions/message_loop_prompts_after/_65_include_loaded_skills.py
+10
-11
@@ -1,24 +1,23 @@
1
from python.helpers.extension import Extension
2
+from python.helpers import skills
3
+from python.tools.skills_tool import DATA_NAME_LOADED_SKILLS
4
from agent import LoopData
5
6
5
-DATA_NAME_LOADED_SKILL = "loaded_skill"
6
-
7
-
7
class IncludeLoadedSkills(Extension):
8
async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
9
extras = loop_data.extras_persistent
10
12
- # Clear previous
13
- if "loaded_skills" in extras:
14
- del extras["loaded_skills"]
15
-
16
- # Get single loaded skill
17
- skill_data = self.agent.data.get(DATA_NAME_LOADED_SKILL)
18
- if not skill_data or not isinstance(skill_data, dict):
11
+ # Get loaded skills names
12
+ skill_names = self.agent.data.get(DATA_NAME_LOADED_SKILLS)
13
+ if not skill_names:
14
return
15
21
- content = str(skill_data.get("content") or "").strip()
16
+ # load skill text here
17
+ content = ""
18
+ for skill_name in skill_names:
19
+ skill_data = skills.load_skill_for_agent(skill_name=skill_name, agent=self.agent)
20
+ content += "\n\n" + skill_data
21
if not content:
22
return
23
python/helpers/skills.py
+8
@@ -464,6 +464,14 @@ def find_skill(
464
return s
465
return None
466
467
+def load_skill_for_agent(
468
+ skill_name: str,
469
+ agent:Agent|None=None,
470
+) -> str:
471
+ skill = find_skill(skill_name, agent=agent, include_content=True)
472
+ if not skill:
473
+ return "TODO"
474
+ return "TODO"
475
476
def search_skills(
477
query: str,
python/tools/skills_tool.py
+31
-29
@@ -8,7 +8,7 @@ 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"
11
+DATA_NAME_LOADED_SKILLS = "loaded_skills"
12
13
14
class SkillsTool(Tool):
@@ -118,34 +118,36 @@ class SkillsTool(Tool):
118
if not skill_name:
119
return "Error: 'skill_name' is required for method=load."
120
121
- skill = skills_helper.find_skill(
122
- skill_name,
123
- include_content=True,
124
- agent=self.agent,
125
- )
126
- if not skill:
127
- return f"Error: skill not found: {skill_name!r}. Try skills_tool method=list or method=search."
128
-
129
- # Build skill content block
130
- files_tree = self._list_skill_files(skill.path, max_files=80)
131
- if self.agent.config.code_exec_ssh_enabled:
132
- runtime_path = files.normalize_a0_path(str(skill.path))
133
- else:
134
- runtime_path = str(skill.path)
135
-
136
- content_block = self._build_loaded_skill_block(
137
- skill=skill,
138
- runtime_path=runtime_path,
139
- files_tree=files_tree,
140
- )
141
-
142
- # Store single skill in agent.data (replaces previous)
143
- self.agent.data[DATA_NAME_LOADED_SKILL] = {
144
- "name": skill.name,
145
- "content": content_block,
146
- }
147
-
148
- return f"Loaded skill '{skill.name}' into persistent extras."
121
+ self.agent.data[DATA_NAME_LOADED_SKILL] = [skill_name]
122
+
123
+ # skill = skills_helper.find_skill(
124
+ # skill_name,
125
+ # include_content=True,
126
+ # agent=self.agent,
127
+ # )
128
+ # if not skill:
129
+ # return f"Error: skill not found: {skill_name!r}. Try skills_tool method=list or method=search."
130
+
131
+ # # Build skill content block
132
+ # files_tree = self._list_skill_files(skill.path, max_files=80)
133
+ # if self.agent.config.code_exec_ssh_enabled:
134
+ # runtime_path = files.normalize_a0_path(str(skill.path))
135
+ # else:
136
+ # runtime_path = str(skill.path)
137
+
138
+ # content_block = self._build_loaded_skill_block(
139
+ # skill=skill,
140
+ # runtime_path=runtime_path,
141
+ # files_tree=files_tree,
142
+ # )
143
+
144
+ # # Store single skill in agent.data (replaces previous)
145
+ # self.agent.data[DATA_NAME_LOADED_SKILL] = {
146
+ # "name": skill.name,
147
+ # "content": content_block,
148
+ # }
149
+
150
+ return f"Loaded skill '{skill_name}' into EXTRAS."
151
152
def _build_loaded_skill_block(
153
self, *, skill: skills_helper.Skill, runtime_path: str, files_tree: str