mirror of
https://github.com/shareAI-lab/analysis_claude_code.git
synced 2026-09-20 12:13:38 +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):
|
def run_glob(pattern):
|
||||||
import glob as g
|
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):
|
def run_glob(pattern):
|
||||||
import glob as g
|
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):
|
def run_glob(pattern):
|
||||||
import glob as g
|
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:
|
def run_glob(pattern: str) -> str:
|
||||||
import glob as g
|
import glob as g
|
||||||
try:
|
try:
|
||||||
results = []
|
matches = sorted({
|
||||||
for match in g.glob(pattern, root_dir=WORKDIR):
|
match for match in g.glob(
|
||||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR):
|
pattern, root_dir=WORKDIR, recursive=True)
|
||||||
results.append(match)
|
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||||
return "\n".join(results) if results 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 e:
|
except Exception as e:
|
||||||
return f"Error: {e}"
|
return f"Error: {e}"
|
||||||
|
|
||||||
@@ -130,7 +134,7 @@ TOOLS = [
|
|||||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
"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.",
|
{"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"]}},
|
"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"]}},
|
"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:
|
def run_glob(pattern: str) -> str:
|
||||||
import glob as g
|
import glob as g
|
||||||
try:
|
try:
|
||||||
results = []
|
matches = sorted({
|
||||||
for match in g.glob(pattern, root_dir=WORKDIR):
|
match for match in g.glob(
|
||||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR):
|
pattern, root_dir=WORKDIR, recursive=True)
|
||||||
results.append(match)
|
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||||
return "\n".join(results) if results 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 e:
|
except Exception as e:
|
||||||
return f"Error: {e}"
|
return f"Error: {e}"
|
||||||
|
|
||||||
@@ -125,7 +129,7 @@ TOOLS = [
|
|||||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
"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.",
|
{"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"]}},
|
"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"]}},
|
"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:
|
def run_glob(pattern: str) -> str:
|
||||||
import glob as g
|
import glob as g
|
||||||
try:
|
try:
|
||||||
results = []
|
matches = sorted({
|
||||||
for match in g.glob(pattern, root_dir=WORKDIR):
|
match for match in g.glob(
|
||||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR):
|
pattern, root_dir=WORKDIR, recursive=True)
|
||||||
results.append(match)
|
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||||
return "\n".join(results) if results 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 e:
|
except Exception as e:
|
||||||
return f"Error: {e}"
|
return f"Error: {e}"
|
||||||
|
|
||||||
@@ -108,7 +112,7 @@ TOOLS = [
|
|||||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
"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.",
|
{"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"]}},
|
"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"]}},
|
"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:
|
def run_glob(pattern: str) -> str:
|
||||||
import glob as g
|
import glob as g
|
||||||
try:
|
try:
|
||||||
results = []
|
matches = sorted({
|
||||||
for match in g.glob(pattern, root_dir=WORKDIR):
|
match for match in g.glob(
|
||||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR):
|
pattern, root_dir=WORKDIR, recursive=True)
|
||||||
results.append(match)
|
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
||||||
return "\n".join(results) if results 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 e:
|
except Exception as e:
|
||||||
return f"Error: {e}"
|
return f"Error: {e}"
|
||||||
|
|
||||||
@@ -186,7 +190,7 @@ TOOLS = [
|
|||||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
"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.",
|
{"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"]}},
|
"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"]}},
|
"input_schema": {"type": "object", "properties": {"pattern": {"type": "string"}}, "required": ["pattern"]}},
|
||||||
# s05: new tool
|
# s05: new tool
|
||||||
{"name": "todo_write", "description": "Create and manage a task list for your current coding session.",
|
{"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:
|
def run_glob(pattern: str) -> str:
|
||||||
import glob
|
import glob
|
||||||
try:
|
try:
|
||||||
matches = []
|
matches = sorted({
|
||||||
for match in glob.glob(pattern, root_dir=WORKDIR):
|
match for match in glob.glob(
|
||||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR):
|
pattern, root_dir=WORKDIR, recursive=True)
|
||||||
matches.append(match)
|
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 e:
|
except Exception as e:
|
||||||
return f"Error: {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"]}},
|
"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.",
|
{"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"]}},
|
"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"]}},
|
"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:
|
def run_glob(pattern: str) -> str:
|
||||||
import glob
|
import glob
|
||||||
try:
|
try:
|
||||||
matches = []
|
matches = sorted({
|
||||||
for match in glob.glob(pattern, root_dir=WORKDIR):
|
match for match in glob.glob(
|
||||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR):
|
pattern, root_dir=WORKDIR, recursive=True)
|
||||||
matches.append(match)
|
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 e:
|
except Exception as e:
|
||||||
return f"Error: {e}"
|
return f"Error: {e}"
|
||||||
|
|
||||||
@@ -203,7 +207,7 @@ TOOLS = [
|
|||||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
"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.",
|
{"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"]}},
|
"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"]}},
|
"input_schema": {"type": "object", "properties": {"pattern": {"type": "string"}}, "required": ["pattern"]}},
|
||||||
{"name": "load_skill", "description": "Load the full SKILL.md content by skill name.",
|
{"name": "load_skill", "description": "Load the full SKILL.md content by skill name.",
|
||||||
"input_schema": {"type": "object", "properties": {"name": {"type": "string"}}, "required": ["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:
|
def run_glob(pattern: str) -> str:
|
||||||
try:
|
try:
|
||||||
matches = [
|
matches = sorted({
|
||||||
match for match in glob.glob(pattern, root_dir=WORKDIR, recursive=True)
|
match for match in glob.glob(pattern, root_dir=WORKDIR, recursive=True)
|
||||||
if (WORKDIR / match).resolve().is_relative_to(WORKDIR)
|
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:
|
except Exception as error:
|
||||||
return f"Error: {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"]}},
|
"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.",
|
{"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"]}},
|
"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"]}},
|
"input_schema": {"type": "object", "properties": {"pattern": {"type": "string"}}, "required": ["pattern"]}},
|
||||||
]
|
]
|
||||||
COMPACT_TOOL = {
|
COMPACT_TOOL = {
|
||||||
|
|||||||
@@ -579,12 +579,15 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
|||||||
|
|
||||||
def run_glob(pattern: str) -> str:
|
def run_glob(pattern: str) -> str:
|
||||||
try:
|
try:
|
||||||
matches = [
|
matches = sorted({
|
||||||
match
|
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)
|
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:
|
except Exception as error:
|
||||||
return f"Error: {error}"
|
return f"Error: {error}"
|
||||||
|
|
||||||
@@ -597,7 +600,7 @@ TOOLS = [
|
|||||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
"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.",
|
{"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"]}},
|
"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"]}},
|
"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:
|
def run_glob(pattern: str) -> str:
|
||||||
try:
|
try:
|
||||||
matches = [
|
matches = sorted({
|
||||||
match
|
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)
|
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:
|
except Exception as error:
|
||||||
return f"Error: {error}"
|
return f"Error: {error}"
|
||||||
|
|
||||||
@@ -392,7 +395,7 @@ TOOLS = [
|
|||||||
"input_schema": {"type": "object", "properties": {"path": {"type": "string"}, "content": {"type": "string"}}, "required": ["path", "content"]}},
|
"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.",
|
{"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"]}},
|
"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"]}},
|
"input_schema": {"type": "object", "properties": {"pattern": {"type": "string"}}, "required": ["pattern"]}},
|
||||||
{"name": "create_task", "description": "Create a task and return its runtime-generated ID.",
|
{"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}},
|
"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:
|
def run_glob(pattern: str) -> str:
|
||||||
try:
|
try:
|
||||||
matches = [
|
matches = sorted({
|
||||||
match
|
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)
|
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:
|
except Exception as error:
|
||||||
return f"Error: {error}"
|
return f"Error: {error}"
|
||||||
|
|
||||||
@@ -189,7 +192,7 @@ TOOLS = [
|
|||||||
"old_text": {"type": "string"},
|
"old_text": {"type": "string"},
|
||||||
"new_text": {"type": "string"}},
|
"new_text": {"type": "string"}},
|
||||||
"required": ["path", "old_text", "new_text"]}},
|
"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",
|
"input_schema": {"type": "object",
|
||||||
"properties": {"pattern": {"type": "string"}},
|
"properties": {"pattern": {"type": "string"}},
|
||||||
"required": ["pattern"]}},
|
"required": ["pattern"]}},
|
||||||
|
|||||||
@@ -106,12 +106,15 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
|||||||
|
|
||||||
def run_glob(pattern: str) -> str:
|
def run_glob(pattern: str) -> str:
|
||||||
try:
|
try:
|
||||||
matches = [
|
matches = sorted({
|
||||||
match
|
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)
|
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:
|
except Exception as error:
|
||||||
return f"Error: {error}"
|
return f"Error: {error}"
|
||||||
|
|
||||||
@@ -137,7 +140,7 @@ TOOLS = [
|
|||||||
"old_text": {"type": "string"},
|
"old_text": {"type": "string"},
|
||||||
"new_text": {"type": "string"}},
|
"new_text": {"type": "string"}},
|
||||||
"required": ["path", "old_text", "new_text"]}},
|
"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",
|
"input_schema": {"type": "object",
|
||||||
"properties": {"pattern": {"type": "string"}},
|
"properties": {"pattern": {"type": "string"}},
|
||||||
"required": ["pattern"]}},
|
"required": ["pattern"]}},
|
||||||
|
|||||||
@@ -727,7 +727,10 @@ def run_glob(pattern: str, cwd: Path | None = None) -> str:
|
|||||||
for path in sorted(base.glob(pattern))
|
for path in sorted(base.glob(pattern))
|
||||||
if path.resolve().is_relative_to(base)
|
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:
|
except Exception as exc:
|
||||||
return f"Error: {exc}"
|
return f"Error: {exc}"
|
||||||
|
|
||||||
@@ -1519,7 +1522,7 @@ BASE_TOOLS = [
|
|||||||
"old_text": {"type": "string"},
|
"old_text": {"type": "string"},
|
||||||
"new_text": {"type": "string"}},
|
"new_text": {"type": "string"}},
|
||||||
"required": ["path", "old_text", "new_text"]}},
|
"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",
|
"input_schema": {"type": "object",
|
||||||
"properties": {"pattern": {"type": "string"}},
|
"properties": {"pattern": {"type": "string"}},
|
||||||
"required": ["pattern"]}},
|
"required": ["pattern"]}},
|
||||||
|
|||||||
@@ -109,12 +109,15 @@ def run_edit(path: str, old_text: str, new_text: str) -> str:
|
|||||||
|
|
||||||
def run_glob(pattern: str) -> str:
|
def run_glob(pattern: str) -> str:
|
||||||
try:
|
try:
|
||||||
matches = [
|
matches = sorted({
|
||||||
match
|
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())
|
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:
|
except Exception as exc:
|
||||||
return f"Error: {exc}"
|
return f"Error: {exc}"
|
||||||
|
|
||||||
@@ -140,7 +143,7 @@ BASE_TOOLS = [
|
|||||||
"old_text": {"type": "string"},
|
"old_text": {"type": "string"},
|
||||||
"new_text": {"type": "string"}},
|
"new_text": {"type": "string"}},
|
||||||
"required": ["path", "old_text", "new_text"]}},
|
"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",
|
"input_schema": {"type": "object",
|
||||||
"properties": {"pattern": {"type": "string"}},
|
"properties": {"pattern": {"type": "string"}},
|
||||||
"required": ["pattern"]}},
|
"required": ["pattern"]}},
|
||||||
|
|||||||
@@ -972,11 +972,15 @@ def run_glob(pattern: str, cwd: Path | None = None) -> str:
|
|||||||
import glob as g
|
import glob as g
|
||||||
try:
|
try:
|
||||||
base = (cwd or WORKDIR).resolve()
|
base = (cwd or WORKDIR).resolve()
|
||||||
results = []
|
matches = sorted({
|
||||||
for match in g.glob(pattern, root_dir=base):
|
match for match in g.glob(
|
||||||
if (base / match).resolve().is_relative_to(base):
|
pattern, root_dir=base, recursive=True)
|
||||||
results.append(match)
|
if (base / match).resolve().is_relative_to(base)
|
||||||
return "\n".join(results) if results 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 e:
|
except Exception as e:
|
||||||
return f"Error: {e}"
|
return f"Error: {e}"
|
||||||
|
|
||||||
@@ -1487,7 +1491,7 @@ def spawn_teammate_thread(name: str, role: str, prompt: str,
|
|||||||
"old_text": {"type": "string"},
|
"old_text": {"type": "string"},
|
||||||
"new_text": {"type": "string"}},
|
"new_text": {"type": "string"}},
|
||||||
"required": ["path", "old_text", "new_text"]}},
|
"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",
|
"input_schema": {"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"pattern": {"type": "string"}},
|
"pattern": {"type": "string"}},
|
||||||
@@ -1849,7 +1853,7 @@ SUB_TOOLS = [
|
|||||||
"old_text": {"type": "string"},
|
"old_text": {"type": "string"},
|
||||||
"new_text": {"type": "string"}},
|
"new_text": {"type": "string"}},
|
||||||
"required": ["path", "old_text", "new_text"]}},
|
"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",
|
"input_schema": {"type": "object",
|
||||||
"properties": {"pattern": {"type": "string"}},
|
"properties": {"pattern": {"type": "string"}},
|
||||||
"required": ["pattern"]}},
|
"required": ["pattern"]}},
|
||||||
@@ -2776,7 +2780,7 @@ BUILTIN_TOOLS = [
|
|||||||
"old_text": {"type": "string"},
|
"old_text": {"type": "string"},
|
||||||
"new_text": {"type": "string"}},
|
"new_text": {"type": "string"}},
|
||||||
"required": ["path", "old_text", "new_text"]}},
|
"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",
|
"input_schema": {"type": "object",
|
||||||
"properties": {"pattern": {"type": "string"}},
|
"properties": {"pattern": {"type": "string"}},
|
||||||
"required": ["pattern"]}},
|
"required": ["pattern"]}},
|
||||||
|
|||||||
@@ -515,7 +515,7 @@ TOOLS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "glob",
|
"name": "glob",
|
||||||
"description": "Find files matching a glob pattern.",
|
"description": "Find files matching a glob pattern; ** matches recursively.",
|
||||||
"input_schema": {
|
"input_schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {"pattern": {"type": "string"}},
|
"properties": {"pattern": {"type": "string"}},
|
||||||
@@ -797,12 +797,16 @@ class AgentSession:
|
|||||||
return f"Edited {path.relative_to(self.workdir)}"
|
return f"Edited {path.relative_to(self.workdir)}"
|
||||||
|
|
||||||
if name == "glob":
|
if name == "glob":
|
||||||
matches = [
|
matches = sorted({
|
||||||
match
|
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)
|
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}'")
|
raise GoalError(f"unknown tool '{name}'")
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ LESSONS = tuple(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
INTEGRATED_LESSON = ROOT / "s15_integrated_harness" / "code.py"
|
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:
|
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("lesson_path", LESSONS, ids=lambda path: path.parent.name)
|
||||||
@pytest.mark.parametrize("content", ([], None), ids=("empty-content", "empty-text"))
|
@pytest.mark.parametrize("content", ([], None), ids=("empty-content", "empty-text"))
|
||||||
def test_parent_loop_does_not_append_an_empty_tool_result_turn(
|
def test_parent_loop_does_not_append_an_empty_tool_result_turn(
|
||||||
|
|||||||
Reference in New Issue
Block a user