tactus 0.31.2__py3-none-any.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.
- tactus/__init__.py +49 -0
- tactus/adapters/__init__.py +9 -0
- tactus/adapters/broker_log.py +76 -0
- tactus/adapters/cli_hitl.py +189 -0
- tactus/adapters/cli_log.py +223 -0
- tactus/adapters/cost_collector_log.py +56 -0
- tactus/adapters/file_storage.py +367 -0
- tactus/adapters/http_callback_log.py +109 -0
- tactus/adapters/ide_log.py +71 -0
- tactus/adapters/lua_tools.py +336 -0
- tactus/adapters/mcp.py +289 -0
- tactus/adapters/mcp_manager.py +196 -0
- tactus/adapters/memory.py +53 -0
- tactus/adapters/plugins.py +419 -0
- tactus/backends/http_backend.py +58 -0
- tactus/backends/model_backend.py +35 -0
- tactus/backends/pytorch_backend.py +110 -0
- tactus/broker/__init__.py +12 -0
- tactus/broker/client.py +247 -0
- tactus/broker/protocol.py +183 -0
- tactus/broker/server.py +1123 -0
- tactus/broker/stdio.py +12 -0
- tactus/cli/__init__.py +7 -0
- tactus/cli/app.py +2245 -0
- tactus/cli/commands/__init__.py +0 -0
- tactus/core/__init__.py +32 -0
- tactus/core/config_manager.py +790 -0
- tactus/core/dependencies/__init__.py +14 -0
- tactus/core/dependencies/registry.py +180 -0
- tactus/core/dsl_stubs.py +2117 -0
- tactus/core/exceptions.py +66 -0
- tactus/core/execution_context.py +480 -0
- tactus/core/lua_sandbox.py +508 -0
- tactus/core/message_history_manager.py +236 -0
- tactus/core/mocking.py +286 -0
- tactus/core/output_validator.py +291 -0
- tactus/core/registry.py +499 -0
- tactus/core/runtime.py +2907 -0
- tactus/core/template_resolver.py +142 -0
- tactus/core/yaml_parser.py +301 -0
- tactus/docker/Dockerfile +61 -0
- tactus/docker/entrypoint.sh +69 -0
- tactus/dspy/__init__.py +39 -0
- tactus/dspy/agent.py +1144 -0
- tactus/dspy/broker_lm.py +181 -0
- tactus/dspy/config.py +212 -0
- tactus/dspy/history.py +196 -0
- tactus/dspy/module.py +405 -0
- tactus/dspy/prediction.py +318 -0
- tactus/dspy/signature.py +185 -0
- tactus/formatting/__init__.py +7 -0
- tactus/formatting/formatter.py +437 -0
- tactus/ide/__init__.py +9 -0
- tactus/ide/coding_assistant.py +343 -0
- tactus/ide/server.py +2223 -0
- tactus/primitives/__init__.py +49 -0
- tactus/primitives/control.py +168 -0
- tactus/primitives/file.py +229 -0
- tactus/primitives/handles.py +378 -0
- tactus/primitives/host.py +94 -0
- tactus/primitives/human.py +342 -0
- tactus/primitives/json.py +189 -0
- tactus/primitives/log.py +187 -0
- tactus/primitives/message_history.py +157 -0
- tactus/primitives/model.py +163 -0
- tactus/primitives/procedure.py +564 -0
- tactus/primitives/procedure_callable.py +318 -0
- tactus/primitives/retry.py +155 -0
- tactus/primitives/session.py +152 -0
- tactus/primitives/state.py +182 -0
- tactus/primitives/step.py +209 -0
- tactus/primitives/system.py +93 -0
- tactus/primitives/tool.py +375 -0
- tactus/primitives/tool_handle.py +279 -0
- tactus/primitives/toolset.py +229 -0
- tactus/protocols/__init__.py +38 -0
- tactus/protocols/chat_recorder.py +81 -0
- tactus/protocols/config.py +97 -0
- tactus/protocols/cost.py +31 -0
- tactus/protocols/hitl.py +71 -0
- tactus/protocols/log_handler.py +27 -0
- tactus/protocols/models.py +355 -0
- tactus/protocols/result.py +33 -0
- tactus/protocols/storage.py +90 -0
- tactus/providers/__init__.py +13 -0
- tactus/providers/base.py +92 -0
- tactus/providers/bedrock.py +117 -0
- tactus/providers/google.py +105 -0
- tactus/providers/openai.py +98 -0
- tactus/sandbox/__init__.py +63 -0
- tactus/sandbox/config.py +171 -0
- tactus/sandbox/container_runner.py +1099 -0
- tactus/sandbox/docker_manager.py +433 -0
- tactus/sandbox/entrypoint.py +227 -0
- tactus/sandbox/protocol.py +213 -0
- tactus/stdlib/__init__.py +10 -0
- tactus/stdlib/io/__init__.py +13 -0
- tactus/stdlib/io/csv.py +88 -0
- tactus/stdlib/io/excel.py +136 -0
- tactus/stdlib/io/file.py +90 -0
- tactus/stdlib/io/fs.py +154 -0
- tactus/stdlib/io/hdf5.py +121 -0
- tactus/stdlib/io/json.py +109 -0
- tactus/stdlib/io/parquet.py +83 -0
- tactus/stdlib/io/tsv.py +88 -0
- tactus/stdlib/loader.py +274 -0
- tactus/stdlib/tac/tactus/tools/done.tac +33 -0
- tactus/stdlib/tac/tactus/tools/log.tac +50 -0
- tactus/testing/README.md +273 -0
- tactus/testing/__init__.py +61 -0
- tactus/testing/behave_integration.py +380 -0
- tactus/testing/context.py +486 -0
- tactus/testing/eval_models.py +114 -0
- tactus/testing/evaluation_runner.py +222 -0
- tactus/testing/evaluators.py +634 -0
- tactus/testing/events.py +94 -0
- tactus/testing/gherkin_parser.py +134 -0
- tactus/testing/mock_agent.py +315 -0
- tactus/testing/mock_dependencies.py +234 -0
- tactus/testing/mock_hitl.py +171 -0
- tactus/testing/mock_registry.py +168 -0
- tactus/testing/mock_tools.py +133 -0
- tactus/testing/models.py +115 -0
- tactus/testing/pydantic_eval_runner.py +508 -0
- tactus/testing/steps/__init__.py +13 -0
- tactus/testing/steps/builtin.py +902 -0
- tactus/testing/steps/custom.py +69 -0
- tactus/testing/steps/registry.py +68 -0
- tactus/testing/test_runner.py +489 -0
- tactus/tracing/__init__.py +5 -0
- tactus/tracing/trace_manager.py +417 -0
- tactus/utils/__init__.py +1 -0
- tactus/utils/cost_calculator.py +72 -0
- tactus/utils/model_pricing.py +132 -0
- tactus/utils/safe_file_library.py +502 -0
- tactus/utils/safe_libraries.py +234 -0
- tactus/validation/LuaLexerBase.py +66 -0
- tactus/validation/LuaParserBase.py +23 -0
- tactus/validation/README.md +224 -0
- tactus/validation/__init__.py +7 -0
- tactus/validation/error_listener.py +21 -0
- tactus/validation/generated/LuaLexer.interp +231 -0
- tactus/validation/generated/LuaLexer.py +5548 -0
- tactus/validation/generated/LuaLexer.tokens +124 -0
- tactus/validation/generated/LuaLexerBase.py +66 -0
- tactus/validation/generated/LuaParser.interp +173 -0
- tactus/validation/generated/LuaParser.py +6439 -0
- tactus/validation/generated/LuaParser.tokens +124 -0
- tactus/validation/generated/LuaParserBase.py +23 -0
- tactus/validation/generated/LuaParserVisitor.py +118 -0
- tactus/validation/generated/__init__.py +7 -0
- tactus/validation/grammar/LuaLexer.g4 +123 -0
- tactus/validation/grammar/LuaParser.g4 +178 -0
- tactus/validation/semantic_visitor.py +817 -0
- tactus/validation/validator.py +157 -0
- tactus-0.31.2.dist-info/METADATA +1809 -0
- tactus-0.31.2.dist-info/RECORD +160 -0
- tactus-0.31.2.dist-info/WHEEL +4 -0
- tactus-0.31.2.dist-info/entry_points.txt +2 -0
- tactus-0.31.2.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Coding Assistant Agent for Tactus IDE.
|
|
3
|
+
|
|
4
|
+
Provides an AI-powered coding assistant that uses Tactus primitives
|
|
5
|
+
to interact with the workspace, files, and user.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Dict, Any, Optional, List
|
|
12
|
+
import dspy
|
|
13
|
+
from tactus.primitives.file import FilePrimitive
|
|
14
|
+
from tactus.primitives.human import HumanPrimitive
|
|
15
|
+
from tactus.core.execution_context import ExecutionContext
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CodingAssistantAgent:
|
|
21
|
+
"""
|
|
22
|
+
AI Coding Assistant powered by Tactus primitives.
|
|
23
|
+
|
|
24
|
+
Uses DSPy for agent inference and Tactus primitives for:
|
|
25
|
+
- File operations (FilePrimitive with workspace sandboxing)
|
|
26
|
+
- Human interaction (HumanPrimitive for HITL)
|
|
27
|
+
- Tool tracking (ToolPrimitive)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, workspace_root: str, config: Optional[Dict[str, Any]] = None):
|
|
31
|
+
"""
|
|
32
|
+
Initialize the coding assistant.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
workspace_root: Root directory for file operations (sandboxed)
|
|
36
|
+
config: Optional configuration dict with provider/model settings
|
|
37
|
+
"""
|
|
38
|
+
self.workspace_root = Path(workspace_root).resolve()
|
|
39
|
+
self.config = config or {}
|
|
40
|
+
|
|
41
|
+
# Initialize Tactus primitives
|
|
42
|
+
self.file_primitive = FilePrimitive(base_path=str(self.workspace_root))
|
|
43
|
+
|
|
44
|
+
# Create a minimal execution context for Human primitive
|
|
45
|
+
# (We don't need full runtime features, just HITL support)
|
|
46
|
+
self.execution_context = self._create_execution_context()
|
|
47
|
+
self.human_primitive = HumanPrimitive(self.execution_context)
|
|
48
|
+
|
|
49
|
+
# Chat history
|
|
50
|
+
self.messages: List[Dict[str, Any]] = []
|
|
51
|
+
|
|
52
|
+
# Initialize DSPy with configured model
|
|
53
|
+
self._setup_dspy()
|
|
54
|
+
|
|
55
|
+
logger.info(f"CodingAssistantAgent initialized with workspace: {self.workspace_root}")
|
|
56
|
+
|
|
57
|
+
def _create_execution_context(self) -> ExecutionContext:
|
|
58
|
+
"""Create a minimal execution context for primitives."""
|
|
59
|
+
# Import here to avoid circular dependencies
|
|
60
|
+
from tactus.protocols.hitl import HITLResponse
|
|
61
|
+
|
|
62
|
+
class MinimalExecutionContext:
|
|
63
|
+
"""Minimal context that supports wait_for_human."""
|
|
64
|
+
|
|
65
|
+
def __init__(self):
|
|
66
|
+
self._inside_checkpoint = False
|
|
67
|
+
self.hitl_handler = None
|
|
68
|
+
|
|
69
|
+
def wait_for_human(
|
|
70
|
+
self,
|
|
71
|
+
request_type: str,
|
|
72
|
+
message: str,
|
|
73
|
+
timeout_seconds: Optional[int] = None,
|
|
74
|
+
default_value: Any = None,
|
|
75
|
+
options: Optional[List[Dict[str, Any]]] = None,
|
|
76
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
77
|
+
) -> HITLResponse:
|
|
78
|
+
"""
|
|
79
|
+
Handle human interaction requests.
|
|
80
|
+
|
|
81
|
+
For the IDE, we'll emit events that the frontend can respond to.
|
|
82
|
+
This is a simplified version - full HITL will be implemented
|
|
83
|
+
via the streaming API.
|
|
84
|
+
"""
|
|
85
|
+
logger.info(f"HITL request: {request_type} - {message}")
|
|
86
|
+
|
|
87
|
+
# For now, return default values
|
|
88
|
+
# The streaming API will handle actual HITL interaction
|
|
89
|
+
return HITLResponse(
|
|
90
|
+
request_type=request_type, value=default_value, metadata=metadata or {}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return MinimalExecutionContext()
|
|
94
|
+
|
|
95
|
+
def _setup_dspy(self):
|
|
96
|
+
"""Configure DSPy with the appropriate model."""
|
|
97
|
+
# Get configuration
|
|
98
|
+
assistant_config = self.config.get("coding_assistant", {})
|
|
99
|
+
provider = assistant_config.get(
|
|
100
|
+
"provider", os.environ.get("TACTUS_DEFAULT_PROVIDER", "openai")
|
|
101
|
+
)
|
|
102
|
+
model = assistant_config.get("model", os.environ.get("TACTUS_DEFAULT_MODEL", "gpt-4"))
|
|
103
|
+
|
|
104
|
+
logger.info(f"Setting up DSPy with provider={provider}, model={model}")
|
|
105
|
+
|
|
106
|
+
# Configure DSPy LM based on provider
|
|
107
|
+
if provider == "openai":
|
|
108
|
+
api_key = self.config.get("openai_api_key") or os.environ.get("OPENAI_API_KEY")
|
|
109
|
+
if not api_key:
|
|
110
|
+
raise ValueError("OpenAI API key not found in config or environment")
|
|
111
|
+
|
|
112
|
+
lm = dspy.OpenAI(model=model, api_key=api_key, max_tokens=4000)
|
|
113
|
+
elif provider == "anthropic":
|
|
114
|
+
api_key = self.config.get("anthropic_api_key") or os.environ.get("ANTHROPIC_API_KEY")
|
|
115
|
+
if not api_key:
|
|
116
|
+
raise ValueError("Anthropic API key not found in config or environment")
|
|
117
|
+
|
|
118
|
+
lm = dspy.Claude(model=model, api_key=api_key, max_tokens=4000)
|
|
119
|
+
else:
|
|
120
|
+
raise ValueError(f"Unsupported provider: {provider}")
|
|
121
|
+
|
|
122
|
+
dspy.settings.configure(lm=lm)
|
|
123
|
+
|
|
124
|
+
# Create the agent signature
|
|
125
|
+
self.agent = self._create_agent()
|
|
126
|
+
|
|
127
|
+
def _create_agent(self):
|
|
128
|
+
"""Create the DSPy agent with appropriate signature."""
|
|
129
|
+
|
|
130
|
+
class CodingAssistantSignature(dspy.Signature):
|
|
131
|
+
"""AI Coding Assistant for Tactus IDE.
|
|
132
|
+
|
|
133
|
+
You are a helpful coding assistant integrated into the Tactus IDE.
|
|
134
|
+
You can help users with:
|
|
135
|
+
- Reading and analyzing code files
|
|
136
|
+
- Writing and editing files
|
|
137
|
+
- Explaining code concepts
|
|
138
|
+
- Debugging issues
|
|
139
|
+
- Refactoring code
|
|
140
|
+
|
|
141
|
+
You have access to tools for file operations within the workspace.
|
|
142
|
+
Always be helpful, concise, and accurate.
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
chat_history = dspy.InputField(desc="Previous conversation messages")
|
|
146
|
+
user_message = dspy.InputField(desc="User's current message")
|
|
147
|
+
workspace_root = dspy.InputField(desc="Root directory of the workspace")
|
|
148
|
+
|
|
149
|
+
response = dspy.OutputField(desc="Your response to the user")
|
|
150
|
+
tool_calls = dspy.OutputField(desc="List of tool calls to make (if any)")
|
|
151
|
+
|
|
152
|
+
return dspy.ChainOfThought(CodingAssistantSignature)
|
|
153
|
+
|
|
154
|
+
def process_message(self, user_message: str) -> Dict[str, Any]:
|
|
155
|
+
"""
|
|
156
|
+
Process a user message and generate a response.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
user_message: The user's message
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Dict with 'response' and optional 'tool_calls'
|
|
163
|
+
"""
|
|
164
|
+
logger.info(f"Processing message: {user_message[:100]}...")
|
|
165
|
+
|
|
166
|
+
# Add user message to history
|
|
167
|
+
self.messages.append({"role": "user", "content": user_message})
|
|
168
|
+
|
|
169
|
+
# Format chat history for DSPy
|
|
170
|
+
chat_history_str = self._format_chat_history()
|
|
171
|
+
|
|
172
|
+
# Get response from agent
|
|
173
|
+
try:
|
|
174
|
+
result = self.agent(
|
|
175
|
+
chat_history=chat_history_str,
|
|
176
|
+
user_message=user_message,
|
|
177
|
+
workspace_root=str(self.workspace_root),
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
response = result.response
|
|
181
|
+
tool_calls = result.tool_calls if hasattr(result, "tool_calls") else []
|
|
182
|
+
|
|
183
|
+
# Add assistant response to history
|
|
184
|
+
self.messages.append({"role": "assistant", "content": response})
|
|
185
|
+
|
|
186
|
+
logger.info(f"Generated response: {response[:100]}...")
|
|
187
|
+
|
|
188
|
+
return {"response": response, "tool_calls": tool_calls}
|
|
189
|
+
|
|
190
|
+
except Exception as e:
|
|
191
|
+
logger.error(f"Error processing message: {e}", exc_info=True)
|
|
192
|
+
error_response = f"I encountered an error: {str(e)}"
|
|
193
|
+
|
|
194
|
+
self.messages.append({"role": "assistant", "content": error_response})
|
|
195
|
+
|
|
196
|
+
return {"response": error_response, "tool_calls": []}
|
|
197
|
+
|
|
198
|
+
def _format_chat_history(self) -> str:
|
|
199
|
+
"""Format chat history as a string for the agent."""
|
|
200
|
+
if not self.messages:
|
|
201
|
+
return "No previous messages."
|
|
202
|
+
|
|
203
|
+
formatted = []
|
|
204
|
+
for msg in self.messages[-10:]: # Last 10 messages
|
|
205
|
+
role = msg["role"].capitalize()
|
|
206
|
+
content = msg["content"]
|
|
207
|
+
formatted.append(f"{role}: {content}")
|
|
208
|
+
|
|
209
|
+
return "\n".join(formatted)
|
|
210
|
+
|
|
211
|
+
# Tool methods that can be called by the agent
|
|
212
|
+
|
|
213
|
+
def read_file(self, path: str) -> str:
|
|
214
|
+
"""
|
|
215
|
+
Read a file from the workspace.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
path: Relative path to file within workspace
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
File contents as string
|
|
222
|
+
"""
|
|
223
|
+
try:
|
|
224
|
+
logger.info(f"Tool call: read_file({path})")
|
|
225
|
+
content = self.file_primitive.read(path)
|
|
226
|
+
return content
|
|
227
|
+
except Exception as e:
|
|
228
|
+
logger.error(f"Error reading file {path}: {e}")
|
|
229
|
+
return f"Error reading file: {str(e)}"
|
|
230
|
+
|
|
231
|
+
def write_file(self, path: str, content: str) -> str:
|
|
232
|
+
"""
|
|
233
|
+
Write content to a file in the workspace.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
path: Relative path to file within workspace
|
|
237
|
+
content: Content to write
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Success message or error
|
|
241
|
+
"""
|
|
242
|
+
try:
|
|
243
|
+
logger.info(f"Tool call: write_file({path}, {len(content)} bytes)")
|
|
244
|
+
self.file_primitive.write(path, content)
|
|
245
|
+
return f"Successfully wrote {len(content)} bytes to {path}"
|
|
246
|
+
except Exception as e:
|
|
247
|
+
logger.error(f"Error writing file {path}: {e}")
|
|
248
|
+
return f"Error writing file: {str(e)}"
|
|
249
|
+
|
|
250
|
+
def list_directory(self, path: str = ".") -> str:
|
|
251
|
+
"""
|
|
252
|
+
List files in a directory within the workspace.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
path: Relative path to directory within workspace
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Formatted list of files and directories
|
|
259
|
+
"""
|
|
260
|
+
try:
|
|
261
|
+
logger.info(f"Tool call: list_directory({path})")
|
|
262
|
+
|
|
263
|
+
# Resolve path safely
|
|
264
|
+
target_path = self.file_primitive._resolve_path(path)
|
|
265
|
+
|
|
266
|
+
if not target_path.exists():
|
|
267
|
+
return f"Directory not found: {path}"
|
|
268
|
+
|
|
269
|
+
if not target_path.is_dir():
|
|
270
|
+
return f"Not a directory: {path}"
|
|
271
|
+
|
|
272
|
+
# List contents
|
|
273
|
+
entries = []
|
|
274
|
+
for item in sorted(target_path.iterdir(), key=lambda x: (not x.is_dir(), x.name)):
|
|
275
|
+
if item.is_dir():
|
|
276
|
+
entries.append(f"📁 {item.name}/")
|
|
277
|
+
else:
|
|
278
|
+
size = item.stat().st_size
|
|
279
|
+
entries.append(f"📄 {item.name} ({size} bytes)")
|
|
280
|
+
|
|
281
|
+
if not entries:
|
|
282
|
+
return f"Directory is empty: {path}"
|
|
283
|
+
|
|
284
|
+
return "\n".join(entries)
|
|
285
|
+
|
|
286
|
+
except Exception as e:
|
|
287
|
+
logger.error(f"Error listing directory {path}: {e}")
|
|
288
|
+
return f"Error listing directory: {str(e)}"
|
|
289
|
+
|
|
290
|
+
def file_exists(self, path: str) -> bool:
|
|
291
|
+
"""
|
|
292
|
+
Check if a file exists in the workspace.
|
|
293
|
+
|
|
294
|
+
Args:
|
|
295
|
+
path: Relative path to file within workspace
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
True if file exists, False otherwise
|
|
299
|
+
"""
|
|
300
|
+
try:
|
|
301
|
+
logger.info(f"Tool call: file_exists({path})")
|
|
302
|
+
return self.file_primitive.exists(path)
|
|
303
|
+
except Exception as e:
|
|
304
|
+
logger.error(f"Error checking file existence {path}: {e}")
|
|
305
|
+
return False
|
|
306
|
+
|
|
307
|
+
def reset_conversation(self):
|
|
308
|
+
"""Clear conversation history."""
|
|
309
|
+
logger.info("Resetting conversation history")
|
|
310
|
+
self.messages.clear()
|
|
311
|
+
|
|
312
|
+
def get_available_tools(self) -> List[Dict[str, Any]]:
|
|
313
|
+
"""
|
|
314
|
+
Get list of available tools for the agent.
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
List of tool definitions
|
|
318
|
+
"""
|
|
319
|
+
return [
|
|
320
|
+
{
|
|
321
|
+
"name": "read_file",
|
|
322
|
+
"description": "Read contents of a file in the workspace",
|
|
323
|
+
"parameters": {"path": "Relative path to the file"},
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"name": "write_file",
|
|
327
|
+
"description": "Write content to a file in the workspace",
|
|
328
|
+
"parameters": {
|
|
329
|
+
"path": "Relative path to the file",
|
|
330
|
+
"content": "Content to write to the file",
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"name": "list_directory",
|
|
335
|
+
"description": "List files and directories in the workspace",
|
|
336
|
+
"parameters": {"path": "Relative path to directory (default: current directory)"},
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"name": "file_exists",
|
|
340
|
+
"description": "Check if a file exists in the workspace",
|
|
341
|
+
"parameters": {"path": "Relative path to the file"},
|
|
342
|
+
},
|
|
343
|
+
]
|