From 6e3e4562a25c0cc3340eeaf2f478525098203652 Mon Sep 17 00:00:00 2001 From: Lance Martin Date: Tue, 4 Nov 2025 15:35:45 -0800 Subject: [PATCH 1/6] Update --- libs/deepagents-cli/deepagents_cli/README.md | 118 ++++++++ libs/deepagents-cli/deepagents_cli/agent.py | 12 +- libs/deepagents-cli/deepagents_cli/main.py | 31 ++ .../deepagents_cli/skill_loader.py | 186 ++++++++++++ .../deepagents_cli/skills_commands.py | 193 +++++++++++++ .../deepagents_cli/skills_middleware.py | 269 ++++++++++++++++++ 6 files changed, 807 insertions(+), 2 deletions(-) create mode 100644 libs/deepagents-cli/deepagents_cli/skill_loader.py create mode 100644 libs/deepagents-cli/deepagents_cli/skills_commands.py create mode 100644 libs/deepagents-cli/deepagents_cli/skills_middleware.py diff --git a/libs/deepagents-cli/deepagents_cli/README.md b/libs/deepagents-cli/deepagents_cli/README.md index 63caf24d..8c986421 100644 --- a/libs/deepagents-cli/deepagents_cli/README.md +++ b/libs/deepagents-cli/deepagents_cli/README.md @@ -194,3 +194,121 @@ deepagents = "deepagents.cli:cli_main" ``` This means when users install the package, they can run `deepagents` directly. + +## Skills + +The CLI implements **Agent Skills** - Anthropic's pattern for equipping agents with specialized, discoverable capabilities. Skills are markdown files with YAML frontmatter that provide structured guidance for specific tasks. + +### Creating Skills + +```bash +# Create a new skill from template +deepagents skills create web-scraping + +# This creates ~/.deepagents/skills/web-scraping/SKILL.md with: +# --- +# name: web-scraping +# description: [Brief description] +# --- +# [Detailed instructions, examples, and best practices] +``` + +### Managing Skills + +```bash +# List all available skills +deepagents skills list + +# View detailed skill information +deepagents skills info web-research +``` + +### Skill Directory Structure + +Skills are stored globally in `~/.deepagents/skills/` and shared across all agents: + +``` +~/.deepagents/ +├── skills/ # Global skills library +│ ├── web-research/ +│ │ ├── SKILL.md # Main instructions (YAML + Markdown) +│ │ └── helper.py # Optional: supporting files +│ └── code-review/ +│ ├── SKILL.md +│ └── checklist.md +└── agent/ # Per-agent memory + └── agent.md +``` + +### How Skills Work (Progressive Disclosure) + +Skills follow Anthropic's **progressive disclosure** pattern: + +1. **Discovery**: Agent sees skill names + descriptions in system prompt at startup +2. **Loading**: Agent reads full `SKILL.md` when task matches skill domain +3. **Execution**: Agent follows skill's step-by-step instructions +4. **Resources**: Agent accesses supporting files as needed + +### Built-in Skills + +DeepAgents CLI includes two example skills: + +**`web-research`**: Comprehensive methodology for conducting structured web research +- Search strategy and source evaluation +- Information organization patterns +- Synthesis techniques with examples + +**`code-review`**: Systematic code review across 8 dimensions +- Functionality, readability, security, performance +- Review templates and examples +- Integration with memory system + +### Using Skills in the CLI + +When you run the CLI, agents automatically: + +```python +# 1. Skills are loaded at session start +Skills Middleware → Loads metadata from ~/.deepagents/skills/ + +# 2. Skills list injected into system prompt +System Prompt includes: +""" +Available Skills: +- web-research: Structured approach to web research + → Read /skills/web-research/SKILL.md for full instructions +- code-review: Systematic code review methodology + → Read /skills/code-review/SKILL.md for full instructions +""" + +# 3. Agent recognizes when skills apply +User: "Can you research the latest Python frameworks?" +Agent: Recognizes web-research skill is relevant + +# 4. Agent loads full skill instructions +Agent: read_file('/skills/web-research/SKILL.md') + +# 5. Agent follows skill workflow +Agent: Executes research following skill's structured approach +``` + +### Example Session with Skills + +```bash +$ deepagents --agent researcher + +> Research the latest developments in quantum computing + +[Agent recognizes web-research skill applies] + +✓ Reading /skills/web-research/SKILL.md +✓ Following research methodology from skill +✓ Step 1: Defining research questions +✓ Step 2: Executing targeted searches + - web_search("quantum computing 2025 breakthroughs") + - web_search("quantum error correction advances") +✓ Step 3: Organizing findings in /memories/research-quantum.md +✓ Step 4: Synthesizing report + +[Agent delivers comprehensive research report following skill's structure] +``` diff --git a/libs/deepagents-cli/deepagents_cli/agent.py b/libs/deepagents-cli/deepagents_cli/agent.py index 3626b984..62043774 100644 --- a/libs/deepagents-cli/deepagents_cli/agent.py +++ b/libs/deepagents-cli/deepagents_cli/agent.py @@ -13,6 +13,7 @@ from .agent_memory import AgentMemoryMiddleware from .config import COLORS, config, console, get_default_coding_instructions +from .skills_middleware import SkillsMiddleware def list_agents(): @@ -156,14 +157,21 @@ def create_agent_with_config(model, assistant_id: str, tools: list): # This handles both /memories/ files and /agent.md long_term_backend = FilesystemBackend(root_dir=agent_dir, virtual_mode=True) - # Composite backend: current working directory for default, agent directory for /memories/ + # Skills backend - global skills directory shared across agents + skills_dir = Path.home() / ".deepagents" / "skills" + skills_dir.mkdir(parents=True, exist_ok=True) + skills_backend = FilesystemBackend(root_dir=skills_dir, virtual_mode=True) + + # Composite backend: current working directory for default, agent directory for /memories/, skills for /skills/ backend = CompositeBackend( - default=FilesystemBackend(), routes={"/memories/": long_term_backend} + default=FilesystemBackend(), + routes={"/memories/": long_term_backend, "/skills/": skills_backend}, ) # Use the same backend for agent memory middleware agent_middleware = [ AgentMemoryMiddleware(backend=long_term_backend, memory_path="/memories/"), + SkillsMiddleware(skills_dir=skills_dir, skills_path="/skills/"), shell_middleware, ] diff --git a/libs/deepagents-cli/deepagents_cli/main.py b/libs/deepagents-cli/deepagents_cli/main.py index 50be3225..25678097 100644 --- a/libs/deepagents-cli/deepagents_cli/main.py +++ b/libs/deepagents-cli/deepagents_cli/main.py @@ -10,6 +10,7 @@ from .config import COLORS, DEEP_AGENTS_ASCII, SessionState, console, create_model from .execution import execute_task from .input import create_prompt_session +from .skills_commands import create_skill, list_skills, show_skill_info from .tools import fetch_url, http_request, tavily_client, web_search from .ui import TokenTracker, show_help @@ -78,6 +79,21 @@ def parse_args(): "--target", dest="source_agent", help="Copy prompt from another agent" ) + # Skills command + skills_parser = subparsers.add_parser("skills", help="Manage agent skills") + skills_subparsers = skills_parser.add_subparsers(dest="skills_command", help="Skills command") + + # Skills list + skills_subparsers.add_parser("list", help="List all available skills") + + # Skills create + create_parser = skills_subparsers.add_parser("create", help="Create a new skill") + create_parser.add_argument("name", help="Name of the skill to create (e.g., web-research)") + + # Skills info + info_parser = skills_subparsers.add_parser("info", help="Show detailed information about a skill") + info_parser.add_argument("name", help="Name of the skill to show info for") + # Default interactive mode parser.add_argument( "--agent", @@ -210,6 +226,21 @@ def cli_main(): list_agents() elif args.command == "reset": reset_agent(args.agent, args.source_agent) + elif args.command == "skills": + # Handle skills subcommands + if args.skills_command == "list": + list_skills() + elif args.skills_command == "create": + create_skill(args.name) + elif args.skills_command == "info": + show_skill_info(args.name) + else: + # No subcommand provided, show help + console.print("[yellow]Please specify a skills subcommand: list, create, or info[/yellow]") + console.print("\nExamples:") + console.print(" deepagents skills list") + console.print(" deepagents skills create web-research") + console.print(" deepagents skills info web-research") else: # Create session state from args session_state = SessionState(auto_approve=args.auto_approve) diff --git a/libs/deepagents-cli/deepagents_cli/skill_loader.py b/libs/deepagents-cli/deepagents_cli/skill_loader.py new file mode 100644 index 00000000..f6d10ef7 --- /dev/null +++ b/libs/deepagents-cli/deepagents_cli/skill_loader.py @@ -0,0 +1,186 @@ +"""Skill loader for parsing and loading agent skills from SKILL.md files. + +This module implements Anthropic's agent skills pattern with YAML frontmatter parsing. +Each skill is a directory containing a SKILL.md file with: +- YAML frontmatter (name, description required) +- Markdown instructions for the agent +- Optional supporting files (scripts, configs, etc.) + +Example SKILL.md structure: +```markdown +--- +name: web-research +description: Structured approach to conducting thorough web research +--- + +# Web Research Skill + +## When to Use +- User asks you to research a topic +... +``` +""" + +import re +from pathlib import Path +from typing import TypedDict + + +class SkillMetadata(TypedDict): + """Metadata for a skill.""" + + name: str + """Name of the skill.""" + + description: str + """Description of what the skill does.""" + + path: str + """Path to the SKILL.md file.""" + + +class SkillLoader: + """Loader for agent skills with YAML frontmatter parsing. + + Skills are organized as: + skills/ + ├── skill-name/ + │ ├── SKILL.md # Required: instructions with YAML frontmatter + │ ├── script.py # Optional: supporting files + │ └── config.json # Optional: supporting files + + Example: + ```python + loader = SkillLoader(skills_dir="~/.deepagents/skills") + skills = loader.load_skills() + for skill in skills: + print(f"{skill['name']}: {skill['description']}") + ``` + """ + + def __init__(self, skills_dir: str | Path = "~/.deepagents/skills") -> None: + """Initialize the skill loader. + + Args: + skills_dir: Path to the skills directory. Defaults to ~/.deepagents/skills + """ + self.skills_dir = Path(skills_dir).expanduser() + self.skills: list[SkillMetadata] = [] + + def _parse_skill_metadata(self, skill_md_path: Path) -> SkillMetadata | None: + """Parse YAML frontmatter from a SKILL.md file. + + Args: + skill_md_path: Path to the SKILL.md file. + + Returns: + SkillMetadata with name, description, and path, or None if parsing fails. + """ + try: + content = skill_md_path.read_text(encoding="utf-8") + + # Match YAML frontmatter between --- delimiters + frontmatter_pattern = r"^---\s*\n(.*?)\n---\s*\n" + match = re.match(frontmatter_pattern, content, re.DOTALL) + + if not match: + return None + + frontmatter = match.group(1) + + # Parse key-value pairs from YAML (simple parsing, no nested structures) + metadata: dict[str, str] = {} + for line in frontmatter.split("\n"): + # Match "key: value" pattern + kv_match = re.match(r"^(\w+):\s*(.+)$", line.strip()) + if kv_match: + key, value = kv_match.groups() + metadata[key] = value.strip() + + # Validate required fields + if "name" not in metadata or "description" not in metadata: + return None + + return SkillMetadata( + name=metadata["name"], + description=metadata["description"], + path=str(skill_md_path), + ) + + except Exception: + # Silently skip malformed files + return None + + def load_skills(self) -> list[SkillMetadata]: + """Load all skills from the skills directory. + + Scans the skills directory for subdirectories containing SKILL.md files, + parses YAML frontmatter, and returns skill metadata. + + Returns: + List of skill metadata dictionaries with name, description, and path. + """ + self.skills = [] + + # Check if skills directory exists + if not self.skills_dir.exists(): + return self.skills + + # Iterate through subdirectories + for skill_dir in self.skills_dir.iterdir(): + if not skill_dir.is_dir(): + continue + + # Look for SKILL.md file + skill_md_path = skill_dir / "SKILL.md" + if not skill_md_path.exists(): + continue + + # Parse metadata + metadata = self._parse_skill_metadata(skill_md_path) + if metadata: + self.skills.append(metadata) + + return self.skills + + def get_skill_names(self) -> list[str]: + """Get list of loaded skill names. + + Returns: + List of skill names. + """ + return [skill["name"] for skill in self.skills] + + def format_skills_for_system_message(self) -> str: + """Format skills metadata for injection into system prompt. + + Creates a formatted list of skills with their descriptions and paths, + following Anthropic's progressive disclosure pattern. + + Returns: + Formatted string for system prompt. + """ + if not self.skills: + return "No skills available." + + lines = ["Available skills:"] + for skill in self.skills: + lines.append(f"- **{skill['name']}**: {skill['description']}") + lines.append(f" Read `/skills/{Path(skill['path']).parent.name}/SKILL.md` for details") + + return "\n".join(lines) + + +def load_skills(skills_dir: str | Path = "~/.deepagents/skills") -> tuple[list[SkillMetadata], str]: + """Convenience function to load skills and format for system prompt. + + Args: + skills_dir: Path to the skills directory. + + Returns: + Tuple of (skills metadata list, formatted string for system prompt). + """ + loader = SkillLoader(skills_dir) + skills = loader.load_skills() + formatted = loader.format_skills_for_system_message() + return skills, formatted diff --git a/libs/deepagents-cli/deepagents_cli/skills_commands.py b/libs/deepagents-cli/deepagents_cli/skills_commands.py new file mode 100644 index 00000000..27933a57 --- /dev/null +++ b/libs/deepagents-cli/deepagents_cli/skills_commands.py @@ -0,0 +1,193 @@ +"""CLI commands for skill management.""" + +from pathlib import Path + +from .config import COLORS, console +from .skill_loader import SkillLoader + + +def list_skills(): + """List all available skills.""" + skills_dir = Path.home() / ".deepagents" / "skills" + + if not skills_dir.exists() or not any(skills_dir.iterdir()): + console.print("[yellow]No skills found.[/yellow]") + console.print( + "[dim]Skills will be created in ~/.deepagents/skills/ when you add them.[/dim]", + style=COLORS["dim"], + ) + console.print( + f"\n[dim]Create your first skill:\n deepagents skills create my-skill[/dim]", + style=COLORS["dim"], + ) + return + + # Load skills + loader = SkillLoader(skills_dir=skills_dir) + skills = loader.load_skills() + + if not skills: + console.print("[yellow]No valid skills found.[/yellow]") + console.print( + "[dim]Skills must have a SKILL.md file with YAML frontmatter (name, description).[/dim]", + style=COLORS["dim"], + ) + return + + console.print("\n[bold]Available Skills:[/bold]\n", style=COLORS["primary"]) + + for skill in skills: + skill_path = Path(skill["path"]) + skill_dir_name = skill_path.parent.name + + console.print(f" • [bold]{skill['name']}[/bold]", style=COLORS["primary"]) + console.print(f" {skill['description']}", style=COLORS["dim"]) + console.print(f" Location: ~/.deepagents/skills/{skill_dir_name}/", style=COLORS["dim"]) + console.print() + + +def create_skill(skill_name: str): + """Create a new skill with a template SKILL.md file.""" + skills_dir = Path.home() / ".deepagents" / "skills" + skill_dir = skills_dir / skill_name + + if skill_dir.exists(): + console.print( + f"[bold red]Error:[/bold red] Skill '{skill_name}' already exists at {skill_dir}" + ) + return + + # Create skill directory + skill_dir.mkdir(parents=True, exist_ok=True) + + # Create template SKILL.md + template = f"""--- +name: {skill_name} +description: [Brief description of what this skill does] +--- + +# {skill_name.title().replace('-', ' ')} Skill + +## Description + +[Provide a detailed explanation of what this skill does and when it should be used] + +## When to Use + +- [Scenario 1: When the user asks...] +- [Scenario 2: When you need to...] +- [Scenario 3: When the task involves...] + +## How to Use + +### Step 1: [First Action] +[Explain what to do first] + +### Step 2: [Second Action] +[Explain what to do next] + +### Step 3: [Final Action] +[Explain how to complete the task] + +## Best Practices + +- [Best practice 1] +- [Best practice 2] +- [Best practice 3] + +## Supporting Files + +This skill directory can include supporting files referenced in the instructions: +- `helper.py` - Python scripts for automation +- `config.json` - Configuration files +- `reference.md` - Additional reference documentation + +## Examples + +### Example 1: [Scenario Name] + +**User Request:** "[Example user request]" + +**Approach:** +1. [Step-by-step breakdown] +2. [Using tools and commands] +3. [Expected outcome] + +### Example 2: [Another Scenario] + +**User Request:** "[Another example]" + +**Approach:** +1. [Different approach] +2. [Relevant commands] +3. [Expected result] + +## Notes + +- [Additional tips, warnings, or context] +- [Known limitations or edge cases] +- [Links to external resources if helpful] +""" + + skill_md = skill_dir / "SKILL.md" + skill_md.write_text(template) + + console.print( + f"✓ Skill '{skill_name}' created successfully!", style=COLORS["primary"] + ) + console.print(f"Location: {skill_dir}\n", style=COLORS["dim"]) + console.print( + "[dim]Edit the SKILL.md file to customize:\n" + " 1. Update the description in YAML frontmatter\n" + " 2. Fill in the instructions and examples\n" + " 3. Add any supporting files (scripts, configs, etc.)\n" + "\n" + f" nano {skill_md}\n", + style=COLORS["dim"], + ) + + +def show_skill_info(skill_name: str): + """Show detailed information about a specific skill.""" + skills_dir = Path.home() / ".deepagents" / "skills" + + # Load skills + loader = SkillLoader(skills_dir=skills_dir) + skills = loader.load_skills() + + # Find the skill + skill = next((s for s in skills if s["name"] == skill_name), None) + + if not skill: + console.print( + f"[bold red]Error:[/bold red] Skill '{skill_name}' not found." + ) + console.print( + f"\n[dim]Available skills:[/dim]", style=COLORS["dim"] + ) + for s in skills: + console.print(f" - {s['name']}", style=COLORS["dim"]) + return + + # Read the full SKILL.md file + skill_path = Path(skill["path"]) + skill_content = skill_path.read_text() + + console.print(f"\n[bold]Skill: {skill['name']}[/bold]\n", style=COLORS["primary"]) + console.print(f"[bold]Description:[/bold] {skill['description']}\n", style=COLORS["dim"]) + console.print(f"[bold]Location:[/bold] {skill_path.parent}/\n", style=COLORS["dim"]) + + # List supporting files + skill_dir = skill_path.parent + supporting_files = [f for f in skill_dir.iterdir() if f.name != "SKILL.md"] + + if supporting_files: + console.print("[bold]Supporting Files:[/bold]", style=COLORS["dim"]) + for file in supporting_files: + console.print(f" - {file.name}", style=COLORS["dim"]) + console.print() + + # Show the full SKILL.md content + console.print("[bold]Full SKILL.md Content:[/bold]\n", style=COLORS["primary"]) + console.print(skill_content, style=COLORS["dim"]) + console.print() diff --git a/libs/deepagents-cli/deepagents_cli/skills_middleware.py b/libs/deepagents-cli/deepagents_cli/skills_middleware.py new file mode 100644 index 00000000..3a44969a --- /dev/null +++ b/libs/deepagents-cli/deepagents_cli/skills_middleware.py @@ -0,0 +1,269 @@ +"""Middleware for loading and exposing agent skills to the system prompt. + +This middleware implements Anthropic's "Agent Skills" pattern with progressive disclosure: +1. Parse YAML frontmatter from SKILL.md files at session start +2. Inject skills metadata (name + description) into system prompt +3. Agent reads full SKILL.md content when relevant to a task + +Skills directory structure: +~/.deepagents/skills/ +├── web-research/ +│ ├── SKILL.md # Required: YAML frontmatter + instructions +│ └── helper.py # Optional: supporting files +├── code-review/ +│ ├── SKILL.md +│ └── checklist.md +""" + +from collections.abc import Awaitable, Callable +from pathlib import Path +from typing import NotRequired + +from deepagents.backends.protocol import BackendProtocol +from langchain.agents.middleware.types import ( + AgentMiddleware, + AgentState, + ModelRequest, + ModelResponse, +) + +from .skill_loader import SkillLoader, SkillMetadata + + +class SkillsState(AgentState): + """State for the skills middleware.""" + + skills_metadata: NotRequired[list[SkillMetadata] | None] + """List of loaded skill metadata (name, description, path).""" + + +# Skills System Documentation +SKILLS_SYSTEM_PROMPT = """ + +## Skills System + +You have access to a skills library that provides specialized capabilities and domain knowledge. + +**Available Skills:** + +{skills_list} + +**How to Use Skills (Progressive Disclosure):** + +Skills follow a **progressive disclosure** pattern - you know they exist (name + description above), but you only read the full instructions when needed: + +1. **Recognize when a skill applies**: Check if the user's task matches any skill's description +2. **Read the skill's full instructions**: Use `read_file '{skills_path}[skill-name]/SKILL.md'` to see detailed guidance +3. **Follow the skill's instructions**: SKILL.md contains step-by-step workflows, best practices, and examples +4. **Access supporting files**: Skills may include Python scripts, configs, or reference docs - paths are in SKILL.md + +**When to Use Skills:** +- When the user's request matches a skill's domain (e.g., "research X" → web-research skill) +- When you need specialized knowledge or structured workflows +- When a skill provides proven patterns for complex tasks + +**Skills are Self-Documenting:** +- Each SKILL.md tells you exactly what the skill does and how to use it +- You can explore available skills with `ls {skills_path}` +- You can read any skill's directory with `ls {skills_path}[skill-name]/` + +**Example Workflow:** + +User: "Can you research the latest developments in quantum computing?" + +1. Check available skills above → See "web-research" skill +2. Read the skill: `read_file '{skills_path}web-research/SKILL.md'` +3. Follow the skill's research workflow (search → organize → synthesize) +4. Use any helper scripts referenced in SKILL.md + +Remember: Skills are tools to make you more capable and consistent. When in doubt, check if a skill exists for the task! +""" + + +class SkillsMiddleware(AgentMiddleware): + """Middleware for loading and exposing agent skills. + + This middleware implements Anthropic's agent skills pattern: + - Loads skills metadata (name, description) from YAML frontmatter at session start + - Injects skills list into system prompt for discoverability + - Agent reads full SKILL.md content when a skill is relevant (progressive disclosure) + + Args: + skills_dir: Path to the skills directory. Defaults to ~/.deepagents/skills + skills_path: Virtual path prefix for skills in the filesystem (e.g., "/skills/") + system_prompt_template: Optional custom template for skills documentation. + Use {skills_list} for the formatted skills list and {skills_path} for the path. + + Example: + ```python + from pathlib import Path + from deepagents.backends.filesystem import FilesystemBackend + from deepagents.backends.composite import CompositeBackend + from deepagents_cli.skills_middleware import SkillsMiddleware + + # Set up skills backend + skills_dir = Path.home() / ".deepagents" / "skills" + skills_backend = FilesystemBackend(root_dir=skills_dir, virtual_mode=True) + + # Create composite backend with skills routing + backend = CompositeBackend( + default=FilesystemBackend(), + routes={"/skills/": skills_backend} + ) + + # Create middleware + middleware = SkillsMiddleware(skills_dir=skills_dir, skills_path="/skills/") + ``` + """ + + state_schema = SkillsState + + def __init__( + self, + *, + skills_dir: str | Path = "~/.deepagents/skills", + skills_path: str = "/skills/", + system_prompt_template: str | None = None, + ) -> None: + """Initialize the skills middleware. + + Args: + skills_dir: Path to the skills directory. + skills_path: Virtual path prefix for skills in the filesystem. + system_prompt_template: Optional custom template for skills docs. + """ + self.skills_dir = Path(skills_dir).expanduser() + self.skills_path = skills_path + self.system_prompt_template = system_prompt_template or SKILLS_SYSTEM_PROMPT + self.loader = SkillLoader(skills_dir=self.skills_dir) + + def _format_skills_list(self, skills: list[SkillMetadata]) -> str: + """Format skills metadata for display in system prompt. + + Args: + skills: List of skill metadata. + + Returns: + Formatted string with skills list. + """ + if not skills: + return "(No skills available yet. You can create skills in ~/.deepagents/skills/)" + + lines = [] + for skill in skills: + skill_dir = Path(skill["path"]).parent.name + lines.append(f"- **{skill['name']}**: {skill['description']}") + lines.append(f" → Read `{self.skills_path}{skill_dir}/SKILL.md` for full instructions") + + return "\n".join(lines) + + def before_agent( + self, + state: SkillsState, + runtime, + ) -> SkillsState: + """Load skills metadata before agent execution. + + This runs once at session start to discover available skills. + + Args: + state: Current agent state. + runtime: Runtime context. + + Returns: + Updated state with skills_metadata populated. + """ + # Only load skills if not already loaded + if "skills_metadata" not in state or state.get("skills_metadata") is None: + try: + # Load skills from directory + skills = self.loader.load_skills() + return {"skills_metadata": skills} + except Exception: + # Silently handle errors, return empty list + return {"skills_metadata": []} + + async def abefore_agent( + self, + state: SkillsState, + runtime, + ) -> SkillsState: + """(async) Load skills metadata before agent execution. + + Args: + state: Current agent state. + runtime: Runtime context. + + Returns: + Updated state with skills_metadata populated. + """ + # Sync version is fine since file operations are fast + return self.before_agent(state, runtime) + + def wrap_model_call( + self, + request: ModelRequest, + handler: Callable[[ModelRequest], ModelResponse], + ) -> ModelResponse: + """Inject skills documentation into the system prompt. + + This runs on every model call to ensure skills info is always available. + + Args: + request: The model request being processed. + handler: The handler function to call with the modified request. + + Returns: + The model response from the handler. + """ + # Get skills metadata from state + skills_metadata = request.state.get("skills_metadata", []) + + # Format skills list + skills_list = self._format_skills_list(skills_metadata) + + # Format the skills documentation + skills_section = self.system_prompt_template.format( + skills_list=skills_list, skills_path=self.skills_path + ) + + # Inject into system prompt + if request.system_prompt: + request.system_prompt = request.system_prompt + "\n\n" + skills_section + else: + request.system_prompt = skills_section + + return handler(request) + + async def awrap_model_call( + self, + request: ModelRequest, + handler: Callable[[ModelRequest], Awaitable[ModelResponse]], + ) -> ModelResponse: + """(async) Inject skills documentation into the system prompt. + + Args: + request: The model request being processed. + handler: The handler function to call with the modified request. + + Returns: + The model response from the handler. + """ + # Get skills metadata from state + skills_metadata = request.state.get("skills_metadata", []) + + # Format skills list + skills_list = self._format_skills_list(skills_metadata) + + # Format the skills documentation + skills_section = self.system_prompt_template.format( + skills_list=skills_list, skills_path=self.skills_path + ) + + # Inject into system prompt + if request.system_prompt: + request.system_prompt = request.system_prompt + "\n\n" + skills_section + else: + request.system_prompt = skills_section + + return await handler(request) From 7785b936c03a9ba3d634d6693f9ab67c6996ed7d Mon Sep 17 00:00:00 2001 From: Lance Martin Date: Thu, 6 Nov 2025 11:45:09 -0800 Subject: [PATCH 2/6] feat(skills): add web-research skill with structured workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a web-research skill that provides a structured approach to conducting comprehensive web research using the task tool to spawn research subagents. Features: - Three-step workflow: Plan → Delegate → Synthesize - File-based communication pattern for context management - Guidance on parallel vs sequential subagent execution - Configurable for use with --auto-approve or manual HITL The skill emphasizes: - Writing research plans before delegating - Having subagents save findings to files - Reading and synthesizing all findings at the end --- SKILL.md | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 SKILL.md diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 00000000..e799ae83 --- /dev/null +++ b/SKILL.md @@ -0,0 +1,91 @@ +--- +name: web-research +description: Structured web research workflow with planning, parallel delegation, and synthesis +--- + +# Web Research Skill + +This skill provides a structured approach to conducting comprehensive web research using the `task` tool to spawn research subagents. It emphasizes planning, efficient delegation, and systematic synthesis of findings. + +## When to Use This Skill + +Use this skill when you need to: +- Research complex topics requiring multiple information sources +- Gather and synthesize current information from the web +- Conduct comparative analysis across multiple subjects +- Produce well-sourced research reports with clear citations + +## Research Process + +### Step 1: Create and Save Research Plan + +Before delegating to subagents, you MUST: + +1. **Analyze the research question** - Break it down into distinct, non-overlapping subtopics +2. **Write a research plan file** - Use the `write_file` tool to create `/research_plan.md` containing: + - The main research question + - 2-5 specific subtopics to investigate + - Expected information from each subtopic + - How results will be synthesized + +**Planning Guidelines:** +- **Simple fact-finding**: 1-2 subtopics +- **Comparative analysis**: 1 subtopic per comparison element (max 3) +- **Complex investigations**: 3-5 subtopics + +### Step 2: Delegate to Research Subagents + +For each subtopic in your plan: + +1. **Use the `task` tool** to spawn a research subagent with: + - Clear, specific research question (no acronyms) + - Instructions to write findings to a file: `/findings_[subtopic].md` + - Budget: 3-5 web searches maximum + +2. **Parallel vs Sequential Execution:** + - **With `--auto-approve` enabled**: Run up to 3 subagents in parallel for efficient research + - **Without `--auto-approve`**: Run subagents sequentially (one at a time) to avoid multiple pending interrupts + - Note: Parallel subagents with HITL approval can cause interrupt handling issues + +**Subagent Instructions Template:** +``` +Research [SPECIFIC TOPIC]. Use the web_search tool to gather information. +After completing your research, use write_file to save your findings to /findings_[subtopic].md. +Include key facts, relevant quotes, and source URLs. +Use 3-5 web searches maximum. +``` + +### Step 3: Synthesize Findings + +After all subagents complete: + +1. **Use `list_files` and `read_file`** to review all findings files +2. **Synthesize the information** - Create a comprehensive response that: + - Directly answers the original question + - Integrates insights from all subtopics + - Cites specific sources with URLs + - Identifies any gaps or limitations + +3. **Write final report** (optional) - Use `write_file` to create `/research_report.md` if requested + +## Available Tools + +You have access to: +- **write_file**: Save research plans and findings +- **read_file**: Review findings from subagents +- **list_files**: See what files exist +- **task**: Spawn research subagents with web_search access + +## Research Subagent Configuration + +Each subagent you spawn will have access to: +- **web_search**: Search the web using Tavily (parameters: query, max_results, topic, include_raw_content) +- **write_file**: Save their findings to the filesystem + +## Best Practices + +- **Plan before delegating** - Always write research_plan.md first +- **Clear subtopics** - Ensure each subagent has distinct, non-overlapping scope +- **File-based communication** - Have subagents save findings to files, not return them directly +- **Systematic synthesis** - Read all findings files before creating final response +- **Stop appropriately** - Don't over-research; 3-5 searches per subtopic is usually sufficient From 098854ef77555b24798ecb8d0b49914874ab0da4 Mon Sep 17 00:00:00 2001 From: Lance Martin Date: Fri, 7 Nov 2025 08:47:39 -0800 Subject: [PATCH 3/6] refactor: address PR review comments for skill loader - Remove hard-coded default in SkillLoader.__init__, restrict to Path type only - Rename load_skills() to list() for clearer API - Remove get_skill_names() helper method (unnecessary) - Remove stateful self.skills caching, make loader stateless - Use specific exceptions (OSError, UnicodeDecodeError) instead of bare Exception - Update format_skills_for_system_message() to take skills as parameter - Add docstring comment explaining CLI entrypoint registration - Update all call sites to use new API --- .../deepagents_cli/skill_loader.py | 57 +++++++++---------- .../deepagents_cli/skills_commands.py | 12 +++- .../deepagents_cli/skills_middleware.py | 2 +- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/libs/deepagents-cli/deepagents_cli/skill_loader.py b/libs/deepagents-cli/deepagents_cli/skill_loader.py index f6d10ef7..2f1ec519 100644 --- a/libs/deepagents-cli/deepagents_cli/skill_loader.py +++ b/libs/deepagents-cli/deepagents_cli/skill_loader.py @@ -51,21 +51,23 @@ class SkillLoader: Example: ```python - loader = SkillLoader(skills_dir="~/.deepagents/skills") - skills = loader.load_skills() + from pathlib import Path + + skills_dir = Path.home() / ".deepagents" / "skills" + loader = SkillLoader(skills_dir=skills_dir) + skills = loader.list() for skill in skills: print(f"{skill['name']}: {skill['description']}") ``` """ - def __init__(self, skills_dir: str | Path = "~/.deepagents/skills") -> None: + def __init__(self, skills_dir: Path) -> None: """Initialize the skill loader. Args: - skills_dir: Path to the skills directory. Defaults to ~/.deepagents/skills + skills_dir: Path to the skills directory. """ - self.skills_dir = Path(skills_dir).expanduser() - self.skills: list[SkillMetadata] = [] + self.skills_dir = skills_dir.expanduser() def _parse_skill_metadata(self, skill_md_path: Path) -> SkillMetadata | None: """Parse YAML frontmatter from a SKILL.md file. @@ -107,12 +109,12 @@ def _parse_skill_metadata(self, skill_md_path: Path) -> SkillMetadata | None: path=str(skill_md_path), ) - except Exception: - # Silently skip malformed files + except (OSError, UnicodeDecodeError): + # Silently skip malformed or inaccessible files return None - def load_skills(self) -> list[SkillMetadata]: - """Load all skills from the skills directory. + def list(self) -> list[SkillMetadata]: + """List all skills from the skills directory. Scans the skills directory for subdirectories containing SKILL.md files, parses YAML frontmatter, and returns skill metadata. @@ -120,11 +122,11 @@ def load_skills(self) -> list[SkillMetadata]: Returns: List of skill metadata dictionaries with name, description, and path. """ - self.skills = [] + skills: list[SkillMetadata] = [] # Check if skills directory exists if not self.skills_dir.exists(): - return self.skills + return skills # Iterate through subdirectories for skill_dir in self.skills_dir.iterdir(): @@ -139,48 +141,45 @@ def load_skills(self) -> list[SkillMetadata]: # Parse metadata metadata = self._parse_skill_metadata(skill_md_path) if metadata: - self.skills.append(metadata) - - return self.skills + skills.append(metadata) - def get_skill_names(self) -> list[str]: - """Get list of loaded skill names. - - Returns: - List of skill names. - """ - return [skill["name"] for skill in self.skills] + return skills - def format_skills_for_system_message(self) -> str: + def format_skills_for_system_message(self, skills: list[SkillMetadata]) -> str: """Format skills metadata for injection into system prompt. Creates a formatted list of skills with their descriptions and paths, following Anthropic's progressive disclosure pattern. + Args: + skills: List of skill metadata to format. + Returns: Formatted string for system prompt. """ - if not self.skills: + if not skills: return "No skills available." lines = ["Available skills:"] - for skill in self.skills: + for skill in skills: lines.append(f"- **{skill['name']}**: {skill['description']}") lines.append(f" Read `/skills/{Path(skill['path']).parent.name}/SKILL.md` for details") return "\n".join(lines) -def load_skills(skills_dir: str | Path = "~/.deepagents/skills") -> tuple[list[SkillMetadata], str]: +def load_skills(skills_dir: Path | None = None) -> tuple[list[SkillMetadata], str]: """Convenience function to load skills and format for system prompt. Args: - skills_dir: Path to the skills directory. + skills_dir: Path to the skills directory. Defaults to ~/.deepagents/skills Returns: Tuple of (skills metadata list, formatted string for system prompt). """ + if skills_dir is None: + skills_dir = Path.home() / ".deepagents" / "skills" loader = SkillLoader(skills_dir) - skills = loader.load_skills() - formatted = loader.format_skills_for_system_message() + skills = loader.list() + formatted = loader.format_skills_for_system_message(skills) return skills, formatted diff --git a/libs/deepagents-cli/deepagents_cli/skills_commands.py b/libs/deepagents-cli/deepagents_cli/skills_commands.py index 27933a57..6c294ac5 100644 --- a/libs/deepagents-cli/deepagents_cli/skills_commands.py +++ b/libs/deepagents-cli/deepagents_cli/skills_commands.py @@ -1,4 +1,10 @@ -"""CLI commands for skill management.""" +"""CLI commands for skill management. + +These commands are registered with the CLI via cli.py: +- deepagents skills list +- deepagents skills create +- deepagents skills info +""" from pathlib import Path @@ -24,7 +30,7 @@ def list_skills(): # Load skills loader = SkillLoader(skills_dir=skills_dir) - skills = loader.load_skills() + skills = loader.list() if not skills: console.print("[yellow]No valid skills found.[/yellow]") @@ -153,7 +159,7 @@ def show_skill_info(skill_name: str): # Load skills loader = SkillLoader(skills_dir=skills_dir) - skills = loader.load_skills() + skills = loader.list() # Find the skill skill = next((s for s in skills if s["name"] == skill_name), None) diff --git a/libs/deepagents-cli/deepagents_cli/skills_middleware.py b/libs/deepagents-cli/deepagents_cli/skills_middleware.py index 3a44969a..995eca72 100644 --- a/libs/deepagents-cli/deepagents_cli/skills_middleware.py +++ b/libs/deepagents-cli/deepagents_cli/skills_middleware.py @@ -177,7 +177,7 @@ def before_agent( if "skills_metadata" not in state or state.get("skills_metadata") is None: try: # Load skills from directory - skills = self.loader.load_skills() + skills = self.loader.list() return {"skills_metadata": skills} except Exception: # Silently handle errors, return empty list From d4daf9755d607fd67c011e10cb29e74f766a7c23 Mon Sep 17 00:00:00 2001 From: Lance Martin Date: Fri, 7 Nov 2025 11:13:16 -0800 Subject: [PATCH 4/6] refactor: reorganize skills module and move example skills to examples/ - Move skill_loader.py, skills_commands.py, skills_middleware.py into deepagents_cli/skills/ package - Add __init__.py to expose public API from skills module - Update all imports to use new package structure (from .skills import ...) - Add from __future__ import annotations for Python 3.9+ compatibility - Move example skills to examples/skills/ directory: - examples/skills/web-research/ - examples/skills/langgraph-docs/ - Update README with Skills section documenting: - Location of example skills (examples/skills/) - How to install example skills to ~/.deepagents/skills/ - Skills management commands (list, create, info) --- examples/skills/langgraph-docs/SKILL.md | 35 +++++++++++++++++++ .../skills/web-research/SKILL.md | 5 +-- libs/deepagents-cli/README.md | 31 ++++++++++++++++ libs/deepagents-cli/deepagents_cli/agent.py | 2 +- libs/deepagents-cli/deepagents_cli/main.py | 2 +- .../deepagents_cli/skills/__init__.py | 15 ++++++++ .../{ => skills}/skill_loader.py | 2 ++ .../{ => skills}/skills_commands.py | 2 +- .../{ => skills}/skills_middleware.py | 2 +- 9 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 examples/skills/langgraph-docs/SKILL.md rename SKILL.md => examples/skills/web-research/SKILL.md (90%) create mode 100644 libs/deepagents-cli/deepagents_cli/skills/__init__.py rename libs/deepagents-cli/deepagents_cli/{ => skills}/skill_loader.py (99%) rename libs/deepagents-cli/deepagents_cli/{ => skills}/skills_commands.py (99%) rename libs/deepagents-cli/deepagents_cli/{ => skills}/skills_middleware.py (99%) diff --git a/examples/skills/langgraph-docs/SKILL.md b/examples/skills/langgraph-docs/SKILL.md new file mode 100644 index 00000000..2a4ffa91 --- /dev/null +++ b/examples/skills/langgraph-docs/SKILL.md @@ -0,0 +1,35 @@ +--- +name: langgraph-docs +description: Use this skill for requests related to LangGraph in order to fetch relevant documentation to provide accurate, up-to-date guidance. +--- + +# langgraph-docs + +## Overview + +This skill explains how to access LangGraph Python documentation to help answer questions and guide implementation. + +## Instructions + +### 1. Fetch the Documentation Index + +Use the fetch_url tool to read the following URL: +https://docs.langchain.com/llms.txt + +This provides a structured list of all available documentation with descriptions. + +### 2. Select Relevant Documentation + +Based on the question, identify 2-4 most relevant documentation URLs from the index. Prioritize: +- Specific how-to guides for implementation questions +- Core concept pages for understanding questions +- Tutorials for end-to-end examples +- Reference docs for API details + +### 3. Fetch Selected Documentation + +Use the fetch_url tool to read the selected documentation URLs. + +### 4. Provide Accurate Guidance + +After reading the documentation, complete the users request. diff --git a/SKILL.md b/examples/skills/web-research/SKILL.md similarity index 90% rename from SKILL.md rename to examples/skills/web-research/SKILL.md index e799ae83..fda07f30 100644 --- a/SKILL.md +++ b/examples/skills/web-research/SKILL.md @@ -42,10 +42,7 @@ For each subtopic in your plan: - Instructions to write findings to a file: `/findings_[subtopic].md` - Budget: 3-5 web searches maximum -2. **Parallel vs Sequential Execution:** - - **With `--auto-approve` enabled**: Run up to 3 subagents in parallel for efficient research - - **Without `--auto-approve`**: Run subagents sequentially (one at a time) to avoid multiple pending interrupts - - Note: Parallel subagents with HITL approval can cause interrupt handling issues +2. **Run up to 3 subagents in parallel** for efficient research **Subagent Instructions Template:** ``` diff --git a/libs/deepagents-cli/README.md b/libs/deepagents-cli/README.md index 6d1cea08..e6a87723 100644 --- a/libs/deepagents-cli/README.md +++ b/libs/deepagents-cli/README.md @@ -1,3 +1,34 @@ # deepagents cli This is the CLI for deepagents + +## Skills + +Skills are reusable agent capabilities that can be loaded into the CLI. The CLI looks for skills in `~/.deepagents/skills/` by default. + +### Example Skills + +Example skills are provided in the `examples/skills/` directory: + +- **web-research** - Structured web research workflow with planning, parallel delegation, and synthesis +- **langgraph-docs** - LangGraph documentation lookup and guidance + +To use an example skill, copy it to your skills directory: + +```bash +mkdir -p ~/.deepagents/skills +cp -r examples/skills/web-research ~/.deepagents/skills/ +``` + +### Managing Skills + +```bash +# List available skills +deepagents skills list + +# Create a new skill from template +deepagents skills create my-skill + +# View detailed information about a skill +deepagents skills info web-research +``` diff --git a/libs/deepagents-cli/deepagents_cli/agent.py b/libs/deepagents-cli/deepagents_cli/agent.py index 62043774..a3222cd1 100644 --- a/libs/deepagents-cli/deepagents_cli/agent.py +++ b/libs/deepagents-cli/deepagents_cli/agent.py @@ -13,7 +13,7 @@ from .agent_memory import AgentMemoryMiddleware from .config import COLORS, config, console, get_default_coding_instructions -from .skills_middleware import SkillsMiddleware +from .skills import SkillsMiddleware def list_agents(): diff --git a/libs/deepagents-cli/deepagents_cli/main.py b/libs/deepagents-cli/deepagents_cli/main.py index 25678097..9126d86b 100644 --- a/libs/deepagents-cli/deepagents_cli/main.py +++ b/libs/deepagents-cli/deepagents_cli/main.py @@ -10,7 +10,7 @@ from .config import COLORS, DEEP_AGENTS_ASCII, SessionState, console, create_model from .execution import execute_task from .input import create_prompt_session -from .skills_commands import create_skill, list_skills, show_skill_info +from .skills import create_skill, list_skills, show_skill_info from .tools import fetch_url, http_request, tavily_client, web_search from .ui import TokenTracker, show_help diff --git a/libs/deepagents-cli/deepagents_cli/skills/__init__.py b/libs/deepagents-cli/deepagents_cli/skills/__init__.py new file mode 100644 index 00000000..1a8a8c60 --- /dev/null +++ b/libs/deepagents-cli/deepagents_cli/skills/__init__.py @@ -0,0 +1,15 @@ +"""Skills module for deepagents CLI.""" + +from .skill_loader import SkillLoader, SkillMetadata, load_skills +from .skills_commands import create_skill, list_skills, show_skill_info +from .skills_middleware import SkillsMiddleware + +__all__ = [ + "SkillLoader", + "SkillMetadata", + "load_skills", + "create_skill", + "list_skills", + "show_skill_info", + "SkillsMiddleware", +] diff --git a/libs/deepagents-cli/deepagents_cli/skill_loader.py b/libs/deepagents-cli/deepagents_cli/skills/skill_loader.py similarity index 99% rename from libs/deepagents-cli/deepagents_cli/skill_loader.py rename to libs/deepagents-cli/deepagents_cli/skills/skill_loader.py index 2f1ec519..a236f9ef 100644 --- a/libs/deepagents-cli/deepagents_cli/skill_loader.py +++ b/libs/deepagents-cli/deepagents_cli/skills/skill_loader.py @@ -21,6 +21,8 @@ ``` """ +from __future__ import annotations + import re from pathlib import Path from typing import TypedDict diff --git a/libs/deepagents-cli/deepagents_cli/skills_commands.py b/libs/deepagents-cli/deepagents_cli/skills/skills_commands.py similarity index 99% rename from libs/deepagents-cli/deepagents_cli/skills_commands.py rename to libs/deepagents-cli/deepagents_cli/skills/skills_commands.py index 6c294ac5..abc20e80 100644 --- a/libs/deepagents-cli/deepagents_cli/skills_commands.py +++ b/libs/deepagents-cli/deepagents_cli/skills/skills_commands.py @@ -8,7 +8,7 @@ from pathlib import Path -from .config import COLORS, console +from ..config import COLORS, console from .skill_loader import SkillLoader diff --git a/libs/deepagents-cli/deepagents_cli/skills_middleware.py b/libs/deepagents-cli/deepagents_cli/skills/skills_middleware.py similarity index 99% rename from libs/deepagents-cli/deepagents_cli/skills_middleware.py rename to libs/deepagents-cli/deepagents_cli/skills/skills_middleware.py index 995eca72..36c93804 100644 --- a/libs/deepagents-cli/deepagents_cli/skills_middleware.py +++ b/libs/deepagents-cli/deepagents_cli/skills/skills_middleware.py @@ -99,7 +99,7 @@ class SkillsMiddleware(AgentMiddleware): from pathlib import Path from deepagents.backends.filesystem import FilesystemBackend from deepagents.backends.composite import CompositeBackend - from deepagents_cli.skills_middleware import SkillsMiddleware + from deepagents_cli.skills import SkillsMiddleware # Set up skills backend skills_dir = Path.home() / ".deepagents" / "skills" From ccd5a7cc652391d34cf6d65f8f58338627543995 Mon Sep 17 00:00:00 2001 From: Lance Martin Date: Fri, 7 Nov 2025 11:51:19 -0800 Subject: [PATCH 5/6] docs(skills): organize web-research files in dedicated folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses TODO(Claude) to specify that all research files should be organized in a dedicated folder structure. Changes: - Added step to create /research_[topic_name]/ folder - Updated all file paths to use folder structure: - /research_plan.md → /research_[topic_name]/research_plan.md - /findings_[subtopic].md → /research_[topic_name]/findings_[subtopic].md - /research_report.md → /research_[topic_name]/research_report.md - Updated synthesis step to reference the folder Benefits: - Keeps files organized and prevents clutter in working directory - Makes it easy to manage multiple research projects - Clear file organization for subagents --- examples/skills/web-research/SKILL.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/examples/skills/web-research/SKILL.md b/examples/skills/web-research/SKILL.md index fda07f30..503615e0 100644 --- a/examples/skills/web-research/SKILL.md +++ b/examples/skills/web-research/SKILL.md @@ -21,8 +21,15 @@ Use this skill when you need to: Before delegating to subagents, you MUST: -1. **Analyze the research question** - Break it down into distinct, non-overlapping subtopics -2. **Write a research plan file** - Use the `write_file` tool to create `/research_plan.md` containing: +1. **Create a research folder** - Organize all research files in a dedicated folder: + ``` + mkdir /research_[topic_name]/ + ``` + This keeps files organized and prevents clutter in the working directory. + +2. **Analyze the research question** - Break it down into distinct, non-overlapping subtopics + +3. **Write a research plan file** - Use the `write_file` tool to create `/research_[topic_name]/research_plan.md` containing: - The main research question - 2-5 specific subtopics to investigate - Expected information from each subtopic @@ -39,7 +46,7 @@ For each subtopic in your plan: 1. **Use the `task` tool** to spawn a research subagent with: - Clear, specific research question (no acronyms) - - Instructions to write findings to a file: `/findings_[subtopic].md` + - Instructions to write findings to a file: `/research_[topic_name]/findings_[subtopic].md` - Budget: 3-5 web searches maximum 2. **Run up to 3 subagents in parallel** for efficient research @@ -47,7 +54,7 @@ For each subtopic in your plan: **Subagent Instructions Template:** ``` Research [SPECIFIC TOPIC]. Use the web_search tool to gather information. -After completing your research, use write_file to save your findings to /findings_[subtopic].md. +After completing your research, use write_file to save your findings to /research_[topic_name]/findings_[subtopic].md. Include key facts, relevant quotes, and source URLs. Use 3-5 web searches maximum. ``` @@ -56,14 +63,14 @@ Use 3-5 web searches maximum. After all subagents complete: -1. **Use `list_files` and `read_file`** to review all findings files +1. **Use `list_files` and `read_file`** to review all findings files in `/research_[topic_name]/` 2. **Synthesize the information** - Create a comprehensive response that: - Directly answers the original question - Integrates insights from all subtopics - Cites specific sources with URLs - Identifies any gaps or limitations -3. **Write final report** (optional) - Use `write_file` to create `/research_report.md` if requested +3. **Write final report** (optional) - Use `write_file` to create `/research_[topic_name]/research_report.md` if requested ## Available Tools From ccfe875b76e4ac996d3f4586e8f14fba5396f1ef Mon Sep 17 00:00:00 2001 From: Lance Martin Date: Fri, 7 Nov 2025 15:14:03 -0800 Subject: [PATCH 6/6] style: run ruff format to fix linting errors --- libs/deepagents-cli/deepagents_cli/agent.py | 4 +++- libs/deepagents-cli/deepagents_cli/main.py | 8 ++++++-- .../deepagents_cli/skills/skills_commands.py | 14 ++++---------- .../deepagents_cli/skills/skills_middleware.py | 5 +---- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/libs/deepagents-cli/deepagents_cli/agent.py b/libs/deepagents-cli/deepagents_cli/agent.py index a3222cd1..d33dc6a5 100644 --- a/libs/deepagents-cli/deepagents_cli/agent.py +++ b/libs/deepagents-cli/deepagents_cli/agent.py @@ -215,7 +215,9 @@ def format_fetch_url_description(tool_call: dict) -> str: url = args.get("url", "unknown") timeout = args.get("timeout", 30) - return f"URL: {url}\nTimeout: {timeout}s\n\n⚠️ Will fetch and convert web content to markdown" + return ( + f"URL: {url}\nTimeout: {timeout}s\n\n⚠️ Will fetch and convert web content to markdown" + ) def format_task_description(tool_call: dict) -> str: """Format task (subagent) tool call for approval prompt.""" diff --git a/libs/deepagents-cli/deepagents_cli/main.py b/libs/deepagents-cli/deepagents_cli/main.py index 9126d86b..9cb28259 100644 --- a/libs/deepagents-cli/deepagents_cli/main.py +++ b/libs/deepagents-cli/deepagents_cli/main.py @@ -91,7 +91,9 @@ def parse_args(): create_parser.add_argument("name", help="Name of the skill to create (e.g., web-research)") # Skills info - info_parser = skills_subparsers.add_parser("info", help="Show detailed information about a skill") + info_parser = skills_subparsers.add_parser( + "info", help="Show detailed information about a skill" + ) info_parser.add_argument("name", help="Name of the skill to show info for") # Default interactive mode @@ -236,7 +238,9 @@ def cli_main(): show_skill_info(args.name) else: # No subcommand provided, show help - console.print("[yellow]Please specify a skills subcommand: list, create, or info[/yellow]") + console.print( + "[yellow]Please specify a skills subcommand: list, create, or info[/yellow]" + ) console.print("\nExamples:") console.print(" deepagents skills list") console.print(" deepagents skills create web-research") diff --git a/libs/deepagents-cli/deepagents_cli/skills/skills_commands.py b/libs/deepagents-cli/deepagents_cli/skills/skills_commands.py index abc20e80..196f32a4 100644 --- a/libs/deepagents-cli/deepagents_cli/skills/skills_commands.py +++ b/libs/deepagents-cli/deepagents_cli/skills/skills_commands.py @@ -72,7 +72,7 @@ def create_skill(skill_name: str): description: [Brief description of what this skill does] --- -# {skill_name.title().replace('-', ' ')} Skill +# {skill_name.title().replace("-", " ")} Skill ## Description @@ -138,9 +138,7 @@ def create_skill(skill_name: str): skill_md = skill_dir / "SKILL.md" skill_md.write_text(template) - console.print( - f"✓ Skill '{skill_name}' created successfully!", style=COLORS["primary"] - ) + console.print(f"✓ Skill '{skill_name}' created successfully!", style=COLORS["primary"]) console.print(f"Location: {skill_dir}\n", style=COLORS["dim"]) console.print( "[dim]Edit the SKILL.md file to customize:\n" @@ -165,12 +163,8 @@ def show_skill_info(skill_name: str): skill = next((s for s in skills if s["name"] == skill_name), None) if not skill: - console.print( - f"[bold red]Error:[/bold red] Skill '{skill_name}' not found." - ) - console.print( - f"\n[dim]Available skills:[/dim]", style=COLORS["dim"] - ) + console.print(f"[bold red]Error:[/bold red] Skill '{skill_name}' not found.") + console.print(f"\n[dim]Available skills:[/dim]", style=COLORS["dim"]) for s in skills: console.print(f" - {s['name']}", style=COLORS["dim"]) return diff --git a/libs/deepagents-cli/deepagents_cli/skills/skills_middleware.py b/libs/deepagents-cli/deepagents_cli/skills/skills_middleware.py index 36c93804..7043af70 100644 --- a/libs/deepagents-cli/deepagents_cli/skills/skills_middleware.py +++ b/libs/deepagents-cli/deepagents_cli/skills/skills_middleware.py @@ -106,10 +106,7 @@ class SkillsMiddleware(AgentMiddleware): skills_backend = FilesystemBackend(root_dir=skills_dir, virtual_mode=True) # Create composite backend with skills routing - backend = CompositeBackend( - default=FilesystemBackend(), - routes={"/skills/": skills_backend} - ) + backend = CompositeBackend(default=FilesystemBackend(), routes={"/skills/": skills_backend}) # Create middleware middleware = SkillsMiddleware(skills_dir=skills_dir, skills_path="/skills/")