mirror of
https://github.com/shareAI-lab/analysis_claude_code.git
synced 2026-09-21 12:53:37 +08:00
fix: support bounded recursive glob across lessons
This commit is contained in:
@@ -74,7 +74,12 @@ def run_edit(path, old_text, new_text):
|
||||
|
||||
def run_glob(pattern):
|
||||
import glob as g
|
||||
return "\n".join(g.glob(pattern, root_dir=WORKDIR))
|
||||
matches = sorted(set(g.glob(
|
||||
pattern, root_dir=WORKDIR, recursive=True)))
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -74,7 +74,12 @@ def run_edit(path, old_text, new_text):
|
||||
|
||||
def run_glob(pattern):
|
||||
import glob as g
|
||||
return "\n".join(g.glob(pattern, root_dir=WORKDIR))
|
||||
matches = sorted(set(g.glob(
|
||||
pattern, root_dir=WORKDIR, recursive=True)))
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -74,7 +74,12 @@ def run_edit(path, old_text, new_text):
|
||||
|
||||
def run_glob(pattern):
|
||||
import glob as g
|
||||
return "\n".join(g.glob(pattern, root_dir=WORKDIR))
|
||||
matches = sorted(set(g.glob(
|
||||
pattern, root_dir=WORKDIR, recursive=True)))
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -110,11 +110,15 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
||||
def run_glob(pattern: str) -> str:
|
||||
import glob as g
|
||||
try:
|
||||
results = []
|
||||
for match in g.glob(pattern, root_dir=WORKDIR):
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR):
|
||||
results.append(match)
|
||||
return "\n".join(results) if results else "(no matches)"
|
||||
matches = sorted({
|
||||
match for match in g.glob(
|
||||
pattern, root_dir=WORKDIR, recursive=True)
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||
})
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) if shown else "(no matches)"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -130,7 +134,7 @@ TOOLS = [
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
||||
{"name": "edit_file", "description": "Replace exact text in a file once.",
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files matching a glob pattern.",
|
||||
{"name": "glob", "description": "Find files matching a glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object", "properties": {"pattern": {"type": "string"}}, "required": ["pattern"]}},
|
||||
]
|
||||
|
||||
|
||||
@@ -105,11 +105,15 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
||||
def run_glob(pattern: str) -> str:
|
||||
import glob as g
|
||||
try:
|
||||
results = []
|
||||
for match in g.glob(pattern, root_dir=WORKDIR):
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR):
|
||||
results.append(match)
|
||||
return "\n".join(results) if results else "(no matches)"
|
||||
matches = sorted({
|
||||
match for match in g.glob(
|
||||
pattern, root_dir=WORKDIR, recursive=True)
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||
})
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) if shown else "(no matches)"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -125,7 +129,7 @@ TOOLS = [
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
||||
{"name": "edit_file", "description": "Replace exact text in a file once.",
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files matching a glob pattern.",
|
||||
{"name": "glob", "description": "Find files matching a glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object", "properties": {"pattern": {"type": "string"}}, "required": ["pattern"]}},
|
||||
]
|
||||
|
||||
|
||||
@@ -91,11 +91,15 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
||||
def run_glob(pattern: str) -> str:
|
||||
import glob as g
|
||||
try:
|
||||
results = []
|
||||
for match in g.glob(pattern, root_dir=WORKDIR):
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR):
|
||||
results.append(match)
|
||||
return "\n".join(results) if results else "(no matches)"
|
||||
matches = sorted({
|
||||
match for match in g.glob(
|
||||
pattern, root_dir=WORKDIR, recursive=True)
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||
})
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) if shown else "(no matches)"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -108,7 +112,7 @@ TOOLS = [
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
||||
{"name": "edit_file", "description": "Replace exact text in a file once.",
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files matching a glob pattern.",
|
||||
{"name": "glob", "description": "Find files matching a glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object", "properties": {"pattern": {"type": "string"}}, "required": ["pattern"]}},
|
||||
]
|
||||
|
||||
|
||||
@@ -96,11 +96,15 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
||||
def run_glob(pattern: str) -> str:
|
||||
import glob as g
|
||||
try:
|
||||
results = []
|
||||
for match in g.glob(pattern, root_dir=WORKDIR):
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR):
|
||||
results.append(match)
|
||||
return "\n".join(results) if results else "(no matches)"
|
||||
matches = sorted({
|
||||
match for match in g.glob(
|
||||
pattern, root_dir=WORKDIR, recursive=True)
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||
})
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) if shown else "(no matches)"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -186,7 +190,7 @@ TOOLS = [
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
||||
{"name": "edit_file", "description": "Replace exact text in a file once.",
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files matching a glob pattern.",
|
||||
{"name": "glob", "description": "Find files matching a glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object", "properties": {"pattern": {"type": "string"}}, "required": ["pattern"]}},
|
||||
# s05: new tool
|
||||
{"name": "todo_write", "description": "Create and manage a task list for your current coding session.",
|
||||
|
||||
@@ -101,11 +101,15 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
||||
def run_glob(pattern: str) -> str:
|
||||
import glob
|
||||
try:
|
||||
matches = []
|
||||
for match in glob.glob(pattern, root_dir=WORKDIR):
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR):
|
||||
matches.append(match)
|
||||
return "\n".join(matches) if matches else "(no matches)"
|
||||
matches = sorted({
|
||||
match for match in glob.glob(
|
||||
pattern, root_dir=WORKDIR, recursive=True)
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||
})
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) if shown else "(no matches)"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -119,7 +123,7 @@ BASE_TOOLS = [
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
||||
{"name": "edit_file", "description": "Replace exact text in a file once.",
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files matching a glob pattern.",
|
||||
{"name": "glob", "description": "Find files matching a glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object", "properties": {"pattern": {"type": "string"}}, "required": ["pattern"]}},
|
||||
]
|
||||
|
||||
|
||||
@@ -185,11 +185,15 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
||||
def run_glob(pattern: str) -> str:
|
||||
import glob
|
||||
try:
|
||||
matches = []
|
||||
for match in glob.glob(pattern, root_dir=WORKDIR):
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR):
|
||||
matches.append(match)
|
||||
return "\n".join(matches) if matches else "(no matches)"
|
||||
matches = sorted({
|
||||
match for match in glob.glob(
|
||||
pattern, root_dir=WORKDIR, recursive=True)
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||
})
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) if shown else "(no matches)"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -203,7 +207,7 @@ TOOLS = [
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
||||
{"name": "edit_file", "description": "Replace exact text in a file once.",
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files matching a glob pattern.",
|
||||
{"name": "glob", "description": "Find files matching a glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object", "properties": {"pattern": {"type": "string"}}, "required": ["pattern"]}},
|
||||
{"name": "load_skill", "description": "Load the full SKILL.md content by skill name.",
|
||||
"input_schema": {"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}},
|
||||
|
||||
@@ -118,11 +118,14 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
||||
|
||||
def run_glob(pattern: str) -> str:
|
||||
try:
|
||||
matches = [
|
||||
matches = sorted({
|
||||
match for match in glob.glob(pattern, root_dir=WORKDIR, recursive=True)
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||
]
|
||||
return "\n".join(matches) if matches else "(no matches)"
|
||||
})
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) if shown else "(no matches)"
|
||||
except Exception as error:
|
||||
return f"Error: {error}"
|
||||
|
||||
@@ -136,7 +139,7 @@ BASE_TOOLS = [
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
||||
{"name": "edit_file", "description": "Replace exact text in a file once.",
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files matching a glob pattern.",
|
||||
{"name": "glob", "description": "Find files matching a glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object", "properties": {"pattern": {"type": "string"}}, "required": ["pattern"]}},
|
||||
]
|
||||
COMPACT_TOOL = {
|
||||
|
||||
@@ -579,12 +579,15 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
||||
|
||||
def run_glob(pattern: str) -> str:
|
||||
try:
|
||||
matches = [
|
||||
matches = sorted({
|
||||
match
|
||||
for match in glob.glob(pattern, root_dir=WORKDIR)
|
||||
for match in glob.glob(pattern, root_dir=WORKDIR, recursive=True)
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||
]
|
||||
return "\n".join(matches) if matches else "(no matches)"
|
||||
})
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) if shown else "(no matches)"
|
||||
except Exception as error:
|
||||
return f"Error: {error}"
|
||||
|
||||
@@ -597,7 +600,7 @@ TOOLS = [
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
||||
{"name": "edit_file", "description": "Replace exact text in a file once.",
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files matching a glob pattern.",
|
||||
{"name": "glob", "description": "Find files matching a glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object", "properties": {"pattern": {"type": "string"}}, "required": ["pattern"]}},
|
||||
]
|
||||
|
||||
|
||||
@@ -325,12 +325,15 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
||||
|
||||
def run_glob(pattern: str) -> str:
|
||||
try:
|
||||
matches = [
|
||||
matches = sorted({
|
||||
match
|
||||
for match in glob.glob(pattern, root_dir=WORKDIR)
|
||||
for match in glob.glob(pattern, root_dir=WORKDIR, recursive=True)
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||
]
|
||||
return "\n".join(matches) if matches else "(no matches)"
|
||||
})
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) if shown else "(no matches)"
|
||||
except Exception as error:
|
||||
return f"Error: {error}"
|
||||
|
||||
@@ -392,7 +395,7 @@ TOOLS = [
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
||||
{"name": "edit_file", "description": "Replace exact text in a file once.",
|
||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "old_text": {"type": "string"}, "new_text": {"type": "string"}}, "required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files matching a glob pattern.",
|
||||
{"name": "glob", "description": "Find files matching a glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object", "properties": {"pattern": {"type": "string"}}, "required": ["pattern"]}},
|
||||
{"name": "create_task", "description": "Create a task and return its runtime-generated ID.",
|
||||
"input_schema": {"type": "object", "properties": {"subject": {"type": "string"}, "description": {"type": "string"}}, "required": ["subject"], "additionalProperties": False}},
|
||||
|
||||
@@ -156,12 +156,15 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
||||
|
||||
def run_glob(pattern: str) -> str:
|
||||
try:
|
||||
matches = [
|
||||
matches = sorted({
|
||||
match
|
||||
for match in glob.glob(pattern, root_dir=WORKDIR)
|
||||
for match in glob.glob(pattern, root_dir=WORKDIR, recursive=True)
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||
]
|
||||
return "\n".join(matches) if matches else "(no matches)"
|
||||
})
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) if shown else "(no matches)"
|
||||
except Exception as error:
|
||||
return f"Error: {error}"
|
||||
|
||||
@@ -189,7 +192,7 @@ TOOLS = [
|
||||
"old_text": {"type": "string"},
|
||||
"new_text": {"type": "string"}},
|
||||
"required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files matching a glob pattern.",
|
||||
{"name": "glob", "description": "Find files matching a glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object",
|
||||
"properties": {"pattern": {"type": "string"}},
|
||||
"required": ["pattern"]}},
|
||||
|
||||
@@ -106,12 +106,15 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
||||
|
||||
def run_glob(pattern: str) -> str:
|
||||
try:
|
||||
matches = [
|
||||
matches = sorted({
|
||||
match
|
||||
for match in glob.glob(pattern, root_dir=WORKDIR)
|
||||
for match in glob.glob(pattern, root_dir=WORKDIR, recursive=True)
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||
]
|
||||
return "\n".join(matches) if matches else "(no matches)"
|
||||
})
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) if shown else "(no matches)"
|
||||
except Exception as error:
|
||||
return f"Error: {error}"
|
||||
|
||||
@@ -137,7 +140,7 @@ TOOLS = [
|
||||
"old_text": {"type": "string"},
|
||||
"new_text": {"type": "string"}},
|
||||
"required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files matching a glob pattern.",
|
||||
{"name": "glob", "description": "Find files matching a glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object",
|
||||
"properties": {"pattern": {"type": "string"}},
|
||||
"required": ["pattern"]}},
|
||||
|
||||
@@ -727,7 +727,10 @@ def run_glob(pattern: str, cwd: Path | None = None) -> str:
|
||||
for path in sorted(base.glob(pattern))
|
||||
if path.resolve().is_relative_to(base)
|
||||
]
|
||||
return "\n".join(matches[:200]) or "No files found"
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) or "No files found"
|
||||
except Exception as exc:
|
||||
return f"Error: {exc}"
|
||||
|
||||
@@ -1519,7 +1522,7 @@ BASE_TOOLS = [
|
||||
"old_text": {"type": "string"},
|
||||
"new_text": {"type": "string"}},
|
||||
"required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files by glob pattern.",
|
||||
{"name": "glob", "description": "Find files by glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object",
|
||||
"properties": {"pattern": {"type": "string"}},
|
||||
"required": ["pattern"]}},
|
||||
|
||||
@@ -109,12 +109,15 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
||||
|
||||
def run_glob(pattern: str) -> str:
|
||||
try:
|
||||
matches = [
|
||||
matches = sorted({
|
||||
match
|
||||
for match in glob.glob(pattern, root_dir=WORKDIR)
|
||||
for match in glob.glob(pattern, root_dir=WORKDIR, recursive=True)
|
||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR.resolve())
|
||||
]
|
||||
return "\n".join(matches[:200]) if matches else "(no matches)"
|
||||
})
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) if shown else "(no matches)"
|
||||
except Exception as exc:
|
||||
return f"Error: {exc}"
|
||||
|
||||
@@ -140,7 +143,7 @@ BASE_TOOLS = [
|
||||
"old_text": {"type": "string"},
|
||||
"new_text": {"type": "string"}},
|
||||
"required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files by glob pattern.",
|
||||
{"name": "glob", "description": "Find files by glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object",
|
||||
"properties": {"pattern": {"type": "string"}},
|
||||
"required": ["pattern"]}},
|
||||
|
||||
@@ -972,11 +972,15 @@ def run_glob(pattern: str, cwd: Path | None = None) -> str:
|
||||
import glob as g
|
||||
try:
|
||||
base = (cwd or WORKDIR).resolve()
|
||||
results = []
|
||||
for match in g.glob(pattern, root_dir=base):
|
||||
if (base / match).resolve().is_relative_to(base):
|
||||
results.append(match)
|
||||
return "\n".join(results) if results else "(no matches)"
|
||||
matches = sorted({
|
||||
match for match in g.glob(
|
||||
pattern, root_dir=base, recursive=True)
|
||||
if (base / match).resolve().is_relative_to(base)
|
||||
})
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) if shown else "(no matches)"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -1487,7 +1491,7 @@ def spawn_teammate_thread(name: str, role: str, prompt: str,
|
||||
"old_text": {"type": "string"},
|
||||
"new_text": {"type": "string"}},
|
||||
"required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files by glob pattern.",
|
||||
{"name": "glob", "description": "Find files by glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object",
|
||||
"properties": {
|
||||
"pattern": {"type": "string"}},
|
||||
@@ -1849,7 +1853,7 @@ SUB_TOOLS = [
|
||||
"old_text": {"type": "string"},
|
||||
"new_text": {"type": "string"}},
|
||||
"required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files matching a glob pattern.",
|
||||
{"name": "glob", "description": "Find files matching a glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object",
|
||||
"properties": {"pattern": {"type": "string"}},
|
||||
"required": ["pattern"]}},
|
||||
@@ -2776,7 +2780,7 @@ BUILTIN_TOOLS = [
|
||||
"old_text": {"type": "string"},
|
||||
"new_text": {"type": "string"}},
|
||||
"required": ["path", "old_text", "new_text"]}},
|
||||
{"name": "glob", "description": "Find files matching a glob pattern.",
|
||||
{"name": "glob", "description": "Find files matching a glob pattern; ** matches recursively.",
|
||||
"input_schema": {"type": "object",
|
||||
"properties": {"pattern": {"type": "string"}},
|
||||
"required": ["pattern"]}},
|
||||
|
||||
@@ -515,7 +515,7 @@ TOOLS = [
|
||||
},
|
||||
{
|
||||
"name": "glob",
|
||||
"description": "Find files matching a glob pattern.",
|
||||
"description": "Find files matching a glob pattern; ** matches recursively.",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {"pattern": {"type": "string"}},
|
||||
@@ -797,12 +797,16 @@ class AgentSession:
|
||||
return f"Edited {path.relative_to(self.workdir)}"
|
||||
|
||||
if name == "glob":
|
||||
matches = [
|
||||
matches = sorted({
|
||||
match
|
||||
for match in glob.glob(str(arguments["pattern"]), root_dir=self.workdir)
|
||||
for match in glob.glob(
|
||||
str(arguments["pattern"]), root_dir=self.workdir, recursive=True)
|
||||
if (self.workdir / match).resolve().is_relative_to(self.workdir)
|
||||
]
|
||||
return "\n".join(matches[:200]) if matches else "(no matches)"
|
||||
})
|
||||
shown = matches[:200]
|
||||
if len(matches) > 200:
|
||||
shown.append("... (more matches omitted; narrow the pattern)")
|
||||
return "\n".join(shown) if shown else "(no matches)"
|
||||
|
||||
raise GoalError(f"unknown tool '{name}'")
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ LESSONS = tuple(
|
||||
)
|
||||
)
|
||||
INTEGRATED_LESSON = ROOT / "s15_integrated_harness" / "code.py"
|
||||
GOAL_LESSON = ROOT / "s17_goal_loop" / "code.py"
|
||||
GLOB_LESSONS = (*LESSONS[1:], INTEGRATED_LESSON, GOAL_LESSON)
|
||||
|
||||
|
||||
class FakeMessagesApi:
|
||||
@@ -137,6 +139,42 @@ def bash_tool_call():
|
||||
)
|
||||
|
||||
|
||||
def run_glob_tool(lesson, workdir: Path, pattern: str) -> str:
|
||||
if hasattr(lesson, "run_glob"):
|
||||
return lesson.run_glob(pattern)
|
||||
session = object.__new__(lesson.AgentSession)
|
||||
session.workdir = workdir.resolve()
|
||||
return session._run_tool("glob", {"pattern": pattern})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("lesson_path", GLOB_LESSONS,
|
||||
ids=lambda path: path.parent.name)
|
||||
def test_glob_double_star_matches_files_at_any_depth(
|
||||
tmp_path: Path, lesson_path: Path):
|
||||
(tmp_path / "root.py").write_text("")
|
||||
(tmp_path / "one" / "two").mkdir(parents=True)
|
||||
(tmp_path / "one" / "one.py").write_text("")
|
||||
(tmp_path / "one" / "two" / "deep.py").write_text("")
|
||||
lesson = load_lesson(tmp_path, lesson_path)
|
||||
|
||||
matches = set(run_glob_tool(lesson, tmp_path, "**/*.py").splitlines())
|
||||
|
||||
assert matches == {"root.py", "one/one.py", "one/two/deep.py"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("lesson_path", GLOB_LESSONS,
|
||||
ids=lambda path: path.parent.name)
|
||||
def test_glob_caps_large_result_sets(tmp_path: Path, lesson_path: Path):
|
||||
for index in range(205):
|
||||
(tmp_path / f"file-{index:03}.txt").write_text("")
|
||||
lesson = load_lesson(tmp_path, lesson_path)
|
||||
|
||||
lines = run_glob_tool(lesson, tmp_path, "*.txt").splitlines()
|
||||
|
||||
assert len(lines) == 201
|
||||
assert lines[-1] == "... (more matches omitted; narrow the pattern)"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("lesson_path", LESSONS, ids=lambda path: path.parent.name)
|
||||
@pytest.mark.parametrize("content", ([], None), ids=("empty-content", "empty-text"))
|
||||
def test_parent_loop_does_not_append_an_empty_tool_result_turn(
|
||||
|
||||
Reference in New Issue
Block a user