CWD in projects code exec

frdel committed Nov 4, 2025 at 18:48 UTC a7f74f907819ade41ce8ed109e6eb259ded78216
8 files changed +36 -8
prompts/agent.system.main.tips.md
+1 -1
@@ -7,7 +7,7 @@ never assume success
7 memory refers memory tools not own knowledge
8
9 ## Files
10 -save files in /root
10 +when not in project save files in /root
11 don't use spaces in file names
12
13 ## Instruments
prompts/agent.system.projects.active.md
+2 -1
@@ -6,6 +6,7 @@ Description: {{project_description}}
6
7 ### Important project instructions MUST follow
8 - always work inside {{project_path}} directory
9 -- do not rename project directory, do no change meta files in .a0proj folder
9 +- do not rename project directory do not change meta files in .a0proj folder
10 +- cleanup when code accidentaly creates files outside move them
11
12 {{project_instructions}}
\ No newline at end of file
python/helpers/files.py
+8
@@ -423,6 +423,14 @@ def fix_dev_path(path: str):
423 return get_abs_path(path)
424
425
426 +def normalize_a0_path(path: str):
427 + "Convert absolute paths into /a0/... paths"
428 + if is_in_base_dir(path):
429 + deabs = deabsolute_path(path)
430 + return "/a0/" + deabs
431 + return path
432 +
433 +
434 def exists(*relative_paths):
435 path = get_abs_path(*relative_paths)
436 return os.path.exists(path)
python/helpers/projects.py
+1
@@ -244,6 +244,7 @@ def build_system_prompt_vars(name: str):
244 "project_name": project_data.get("title", ""),
245 "project_description": project_data.get("description", ""),
246 "project_instructions": complete_instructions or "",
247 + "project_path": files.normalize_a0_path(get_project_folder(name)),
248 }
249
250
python/helpers/shell_local.py
+3 -2
@@ -7,12 +7,13 @@ from python.helpers import tty_session
7 from python.helpers.shell_ssh import clean_string
8
9 class LocalInteractiveSession:
10 - def __init__(self):
10 + def __init__(self, cwd: str|None = None):
11 self.session: tty_session.TTYSession|None = None
12 self.full_output = ''
13 + self.cwd = cwd
14
15 async def connect(self):
15 - self.session = tty_session.TTYSession("/bin/bash")
16 + self.session = tty_session.TTYSession("/bin/bash", cwd=self.cwd)
17 await self.session.start()
18 await self.session.read_full_until_idle(idle_timeout=1, total_timeout=1)
19
python/helpers/shell_ssh.py
+7 -2
@@ -14,7 +14,7 @@ class SSHInteractiveSession:
14 # ps1_label = "SSHInteractiveSession CLI>"
15
16 def __init__(
17 - self, logger: Log, hostname: str, port: int, username: str, password: str
17 + self, logger: Log, hostname: str, port: int, username: str, password: str, cwd: str|None = None
18 ):
19 self.logger = logger
20 self.hostname = hostname
@@ -27,6 +27,7 @@ class SSHInteractiveSession:
27 self.full_output = b""
28 self.last_command = b""
29 self.trimmed_command_length = 0 # Initialize trimmed_command_length
30 + self.cwd = cwd
31
32 async def connect(self, keepalive_interval: int = 5):
33 """
@@ -60,7 +61,11 @@ class SSHInteractiveSession:
61
62 # invoke interactive shell
63 self.shell = self.client.invoke_shell(width=100, height=50)
63 - self.shell.send("stty -echo\n".encode()) # disable local echo
64 +
65 + initial_command = "stty -echo"
66 + if self.cwd:
67 + initial_command = f"cd {self.cwd}; {initial_command}"
68 + self.shell.send(f"{initial_command}\n".encode())
69
70 # wait for initial prompt/output to settle
71 while True:
python/tools/code_execution_tool.py
+14 -2
@@ -3,7 +3,7 @@ from dataclasses import dataclass
3 import shlex
4 import time
5 from python.helpers.tool import Tool, Response
6 -from python.helpers import files, rfc_exchange
6 +from python.helpers import files, rfc_exchange, projects
7 from python.helpers.print_style import PrintStyle
8 from python.helpers.shell_local import LocalInteractiveSession
9 from python.helpers.shell_ssh import SSHInteractiveSession
@@ -146,9 +146,10 @@ class CodeExecution(Tool):
146 self.agent.config.code_exec_ssh_port,
147 self.agent.config.code_exec_ssh_user,
148 pswd,
149 + cwd=self.get_cwd(),
150 )
151 else:
151 - shell = LocalInteractiveSession()
152 + shell = LocalInteractiveSession(cwd=self.get_cwd())
153
154 shells[session] = ShellWrap(id=session, session=shell, running=False)
155 await shell.connect()
@@ -469,3 +470,14 @@ class CodeExecution(Tool):
470 output = "\n".join(line.strip() for line in output.splitlines())
471 output = truncate_text_agent(agent=self.agent, output=output, threshold=1000000) # ~1MB, larger outputs should be dumped to file, not read from terminal
472 return output
473 +
474 + def get_cwd(self):
475 + project_name = projects.get_context_project_name(self.agent.context)
476 + if not project_name:
477 + return None
478 + project_path = projects.get_project_folder(project_name)
479 + normalized = files.normalize_a0_path(project_path)
480 + return normalized
481 +
482 +
483 +
\ No newline at end of file
usr/.gitkeep