Add skill definitions (SKILL.md + run.py) for all agent tools
This commit is contained in:
9
skills/fetch_url/SKILL.md
Normal file
9
skills/fetch_url/SKILL.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
name: fetch_url
|
||||
description: Fetch a URL and return its text content. HTML is stripped to plain text. Use this to read web pages, documentation, articles, etc.
|
||||
parameters:
|
||||
url:
|
||||
type: string
|
||||
description: The URL to fetch
|
||||
required: true
|
||||
---
|
||||
51
skills/fetch_url/run.py
Normal file
51
skills/fetch_url/run.py
Normal file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import json
|
||||
import re
|
||||
import urllib.request
|
||||
from html.parser import HTMLParser
|
||||
|
||||
args = json.loads(sys.stdin.read())
|
||||
url = args.get("url", "")
|
||||
|
||||
|
||||
class TextExtractor(HTMLParser):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.text = []
|
||||
self._skip = False
|
||||
|
||||
def handle_starttag(self, tag, attrs):
|
||||
if tag in ("script", "style", "noscript"):
|
||||
self._skip = True
|
||||
|
||||
def handle_endtag(self, tag):
|
||||
if tag in ("script", "style", "noscript"):
|
||||
self._skip = False
|
||||
if tag in ("p", "br", "div", "h1", "h2", "h3", "h4", "li", "tr"):
|
||||
self.text.append("\n")
|
||||
|
||||
def handle_data(self, data):
|
||||
if not self._skip:
|
||||
self.text.append(data)
|
||||
|
||||
|
||||
try:
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "fireclaw-agent"})
|
||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||
content_type = resp.headers.get("Content-Type", "")
|
||||
raw = resp.read(50_000).decode("utf-8", errors="replace")
|
||||
|
||||
if "html" in content_type:
|
||||
parser = TextExtractor()
|
||||
parser.feed(raw)
|
||||
text = "".join(parser.text)
|
||||
else:
|
||||
text = raw
|
||||
|
||||
text = re.sub(r"\n{3,}", "\n\n", text).strip()
|
||||
if len(text) > 3000:
|
||||
text = text[:3000] + "\n[truncated]"
|
||||
print(text or "[empty page]")
|
||||
except Exception as e:
|
||||
print(f"[fetch error: {e}]")
|
||||
9
skills/run_command/SKILL.md
Normal file
9
skills/run_command/SKILL.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
name: run_command
|
||||
description: Execute a shell command on this system and return the output. Use this to check system info, run scripts, fetch URLs, process data, etc.
|
||||
parameters:
|
||||
command:
|
||||
type: string
|
||||
description: The shell command to execute (bash)
|
||||
required: true
|
||||
---
|
||||
27
skills/run_command/run.py
Normal file
27
skills/run_command/run.py
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
|
||||
args = json.loads(sys.stdin.read())
|
||||
command = args.get("command", "")
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["bash", "-c", command],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120,
|
||||
)
|
||||
output = result.stdout
|
||||
if result.stderr:
|
||||
output += f"\n[stderr] {result.stderr}"
|
||||
if result.returncode != 0:
|
||||
output += f"\n[exit code: {result.returncode}]"
|
||||
if len(output) > 2000:
|
||||
output = output[:2000] + "\n[output truncated]"
|
||||
print(output.strip() or "[no output]")
|
||||
except subprocess.TimeoutExpired:
|
||||
print("[command timed out after 120s]")
|
||||
except Exception as e:
|
||||
print(f"[error: {e}]")
|
||||
13
skills/save_memory/SKILL.md
Normal file
13
skills/save_memory/SKILL.md
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
name: save_memory
|
||||
description: Save something important to your persistent memory. Use this to remember facts about users, lessons learned, project context, or anything you want to recall in future conversations. Memories survive restarts.
|
||||
parameters:
|
||||
topic:
|
||||
type: string
|
||||
description: "Short topic name for the memory file (e.g. 'user_prefs', 'project_x', 'lessons')"
|
||||
required: true
|
||||
content:
|
||||
type: string
|
||||
description: The memory content to save
|
||||
required: true
|
||||
---
|
||||
33
skills/save_memory/run.py
Normal file
33
skills/save_memory/run.py
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
|
||||
args = json.loads(sys.stdin.read())
|
||||
topic = args.get("topic", "note")
|
||||
content = args.get("content", "")
|
||||
workspace = os.environ.get("WORKSPACE", "/workspace")
|
||||
|
||||
mem_dir = f"{workspace}/memory"
|
||||
os.makedirs(mem_dir, exist_ok=True)
|
||||
|
||||
# Write the memory file
|
||||
filepath = f"{mem_dir}/{topic}.md"
|
||||
with open(filepath, "w") as f:
|
||||
f.write(content + "\n")
|
||||
|
||||
# Update MEMORY.md index
|
||||
index_path = f"{workspace}/MEMORY.md"
|
||||
existing = ""
|
||||
try:
|
||||
with open(index_path) as f:
|
||||
existing = f.read()
|
||||
except FileNotFoundError:
|
||||
existing = "# Agent Memory\n"
|
||||
|
||||
entry = f"- [{topic}](memory/{topic}.md)"
|
||||
if topic not in existing:
|
||||
with open(index_path, "a") as f:
|
||||
f.write(f"\n{entry}")
|
||||
|
||||
print(f"Memory saved to {filepath}")
|
||||
13
skills/web_search/SKILL.md
Normal file
13
skills/web_search/SKILL.md
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
name: web_search
|
||||
description: Search the web using SearXNG. Returns titles, URLs, and snippets for the top results. Use this when you need current information or facts you're unsure about.
|
||||
parameters:
|
||||
query:
|
||||
type: string
|
||||
description: The search query
|
||||
required: true
|
||||
num_results:
|
||||
type: integer
|
||||
description: Number of results to return (default 5)
|
||||
required: false
|
||||
---
|
||||
32
skills/web_search/run.py
Normal file
32
skills/web_search/run.py
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import json
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
|
||||
args = json.loads(sys.stdin.read())
|
||||
query = args.get("query", "")
|
||||
num_results = args.get("num_results", 5)
|
||||
searx_url = args.get("_searx_url", "https://searx.mymx.me")
|
||||
|
||||
try:
|
||||
params = urllib.parse.urlencode({"q": query, "format": "json"})
|
||||
req = urllib.request.Request(
|
||||
f"{searx_url}/search?{params}",
|
||||
headers={"User-Agent": "fireclaw-agent"},
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||
data = json.loads(resp.read())
|
||||
results = data.get("results", [])[:num_results]
|
||||
if not results:
|
||||
print("No results found.")
|
||||
else:
|
||||
lines = []
|
||||
for r in results:
|
||||
title = r.get("title", "")
|
||||
url = r.get("url", "")
|
||||
snippet = r.get("content", "")[:150]
|
||||
lines.append(f"- {title}\n {url}\n {snippet}")
|
||||
print("\n".join(lines))
|
||||
except Exception as e:
|
||||
print(f"[search error: {e}]")
|
||||
Reference in New Issue
Block a user