Add new skills descriptions and enhance skills tool functionality

- Added detailed descriptions for new skills categories: Machine Learning Operations and Note Taking.
- Introduced a new Obsidian skill with commands for reading, listing, searching, creating, and appending notes.
- Enhanced the skills tool to load and display category descriptions from DESCRIPTION.md files, improving user guidance and discovery of available skills.
This commit is contained in:
teknium1 2026-02-01 01:32:21 -08:00
parent 32254d3010
commit 8f5f99c22a
5 changed files with 149 additions and 7 deletions

20
TODO.md
View file

@ -276,6 +276,26 @@ These items need to be addressed ASAP:
### Medium-Impact
- [ ] **Canvas / Visual Workspace** 🖼️
- Agent-controlled visual panel for rendering interactive UI
- Inspired by OpenClaw's Canvas feature
- **Capabilities:**
- `present` / `hide` - Show/hide the canvas panel
- `navigate` - Load HTML files or URLs into the canvas
- `eval` - Execute JavaScript in the canvas context
- `snapshot` - Capture the rendered UI as an image
- **Use cases:**
- Display generated HTML/CSS/JS previews
- Show interactive data visualizations (charts, graphs)
- Render diagrams (Mermaid → rendered output)
- Present structured information in rich format
- A2UI-style component system for structured agent UI
- **Implementation options:**
- Electron-based panel for CLI
- WebSocket-connected web app
- VS Code webview extension
- *Would let agent "show" things rather than just describe them*
- [ ] **Document Generation** 📄
- Create styled PDFs, Word docs, presentations
- *Can do basic PDF via terminal tools, but limited*

View file

@ -0,0 +1,3 @@
---
description: Knowledge and Tools for Machine Learning Operations - tools and frameworks for training, fine-tuning, deploying, and optimizing ML/AI models
---

View file

@ -0,0 +1,3 @@
---
description: Note taking skills, to save information, assist with research, and collab on multi-session planning and information sharing.
---

View file

@ -0,0 +1,57 @@
---
name: obsidian
description: Read, search, and create notes in the Obsidian vault.
---
# Obsidian Vault
**Location:** `/home/teknium/Documents/Primary Vault`
Note: Path contains a space - always quote it.
## Read a note
```bash
cat "/home/teknium/Documents/Primary Vault/Note Name.md"
```
## List notes
```bash
# All notes
find "/home/teknium/Documents/Primary Vault" -name "*.md" -type f
# In a specific folder
ls "/home/teknium/Documents/Primary Vault/AI Research/"
```
## Search
```bash
# By filename
find "/home/teknium/Documents/Primary Vault" -name "*.md" -iname "*keyword*"
# By content
grep -rli "keyword" "/home/teknium/Documents/Primary Vault" --include="*.md"
```
## Create a note
```bash
cat > "/home/teknium/Documents/Primary Vault/New Note.md" << 'ENDNOTE'
# Title
Content here.
ENDNOTE
```
## Append to a note
```bash
echo "
New content here." >> "/home/teknium/Documents/Primary Vault/Existing Note.md"
```
## Wikilinks
Obsidian links notes with `[[Note Name]]` syntax. When creating notes, use these to link related content.

View file

@ -312,17 +312,56 @@ def _find_all_skills() -> List[Dict[str, Any]]:
return skills
def _load_category_description(category_dir: Path) -> Optional[str]:
"""
Load category description from DESCRIPTION.md if it exists.
Args:
category_dir: Path to the category directory
Returns:
Description string or None if not found
"""
desc_file = category_dir / "DESCRIPTION.md"
if not desc_file.exists():
return None
try:
content = desc_file.read_text(encoding='utf-8')
# Parse frontmatter if present
frontmatter, body = _parse_frontmatter(content)
# Prefer frontmatter description, fall back to first non-header line
description = frontmatter.get('description', '')
if not description:
for line in body.strip().split('\n'):
line = line.strip()
if line and not line.startswith('#'):
description = line
break
# Truncate to reasonable length
if len(description) > MAX_DESCRIPTION_LENGTH:
description = description[:MAX_DESCRIPTION_LENGTH - 3] + "..."
return description if description else None
except Exception:
return None
def skills_categories(task_id: str = None) -> str:
"""
List available skill categories (progressive disclosure tier 0).
List available skill categories with descriptions (progressive disclosure tier 0).
Returns just category names for efficient discovery before filtering.
Returns category names and descriptions for efficient discovery before drilling down.
Categories can have a DESCRIPTION.md file with a description frontmatter field
or first paragraph to explain what skills are in that category.
Args:
task_id: Optional task identifier (unused, for API consistency)
Returns:
JSON string with list of category names
JSON string with list of categories and their descriptions
"""
try:
if not SKILLS_DIR.exists():
@ -333,16 +372,36 @@ def skills_categories(task_id: str = None) -> str:
}, ensure_ascii=False)
# Scan for categories (top-level directories containing skills)
categories = set()
category_dirs = {}
for skill_md in SKILLS_DIR.rglob("SKILL.md"):
category = _get_category_from_path(skill_md)
if category:
categories.add(category)
category_dir = SKILLS_DIR / category
if category not in category_dirs:
category_dirs[category] = category_dir
# Build category list with descriptions
categories = []
for name in sorted(category_dirs.keys()):
category_dir = category_dirs[name]
description = _load_category_description(category_dir)
# Count skills in this category
skill_count = sum(1 for _ in category_dir.rglob("SKILL.md"))
cat_entry = {
"name": name,
"skill_count": skill_count
}
if description:
cat_entry["description"] = description
categories.append(cat_entry)
return json.dumps({
"success": True,
"categories": sorted(categories),
"hint": "Use skills_list(category) to see skills in a category"
"categories": categories,
"hint": "If a category is relevant to your task, use skills_list with that category to see available skills"
}, ensure_ascii=False)
except Exception as e: