crabcode 0.1.6__py3-none-win_amd64.whl
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.
- crabcode-0.1.6.dist-info/METADATA +63 -0
- crabcode-0.1.6.dist-info/RECORD +205 -0
- crabcode-0.1.6.dist-info/WHEEL +4 -0
- crabcode-0.1.6.dist-info/entry_points.txt +2 -0
- crabcode_cli/__init__.py +7 -0
- crabcode_cli/__main__.py +4 -0
- crabcode_cli/_native/crabcode-computer-use-host.exe +0 -0
- crabcode_cli/_native/runtime.json +6 -0
- crabcode_cli/app.py +776 -0
- crabcode_cli/banner.py +39 -0
- crabcode_cli/computer_use.py +286 -0
- crabcode_cli/model_manager.py +649 -0
- crabcode_cli/model_picker.py +168 -0
- crabcode_cli/pipe.py +148 -0
- crabcode_cli/render/__init__.py +0 -0
- crabcode_cli/render/messages.py +19 -0
- crabcode_cli/repl.py +4841 -0
- crabcode_cli/session_picker.py +239 -0
- crabcode_core/__init__.py +7 -0
- crabcode_core/_version.py +3 -0
- crabcode_core/_windows_process.py +68 -0
- crabcode_core/agent_manager.py +1731 -0
- crabcode_core/api/__init__.py +20 -0
- crabcode_core/api/anthropic_adapter.py +648 -0
- crabcode_core/api/azure_adapter.py +69 -0
- crabcode_core/api/base.py +233 -0
- crabcode_core/api/codex_adapter.py +1327 -0
- crabcode_core/api/error_logging.py +132 -0
- crabcode_core/api/gemini_adapter.py +26 -0
- crabcode_core/api/model_info.py +257 -0
- crabcode_core/api/network.py +62 -0
- crabcode_core/api/ollama_adapter.py +27 -0
- crabcode_core/api/openai_adapter.py +323 -0
- crabcode_core/api/registry.py +58 -0
- crabcode_core/command_line.py +18 -0
- crabcode_core/compact/__init__.py +17 -0
- crabcode_core/compact/compact.py +514 -0
- crabcode_core/compact/context_tokens.py +165 -0
- crabcode_core/computer_use_mode.py +38 -0
- crabcode_core/computer_use_observation.py +144 -0
- crabcode_core/config/__init__.py +3 -0
- crabcode_core/config/claudemd.py +61 -0
- crabcode_core/config/manager.py +237 -0
- crabcode_core/config/model_catalog.py +256 -0
- crabcode_core/events.py +4896 -0
- crabcode_core/file_lock.py +81 -0
- crabcode_core/filesystem.py +22 -0
- crabcode_core/goal.py +149 -0
- crabcode_core/hooks/__init__.py +5 -0
- crabcode_core/hooks/manager.py +290 -0
- crabcode_core/image_io.py +111 -0
- crabcode_core/io_worker.py +211 -0
- crabcode_core/logging_utils.py +168 -0
- crabcode_core/lsp/__init__.py +6 -0
- crabcode_core/lsp/client.py +786 -0
- crabcode_core/lsp/diagnostics.py +150 -0
- crabcode_core/lsp/manager.py +583 -0
- crabcode_core/mcp/__init__.py +4 -0
- crabcode_core/mcp/client.py +169 -0
- crabcode_core/mcp/config.py +57 -0
- crabcode_core/path_validation.py +34 -0
- crabcode_core/paths.py +24 -0
- crabcode_core/peer/__init__.py +10 -0
- crabcode_core/peer/runtime.py +518 -0
- crabcode_core/permissions/__init__.py +7 -0
- crabcode_core/permissions/ai_reviewer.py +184 -0
- crabcode_core/permissions/manager.py +187 -0
- crabcode_core/plan/__init__.py +5 -0
- crabcode_core/plan/executor.py +270 -0
- crabcode_core/plan/types.py +169 -0
- crabcode_core/prompts/__init__.py +0 -0
- crabcode_core/prompts/blocks.py +10 -0
- crabcode_core/prompts/context.py +229 -0
- crabcode_core/prompts/library.py +92 -0
- crabcode_core/prompts/profile.py +79 -0
- crabcode_core/prompts/system.py +356 -0
- crabcode_core/prompts/templates.py +104 -0
- crabcode_core/query/__init__.py +0 -0
- crabcode_core/query/computer_use_history.py +83 -0
- crabcode_core/query/loop.py +2446 -0
- crabcode_core/query/retry.py +116 -0
- crabcode_core/schedule/__init__.py +13 -0
- crabcode_core/schedule/manager.py +631 -0
- crabcode_core/schedule/models.py +204 -0
- crabcode_core/schedule/store.py +558 -0
- crabcode_core/schedule/timing.py +287 -0
- crabcode_core/session/__init__.py +12 -0
- crabcode_core/session/export.py +141 -0
- crabcode_core/session/meta_db.py +684 -0
- crabcode_core/session/storage.py +2032 -0
- crabcode_core/session/title_gen.py +52 -0
- crabcode_core/skills/__init__.py +6 -0
- crabcode_core/skills/context.py +23 -0
- crabcode_core/skills/loader.py +214 -0
- crabcode_core/skills/matcher.py +203 -0
- crabcode_core/snapshot/__init__.py +21 -0
- crabcode_core/snapshot/snapshot.py +436 -0
- crabcode_core/snapshot/tracker.py +359 -0
- crabcode_core/subprocess_utils.py +260 -0
- crabcode_core/team/__init__.py +27 -0
- crabcode_core/team/bridge.py +66 -0
- crabcode_core/team/inbox.py +237 -0
- crabcode_core/team/manager.py +1660 -0
- crabcode_core/team/message_bus.py +821 -0
- crabcode_core/team/models.py +191 -0
- crabcode_core/team/recovery.py +192 -0
- crabcode_core/text_io.py +52 -0
- crabcode_core/tools/__init__.py +112 -0
- crabcode_core/tools/_input_helpers.py +39 -0
- crabcode_core/tools/_input_schema.py +130 -0
- crabcode_core/tools/agent.py +413 -0
- crabcode_core/tools/apply_patch.py +587 -0
- crabcode_core/tools/ask_user.py +122 -0
- crabcode_core/tools/bash.py +154 -0
- crabcode_core/tools/browser.py +678 -0
- crabcode_core/tools/checklist.py +241 -0
- crabcode_core/tools/checkpoint.py +78 -0
- crabcode_core/tools/computer_use.py +599 -0
- crabcode_core/tools/diff_utils.py +123 -0
- crabcode_core/tools/file_edit.py +240 -0
- crabcode_core/tools/file_read.py +187 -0
- crabcode_core/tools/file_write.py +154 -0
- crabcode_core/tools/glob.py +123 -0
- crabcode_core/tools/goal.py +134 -0
- crabcode_core/tools/grep.py +247 -0
- crabcode_core/tools/image.py +173 -0
- crabcode_core/tools/image_generate.py +115 -0
- crabcode_core/tools/lint.py +388 -0
- crabcode_core/tools/loading.py +187 -0
- crabcode_core/tools/memory.py +456 -0
- crabcode_core/tools/monitor.py +677 -0
- crabcode_core/tools/peer.py +103 -0
- crabcode_core/tools/revert.py +90 -0
- crabcode_core/tools/schedule.py +683 -0
- crabcode_core/tools/skill.py +108 -0
- crabcode_core/tools/switch_mode.py +213 -0
- crabcode_core/tools/team.py +329 -0
- crabcode_core/tools/tool_search.py +64 -0
- crabcode_core/tools/web_search.py +444 -0
- crabcode_core/turn_details.py +64 -0
- crabcode_core/types/__init__.py +95 -0
- crabcode_core/types/config.py +434 -0
- crabcode_core/types/event.py +334 -0
- crabcode_core/types/message.py +263 -0
- crabcode_core/types/tool.py +208 -0
- crabcode_core/usage.py +289 -0
- crabcode_core/utf8_sanitize.py +29 -0
- crabcode_debugger/__init__.py +5 -0
- crabcode_debugger/adapters.py +332 -0
- crabcode_debugger/dap.py +304 -0
- crabcode_debugger/memory.py +1806 -0
- crabcode_debugger/process.py +707 -0
- crabcode_debugger/runtime.py +58 -0
- crabcode_debugger/sessions.py +387 -0
- crabcode_debugger/tool.py +731 -0
- crabcode_gateway/__init__.py +18 -0
- crabcode_gateway/acp/__init__.py +13 -0
- crabcode_gateway/acp/agent.py +1581 -0
- crabcode_gateway/acp/session.py +114 -0
- crabcode_gateway/acp/transport.py +39 -0
- crabcode_gateway/acp/types.py +90 -0
- crabcode_gateway/adapter.py +30 -0
- crabcode_gateway/auth.py +139 -0
- crabcode_gateway/computer_use.py +364 -0
- crabcode_gateway/document_engine.py +637 -0
- crabcode_gateway/document_worker.py +301 -0
- crabcode_gateway/event_bus.py +655 -0
- crabcode_gateway/grpc/__init__.py +0 -0
- crabcode_gateway/grpc/crabcode_pb2.py +70 -0
- crabcode_gateway/grpc/crabcode_pb2_grpc.py +474 -0
- crabcode_gateway/grpc/proto/crabcode.proto +160 -0
- crabcode_gateway/grpc/server.py +765 -0
- crabcode_gateway/middleware.py +344 -0
- crabcode_gateway/protocol.py +17 -0
- crabcode_gateway/routes/__init__.py +0 -0
- crabcode_gateway/routes/agent.py +392 -0
- crabcode_gateway/routes/auth.py +150 -0
- crabcode_gateway/routes/computer_use.py +101 -0
- crabcode_gateway/routes/config.py +1698 -0
- crabcode_gateway/routes/document.py +1810 -0
- crabcode_gateway/routes/event.py +3801 -0
- crabcode_gateway/routes/git_review.py +232 -0
- crabcode_gateway/routes/health.py +14 -0
- crabcode_gateway/routes/peer.py +78 -0
- crabcode_gateway/routes/permission.py +121 -0
- crabcode_gateway/routes/schedule.py +300 -0
- crabcode_gateway/routes/session.py +1496 -0
- crabcode_gateway/routes/snapshot.py +211 -0
- crabcode_gateway/routes/tasks.py +318 -0
- crabcode_gateway/routes/team.py +392 -0
- crabcode_gateway/routes/usage.py +49 -0
- crabcode_gateway/routes/workspace.py +299 -0
- crabcode_gateway/safe_file.py +63 -0
- crabcode_gateway/schemas.py +1820 -0
- crabcode_gateway/server.py +514 -0
- crabcode_gateway/session_registry.py +36 -0
- crabcode_gateway/task_registry.py +708 -0
- crabcode_gateway/turn_details.py +60 -0
- crabcode_search/__init__.py +11 -0
- crabcode_search/background.py +368 -0
- crabcode_search/chunker.py +517 -0
- crabcode_search/embedder.py +497 -0
- crabcode_search/indexer.py +215 -0
- crabcode_search/store.py +222 -0
- crabcode_search/tool.py +268 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""ChecklistTool — per-session task tracking for long-running work."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import uuid
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from crabcode_core.types.tool import Tool, ToolContext, ToolResult
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class ChecklistItem:
|
|
15
|
+
text: str
|
|
16
|
+
checked: bool = False
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class Checklist:
|
|
21
|
+
id: str
|
|
22
|
+
title: str
|
|
23
|
+
items: list[ChecklistItem] = field(default_factory=list)
|
|
24
|
+
created_at: str = ""
|
|
25
|
+
|
|
26
|
+
def render(self) -> str:
|
|
27
|
+
lines = [f" 📋 {self.title}"]
|
|
28
|
+
for i, item in enumerate(self.items, 1):
|
|
29
|
+
mark = "✅" if item.checked else "◻"
|
|
30
|
+
lines.append(f" {mark} {i}. {item.text}")
|
|
31
|
+
done = sum(1 for it in self.items if it.checked)
|
|
32
|
+
total = len(self.items)
|
|
33
|
+
lines.append(f" ({done}/{total} completed)")
|
|
34
|
+
return "\n".join(lines)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ChecklistTool(Tool):
|
|
38
|
+
name = "Checklist"
|
|
39
|
+
description = "Manage per-session checklists for tracking progress on long tasks."
|
|
40
|
+
is_read_only = False
|
|
41
|
+
is_concurrency_safe = False
|
|
42
|
+
input_schema = {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"properties": {
|
|
45
|
+
"action": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"enum": ["create", "update", "check", "uncheck", "list", "clear"],
|
|
48
|
+
"description": "The action to perform.",
|
|
49
|
+
},
|
|
50
|
+
"title": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"description": "Title of the checklist. Required for 'create'.",
|
|
53
|
+
},
|
|
54
|
+
"items": {
|
|
55
|
+
"type": "array",
|
|
56
|
+
"items": {"type": "string"},
|
|
57
|
+
"description": "List of item texts. Required for 'create'. Used for 'update' to add items.",
|
|
58
|
+
},
|
|
59
|
+
"checklist_id": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"description": "ID of an existing checklist. Required for 'update', 'check', 'uncheck'.",
|
|
62
|
+
},
|
|
63
|
+
"item": {
|
|
64
|
+
"type": "integer",
|
|
65
|
+
"description": "1-based index of the item to check/uncheck. Required for 'check' and 'uncheck'.",
|
|
66
|
+
},
|
|
67
|
+
"remove_items": {
|
|
68
|
+
"type": "array",
|
|
69
|
+
"items": {"type": "integer"},
|
|
70
|
+
"description": "1-based indices of items to remove. Used for 'update'.",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
"required": ["action"],
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
def __init__(self) -> None:
|
|
77
|
+
self._checklists: dict[str, Checklist] = {}
|
|
78
|
+
|
|
79
|
+
async def setup(self, context: ToolContext) -> None:
|
|
80
|
+
self._checklists.clear()
|
|
81
|
+
self._setup_context = context
|
|
82
|
+
|
|
83
|
+
async def get_prompt(self, **kwargs: Any) -> str:
|
|
84
|
+
return (
|
|
85
|
+
"Manage per-session checklists to track progress on long or "
|
|
86
|
+
"multi-step tasks. Checklists live only within the current "
|
|
87
|
+
"session — they are not persisted to disk.\n\n"
|
|
88
|
+
"Actions:\n"
|
|
89
|
+
"- create: Start a new checklist with a title and items "
|
|
90
|
+
"(requires title + items)\n"
|
|
91
|
+
"- update: Modify a checklist — add/remove items or rename "
|
|
92
|
+
"(requires checklist_id)\n"
|
|
93
|
+
"- check: Mark an item as done (requires checklist_id + item index)\n"
|
|
94
|
+
"- uncheck: Unmark an item (requires checklist_id + item index)\n"
|
|
95
|
+
"- list: Show all checklists and their progress\n"
|
|
96
|
+
"- clear: Remove one or all checklists\n\n"
|
|
97
|
+
"When to use:\n"
|
|
98
|
+
"- Before starting a long task, create a checklist to plan the steps\n"
|
|
99
|
+
"- After completing each step, check it off\n"
|
|
100
|
+
"- Use 'list' at any time to review progress\n\n"
|
|
101
|
+
"Guidelines:\n"
|
|
102
|
+
"- Use item indices (1-based) for check/uncheck, not text\n"
|
|
103
|
+
"- Keep items concise — one actionable step per item\n"
|
|
104
|
+
"- If you need to persist insights for future sessions, use the "
|
|
105
|
+
"Memory tool instead"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
async def validate_input(self, tool_input: dict[str, Any]) -> str | None:
|
|
109
|
+
action = tool_input.get("action")
|
|
110
|
+
if action not in ("create", "update", "check", "uncheck", "list", "clear"):
|
|
111
|
+
return "action must be one of: create, update, check, uncheck, list, clear"
|
|
112
|
+
|
|
113
|
+
if action == "create":
|
|
114
|
+
if not tool_input.get("title"):
|
|
115
|
+
return "title is required for 'create'"
|
|
116
|
+
if not tool_input.get("items"):
|
|
117
|
+
return "items is required for 'create'"
|
|
118
|
+
|
|
119
|
+
if action in ("update", "check", "uncheck"):
|
|
120
|
+
if not tool_input.get("checklist_id"):
|
|
121
|
+
return "checklist_id is required for this action"
|
|
122
|
+
|
|
123
|
+
if action in ("check", "uncheck"):
|
|
124
|
+
if tool_input.get("item") is None:
|
|
125
|
+
return "item (1-based index) is required for this action"
|
|
126
|
+
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
def _find_checklist(self, checklist_id: str) -> Checklist | None:
|
|
130
|
+
return self._checklists.get(checklist_id)
|
|
131
|
+
|
|
132
|
+
async def call(
|
|
133
|
+
self,
|
|
134
|
+
tool_input: dict[str, Any],
|
|
135
|
+
context: ToolContext,
|
|
136
|
+
) -> ToolResult:
|
|
137
|
+
action = tool_input["action"]
|
|
138
|
+
|
|
139
|
+
if action == "create":
|
|
140
|
+
return self._action_create(tool_input)
|
|
141
|
+
if action == "update":
|
|
142
|
+
return self._action_update(tool_input)
|
|
143
|
+
if action == "check":
|
|
144
|
+
return self._action_check(tool_input, checked=True)
|
|
145
|
+
if action == "uncheck":
|
|
146
|
+
return self._action_check(tool_input, checked=False)
|
|
147
|
+
if action == "list":
|
|
148
|
+
return self._action_list()
|
|
149
|
+
if action == "clear":
|
|
150
|
+
return self._action_clear(tool_input)
|
|
151
|
+
|
|
152
|
+
return ToolResult(result_for_model=f"Unknown action: {action}", is_error=True)
|
|
153
|
+
|
|
154
|
+
def _action_create(self, tool_input: dict[str, Any]) -> ToolResult:
|
|
155
|
+
cl_id = uuid.uuid4().hex[:8]
|
|
156
|
+
items = [ChecklistItem(text=t) for t in tool_input["items"]]
|
|
157
|
+
cl = Checklist(
|
|
158
|
+
id=cl_id,
|
|
159
|
+
title=tool_input["title"],
|
|
160
|
+
items=items,
|
|
161
|
+
created_at=datetime.now(timezone.utc).isoformat(),
|
|
162
|
+
)
|
|
163
|
+
self._checklists[cl_id] = cl
|
|
164
|
+
return ToolResult(
|
|
165
|
+
data={"checklist_id": cl_id, "item_count": len(items)},
|
|
166
|
+
result_for_model=f"Checklist created (id: {cl_id}):\n{cl.render()}",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
def _action_update(self, tool_input: dict[str, Any]) -> ToolResult:
|
|
170
|
+
cl = self._find_checklist(tool_input["checklist_id"])
|
|
171
|
+
if not cl:
|
|
172
|
+
return ToolResult(
|
|
173
|
+
result_for_model=f"Error: checklist '{tool_input['checklist_id']}' not found.",
|
|
174
|
+
is_error=True,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
if tool_input.get("title"):
|
|
178
|
+
cl.title = tool_input["title"]
|
|
179
|
+
|
|
180
|
+
# Remove items (process in reverse to keep indices stable)
|
|
181
|
+
remove_indices = sorted(
|
|
182
|
+
(i for i in tool_input.get("remove_items", []) if isinstance(i, int)),
|
|
183
|
+
reverse=True,
|
|
184
|
+
)
|
|
185
|
+
for idx in remove_indices:
|
|
186
|
+
if 1 <= idx <= len(cl.items):
|
|
187
|
+
cl.items.pop(idx - 1)
|
|
188
|
+
|
|
189
|
+
# Add items
|
|
190
|
+
for text in tool_input.get("items", []):
|
|
191
|
+
cl.items.append(ChecklistItem(text=text))
|
|
192
|
+
|
|
193
|
+
return ToolResult(
|
|
194
|
+
data={"checklist_id": cl.id, "item_count": len(cl.items)},
|
|
195
|
+
result_for_model=f"Checklist updated (id: {cl.id}):\n{cl.render()}",
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
def _action_check(self, tool_input: dict[str, Any], checked: bool) -> ToolResult:
|
|
199
|
+
cl = self._find_checklist(tool_input["checklist_id"])
|
|
200
|
+
if not cl:
|
|
201
|
+
return ToolResult(
|
|
202
|
+
result_for_model=f"Error: checklist '{tool_input['checklist_id']}' not found.",
|
|
203
|
+
is_error=True,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
idx = tool_input["item"]
|
|
207
|
+
if not isinstance(idx, int) or idx < 1 or idx > len(cl.items):
|
|
208
|
+
return ToolResult(
|
|
209
|
+
result_for_model=f"Error: item index {idx} out of range (1-{len(cl.items)}).",
|
|
210
|
+
is_error=True,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
cl.items[idx - 1].checked = checked
|
|
214
|
+
verb = "checked" if checked else "unchecked"
|
|
215
|
+
return ToolResult(
|
|
216
|
+
data={"checklist_id": cl.id, "item": idx, verb: verb},
|
|
217
|
+
result_for_model=f"Item {idx} {verb} (id: {cl.id}):\n{cl.render()}",
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
def _action_list(self) -> ToolResult:
|
|
221
|
+
if not self._checklists:
|
|
222
|
+
return ToolResult(result_for_model="No checklists.")
|
|
223
|
+
parts = [cl.render() for cl in self._checklists.values()]
|
|
224
|
+
return ToolResult(
|
|
225
|
+
data={"count": len(self._checklists)},
|
|
226
|
+
result_for_model=f"{len(self._checklists)} checklist(s):\n\n" + "\n\n".join(parts),
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
def _action_clear(self, tool_input: dict[str, Any]) -> ToolResult:
|
|
230
|
+
cl_id = tool_input.get("checklist_id")
|
|
231
|
+
if cl_id:
|
|
232
|
+
if cl_id not in self._checklists:
|
|
233
|
+
return ToolResult(
|
|
234
|
+
result_for_model=f"Error: checklist '{cl_id}' not found.",
|
|
235
|
+
is_error=True,
|
|
236
|
+
)
|
|
237
|
+
del self._checklists[cl_id]
|
|
238
|
+
return ToolResult(result_for_model=f"Checklist '{cl_id}' cleared.")
|
|
239
|
+
count = len(self._checklists)
|
|
240
|
+
self._checklists.clear()
|
|
241
|
+
return ToolResult(result_for_model=f"All {count} checklist(s) cleared.")
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""CheckpointTool — create a checkpoint (conversation + file snapshot)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from crabcode_core.types.tool import Tool, ToolContext, ToolResult
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CheckpointTool(Tool):
|
|
12
|
+
name = "Checkpoint"
|
|
13
|
+
description = "Create a checkpoint of the current conversation and file state."
|
|
14
|
+
is_read_only = False
|
|
15
|
+
is_concurrency_safe = False
|
|
16
|
+
input_schema = {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"label": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "A short label for this checkpoint, e.g. 'before-refactor' or 'pre-migration'.",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
"required": [],
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async def get_prompt(self, **kwargs: Any) -> str:
|
|
28
|
+
return (
|
|
29
|
+
"Create a checkpoint that saves both the current conversation state "
|
|
30
|
+
"and a file-system snapshot. You can later revert to this checkpoint "
|
|
31
|
+
"using the Revert tool to undo both file changes and conversation history.\n\n"
|
|
32
|
+
"Use this tool proactively before making significant or risky changes, such as:\n"
|
|
33
|
+
"- Large-scale refactoring across multiple files\n"
|
|
34
|
+
"- Destructive operations that modify many files\n"
|
|
35
|
+
"- Changes where rollback would be difficult manually\n"
|
|
36
|
+
"- Before running commands that could alter the codebase unpredictably\n\n"
|
|
37
|
+
"Do NOT use this tool for trivial changes (single-file edits, adding comments, etc.)."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
async def call(
|
|
41
|
+
self,
|
|
42
|
+
tool_input: dict[str, Any],
|
|
43
|
+
context: ToolContext,
|
|
44
|
+
) -> ToolResult:
|
|
45
|
+
session = getattr(context, "session", None)
|
|
46
|
+
if not session:
|
|
47
|
+
return ToolResult(
|
|
48
|
+
result_for_model="No active session; cannot create checkpoint.",
|
|
49
|
+
is_error=True,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
label = tool_input.get("label", "")
|
|
53
|
+
cp_id = await asyncio.to_thread(
|
|
54
|
+
session.checkpoint,
|
|
55
|
+
label=label,
|
|
56
|
+
messages=context.messages,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if not cp_id:
|
|
60
|
+
return ToolResult(
|
|
61
|
+
result_for_model="Failed to create checkpoint (no messages or session storage unavailable).",
|
|
62
|
+
is_error=True,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
snapshot_included = bool(getattr(session, "last_checkpoint_snapshot_included", False))
|
|
66
|
+
display = f"Checkpoint created: {cp_id[:8]}…"
|
|
67
|
+
if label:
|
|
68
|
+
display = f"Checkpoint created ({label}): {cp_id[:8]}…"
|
|
69
|
+
if not snapshot_included:
|
|
70
|
+
display += " (conversation only; file snapshot skipped by size or settings)"
|
|
71
|
+
return ToolResult(
|
|
72
|
+
data={
|
|
73
|
+
"checkpoint_id": cp_id,
|
|
74
|
+
"label": label,
|
|
75
|
+
"snapshot_included": snapshot_included,
|
|
76
|
+
},
|
|
77
|
+
result_for_model=f"{display} You can revert to this checkpoint later with the Revert tool using checkpoint_id '{cp_id[:8]}…'.",
|
|
78
|
+
)
|