@scemoon/cdh 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cdh/__pycache__/__main__.cpython-314.pyc +0 -0
- package/cdh/__pycache__/cli.cpython-314.pyc +0 -0
- package/cdh/__pycache__/scaffold.cpython-314.pyc +0 -0
- package/cdh/cli.py +473 -87
- package/cdh/scaffold.py +386 -121
- package/onecode/__pycache__/cli.cpython-314.pyc +0 -0
- package/onecode/__pycache__/commands.cpython-314.pyc +0 -0
- package/onecode/__pycache__/config.cpython-314.pyc +0 -0
- package/onecode/__pycache__/config_screen.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/cdh_loader.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/engine.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/onecode_agent_acp.cpython-314.pyc +0 -0
- package/onecode/agent/__pycache__/session.cpython-314.pyc +0 -0
- package/onecode/agent/agents/__pycache__/types.cpython-314.pyc +0 -0
- package/onecode/agent/agents/types.py +123 -79
- package/onecode/agent/cdh_loader.py +26 -13
- package/onecode/agent/engine.py +426 -235
- package/onecode/agent/onecode_agent_acp.py +200 -73
- package/onecode/agent/session.py +8 -13
- package/onecode/agent/tools/__pycache__/agent_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/task_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/__pycache__/todo_tools.cpython-314.pyc +0 -0
- package/onecode/agent/tools/agent_tools.py +4 -4
- package/onecode/agent/tools/todo_tools.py +188 -0
- package/onecode/cli.py +3 -3
- package/onecode/commands.py +439 -194
- package/onecode/config.py +3 -3
- package/onecode/config_screen.py +4 -2
- package/onecode/models/__pycache__/errors.cpython-314.pyc +0 -0
- package/onecode/models/__pycache__/messages.cpython-314.pyc +0 -0
- package/onecode/models/__pycache__/provider.cpython-314.pyc +0 -0
- package/onecode/models/errors.py +11 -1
- package/onecode/models/messages.py +35 -16
- package/onecode/models/provider.py +243 -12
- package/onecode/models/providers/__pycache__/anthropic_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/deepseek_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/glm_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/minimax_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/minimaxi_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/ollama_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/__pycache__/openai_provider.cpython-314.pyc +0 -0
- package/onecode/models/providers/anthropic_provider.py +4 -2
- package/onecode/models/providers/deepseek_provider.py +2 -0
- package/onecode/models/providers/glm_provider.py +2 -1
- package/onecode/models/providers/minimax_provider.py +2 -1
- package/onecode/models/providers/minimaxi_provider.py +76 -25
- package/onecode/models/providers/ollama_provider.py +2 -1
- package/onecode/models/providers/openai_provider.py +4 -0
- package/onecode/skills/__pycache__/manager.cpython-314.pyc +0 -0
- package/onecode/tasks/__pycache__/__init__.cpython-314.pyc +0 -0
- package/onecode/tasks/__pycache__/manager.cpython-314.pyc +0 -0
- package/onecode/tasks/__pycache__/models.cpython-314.pyc +0 -0
- package/onecode/tasks/manager.py +1 -1
- package/package.json +1 -1
- package/pyproject.toml +10 -0
- package/run.js +24 -7
- package/tui/__pycache__/app.cpython-314.pyc +0 -0
- package/tui/__pycache__/cli.cpython-314.pyc +0 -0
- package/tui/__pycache__/db.cpython-314.pyc +0 -0
- package/tui/__pycache__/directory_suggester.cpython-314.pyc +0 -0
- package/tui/__pycache__/directory_watcher.cpython-314.pyc +0 -0
- package/tui/__pycache__/messages.cpython-314.pyc +0 -0
- package/tui/__pycache__/paths.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/agent.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/api.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/messages.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/prompt.cpython-314.pyc +0 -0
- package/tui/acp/__pycache__/protocol.cpython-314.pyc +0 -0
- package/tui/acp/agent.py +60 -11
- package/tui/acp/api.py +6 -0
- package/tui/acp/messages.py +11 -0
- package/tui/acp/prompt.py +1 -1
- package/tui/acp/protocol.py +31 -0
- package/tui/app.py +60 -8
- package/tui/db.py +23 -0
- package/tui/directory_watcher.py +35 -1
- package/tui/messages.py +4 -1
- package/tui/paths.py +3 -3
- package/tui/screens/__pycache__/agent_modal.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/component_picker.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/log.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/main.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/projects_app.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/projects_screen.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/settings.cpython-314.pyc +0 -0
- package/tui/screens/__pycache__/store.cpython-314.pyc +0 -0
- package/tui/screens/component_picker.py +148 -0
- package/tui/screens/main.py +47 -95
- package/tui/screens/projects.tcss +10 -0
- package/tui/screens/projects_app.py +33 -5
- package/tui/screens/projects_screen.py +171 -9
- package/tui/widgets/__pycache__/acp_content.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/ask_user.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/conversation.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/diff_view.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/directory_input.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/mandelbrot.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/modified_files.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/plan.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/project_directory_tree.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/project_grid_select.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/question.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/subagent.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/subagent_screen.cpython-314.pyc +0 -0
- package/tui/widgets/__pycache__/tool_call.cpython-314.pyc +0 -0
- package/tui/widgets/ask_user.py +1 -1
- package/tui/widgets/conversation.py +99 -42
- package/tui/widgets/modified_files.py +155 -23
- package/tui/widgets/plan.py +22 -0
- package/tui/widgets/project_directory_tree.py +22 -10
- package/tui/widgets/project_grid_select.py +23 -1
- package/tui/widgets/question.py +1 -1
- package/tui/widgets/subagent.py +202 -36
- package/tui/widgets/subagent_screen.py +186 -0
- package/tui/widgets/tool_call.py +5 -8
- package/onecode/agent/tools/task_tools.py +0 -249
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from textual.app import ComposeResult
|
|
4
|
+
from textual.binding import Binding, BindingType
|
|
5
|
+
from textual.containers import Vertical
|
|
6
|
+
from textual.screen import Screen
|
|
7
|
+
from textual.widgets import Header, Markdown, Static
|
|
8
|
+
|
|
9
|
+
from tui.widgets.subagent import SubAgent
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SubAgentScreen(Screen):
|
|
13
|
+
"""Full-screen view of a SubAgent output.
|
|
14
|
+
|
|
15
|
+
Displays the complete structured output (SUMMARY/CHANGES/EVIDENCE/RISKS/BLOCKERS)
|
|
16
|
+
as Markdown. Esc to close, Up/Down/PageUp/PageDown to scroll.
|
|
17
|
+
Streams live while the SubAgent is running via a refresh timer.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
BINDINGS: list[BindingType] = [
|
|
21
|
+
Binding("escape", "app.pop_screen", "Back", priority=True),
|
|
22
|
+
Binding("up", "scroll_up", "Up", priority=True),
|
|
23
|
+
Binding("down", "scroll_down", "Down", priority=True),
|
|
24
|
+
Binding("pageup", "scroll_page_up", "Page Up", priority=True),
|
|
25
|
+
Binding("pagedown", "scroll_page_down", "Page Down", priority=True),
|
|
26
|
+
Binding("home", "scroll_home", "Home", priority=True),
|
|
27
|
+
Binding("end", "scroll_end", "End", priority=True),
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
DEFAULT_CSS = """
|
|
31
|
+
SubAgentScreen {
|
|
32
|
+
align: center middle;
|
|
33
|
+
|
|
34
|
+
#subagent-screen-frame {
|
|
35
|
+
width: 95%;
|
|
36
|
+
max-width: 160;
|
|
37
|
+
height: 95%;
|
|
38
|
+
border: thick $primary;
|
|
39
|
+
background: $surface;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
SubAgentScreenFooter {
|
|
43
|
+
dock: bottom;
|
|
44
|
+
height: 1;
|
|
45
|
+
padding: 0 1;
|
|
46
|
+
color: $text-muted;
|
|
47
|
+
background: $boost;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#subagent-full-content {
|
|
51
|
+
padding: 0 1;
|
|
52
|
+
height: 1fr;
|
|
53
|
+
overflow-y: auto;
|
|
54
|
+
scrollbar-gutter: stable;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, subagent: SubAgent) -> None:
|
|
60
|
+
super().__init__()
|
|
61
|
+
self.subagent = subagent
|
|
62
|
+
self._auto_follow = True
|
|
63
|
+
self._refresh_timer = None
|
|
64
|
+
self._last_content_hash: str = ""
|
|
65
|
+
|
|
66
|
+
def compose(self) -> ComposeResult:
|
|
67
|
+
yield Header(show_clock=False)
|
|
68
|
+
with Vertical(id="subagent-screen-frame"):
|
|
69
|
+
yield Markdown(self._build_content(), id="subagent-full-content")
|
|
70
|
+
yield Static(
|
|
71
|
+
"[$text-muted]esc[/] 返回 "
|
|
72
|
+
"[$text-muted]↑↓[/] 滚动 "
|
|
73
|
+
"[$text-muted]pageup/pagedown[/] 翻页 "
|
|
74
|
+
"[$text-muted]end[/] 跟随最新",
|
|
75
|
+
id="subagent-screen-footer",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def on_mount(self) -> None:
|
|
79
|
+
self.title = "subagent"
|
|
80
|
+
self.sub_title = f"@{self.subagent.agent_type} [{self.subagent._status}]"
|
|
81
|
+
self._last_content_hash = self._content_signature()
|
|
82
|
+
self._refresh_content(scroll_to_end=True)
|
|
83
|
+
self._refresh_timer = self.set_interval(0.2, self._poll_subagent)
|
|
84
|
+
|
|
85
|
+
def on_unmount(self) -> None:
|
|
86
|
+
if self._refresh_timer is not None:
|
|
87
|
+
self._refresh_timer.stop()
|
|
88
|
+
self._refresh_timer = None
|
|
89
|
+
|
|
90
|
+
def _content_signature(self) -> str:
|
|
91
|
+
sa = self.subagent
|
|
92
|
+
return f"{sa._status}|{sa.spinner_frame}|{len(sa._chunks)}|{len(sa._thinking_chunks)}|{sa._error}"
|
|
93
|
+
|
|
94
|
+
def _poll_subagent(self) -> None:
|
|
95
|
+
sig = self._content_signature()
|
|
96
|
+
if sig != self._last_content_hash:
|
|
97
|
+
self._last_content_hash = sig
|
|
98
|
+
self._refresh_content(scroll_to_end=self._auto_follow)
|
|
99
|
+
self.sub_title = f"@{self.subagent.agent_type} [{self.subagent._status}]"
|
|
100
|
+
if self.subagent._status != "running" and self._refresh_timer is not None:
|
|
101
|
+
self._refresh_timer.stop()
|
|
102
|
+
self._refresh_timer = None
|
|
103
|
+
|
|
104
|
+
def _refresh_content(self, scroll_to_end: bool = False) -> None:
|
|
105
|
+
try:
|
|
106
|
+
md = self.query_one("#subagent-full-content", Markdown)
|
|
107
|
+
md.update(self._build_content())
|
|
108
|
+
if scroll_to_end:
|
|
109
|
+
self.call_after_refresh(md.scroll_end)
|
|
110
|
+
except Exception:
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
def action_scroll_up(self) -> None:
|
|
114
|
+
self._auto_follow = False
|
|
115
|
+
try:
|
|
116
|
+
self.query_one("#subagent-full-content", Markdown).scroll_up()
|
|
117
|
+
except Exception:
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
def action_scroll_down(self) -> None:
|
|
121
|
+
try:
|
|
122
|
+
md = self.query_one("#subagent-full-content", Markdown)
|
|
123
|
+
md.scroll_down()
|
|
124
|
+
except Exception:
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
def action_scroll_page_up(self) -> None:
|
|
128
|
+
self._auto_follow = False
|
|
129
|
+
try:
|
|
130
|
+
self.query_one("#subagent-full-content", Markdown).scroll_page_up()
|
|
131
|
+
except Exception:
|
|
132
|
+
pass
|
|
133
|
+
|
|
134
|
+
def action_scroll_page_down(self) -> None:
|
|
135
|
+
try:
|
|
136
|
+
md = self.query_one("#subagent-full-content", Markdown)
|
|
137
|
+
md.scroll_page_down()
|
|
138
|
+
except Exception:
|
|
139
|
+
pass
|
|
140
|
+
|
|
141
|
+
def action_scroll_home(self) -> None:
|
|
142
|
+
self._auto_follow = False
|
|
143
|
+
try:
|
|
144
|
+
self.query_one("#subagent-full-content", Markdown).scroll_home()
|
|
145
|
+
except Exception:
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
def action_scroll_end(self) -> None:
|
|
149
|
+
self._auto_follow = True
|
|
150
|
+
try:
|
|
151
|
+
self.query_one("#subagent-full-content", Markdown).scroll_end()
|
|
152
|
+
except Exception:
|
|
153
|
+
pass
|
|
154
|
+
|
|
155
|
+
def _build_content(self) -> str:
|
|
156
|
+
sa = self.subagent
|
|
157
|
+
parts: list[str] = []
|
|
158
|
+
parts.append(f"# @ {sa.agent_type} [{sa._status}]\n")
|
|
159
|
+
if sa.prompt:
|
|
160
|
+
prompt_preview = sa.prompt if len(sa.prompt) <= 200 else sa.prompt[:200] + "..."
|
|
161
|
+
parts.append(
|
|
162
|
+
"\n## 任务\n\n"
|
|
163
|
+
f"> {prompt_preview}\n\n"
|
|
164
|
+
"---\n"
|
|
165
|
+
)
|
|
166
|
+
if sa._thinking_chunks:
|
|
167
|
+
parts.append("## 思考\n")
|
|
168
|
+
parts.append("".join(sa._thinking_chunks))
|
|
169
|
+
parts.append("\n\n---\n\n")
|
|
170
|
+
raw = "".join(sa._chunks)
|
|
171
|
+
if sa._status == "failed":
|
|
172
|
+
parts.append(
|
|
173
|
+
f"## ❌ Failed\n\n"
|
|
174
|
+
f"**Error:** {sa._error}\n\n"
|
|
175
|
+
"---\n\n"
|
|
176
|
+
"### Partial output\n\n"
|
|
177
|
+
f"```\n{(raw or '(none)')[:4000]}\n```"
|
|
178
|
+
)
|
|
179
|
+
elif raw:
|
|
180
|
+
parts.append("## 输出\n\n")
|
|
181
|
+
parts.append(raw)
|
|
182
|
+
if sa._status == "running":
|
|
183
|
+
parts.append("\n\n*…streaming*")
|
|
184
|
+
else:
|
|
185
|
+
parts.append("## 输出\n\n_(streaming…)_")
|
|
186
|
+
return "".join(parts)
|
package/tui/widgets/tool_call.py
CHANGED
|
@@ -207,17 +207,14 @@ class ToolCall(containers.VerticalGroup):
|
|
|
207
207
|
self.expanded = status in ("completed", "failed")
|
|
208
208
|
|
|
209
209
|
_TOOL_ICONS: dict[str, str] = {
|
|
210
|
-
"
|
|
210
|
+
"Spawn": "🧠",
|
|
211
211
|
"Agent": "🧠",
|
|
212
|
-
"TaskCreate": "📋",
|
|
213
|
-
"TaskGet": "📋",
|
|
214
|
-
"TaskList": "📋",
|
|
215
|
-
"TaskUpdate": "📋",
|
|
216
|
-
"TaskOutput": "📋",
|
|
217
|
-
"TaskStop": "📋",
|
|
218
212
|
"TodoCreate": "📋",
|
|
213
|
+
"TodoGet": "📋",
|
|
219
214
|
"TodoList": "📋",
|
|
220
|
-
"
|
|
215
|
+
"TodoUpdate": "📋",
|
|
216
|
+
"TodoOutput": "📋",
|
|
217
|
+
"TodoStop": "📋",
|
|
221
218
|
}
|
|
222
219
|
|
|
223
220
|
@property
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
from onecode.agent.tools.protocol import ToolResult
|
|
6
|
-
from onecode.agent.tools.registry import ToolSpec
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class TaskCreateTool:
|
|
10
|
-
def __init__(self, task_manager):
|
|
11
|
-
self._tm = task_manager
|
|
12
|
-
|
|
13
|
-
def spec(self) -> ToolSpec:
|
|
14
|
-
return ToolSpec(
|
|
15
|
-
name="TaskCreate",
|
|
16
|
-
description="Create a fine-grained task (1-3 tool calls each). For large work, split into multiple smaller tasks with dependencies. Returns task id.",
|
|
17
|
-
input_schema={
|
|
18
|
-
"type": "object",
|
|
19
|
-
"properties": {
|
|
20
|
-
"subject": {"type": "string", "description": "Short, action-oriented title (verb + noun, e.g. 'Create User model')"},
|
|
21
|
-
"description": {"type": "string", "description": "What needs to be done + acceptance criteria"},
|
|
22
|
-
"activeForm": {"type": "string", "description": "Active form description"},
|
|
23
|
-
"metadata": {"type": "object", "description": 'Optional metadata. Supports priority (high/medium/low) and effort (small/medium/large)'},
|
|
24
|
-
},
|
|
25
|
-
"required": ["subject", "description"],
|
|
26
|
-
},
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
def run(self, tool_input: dict[str, Any]) -> ToolResult:
|
|
30
|
-
task = self._tm.create_task(
|
|
31
|
-
subject=tool_input.get("subject", ""),
|
|
32
|
-
description=tool_input.get("description", ""),
|
|
33
|
-
active_form=tool_input.get("activeForm", ""),
|
|
34
|
-
metadata=tool_input.get("metadata"),
|
|
35
|
-
)
|
|
36
|
-
return ToolResult(name="TaskCreate", output={"task": {"id": task["id"], "subject": task["subject"]}})
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class TaskGetTool:
|
|
41
|
-
def __init__(self, task_manager):
|
|
42
|
-
self._tm = task_manager
|
|
43
|
-
|
|
44
|
-
def spec(self) -> ToolSpec:
|
|
45
|
-
return ToolSpec(
|
|
46
|
-
name="TaskGet",
|
|
47
|
-
description="Retrieve a task by ID.",
|
|
48
|
-
input_schema={
|
|
49
|
-
"type": "object",
|
|
50
|
-
"properties": {
|
|
51
|
-
"taskId": {"type": "string", "description": "Task id to retrieve"},
|
|
52
|
-
},
|
|
53
|
-
"required": ["taskId"],
|
|
54
|
-
},
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
def run(self, tool_input: dict[str, Any]) -> ToolResult:
|
|
58
|
-
task_id = tool_input.get("taskId", "")
|
|
59
|
-
task = self._tm.get_task(task_id)
|
|
60
|
-
if task is None:
|
|
61
|
-
return ToolResult(name="TaskGet", output={"task": None})
|
|
62
|
-
info = {k: task[k] for k in ("id", "subject", "description", "status", "blocks", "blockedBy")}
|
|
63
|
-
return ToolResult(name="TaskGet", output={"task": info})
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
class TaskListTool:
|
|
68
|
-
def __init__(self, task_manager):
|
|
69
|
-
self._tm = task_manager
|
|
70
|
-
|
|
71
|
-
def spec(self) -> ToolSpec:
|
|
72
|
-
return ToolSpec(
|
|
73
|
-
name="TaskList",
|
|
74
|
-
description="List all tasks with their current status and dependencies.",
|
|
75
|
-
input_schema={"type": "object", "properties": {}, "required": []},
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
def run(self, tool_input: dict[str, Any]) -> ToolResult:
|
|
79
|
-
tasks = self._tm.list_tasks()
|
|
80
|
-
if not tasks:
|
|
81
|
-
return ToolResult(name="TaskList", output={"tasks": []})
|
|
82
|
-
summaries = []
|
|
83
|
-
for t in tasks:
|
|
84
|
-
s = {"id": t["id"], "subject": t["subject"], "status": t["status"]}
|
|
85
|
-
if t.get("owner"):
|
|
86
|
-
s["owner"] = t["owner"]
|
|
87
|
-
s["blockedBy"] = list(t.get("blockedBy") or [])
|
|
88
|
-
summaries.append(s)
|
|
89
|
-
return ToolResult(name="TaskList", output={"tasks": summaries})
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
class TaskUpdateTool:
|
|
94
|
-
def __init__(self, task_manager):
|
|
95
|
-
self._tm = task_manager
|
|
96
|
-
|
|
97
|
-
def spec(self) -> ToolSpec:
|
|
98
|
-
return ToolSpec(
|
|
99
|
-
name="TaskUpdate",
|
|
100
|
-
description="Update a task: subject, description, status, owner, dependencies, output.",
|
|
101
|
-
input_schema={
|
|
102
|
-
"type": "object",
|
|
103
|
-
"properties": {
|
|
104
|
-
"taskId": {"type": "string", "description": "Task id to update"},
|
|
105
|
-
"subject": {"type": "string"},
|
|
106
|
-
"description": {"type": "string"},
|
|
107
|
-
"activeForm": {"type": "string"},
|
|
108
|
-
"status": {"type": "string", "description": "pending|in_progress|completed|deleted"},
|
|
109
|
-
"owner": {"type": "string"},
|
|
110
|
-
"addBlocks": {"type": "array", "items": {"type": "string"}, "description": "Task IDs this task blocks"},
|
|
111
|
-
"addBlockedBy": {"type": "array", "items": {"type": "string"}, "description": "Task IDs that block this"},
|
|
112
|
-
"metadata": {"type": "object"},
|
|
113
|
-
"output": {"type": "string", "description": "Key results to pass to downstream tasks"},
|
|
114
|
-
},
|
|
115
|
-
"required": ["taskId"],
|
|
116
|
-
},
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
def run(self, tool_input: dict[str, Any]) -> ToolResult:
|
|
120
|
-
task_id = tool_input.get("taskId", "")
|
|
121
|
-
updates = {k: v for k, v in tool_input.items() if k != "taskId" and v is not None}
|
|
122
|
-
if "subject" not in updates and "title" in updates:
|
|
123
|
-
updates["subject"] = updates.pop("title")
|
|
124
|
-
if "addBlocks" not in updates and "blocks" in updates and isinstance(updates.get("blocks"), list):
|
|
125
|
-
updates["addBlocks"] = updates.pop("blocks")
|
|
126
|
-
result = self._tm.update_task(task_id, **updates)
|
|
127
|
-
if result is None:
|
|
128
|
-
return ToolResult(name="TaskUpdate", output={"success": False, "taskId": task_id, "error": "Task not found"}, is_error=True)
|
|
129
|
-
if result.get("deleted"):
|
|
130
|
-
return ToolResult(name="TaskUpdate", output={"success": True, "taskId": task_id, "updatedFields": ["deleted"]})
|
|
131
|
-
return ToolResult(name="TaskUpdate", output={"success": True, "taskId": task_id, "task": {"id": result["id"], "subject": result["subject"], "status": result["status"]}})
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
class TaskOutputTool:
|
|
136
|
-
def __init__(self, task_manager):
|
|
137
|
-
self._tm = task_manager
|
|
138
|
-
|
|
139
|
-
def spec(self) -> ToolSpec:
|
|
140
|
-
return ToolSpec(
|
|
141
|
-
name="TaskOutput",
|
|
142
|
-
description="Get output for a task.",
|
|
143
|
-
input_schema={
|
|
144
|
-
"type": "object",
|
|
145
|
-
"properties": {
|
|
146
|
-
"task_id": {"type": "string", "description": "Task id to get output for"},
|
|
147
|
-
},
|
|
148
|
-
"required": ["task_id"],
|
|
149
|
-
},
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
def run(self, tool_input: dict[str, Any]) -> ToolResult:
|
|
153
|
-
task_id = tool_input.get("task_id", "")
|
|
154
|
-
result = self._tm.get_task_output(task_id)
|
|
155
|
-
return ToolResult(name="TaskOutput", output=result)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
class TaskStopTool:
|
|
160
|
-
def __init__(self, task_manager):
|
|
161
|
-
self._tm = task_manager
|
|
162
|
-
|
|
163
|
-
def spec(self) -> ToolSpec:
|
|
164
|
-
return ToolSpec(
|
|
165
|
-
name="TaskStop",
|
|
166
|
-
description="Stop/cancel a running task.",
|
|
167
|
-
input_schema={
|
|
168
|
-
"type": "object",
|
|
169
|
-
"properties": {
|
|
170
|
-
"task_id": {"type": "string", "description": "Task id to stop"},
|
|
171
|
-
},
|
|
172
|
-
"required": ["task_id"],
|
|
173
|
-
},
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
def run(self, tool_input: dict[str, Any]) -> ToolResult:
|
|
177
|
-
task_id = tool_input.get("task_id", "")
|
|
178
|
-
result = self._tm.update_task(task_id, status="completed")
|
|
179
|
-
if result is None:
|
|
180
|
-
return ToolResult(name="TaskStop", output={"success": False, "stopped": False, "error": "Task not found"}, is_error=True)
|
|
181
|
-
if result.get("deleted"):
|
|
182
|
-
return ToolResult(name="TaskStop", output={"success": True, "stopped": True, "task_id": task_id})
|
|
183
|
-
return ToolResult(name="TaskStop", output={"success": True, "stopped": True, "task_id": task_id})
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
class TodoCreateTool:
|
|
188
|
-
def __init__(self, task_manager):
|
|
189
|
-
self._tm = task_manager
|
|
190
|
-
|
|
191
|
-
def spec(self) -> ToolSpec:
|
|
192
|
-
return ToolSpec(
|
|
193
|
-
name="TodoCreate",
|
|
194
|
-
description="Create a todo item. Returns todo id.",
|
|
195
|
-
input_schema={
|
|
196
|
-
"type": "object",
|
|
197
|
-
"properties": {
|
|
198
|
-
"text": {"type": "string", "description": "Todo item text"},
|
|
199
|
-
},
|
|
200
|
-
"required": ["text"],
|
|
201
|
-
},
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
def run(self, tool_input: dict[str, Any]) -> ToolResult:
|
|
205
|
-
text = tool_input.get("text", "")
|
|
206
|
-
todo_id = self._tm.add_todo(text)
|
|
207
|
-
return ToolResult(name="TodoCreate", output={"id": todo_id, "text": text})
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
class TodoListTool:
|
|
212
|
-
def __init__(self, task_manager):
|
|
213
|
-
self._tm = task_manager
|
|
214
|
-
|
|
215
|
-
def spec(self) -> ToolSpec:
|
|
216
|
-
return ToolSpec(
|
|
217
|
-
name="TodoList",
|
|
218
|
-
description="List all todo items.",
|
|
219
|
-
input_schema={"type": "object", "properties": {}, "required": []},
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
def run(self, tool_input: dict[str, Any]) -> ToolResult:
|
|
223
|
-
todos = self._tm.list_todos()
|
|
224
|
-
return ToolResult(name="TodoList", output=todos)
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
class TodoCompleteTool:
|
|
229
|
-
def __init__(self, task_manager):
|
|
230
|
-
self._tm = task_manager
|
|
231
|
-
|
|
232
|
-
def spec(self) -> ToolSpec:
|
|
233
|
-
return ToolSpec(
|
|
234
|
-
name="TodoComplete",
|
|
235
|
-
description="Mark a todo as completed.",
|
|
236
|
-
input_schema={
|
|
237
|
-
"type": "object",
|
|
238
|
-
"properties": {
|
|
239
|
-
"todo_id": {"type": "string", "description": "Todo id to complete"},
|
|
240
|
-
},
|
|
241
|
-
"required": ["todo_id"],
|
|
242
|
-
},
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
def run(self, tool_input: dict[str, Any]) -> ToolResult:
|
|
246
|
-
todo_id = tool_input.get("todo_id", tool_input.get("id", ""))
|
|
247
|
-
ok = self._tm.complete_todo(str(todo_id))
|
|
248
|
-
return ToolResult(name="TodoComplete", output={"success": ok, "todo_id": todo_id}, is_error=not ok)
|
|
249
|
-
|