2980b14e7b
Add ~60 new behave scenarios across output rendering, tool router, file ops, and skill search features targeting uncovered lines and branches. Key additions: - ElementHandle validation guards (empty id, type, negative index, None args) - Handle close/context-manager edge cases - Table list-row and batch-row coverage - Color/box-draw/JSON/YAML materializer edge cases - Format selection paths (detect capabilities, explicit flag, empty format) - Session ColumnDef, double-close guard, force-close open handles - Tool router schema export and provider format scenarios - File ops edge cases and search stat-failure/glob-include tests All nox checks pass: lint, typecheck (0 errors), unit_tests (4730 scenarios), coverage_report (97.0% >= 97% threshold).
250 lines
11 KiB
Gherkin
250 lines
11 KiB
Gherkin
Feature: Skill Search and Directory Tools
|
|
As a developer
|
|
I want skill-level directory listing and search tools
|
|
So that agents can list directories, match files by glob, and
|
|
search file contents with regex
|
|
|
|
# ---- ListDir ----
|
|
|
|
Scenario: ListDir lists directory contents
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "a.txt" with content "a"
|
|
And a search sandbox file "b.py" with content "b"
|
|
When I list-dir "." in the search sandbox
|
|
Then the search tool result should be successful
|
|
And the search output "total" should be greater than or equal to 2
|
|
And the search output "truncated" should be boolean false
|
|
|
|
Scenario: ListDir with type filter for files only
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "file.txt" with content "content"
|
|
And a search sandbox subdirectory "subdir"
|
|
When I list-dir-typed "." with type_filter "files"
|
|
Then the search tool result should be successful
|
|
And all search entries should not be directories
|
|
|
|
Scenario: ListDir with type filter for dirs only
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "file.txt" with content "content"
|
|
And a search sandbox subdirectory "subdir"
|
|
When I list-dir-typed "." with type_filter "dirs"
|
|
Then the search tool result should be successful
|
|
And all search entries should be directories
|
|
|
|
Scenario: ListDir with ignore patterns
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "keep.txt" with content "keep"
|
|
And a search sandbox file "ignore.log" with content "ignore"
|
|
When I list-dir-ignoring "." ignoring "*.log"
|
|
Then the search tool result should be successful
|
|
And no search entry should have name "ignore.log"
|
|
|
|
Scenario: ListDir with size limit
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "1.txt" with content "one"
|
|
And a search sandbox file "2.txt" with content "two"
|
|
And a search sandbox file "3.txt" with content "three"
|
|
When I list-dir-limited "." with limit 2
|
|
Then the search tool result should be successful
|
|
And the search output "truncated" should be boolean true
|
|
And the search output "total" should equal 2
|
|
|
|
Scenario: ListDir on non-directory raises error
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "notadir.txt" with content "x"
|
|
When I list-dir "notadir.txt" in the search sandbox
|
|
Then the search tool result should not be successful
|
|
And the search tool error should mention "not a directory"
|
|
|
|
Scenario: ListDir with invalid type_filter raises error
|
|
Given a skill search sandbox directory
|
|
When I list-dir-typed "." with type_filter "invalid"
|
|
Then the search tool result should not be successful
|
|
And the search tool error should mention "Invalid type_filter"
|
|
|
|
# ---- Glob ----
|
|
|
|
Scenario: Glob matches files by pattern
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "main.py" with content "code"
|
|
And a search sandbox file "test.py" with content "test"
|
|
And a search sandbox file "readme.md" with content "docs"
|
|
When I glob-match "." for "*.py"
|
|
Then the search tool result should be successful
|
|
And the search glob total should be 2
|
|
|
|
Scenario: Glob with exclude patterns
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "main.py" with content "code"
|
|
And a search sandbox file "test.py" with content "test"
|
|
When I glob-exclude "." for "*.py" excluding "test*"
|
|
Then the search tool result should be successful
|
|
And the search glob total should be 1
|
|
And no search match should have name "test.py"
|
|
|
|
Scenario: Glob with include filter
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "main.py" with content "code"
|
|
And a search sandbox file "test.py" with content "test"
|
|
When I glob-include "." for "*" including "*.py"
|
|
Then the search tool result should be successful
|
|
And the search glob total should be at least 2
|
|
|
|
Scenario: Glob returns deterministic sorted results
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "c.txt" with content "c"
|
|
And a search sandbox file "a.txt" with content "a"
|
|
And a search sandbox file "b.txt" with content "b"
|
|
When I glob-match "." for "*.txt"
|
|
Then the search tool result should be successful
|
|
And the search glob results should be sorted by name
|
|
|
|
Scenario: Glob on non-directory raises error
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "notadir.txt" with content "x"
|
|
When I glob-match "notadir.txt" for "*.txt"
|
|
Then the search tool result should not be successful
|
|
And the search tool error should mention "not a directory"
|
|
|
|
Scenario: Glob with limit truncation
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "1.txt" with content "one"
|
|
And a search sandbox file "2.txt" with content "two"
|
|
And a search sandbox file "3.txt" with content "three"
|
|
When I glob-limited "." for "*.txt" with limit 2
|
|
Then the search tool result should be successful
|
|
And the search output "truncated" should be boolean true
|
|
|
|
# ---- Grep ----
|
|
|
|
Scenario: Grep finds matching lines
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "haystack.txt" with content "needle in a haystack"
|
|
When I grep-search "." for "needle"
|
|
Then the search tool result should be successful
|
|
And the search grep total should be at least 1
|
|
|
|
Scenario: Grep with context lines
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "context.txt" with lines "before,match,after"
|
|
When I grep-context "." for "match" with context 1
|
|
Then the search tool result should be successful
|
|
And the search grep first match should have context_before
|
|
And the search grep first match should have context_after
|
|
|
|
Scenario: Grep skips binary files
|
|
Given a skill search sandbox directory
|
|
And a search sandbox binary file "data.bin"
|
|
And a search sandbox file "text.txt" with content "findme"
|
|
When I grep-search "." for "findme"
|
|
Then the search tool result should be successful
|
|
And the search grep matches should not reference "data.bin"
|
|
|
|
Scenario: Grep with per-file match cap
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "many.txt" with lines "match,match,match,match,match"
|
|
When I grep-capped "." for "match" with per_file_cap 2
|
|
Then the search tool result should be successful
|
|
And the search grep total should equal 2
|
|
|
|
Scenario: Grep with total match limit
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "m1.txt" with lines "hit,hit,hit"
|
|
And a search sandbox file "m2.txt" with lines "hit,hit,hit"
|
|
When I grep-maxed "." for "hit" with max_matches 3
|
|
Then the search tool result should be successful
|
|
And the search grep total should equal 3
|
|
And the search output "truncated" should be boolean true
|
|
|
|
Scenario: Grep with invalid regex raises error
|
|
Given a skill search sandbox directory
|
|
When I grep-search "." for "[invalid"
|
|
Then the search tool result should not be successful
|
|
And the search tool error should mention "Invalid regex"
|
|
|
|
Scenario: Grep on non-directory raises error
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "notadir.txt" with content "x"
|
|
When I grep-search "notadir.txt" for "x"
|
|
Then the search tool result should not be successful
|
|
And the search tool error should mention "not a directory"
|
|
|
|
# ---- Path Traversal ----
|
|
|
|
Scenario: Path traversal in list-dir is rejected
|
|
Given a skill search sandbox directory
|
|
When I list-dir "../../etc" in the search sandbox
|
|
Then the search tool result should not be successful
|
|
And the search tool error should mention "traversal"
|
|
|
|
Scenario: Path traversal in glob is rejected
|
|
Given a skill search sandbox directory
|
|
When I glob-match "../../etc" for "*"
|
|
Then the search tool result should not be successful
|
|
And the search tool error should mention "traversal"
|
|
|
|
Scenario: Path traversal in grep is rejected
|
|
Given a skill search sandbox directory
|
|
When I grep-search "../../etc" for "root"
|
|
Then the search tool result should not be successful
|
|
And the search tool error should mention "traversal"
|
|
|
|
# ---- Read-Only Capability ----
|
|
|
|
Scenario: ListDir has read_only capability
|
|
Given a skill search tools registry
|
|
Then the search tool "skill/list-dir" should have read_only capability
|
|
|
|
Scenario: Glob has read_only capability
|
|
Given a skill search tools registry
|
|
Then the search tool "skill/glob" should have read_only capability
|
|
|
|
Scenario: Grep has read_only capability
|
|
Given a skill search tools registry
|
|
Then the search tool "skill/grep" should have read_only capability
|
|
|
|
# ---- Registration ----
|
|
|
|
Scenario: Register all skill search tools
|
|
Given a skill search tools registry
|
|
Then the search registry should contain 3 tools
|
|
And the search registry should contain tool "skill/list-dir"
|
|
And the search registry should contain tool "skill/glob"
|
|
And the search registry should contain tool "skill/grep"
|
|
|
|
# ---- Coverage: OSError fallbacks, include patterns, directory skip ----
|
|
|
|
Scenario: ListDir handles stat failure gracefully
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "statfail.txt" with content "test"
|
|
When I execute list-dir with a stat failure mock
|
|
Then the list-dir result should contain "statfail.txt" with size 0
|
|
|
|
Scenario: Glob filters by include pattern
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "keep.txt" with content "a"
|
|
And a search sandbox file "skip.py" with content "b"
|
|
When I execute glob with pattern "*" and include "*.txt"
|
|
Then the glob results should contain "keep.txt"
|
|
And the glob results should not contain "skip.py"
|
|
|
|
Scenario: Glob handles stat failure gracefully
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "globstat.txt" with content "test"
|
|
When I execute glob with a stat failure mock and pattern "*.txt"
|
|
Then the glob result should contain "globstat.txt" with size 0
|
|
|
|
Scenario: Grep skips directories in rglob
|
|
Given a skill search sandbox directory
|
|
And a search sandbox subdirectory "subdir"
|
|
And a search sandbox file "subdir/match.txt" with content "findme here"
|
|
When I execute grep with pattern "findme" and include "*"
|
|
Then the grep results should contain "match.txt"
|
|
And the grep results should not contain a directory entry
|
|
|
|
Scenario: Grep skips unreadable files gracefully
|
|
Given a skill search sandbox directory
|
|
And a search sandbox file "unreadable.txt" with content "content"
|
|
When I execute grep with a read_text failure mock and pattern "content"
|
|
Then the grep result should be empty
|