# Search and Directory Skills

Skill-level directory listing and search tools for agents.  All tools
enforce path traversal guards and provide deterministic, sorted output.

## Tools

| Tool            | Capability   | Description                                         |
|-----------------|-------------|-----------------------------------------------------|
| `skill/list-dir`| `read_only` | List directory with filters, limits, type filtering |
| `skill/glob`    | `read_only` | Match files by glob with include/exclude filters    |
| `skill/grep`    | `read_only` | Search contents by regex with context lines         |

## ListDir

Lists directory contents with:

- **Ignore patterns**: Glob patterns to exclude entries.
- **Type filter**: Show only files, only directories, or all.
- **Size limit**: Cap the number of entries returned.

### Example

```python
from cleveragents.skills.builtins.search_ops import register_skill_search_tools
from cleveragents.tool.registry import ToolRegistry
from cleveragents.tool.runner import ToolRunner

registry = ToolRegistry()
register_skill_search_tools(registry)
runner = ToolRunner(registry)

result = runner.execute("skill/list-dir", {
    "path": "src",
    "type_filter": "files",
    "ignore_patterns": ["__pycache__", "*.pyc"],
    "sandbox_root": "/project",
})
for entry in result.output["entries"]:
    print(f"{entry['name']} ({entry['size']} bytes)")
```

## Glob

Matches files recursively by glob pattern with:

- **Exclude patterns**: Filter out unwanted matches.
- **Include patterns**: Restrict matches to specific names.
- **Deterministic sorting**: Results are always sorted by path.
- **Size limit**: Cap the number of matches returned.

### Example

```python
result = runner.execute("skill/glob", {
    "path": ".",
    "pattern": "**/*.py",
    "exclude_patterns": ["test_*"],
    "sandbox_root": "/project",
})
```

## Grep

Searches file contents with regex:

- **Context lines**: Include N lines before and after each match.
- **Binary file skipping**: Binary files are automatically skipped.
- **Per-file match cap**: Limit matches per file (default: 50).
- **Total match limit**: Overall cap on matches (default: 500).
- **Regex error handling**: Invalid patterns produce clear error messages.

### Example

```python
result = runner.execute("skill/grep", {
    "path": "src",
    "pattern": "def\\s+\\w+",
    "include": "*.py",
    "context_lines": 2,
    "sandbox_root": "/project",
})
for match in result.output["matches"]:
    print(f"{match['file']}:{match['line']}: {match['content']}")
```

### Regex Error Handling

When an invalid regex pattern is provided, Grep returns a clear error:

```
ValueError: Invalid regex pattern '[unclosed': unterminated character set
```

### Binary File Behavior

Grep automatically detects and skips binary files using the same
heuristics as the ReadFile tool (signature detection + control character
analysis).  Binary files are silently excluded from results.
