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.
Files changed (205) hide show
  1. crabcode-0.1.6.dist-info/METADATA +63 -0
  2. crabcode-0.1.6.dist-info/RECORD +205 -0
  3. crabcode-0.1.6.dist-info/WHEEL +4 -0
  4. crabcode-0.1.6.dist-info/entry_points.txt +2 -0
  5. crabcode_cli/__init__.py +7 -0
  6. crabcode_cli/__main__.py +4 -0
  7. crabcode_cli/_native/crabcode-computer-use-host.exe +0 -0
  8. crabcode_cli/_native/runtime.json +6 -0
  9. crabcode_cli/app.py +776 -0
  10. crabcode_cli/banner.py +39 -0
  11. crabcode_cli/computer_use.py +286 -0
  12. crabcode_cli/model_manager.py +649 -0
  13. crabcode_cli/model_picker.py +168 -0
  14. crabcode_cli/pipe.py +148 -0
  15. crabcode_cli/render/__init__.py +0 -0
  16. crabcode_cli/render/messages.py +19 -0
  17. crabcode_cli/repl.py +4841 -0
  18. crabcode_cli/session_picker.py +239 -0
  19. crabcode_core/__init__.py +7 -0
  20. crabcode_core/_version.py +3 -0
  21. crabcode_core/_windows_process.py +68 -0
  22. crabcode_core/agent_manager.py +1731 -0
  23. crabcode_core/api/__init__.py +20 -0
  24. crabcode_core/api/anthropic_adapter.py +648 -0
  25. crabcode_core/api/azure_adapter.py +69 -0
  26. crabcode_core/api/base.py +233 -0
  27. crabcode_core/api/codex_adapter.py +1327 -0
  28. crabcode_core/api/error_logging.py +132 -0
  29. crabcode_core/api/gemini_adapter.py +26 -0
  30. crabcode_core/api/model_info.py +257 -0
  31. crabcode_core/api/network.py +62 -0
  32. crabcode_core/api/ollama_adapter.py +27 -0
  33. crabcode_core/api/openai_adapter.py +323 -0
  34. crabcode_core/api/registry.py +58 -0
  35. crabcode_core/command_line.py +18 -0
  36. crabcode_core/compact/__init__.py +17 -0
  37. crabcode_core/compact/compact.py +514 -0
  38. crabcode_core/compact/context_tokens.py +165 -0
  39. crabcode_core/computer_use_mode.py +38 -0
  40. crabcode_core/computer_use_observation.py +144 -0
  41. crabcode_core/config/__init__.py +3 -0
  42. crabcode_core/config/claudemd.py +61 -0
  43. crabcode_core/config/manager.py +237 -0
  44. crabcode_core/config/model_catalog.py +256 -0
  45. crabcode_core/events.py +4896 -0
  46. crabcode_core/file_lock.py +81 -0
  47. crabcode_core/filesystem.py +22 -0
  48. crabcode_core/goal.py +149 -0
  49. crabcode_core/hooks/__init__.py +5 -0
  50. crabcode_core/hooks/manager.py +290 -0
  51. crabcode_core/image_io.py +111 -0
  52. crabcode_core/io_worker.py +211 -0
  53. crabcode_core/logging_utils.py +168 -0
  54. crabcode_core/lsp/__init__.py +6 -0
  55. crabcode_core/lsp/client.py +786 -0
  56. crabcode_core/lsp/diagnostics.py +150 -0
  57. crabcode_core/lsp/manager.py +583 -0
  58. crabcode_core/mcp/__init__.py +4 -0
  59. crabcode_core/mcp/client.py +169 -0
  60. crabcode_core/mcp/config.py +57 -0
  61. crabcode_core/path_validation.py +34 -0
  62. crabcode_core/paths.py +24 -0
  63. crabcode_core/peer/__init__.py +10 -0
  64. crabcode_core/peer/runtime.py +518 -0
  65. crabcode_core/permissions/__init__.py +7 -0
  66. crabcode_core/permissions/ai_reviewer.py +184 -0
  67. crabcode_core/permissions/manager.py +187 -0
  68. crabcode_core/plan/__init__.py +5 -0
  69. crabcode_core/plan/executor.py +270 -0
  70. crabcode_core/plan/types.py +169 -0
  71. crabcode_core/prompts/__init__.py +0 -0
  72. crabcode_core/prompts/blocks.py +10 -0
  73. crabcode_core/prompts/context.py +229 -0
  74. crabcode_core/prompts/library.py +92 -0
  75. crabcode_core/prompts/profile.py +79 -0
  76. crabcode_core/prompts/system.py +356 -0
  77. crabcode_core/prompts/templates.py +104 -0
  78. crabcode_core/query/__init__.py +0 -0
  79. crabcode_core/query/computer_use_history.py +83 -0
  80. crabcode_core/query/loop.py +2446 -0
  81. crabcode_core/query/retry.py +116 -0
  82. crabcode_core/schedule/__init__.py +13 -0
  83. crabcode_core/schedule/manager.py +631 -0
  84. crabcode_core/schedule/models.py +204 -0
  85. crabcode_core/schedule/store.py +558 -0
  86. crabcode_core/schedule/timing.py +287 -0
  87. crabcode_core/session/__init__.py +12 -0
  88. crabcode_core/session/export.py +141 -0
  89. crabcode_core/session/meta_db.py +684 -0
  90. crabcode_core/session/storage.py +2032 -0
  91. crabcode_core/session/title_gen.py +52 -0
  92. crabcode_core/skills/__init__.py +6 -0
  93. crabcode_core/skills/context.py +23 -0
  94. crabcode_core/skills/loader.py +214 -0
  95. crabcode_core/skills/matcher.py +203 -0
  96. crabcode_core/snapshot/__init__.py +21 -0
  97. crabcode_core/snapshot/snapshot.py +436 -0
  98. crabcode_core/snapshot/tracker.py +359 -0
  99. crabcode_core/subprocess_utils.py +260 -0
  100. crabcode_core/team/__init__.py +27 -0
  101. crabcode_core/team/bridge.py +66 -0
  102. crabcode_core/team/inbox.py +237 -0
  103. crabcode_core/team/manager.py +1660 -0
  104. crabcode_core/team/message_bus.py +821 -0
  105. crabcode_core/team/models.py +191 -0
  106. crabcode_core/team/recovery.py +192 -0
  107. crabcode_core/text_io.py +52 -0
  108. crabcode_core/tools/__init__.py +112 -0
  109. crabcode_core/tools/_input_helpers.py +39 -0
  110. crabcode_core/tools/_input_schema.py +130 -0
  111. crabcode_core/tools/agent.py +413 -0
  112. crabcode_core/tools/apply_patch.py +587 -0
  113. crabcode_core/tools/ask_user.py +122 -0
  114. crabcode_core/tools/bash.py +154 -0
  115. crabcode_core/tools/browser.py +678 -0
  116. crabcode_core/tools/checklist.py +241 -0
  117. crabcode_core/tools/checkpoint.py +78 -0
  118. crabcode_core/tools/computer_use.py +599 -0
  119. crabcode_core/tools/diff_utils.py +123 -0
  120. crabcode_core/tools/file_edit.py +240 -0
  121. crabcode_core/tools/file_read.py +187 -0
  122. crabcode_core/tools/file_write.py +154 -0
  123. crabcode_core/tools/glob.py +123 -0
  124. crabcode_core/tools/goal.py +134 -0
  125. crabcode_core/tools/grep.py +247 -0
  126. crabcode_core/tools/image.py +173 -0
  127. crabcode_core/tools/image_generate.py +115 -0
  128. crabcode_core/tools/lint.py +388 -0
  129. crabcode_core/tools/loading.py +187 -0
  130. crabcode_core/tools/memory.py +456 -0
  131. crabcode_core/tools/monitor.py +677 -0
  132. crabcode_core/tools/peer.py +103 -0
  133. crabcode_core/tools/revert.py +90 -0
  134. crabcode_core/tools/schedule.py +683 -0
  135. crabcode_core/tools/skill.py +108 -0
  136. crabcode_core/tools/switch_mode.py +213 -0
  137. crabcode_core/tools/team.py +329 -0
  138. crabcode_core/tools/tool_search.py +64 -0
  139. crabcode_core/tools/web_search.py +444 -0
  140. crabcode_core/turn_details.py +64 -0
  141. crabcode_core/types/__init__.py +95 -0
  142. crabcode_core/types/config.py +434 -0
  143. crabcode_core/types/event.py +334 -0
  144. crabcode_core/types/message.py +263 -0
  145. crabcode_core/types/tool.py +208 -0
  146. crabcode_core/usage.py +289 -0
  147. crabcode_core/utf8_sanitize.py +29 -0
  148. crabcode_debugger/__init__.py +5 -0
  149. crabcode_debugger/adapters.py +332 -0
  150. crabcode_debugger/dap.py +304 -0
  151. crabcode_debugger/memory.py +1806 -0
  152. crabcode_debugger/process.py +707 -0
  153. crabcode_debugger/runtime.py +58 -0
  154. crabcode_debugger/sessions.py +387 -0
  155. crabcode_debugger/tool.py +731 -0
  156. crabcode_gateway/__init__.py +18 -0
  157. crabcode_gateway/acp/__init__.py +13 -0
  158. crabcode_gateway/acp/agent.py +1581 -0
  159. crabcode_gateway/acp/session.py +114 -0
  160. crabcode_gateway/acp/transport.py +39 -0
  161. crabcode_gateway/acp/types.py +90 -0
  162. crabcode_gateway/adapter.py +30 -0
  163. crabcode_gateway/auth.py +139 -0
  164. crabcode_gateway/computer_use.py +364 -0
  165. crabcode_gateway/document_engine.py +637 -0
  166. crabcode_gateway/document_worker.py +301 -0
  167. crabcode_gateway/event_bus.py +655 -0
  168. crabcode_gateway/grpc/__init__.py +0 -0
  169. crabcode_gateway/grpc/crabcode_pb2.py +70 -0
  170. crabcode_gateway/grpc/crabcode_pb2_grpc.py +474 -0
  171. crabcode_gateway/grpc/proto/crabcode.proto +160 -0
  172. crabcode_gateway/grpc/server.py +765 -0
  173. crabcode_gateway/middleware.py +344 -0
  174. crabcode_gateway/protocol.py +17 -0
  175. crabcode_gateway/routes/__init__.py +0 -0
  176. crabcode_gateway/routes/agent.py +392 -0
  177. crabcode_gateway/routes/auth.py +150 -0
  178. crabcode_gateway/routes/computer_use.py +101 -0
  179. crabcode_gateway/routes/config.py +1698 -0
  180. crabcode_gateway/routes/document.py +1810 -0
  181. crabcode_gateway/routes/event.py +3801 -0
  182. crabcode_gateway/routes/git_review.py +232 -0
  183. crabcode_gateway/routes/health.py +14 -0
  184. crabcode_gateway/routes/peer.py +78 -0
  185. crabcode_gateway/routes/permission.py +121 -0
  186. crabcode_gateway/routes/schedule.py +300 -0
  187. crabcode_gateway/routes/session.py +1496 -0
  188. crabcode_gateway/routes/snapshot.py +211 -0
  189. crabcode_gateway/routes/tasks.py +318 -0
  190. crabcode_gateway/routes/team.py +392 -0
  191. crabcode_gateway/routes/usage.py +49 -0
  192. crabcode_gateway/routes/workspace.py +299 -0
  193. crabcode_gateway/safe_file.py +63 -0
  194. crabcode_gateway/schemas.py +1820 -0
  195. crabcode_gateway/server.py +514 -0
  196. crabcode_gateway/session_registry.py +36 -0
  197. crabcode_gateway/task_registry.py +708 -0
  198. crabcode_gateway/turn_details.py +60 -0
  199. crabcode_search/__init__.py +11 -0
  200. crabcode_search/background.py +368 -0
  201. crabcode_search/chunker.py +517 -0
  202. crabcode_search/embedder.py +497 -0
  203. crabcode_search/indexer.py +215 -0
  204. crabcode_search/store.py +222 -0
  205. crabcode_search/tool.py +268 -0
