feat: project directory tree + first-round snapshot
- cloud/tree.py: structured worktree tree (dirs first, ignore
.git/node_modules, symlinks not followed, entry/depth
truncation flags, text render for injection)
- GET /tasks/{id}/tree with /files auth + worktree resolution,
max_depth 1..5, max_entries 1..500, escape 400, missing 404,
cloud/cross-host 409
- MCP get_task_tree translating to the new endpoint
- worker first-round injection of a repo-checkout top-level
snapshot (2 layers, 120 entries) after repo resolution,
labelled as checkout snapshot not worktree; skipped for
coordinator/resume/fork, silent when no host source
- docs/project-tree.md; tests for tree, endpoint, injection,
MCP translation
This commit is contained in:
1 parent
85f280e5e7
commit
ce7fb3afd6
9 files changed
+865
No files matched your search
@@ -21,6 +21,7 @@ contributor 等权限校验,403 原样透传。
|
||||
工具(不包管理端):
|
||||
health、list_projects、list_tasks、get_task、create_task、
|
||||
cancel_task、get_task_cost、get_task_trace、get_task_files、
|
||||
get_task_tree、
|
||||
get_task_result_full、retry_task、merge_task。
|
||||
create_task 只传 project_id + prompt + 可选 executor/model/
|
||||
effort/approval/priority,其余字段走服务端缺省;cancel_task
|
||||
@@ -82,6 +83,11 @@ TOOLS = [
|
||||
_tool("get_task_files", "列/读任务 worktree 里的文件;不传 path 列顶层,传相对路径读文件或列目录。",
|
||||
{"task_id": {"type": "string"},
|
||||
"path": {"type": "string"}}, ["task_id"]),
|
||||
_tool("get_task_tree", "取任务 worktree 的结构化目录树(只含名、类型、大小,不读文件内容);不传 path 列 worktree 根,传相对路径列该子目录;max_depth 1..5 缺省 3,max_entries 1..500 缺省 200,截断时 truncated=True。",
|
||||
{"task_id": {"type": "string"},
|
||||
"path": {"type": "string"},
|
||||
"max_depth": {"type": "integer"},
|
||||
"max_entries": {"type": "integer"}}, ["task_id"]),
|
||||
_tool("get_task_result_full", "取单个任务的完整结果文本(result_text 只是摘要)。",
|
||||
{"task_id": {"type": "string"}}, ["task_id"]),
|
||||
_tool("retry_task", "重试一个失败/中断的任务(服务端状态机校验)。",
|
||||
@@ -201,6 +207,20 @@ class MCPServer:
|
||||
path += "?" + urllib.parse.urlencode(
|
||||
{"path": args["path"]})
|
||||
return self._wrap(*self.transport("GET", path))
|
||||
if name == "get_task_tree":
|
||||
if not args.get("task_id"):
|
||||
return True, {"error": "task_id required"}
|
||||
query = {}
|
||||
if args.get("path"):
|
||||
query["path"] = args["path"]
|
||||
if args.get("max_depth") is not None:
|
||||
query["max_depth"] = args["max_depth"]
|
||||
if args.get("max_entries") is not None:
|
||||
query["max_entries"] = args["max_entries"]
|
||||
path = "/tasks/" + args["task_id"] + "/tree"
|
||||
if query:
|
||||
path += "?" + urllib.parse.urlencode(query)
|
||||
return self._wrap(*self.transport("GET", path))
|
||||
if name == "get_task_result_full":
|
||||
if not args.get("task_id"):
|
||||
return True, {"error": "task_id required"}
|
||||
|
||||
Reference in new issue
Block a user