@@ -0,0 +1,122 @@
1
+ """AskUserTool — present choices to the user and wait for selection."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import uuid
7
+ from typing import Any
8
+
9
+ from crabcode_core.types.event import ChoiceRequestEvent, ChoiceResponseEvent
10
+ from crabcode_core.types.tool import Tool, ToolContext, ToolResult
11
+
12
+
13
+ class AskUserTool(Tool):
14
+ name = "AskUser"
15
+ description = "Present a question with options for the user to choose from."
16
+ is_read_only = True
17
+ is_concurrency_safe = False # blocks on user input
18
+ input_schema = {
19
+ "type": "object",
20
+ "properties": {
21
+ "question": {
22
+ "type": "string",
23
+ "description": "The question or prompt to display to the user.",
24
+ },
25
+ "options": {
26
+ "type": "array",
27
+ "items": {"type": "string"},
28
+ "description": "List of options for the user to choose from.",
29
+ },
30
+ "multiple": {
31
+ "type": "boolean",
32
+ "description": (
33
+ "If true, user can select multiple options. "
34
+ "Default: false (single select)."
35
+ ),
36
+ "default": False,
37
+ },
38
+ },
39
+ "required": ["question", "options"],
40
+ }
41
+
42
+ async def get_prompt(self, **kwargs: Any) -> str:
43
+ return (
44
+ "Present a question with choices to the user when you are unsure "
45
+ "about the next step and want their input. This tool pauses "
46
+ "execution and waits for the user to select one or more options.\n\n"
47
+ "Use this tool when:\n"
48
+ "- There are multiple reasonable approaches and you need the user's preference\n"
49
+ "- You want to confirm the direction before making significant changes\n"
50
+ "- The user might have context you don't about which option is best\n\n"
51
+ "Do NOT use this tool when:\n"
52
+ "- The answer is obvious or there's a clear best approach\n"
53
+ "- The user already told you what to do\n"
54
+ "- You just need a simple yes/no confirmation (use text response instead)\n\n"
55
+ "The tool returns the selected option(s) as a list of strings."
56
+ )
57
+
58
+ async def call(
59
+ self,
60
+ tool_input: dict[str, Any],
61
+ context: ToolContext,
62
+ ) -> ToolResult:
63
+ question = tool_input.get("question", "")
64
+ options = tool_input.get("options", [])
65
+ multiple = tool_input.get("multiple", False)
66
+
67
+ if not options:
68
+ return ToolResult(
69
+ result_for_model="Error: at least one option is required",
70
+ is_error=True,
71
+ )
72
+
73
+ if len(options) == 1:
74
+ return ToolResult(
75
+ result_for_model=f"User selected: {options[0]}",
76
+ data={"selected": [options[0]]},
77
+ )
78
+
79
+ if not context.choice_queue or not context.tool_event_queue:
80
+ # No frontend connected — auto-select the first option
81
+ return ToolResult(
82
+ result_for_model=f"Auto-selected (no interactive frontend): {options[0]}",
83
+ data={"selected": [options[0]]},
84
+ )
85
+
86
+ # Generate a unique ID for this choice request
87
+ request_id = str(uuid.uuid4())
88
+
89
+ # Emit the choice request event through the tool_event_queue
90
+ request = ChoiceRequestEvent(
91
+ tool_use_id=request_id,
92
+ question=question,
93
+ options=options,
94
+ multiple=multiple,
95
+ )
96
+ await context.tool_event_queue.put(request)
97
+
98
+ # Wait for the user's response
99
+ while True:
100
+ response: ChoiceResponseEvent = await context.choice_queue.get()
101
+ if response.tool_use_id == request_id:
102
+ break
103
+ # Put back responses for other requests
104
+ await context.choice_queue.put(response)
105
+ await asyncio.sleep(0.05)
106
+
107
+ if response.cancelled:
108
+ return ToolResult(
109
+ result_for_model="User cancelled the selection.",
110
+ is_error=True,
111
+ )
112
+
113
+ selected = response.selected
114
+ if multiple:
115
+ result_text = f"User selected: {', '.join(selected)}"
116
+ else:
117
+ result_text = f"User selected: {selected[0] if selected else '(none)'}"
118
+
119
+ return ToolResult(
120
+ result_for_model=result_text,
121
+ data={"selected": selected},
122
+ )
@@ -0,0 +1,154 @@
1
+ """BashTool — execute shell commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import os
7
+ from typing import Any
8
+
9
+ from crabcode_core.subprocess_utils import (
10
+ decode_subprocess_output,
11
+ managed_process_command,
12
+ shell_command,
13
+ )
14
+ from crabcode_core.types.tool import Tool, ToolContext, ToolResult
15
+ from crabcode_core.io_worker import run_file_tool
16
+
17
+
18
+ class BashTool(Tool):
19
+ name = "Bash"
20
+ description = "Execute a command in the active platform shell."
21
+ is_read_only = False
22
+ is_concurrency_safe = False
23
+ input_schema = {
24
+ "type": "object",
25
+ "properties": {
26
+ "command": {
27
+ "type": "string",
28
+ "description": "The platform-shell command to execute.",
29
+ },
30
+ "timeout": {
31
+ "type": "integer",
32
+ "description": "Timeout in seconds (defaults to filesystem_timeout in settings: 3600 if unset, unlimited if null).",
33
+ },
34
+ },
35
+ "required": ["command"],
36
+ }
37
+
38
+ async def get_prompt(self, **kwargs: Any) -> str:
39
+ windows_chaining = (
40
+ "- On Windows, commands run in PowerShell. Do not assume `&&` is "
41
+ "available because Windows PowerShell 5.1 does not support it; "
42
+ "use `; if ($?) { ... }` for conditional sequencing.\n"
43
+ "- If PowerShell blocks npm.ps1/npx.ps1 with PSSecurityException, "
44
+ "use npm.cmd/npx.cmd; do not change the system execution policy.\n"
45
+ if os.name == "nt"
46
+ else ""
47
+ )
48
+ return (
49
+ "Execute a command in the active platform shell. Use for system commands, "
50
+ "running scripts, git operations, and other terminal tasks. "
51
+ "Prefer dedicated tools (Read, apply_patch, Edit, Write, Glob, Grep) over "
52
+ "bash when they can accomplish the task.\n\n"
53
+ "Commands run in an explicit Bash-compatible shell on Unix and "
54
+ "PowerShell on Windows, with the user's environment. "
55
+ "The command is executed in the working directory of the "
56
+ "current session. Each call starts a new shell, so environment "
57
+ "variables and directory changes do not persist across calls.\n\n"
58
+ "Guidelines:\n"
59
+ "- Always quote file paths that contain spaces with double "
60
+ 'quotes: cd "/path/with spaces" (correct) vs cd /path/with '
61
+ "spaces (incorrect, will fail).\n"
62
+ "- For long-running processes (dev servers, watchers), they "
63
+ "will be killed when the timeout expires. Warn the user if "
64
+ "a command is expected to run indefinitely.\n"
65
+ "- When issuing multiple independent commands, call Bash "
66
+ "multiple times in parallel rather than chaining with &&.\n"
67
+ "- If commands depend on each other and must run sequentially, "
68
+ "use the active platform shell's conditional chaining syntax in "
69
+ "a single call.\n"
70
+ f"{windows_chaining}"
71
+ "- Do NOT use interactive commands (e.g., git rebase -i, "
72
+ "vim, nano) — they require user input that is not supported.\n"
73
+ "- If a command fails, read the error output carefully before "
74
+ "retrying. Do not blindly retry the same command."
75
+ )
76
+
77
+ async def call(
78
+ self, tool_input: dict[str, Any], context: ToolContext,
79
+ ) -> ToolResult:
80
+ return await run_file_tool(self.name, tool_input, context)
81
+
82
+ async def _call_local(
83
+ self,
84
+ tool_input: dict[str, Any],
85
+ context: ToolContext,
86
+ ) -> ToolResult:
87
+ command = tool_input.get("command", "")
88
+
89
+ if not command.strip():
90
+ return ToolResult(
91
+ result_for_model="Error: empty command",
92
+ is_error=True,
93
+ )
94
+
95
+ env = {**os.environ, **context.env}
96
+
97
+ # Create snapshot before bash commands that might modify files
98
+ if context.session_id:
99
+ try:
100
+ from crabcode_core.snapshot.tracker import pre_bash_snapshot
101
+ # The entire operation is isolated, including snapshot I/O.
102
+ pre_bash_snapshot(
103
+ context.cwd,
104
+ context.session_id,
105
+ enabled=context.snapshot_enabled,
106
+ max_size_mb=context.snapshot_max_size_mb,
107
+ )
108
+ except Exception:
109
+ pass # Best-effort; don't block bash if snapshot fails
110
+
111
+ try:
112
+ proc = await asyncio.create_subprocess_exec(
113
+ *managed_process_command(shell_command(command)),
114
+ stdout=asyncio.subprocess.PIPE,
115
+ stderr=asyncio.subprocess.PIPE,
116
+ cwd=context.cwd,
117
+ env=env,
118
+ )
119
+
120
+ # The parent worker supervisor owns the deadline and process group.
121
+ stdout_bytes, stderr_bytes = await proc.communicate()
122
+
123
+ stdout = decode_subprocess_output(stdout_bytes)
124
+ stderr = decode_subprocess_output(stderr_bytes)
125
+ exit_code = proc.returncode or 0
126
+
127
+ max_chars = 100_000
128
+ if len(stdout) > max_chars:
129
+ stdout = stdout[:max_chars] + "\n... (truncated)"
130
+ if len(stderr) > max_chars:
131
+ stderr = stderr[:max_chars] + "\n... (truncated)"
132
+
133
+ parts: list[str] = []
134
+ if stdout:
135
+ parts.append(stdout)
136
+ if stderr:
137
+ parts.append(f"stderr:\n{stderr}")
138
+ if exit_code != 0:
139
+ parts.append(f"Exit code: {exit_code}")
140
+
141
+ output = "\n".join(parts) if parts else "(no output)"
142
+
143
+ return ToolResult(
144
+ data={"stdout": stdout, "stderr": stderr, "exit_code": exit_code},
145
+ result_for_model=output,
146
+ result_for_display=output,
147
+ is_error=exit_code != 0,
148
+ )
149
+
150
+ except Exception as e:
151
+ return ToolResult(
152
+ result_for_model=f"Error executing command: {e}",
153
+ is_error=True,
154
+ )