diff --git a/agents/s01_agent_loop.py b/agents/s01_agent_loop.py
index 8455ebff4..e498f751a 100644
--- a/agents/s01_agent_loop.py
+++ b/agents/s01_agent_loop.py
@@ -68,7 +68,7 @@ def run_bash(command: str) -> str:
return "Error: Dangerous command blocked"
try:
r = subprocess.run(command, shell=True, cwd=os.getcwd(),
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
diff --git a/agents/s02_tool_use.py b/agents/s02_tool_use.py
index deff34143..0de346a03 100644
--- a/agents/s02_tool_use.py
+++ b/agents/s02_tool_use.py
@@ -61,7 +61,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int = None) -> str:
try:
- text = safe_path(path).read_text()
+ text = safe_path(path).read_text(encoding="utf-8")
lines = text.splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
@@ -74,7 +74,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -83,7 +83,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
- content = fp.read_text()
+ content = fp.read_text(encoding="utf-8")
if old_text not in content:
return f"Error: Text not found in {path}"
fp.write_text(content.replace(old_text, new_text, 1))
diff --git a/agents/s03_todo_write.py b/agents/s03_todo_write.py
index 4c7076c55..f3e3c08a4 100644
--- a/agents/s03_todo_write.py
+++ b/agents/s03_todo_write.py
@@ -102,7 +102,7 @@ def run_bash(command: str) -> str:
return "Error: Dangerous command blocked"
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -110,7 +110,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
@@ -121,7 +121,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes"
except Exception as e:
return f"Error: {e}"
@@ -129,7 +129,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
- content = fp.read_text()
+ content = fp.read_text(encoding="utf-8")
if old_text not in content:
return f"Error: Text not found in {path}"
fp.write_text(content.replace(old_text, new_text, 1))
diff --git a/agents/s04_subagent.py b/agents/s04_subagent.py
index dda2737f6..257aac48b 100644
--- a/agents/s04_subagent.py
+++ b/agents/s04_subagent.py
@@ -56,7 +56,7 @@ def run_bash(command: str) -> str:
return "Error: Dangerous command blocked"
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -66,7 +66,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
@@ -77,7 +77,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes"
except Exception as e:
return f"Error: {e}"
@@ -85,7 +85,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
- content = fp.read_text()
+ content = fp.read_text(encoding="utf-8")
if old_text not in content:
return f"Error: Text not found in {path}"
fp.write_text(content.replace(old_text, new_text, 1))
diff --git a/agents/s05_skill_loading.py b/agents/s05_skill_loading.py
index e14167a6c..1c51ead52 100644
--- a/agents/s05_skill_loading.py
+++ b/agents/s05_skill_loading.py
@@ -66,7 +66,7 @@ def _load_all(self):
if not self.skills_dir.exists():
return
for f in sorted(self.skills_dir.rglob("SKILL.md")):
- text = f.read_text()
+ text = f.read_text(encoding="utf-8")
meta, body = self._parse_frontmatter(text)
name = meta.get("name", f.parent.name)
self.skills[name] = {"meta": meta, "body": body, "path": str(f)}
@@ -127,7 +127,7 @@ def run_bash(command: str) -> str:
return "Error: Dangerous command blocked"
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -135,7 +135,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
@@ -146,7 +146,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes"
except Exception as e:
return f"Error: {e}"
@@ -154,7 +154,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
- content = fp.read_text()
+ content = fp.read_text(encoding="utf-8")
if old_text not in content:
return f"Error: Text not found in {path}"
fp.write_text(content.replace(old_text, new_text, 1))
diff --git a/agents/s06_context_compact.py b/agents/s06_context_compact.py
index 79bbe9243..b3789b906 100644
--- a/agents/s06_context_compact.py
+++ b/agents/s06_context_compact.py
@@ -144,7 +144,7 @@ def run_bash(command: str) -> str:
return "Error: Dangerous command blocked"
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -152,7 +152,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
@@ -163,7 +163,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes"
except Exception as e:
return f"Error: {e}"
@@ -171,7 +171,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
- content = fp.read_text()
+ content = fp.read_text(encoding="utf-8")
if old_text not in content:
return f"Error: Text not found in {path}"
fp.write_text(content.replace(old_text, new_text, 1))
diff --git a/agents/s07_task_system.py b/agents/s07_task_system.py
index cf72783e4..b3fe18ecf 100644
--- a/agents/s07_task_system.py
+++ b/agents/s07_task_system.py
@@ -58,7 +58,7 @@ def _load(self, task_id: int) -> dict:
path = self.dir / f"task_{task_id}.json"
if not path.exists():
raise ValueError(f"Task {task_id} not found")
- return json.loads(path.read_text())
+ return json.loads(path.read_text(encoding="utf-8"))
def _save(self, task: dict):
path = self.dir / f"task_{task['id']}.json"
@@ -95,7 +95,7 @@ def update(self, task_id: int, status: str = None,
def _clear_dependency(self, completed_id: int):
"""Remove completed_id from all other tasks' blockedBy lists."""
for f in self.dir.glob("task_*.json"):
- task = json.loads(f.read_text())
+ task = json.loads(f.read_text(encoding="utf-8"))
if completed_id in task.get("blockedBy", []):
task["blockedBy"].remove(completed_id)
self._save(task)
@@ -107,7 +107,7 @@ def list_all(self) -> str:
key=lambda f: int(f.stem.split("_")[1])
)
for f in files:
- tasks.append(json.loads(f.read_text()))
+ tasks.append(json.loads(f.read_text(encoding="utf-8")))
if not tasks:
return "No tasks."
lines = []
@@ -134,7 +134,7 @@ def run_bash(command: str) -> str:
return "Error: Dangerous command blocked"
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -142,7 +142,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
@@ -153,7 +153,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes"
except Exception as e:
return f"Error: {e}"
@@ -161,7 +161,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
- c = fp.read_text()
+ c = fp.read_text(encoding="utf-8")
if old_text not in c:
return f"Error: Text not found in {path}"
fp.write_text(c.replace(old_text, new_text, 1))
diff --git a/agents/s08_background_tasks.py b/agents/s08_background_tasks.py
index 390a77780..5c7f7f952 100644
--- a/agents/s08_background_tasks.py
+++ b/agents/s08_background_tasks.py
@@ -68,7 +68,7 @@ def _execute(self, task_id: str, command: str):
try:
r = subprocess.run(
command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=300
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=300
)
output = (r.stdout + r.stderr).strip()[:50000]
status = "completed"
@@ -124,7 +124,7 @@ def run_bash(command: str) -> str:
return "Error: Dangerous command blocked"
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -132,7 +132,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
@@ -143,7 +143,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes"
except Exception as e:
return f"Error: {e}"
@@ -151,7 +151,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
- c = fp.read_text()
+ c = fp.read_text(encoding="utf-8")
if old_text not in c:
return f"Error: Text not found in {path}"
fp.write_text(c.replace(old_text, new_text, 1))
diff --git a/agents/s09_agent_teams.py b/agents/s09_agent_teams.py
index bd666552a..9df8d3fdf 100644
--- a/agents/s09_agent_teams.py
+++ b/agents/s09_agent_teams.py
@@ -102,7 +102,7 @@ def read_inbox(self, name: str) -> list:
if not inbox_path.exists():
return []
messages = []
- for line in inbox_path.read_text().strip().splitlines():
+ for line in inbox_path.read_text(encoding="utf-8").strip().splitlines():
if line:
messages.append(json.loads(line))
inbox_path.write_text("")
@@ -131,7 +131,7 @@ def __init__(self, team_dir: Path):
def _load_config(self) -> dict:
if self.config_path.exists():
- return json.loads(self.config_path.read_text())
+ return json.loads(self.config_path.read_text(encoding="utf-8"))
return {"team_name": "default", "members": []}
def _save_config(self):
@@ -266,7 +266,7 @@ def _run_bash(command: str) -> str:
try:
r = subprocess.run(
command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120,
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120,
)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
@@ -276,7 +276,7 @@ def _run_bash(command: str) -> str:
def _run_read(path: str, limit: int = None) -> str:
try:
- lines = _safe_path(path).read_text().splitlines()
+ lines = _safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
@@ -288,7 +288,7 @@ def _run_write(path: str, content: str) -> str:
try:
fp = _safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes"
except Exception as e:
return f"Error: {e}"
@@ -297,7 +297,7 @@ def _run_write(path: str, content: str) -> str:
def _run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = _safe_path(path)
- c = fp.read_text()
+ c = fp.read_text(encoding="utf-8")
if old_text not in c:
return f"Error: Text not found in {path}"
fp.write_text(c.replace(old_text, new_text, 1))
diff --git a/agents/s10_team_protocols.py b/agents/s10_team_protocols.py
index 3f9923da2..6fc730145 100644
--- a/agents/s10_team_protocols.py
+++ b/agents/s10_team_protocols.py
@@ -112,7 +112,7 @@ def read_inbox(self, name: str) -> list:
if not inbox_path.exists():
return []
messages = []
- for line in inbox_path.read_text().strip().splitlines():
+ for line in inbox_path.read_text(encoding="utf-8").strip().splitlines():
if line:
messages.append(json.loads(line))
inbox_path.write_text("")
@@ -141,7 +141,7 @@ def __init__(self, team_dir: Path):
def _load_config(self) -> dict:
if self.config_path.exists():
- return json.loads(self.config_path.read_text())
+ return json.loads(self.config_path.read_text(encoding="utf-8"))
return {"team_name": "default", "members": []}
def _save_config(self):
@@ -307,7 +307,7 @@ def _run_bash(command: str) -> str:
try:
r = subprocess.run(
command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120,
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120,
)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
@@ -317,7 +317,7 @@ def _run_bash(command: str) -> str:
def _run_read(path: str, limit: int = None) -> str:
try:
- lines = _safe_path(path).read_text().splitlines()
+ lines = _safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
@@ -329,7 +329,7 @@ def _run_write(path: str, content: str) -> str:
try:
fp = _safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes"
except Exception as e:
return f"Error: {e}"
@@ -338,7 +338,7 @@ def _run_write(path: str, content: str) -> str:
def _run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = _safe_path(path)
- c = fp.read_text()
+ c = fp.read_text(encoding="utf-8")
if old_text not in c:
return f"Error: Text not found in {path}"
fp.write_text(c.replace(old_text, new_text, 1))
diff --git a/agents/s11_autonomous_agents.py b/agents/s11_autonomous_agents.py
index c3a62cdaa..2687ee857 100644
--- a/agents/s11_autonomous_agents.py
+++ b/agents/s11_autonomous_agents.py
@@ -105,7 +105,7 @@ def read_inbox(self, name: str) -> list:
if not inbox_path.exists():
return []
messages = []
- for line in inbox_path.read_text().strip().splitlines():
+ for line in inbox_path.read_text(encoding="utf-8").strip().splitlines():
if line:
messages.append(json.loads(line))
inbox_path.write_text("")
@@ -128,7 +128,7 @@ def scan_unclaimed_tasks() -> list:
TASKS_DIR.mkdir(exist_ok=True)
unclaimed = []
for f in sorted(TASKS_DIR.glob("task_*.json")):
- task = json.loads(f.read_text())
+ task = json.loads(f.read_text(encoding="utf-8"))
if (task.get("status") == "pending"
and not task.get("owner")
and not task.get("blockedBy")):
@@ -141,7 +141,7 @@ def claim_task(task_id: int, owner: str) -> str:
path = TASKS_DIR / f"task_{task_id}.json"
if not path.exists():
return f"Error: Task {task_id} not found"
- task = json.loads(path.read_text())
+ task = json.loads(path.read_text(encoding="utf-8"))
if task.get("owner"):
existing_owner = task.get("owner") or "someone else"
return f"Error: Task {task_id} has already been claimed by {existing_owner}"
@@ -175,7 +175,7 @@ def __init__(self, team_dir: Path):
def _load_config(self) -> dict:
if self.config_path.exists():
- return json.loads(self.config_path.read_text())
+ return json.loads(self.config_path.read_text(encoding="utf-8"))
return {"team_name": "default", "members": []}
def _save_config(self):
@@ -395,7 +395,7 @@ def _run_bash(command: str) -> str:
try:
r = subprocess.run(
command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120,
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120,
)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
@@ -405,7 +405,7 @@ def _run_bash(command: str) -> str:
def _run_read(path: str, limit: int = None) -> str:
try:
- lines = _safe_path(path).read_text().splitlines()
+ lines = _safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
@@ -417,7 +417,7 @@ def _run_write(path: str, content: str) -> str:
try:
fp = _safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes"
except Exception as e:
return f"Error: {e}"
@@ -426,7 +426,7 @@ def _run_write(path: str, content: str) -> str:
def _run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = _safe_path(path)
- c = fp.read_text()
+ c = fp.read_text(encoding="utf-8")
if old_text not in c:
return f"Error: Text not found in {path}"
fp.write_text(c.replace(old_text, new_text, 1))
@@ -571,7 +571,7 @@ def agent_loop(messages: list):
if query.strip() == "/tasks":
TASKS_DIR.mkdir(exist_ok=True)
for f in sorted(TASKS_DIR.glob("task_*.json")):
- t = json.loads(f.read_text())
+ t = json.loads(f.read_text(encoding="utf-8"))
marker = {"pending": "[ ]", "in_progress": "[>]", "completed": "[x]"}.get(t["status"], "[?]")
owner = f" @{t['owner']}" if t.get("owner") else ""
print(f" {marker} #{t['id']}: {t['subject']}{owner}")
diff --git a/agents/s12_worktree_task_isolation.py b/agents/s12_worktree_task_isolation.py
index 09f905253..f05363ca3 100644
--- a/agents/s12_worktree_task_isolation.py
+++ b/agents/s12_worktree_task_isolation.py
@@ -57,7 +57,7 @@ def detect_repo_root(cwd: Path) -> Path | None:
["git", "rev-parse", "--show-toplevel"],
cwd=cwd,
capture_output=True,
- text=True,
+ text=True, encoding="utf-8", errors="replace",
timeout=10,
)
if r.returncode != 0:
@@ -141,7 +141,7 @@ def _load(self, task_id: int) -> dict:
path = self._path(task_id)
if not path.exists():
raise ValueError(f"Task {task_id} not found")
- return json.loads(path.read_text())
+ return json.loads(path.read_text(encoding="utf-8"))
def _save(self, task: dict):
self._path(task["id"]).write_text(json.dumps(task, indent=2))
@@ -201,7 +201,7 @@ def unbind_worktree(self, task_id: int) -> str:
def list_all(self) -> str:
tasks = []
for f in sorted(self.dir.glob("task_*.json")):
- tasks.append(json.loads(f.read_text()))
+ tasks.append(json.loads(f.read_text(encoding="utf-8")))
if not tasks:
return "No tasks."
lines = []
@@ -240,7 +240,7 @@ def _is_git_repo(self) -> bool:
["git", "rev-parse", "--is-inside-work-tree"],
cwd=self.repo_root,
capture_output=True,
- text=True,
+ text=True, encoding="utf-8", errors="replace",
timeout=10,
)
return r.returncode == 0
@@ -254,7 +254,7 @@ def _run_git(self, args: list[str]) -> str:
["git", *args],
cwd=self.repo_root,
capture_output=True,
- text=True,
+ text=True, encoding="utf-8", errors="replace",
timeout=120,
)
if r.returncode != 0:
@@ -263,7 +263,7 @@ def _run_git(self, args: list[str]) -> str:
return (r.stdout + r.stderr).strip() or "(no output)"
def _load_index(self) -> dict:
- return json.loads(self.index_path.read_text())
+ return json.loads(self.index_path.read_text(encoding="utf-8"))
def _save_index(self, data: dict):
self.index_path.write_text(json.dumps(data, indent=2))
@@ -359,7 +359,7 @@ def status(self, name: str) -> str:
["git", "status", "--short", "--branch"],
cwd=path,
capture_output=True,
- text=True,
+ text=True, encoding="utf-8", errors="replace",
timeout=60,
)
text = (r.stdout + r.stderr).strip()
@@ -383,7 +383,7 @@ def run(self, name: str, command: str) -> str:
shell=True,
cwd=path,
capture_output=True,
- text=True,
+ text=True, encoding="utf-8", errors="replace",
timeout=300,
)
out = (r.stdout + r.stderr).strip()
@@ -492,7 +492,7 @@ def run_bash(command: str) -> str:
shell=True,
cwd=WORKDIR,
capture_output=True,
- text=True,
+ text=True, encoding="utf-8", errors="replace",
timeout=120,
)
out = (r.stdout + r.stderr).strip()
@@ -503,7 +503,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
@@ -515,7 +515,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes"
except Exception as e:
return f"Error: {e}"
@@ -524,7 +524,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
- c = fp.read_text()
+ c = fp.read_text(encoding="utf-8")
if old_text not in c:
return f"Error: Text not found in {path}"
fp.write_text(c.replace(old_text, new_text, 1))
diff --git a/agents/s_full.py b/agents/s_full.py
index e2f887b5c..f694d0ea1 100644
--- a/agents/s_full.py
+++ b/agents/s_full.py
@@ -83,7 +83,7 @@ def run_bash(command: str) -> str:
return "Error: Dangerous command blocked"
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -91,7 +91,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more)"]
return "\n".join(lines)[:50000]
@@ -102,7 +102,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -110,7 +110,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
fp = safe_path(path)
- c = fp.read_text()
+ c = fp.read_text(encoding="utf-8")
if old_text not in c:
return f"Error: Text not found in {path}"
fp.write_text(c.replace(old_text, new_text, 1))
@@ -201,7 +201,7 @@ def __init__(self, skills_dir: Path):
self.skills = {}
if skills_dir.exists():
for f in sorted(skills_dir.rglob("SKILL.md")):
- text = f.read_text()
+ text = f.read_text(encoding="utf-8")
match = re.match(r"^---\n(.*?)\n---\n(.*)", text, re.DOTALL)
meta, body = {}, text
if match:
@@ -270,7 +270,7 @@ def _next_id(self) -> int:
def _load(self, tid: int) -> dict:
p = TASKS_DIR / f"task_{tid}.json"
if not p.exists(): raise ValueError(f"Task {tid} not found")
- return json.loads(p.read_text())
+ return json.loads(p.read_text(encoding="utf-8"))
def _save(self, task: dict):
(TASKS_DIR / f"task_{task['id']}.json").write_text(json.dumps(task, indent=2))
@@ -291,7 +291,7 @@ def update(self, tid: int, status: str = None,
task["status"] = status
if status == "completed":
for f in TASKS_DIR.glob("task_*.json"):
- t = json.loads(f.read_text())
+ t = json.loads(f.read_text(encoding="utf-8"))
if tid in t.get("blockedBy", []):
t["blockedBy"].remove(tid)
self._save(t)
@@ -306,7 +306,7 @@ def update(self, tid: int, status: str = None,
return json.dumps(task, indent=2)
def list_all(self) -> str:
- tasks = [json.loads(f.read_text()) for f in sorted(TASKS_DIR.glob("task_*.json"))]
+ tasks = [json.loads(f.read_text(encoding="utf-8")) for f in sorted(TASKS_DIR.glob("task_*.json"))]
if not tasks: return "No tasks."
lines = []
for t in tasks:
@@ -339,7 +339,7 @@ def run(self, command: str, timeout: int = 120) -> str:
def _exec(self, tid: str, command: str, timeout: int):
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=timeout)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=timeout)
output = (r.stdout + r.stderr).strip()[:50000]
self.tasks[tid].update({"status": "completed", "result": output or "(no output)"})
except Exception as e:
@@ -377,7 +377,7 @@ def send(self, sender: str, to: str, content: str,
def read_inbox(self, name: str) -> list:
path = INBOX_DIR / f"{name}.jsonl"
if not path.exists(): return []
- msgs = [json.loads(l) for l in path.read_text().strip().splitlines() if l]
+ msgs = [json.loads(l) for l in path.read_text(encoding="utf-8").strip().splitlines() if l]
path.write_text("")
return msgs
@@ -407,7 +407,7 @@ def __init__(self, bus: MessageBus, task_mgr: TaskManager):
def _load(self) -> dict:
if self.config_path.exists():
- return json.loads(self.config_path.read_text())
+ return json.loads(self.config_path.read_text(encoding="utf-8"))
return {"team_name": "default", "members": []}
def _save(self):
@@ -509,7 +509,7 @@ def _loop(self, name: str, role: str, prompt: str):
break
unclaimed = []
for f in sorted(TASKS_DIR.glob("task_*.json")):
- t = json.loads(f.read_text())
+ t = json.loads(f.read_text(encoding="utf-8"))
if t.get("status") == "pending" and not t.get("owner") and not t.get("blockedBy"):
unclaimed.append(t)
if unclaimed:
diff --git a/s01_agent_loop/code.py b/s01_agent_loop/code.py
index 6a4459d3b..c059bdff1 100644
--- a/s01_agent_loop/code.py
+++ b/s01_agent_loop/code.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+ #!/usr/bin/env python3
"""
s01_agent_loop.py - The Agent Loop
@@ -72,7 +72,7 @@ def run_bash(command: str) -> str:
return "Error: Dangerous command blocked"
try:
r = subprocess.run(command, shell=True, cwd=os.getcwd(),
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
diff --git a/s02_tool_use/code.py b/s02_tool_use/code.py
index e6575119a..3a28652ca 100644
--- a/s02_tool_use/code.py
+++ b/s02_tool_use/code.py
@@ -72,7 +72,7 @@ def safe_path(p: str) -> Path:
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -84,7 +84,7 @@ def run_write(path: str, content: str) -> str:
try:
file_path = safe_path(path)
file_path.parent.mkdir(parents=True, exist_ok=True)
- file_path.write_text(content)
+ file_path.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -93,7 +93,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
file_path = safe_path(path)
- text = file_path.read_text()
+ text = file_path.read_text(encoding="utf-8")
if old_text not in text:
return f"Error: text not found in {path}"
file_path.write_text(text.replace(old_text, new_text, 1))
diff --git a/s03_permission/code.py b/s03_permission/code.py
index ba869c7db..790c9aefd 100644
--- a/s03_permission/code.py
+++ b/s03_permission/code.py
@@ -67,7 +67,7 @@ def safe_path(p: str) -> Path:
def run_bash(command: str) -> str:
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -76,7 +76,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -88,7 +88,7 @@ def run_write(path: str, content: str) -> str:
try:
file_path = safe_path(path)
file_path.parent.mkdir(parents=True, exist_ok=True)
- file_path.write_text(content)
+ file_path.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -97,7 +97,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
file_path = safe_path(path)
- text = file_path.read_text()
+ text = file_path.read_text(encoding="utf-8")
if old_text not in text:
return f"Error: text not found in {path}"
file_path.write_text(text.replace(old_text, new_text, 1))
diff --git a/s04_hooks/code.py b/s04_hooks/code.py
index 756bf7d6d..eab863ab5 100644
--- a/s04_hooks/code.py
+++ b/s04_hooks/code.py
@@ -87,7 +87,7 @@ def safe_path(p: str) -> Path:
def run_bash(command: str) -> str:
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -95,7 +95,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -106,7 +106,7 @@ def run_write(path: str, content: str) -> str:
try:
file_path = safe_path(path)
file_path.parent.mkdir(parents=True, exist_ok=True)
- file_path.write_text(content)
+ file_path.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -114,7 +114,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
file_path = safe_path(path)
- text = file_path.read_text()
+ text = file_path.read_text(encoding="utf-8")
if old_text not in text:
return f"Error: text not found in {path}"
file_path.write_text(text.replace(old_text, new_text, 1))
diff --git a/s05_todo_write/code.py b/s05_todo_write/code.py
index 9c88de359..db7467f28 100644
--- a/s05_todo_write/code.py
+++ b/s05_todo_write/code.py
@@ -70,7 +70,7 @@ def safe_path(p: str) -> Path:
def run_bash(command: str) -> str:
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -78,7 +78,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -89,7 +89,7 @@ def run_write(path: str, content: str) -> str:
try:
file_path = safe_path(path)
file_path.parent.mkdir(parents=True, exist_ok=True)
- file_path.write_text(content)
+ file_path.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -97,7 +97,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
file_path = safe_path(path)
- text = file_path.read_text()
+ text = file_path.read_text(encoding="utf-8")
if old_text not in text:
return f"Error: text not found in {path}"
file_path.write_text(text.replace(old_text, new_text, 1))
diff --git a/s06_subagent/code.py b/s06_subagent/code.py
index 2732246bf..a2fd7c38d 100644
--- a/s06_subagent/code.py
+++ b/s06_subagent/code.py
@@ -75,7 +75,7 @@ def safe_path(p: str) -> Path:
def run_bash(command: str) -> str:
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -83,7 +83,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -94,7 +94,7 @@ def run_write(path: str, content: str) -> str:
try:
file_path = safe_path(path)
file_path.parent.mkdir(parents=True, exist_ok=True)
- file_path.write_text(content)
+ file_path.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -102,7 +102,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
file_path = safe_path(path)
- text = file_path.read_text()
+ text = file_path.read_text(encoding="utf-8")
if old_text not in text:
return f"Error: text not found in {path}"
file_path.write_text(text.replace(old_text, new_text, 1))
diff --git a/s07_skill_loading/code.py b/s07_skill_loading/code.py
index b4506fc57..140aa0791 100644
--- a/s07_skill_loading/code.py
+++ b/s07_skill_loading/code.py
@@ -75,7 +75,7 @@ def _scan_skills():
continue
manifest = d / "SKILL.md"
if manifest.exists():
- raw = manifest.read_text()
+ raw = manifest.read_text(encoding="utf-8")
meta, body = _parse_frontmatter(raw)
name = meta.get("name", d.name)
desc = meta.get("description", raw.split("\n")[0].lstrip("#").strip())
@@ -122,7 +122,7 @@ def safe_path(p: str) -> Path:
def run_bash(command: str) -> str:
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -130,7 +130,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -141,7 +141,7 @@ def run_write(path: str, content: str) -> str:
try:
file_path = safe_path(path)
file_path.parent.mkdir(parents=True, exist_ok=True)
- file_path.write_text(content)
+ file_path.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -149,7 +149,7 @@ def run_write(path: str, content: str) -> str:
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
file_path = safe_path(path)
- text = file_path.read_text()
+ text = file_path.read_text(encoding="utf-8")
if old_text not in text:
return f"Error: text not found in {path}"
file_path.write_text(text.replace(old_text, new_text, 1))
diff --git a/s08_context_compact/code.py b/s08_context_compact/code.py
index 7186df554..ffa922860 100644
--- a/s08_context_compact/code.py
+++ b/s08_context_compact/code.py
@@ -79,7 +79,7 @@ def _scan_skills():
continue
manifest = d / "SKILL.md"
if manifest.exists():
- raw = manifest.read_text()
+ raw = manifest.read_text(encoding="utf-8")
meta, body = _parse_frontmatter(raw)
name = meta.get("name", d.name)
desc = meta.get("description", raw.split("\n")[0].lstrip("#").strip())
@@ -128,14 +128,14 @@ def safe_path(p: str) -> Path:
def run_bash(command: str) -> str:
try:
- r = subprocess.run(command, shell=True, cwd=WORKDIR, capture_output=True, text=True, timeout=120)
+ r = subprocess.run(command, shell=True, cwd=WORKDIR, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired: return "Error: Timeout (120s)"
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines): lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
except Exception as e: return f"Error: {e}"
@@ -143,13 +143,13 @@ def run_read(path: str, limit: int | None = None) -> str:
def run_write(path: str, content: str) -> str:
try:
file_path = safe_path(path); file_path.parent.mkdir(parents=True, exist_ok=True)
- file_path.write_text(content); return f"Wrote {len(content)} bytes to {path}"
+ file_path.write_text(content, encoding="utf-8"); return f"Wrote {len(content)} bytes to {path}"
except Exception as e: return f"Error: {e}"
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
file_path = safe_path(path)
- text = file_path.read_text()
+ text = file_path.read_text(encoding="utf-8")
if old_text not in text: return f"Error: text not found in {path}"
file_path.write_text(text.replace(old_text, new_text, 1))
return f"Edited {path}"
@@ -333,7 +333,7 @@ def persist_large_output(tool_use_id, output):
if len(output) <= PERSIST_THRESHOLD: return output
TOOL_RESULTS_DIR.mkdir(parents=True, exist_ok=True)
path = TOOL_RESULTS_DIR / f"{tool_use_id}.txt"
- if not path.exists(): path.write_text(output)
+ if not path.exists(): path.write_text(output, encoding="utf-8")
return f"\nFull output: {path}\nPreview:\n{output[:2000]}\n"
def tool_result_budget(messages, max_bytes=200_000):
diff --git a/s09_memory/code.py b/s09_memory/code.py
index 117c8359a..8264a8f68 100644
--- a/s09_memory/code.py
+++ b/s09_memory/code.py
@@ -87,7 +87,7 @@ def _rebuild_index():
for f in sorted(MEMORY_DIR.glob("*.md")):
if f.name == "MEMORY.md":
continue
- raw = f.read_text()
+ raw = f.read_text(encoding="utf-8")
meta, body = _parse_frontmatter(raw)
name = meta.get("name", f.stem)
desc = meta.get("description", body.split("\n")[0][:80])
@@ -99,7 +99,7 @@ def read_memory_index() -> str:
"""Read MEMORY.md index (injected into SYSTEM every turn)."""
if not MEMORY_INDEX.exists():
return ""
- text = MEMORY_INDEX.read_text().strip()
+ text = MEMORY_INDEX.read_text(encoding="utf-8").strip()
return text if text else ""
@@ -108,7 +108,7 @@ def read_memory_file(filename: str) -> str | None:
path = MEMORY_DIR / filename
if not path.exists():
return None
- return path.read_text()
+ return path.read_text(encoding="utf-8")
def list_memory_files() -> list[dict]:
@@ -117,7 +117,7 @@ def list_memory_files() -> list[dict]:
for f in sorted(MEMORY_DIR.glob("*.md")):
if f.name == "MEMORY.md":
continue
- raw = f.read_text()
+ raw = f.read_text(encoding="utf-8")
meta, body = _parse_frontmatter(raw)
result.append({
"filename": f.name,
@@ -362,14 +362,14 @@ def safe_path(p: str) -> Path:
def run_bash(command: str) -> str:
try:
- r = subprocess.run(command, shell=True, cwd=WORKDIR, capture_output=True, text=True, timeout=120)
+ r = subprocess.run(command, shell=True, cwd=WORKDIR, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired: return "Error: Timeout (120s)"
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines): lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
except Exception as e: return f"Error: {e}"
@@ -377,13 +377,13 @@ def run_read(path: str, limit: int | None = None) -> str:
def run_write(path: str, content: str) -> str:
try:
file_path = safe_path(path); file_path.parent.mkdir(parents=True, exist_ok=True)
- file_path.write_text(content); return f"Wrote {len(content)} bytes to {path}"
+ file_path.write_text(content, encoding="utf-8"); return f"Wrote {len(content)} bytes to {path}"
except Exception as e: return f"Error: {e}"
def run_edit(path: str, old_text: str, new_text: str) -> str:
try:
file_path = safe_path(path)
- text = file_path.read_text()
+ text = file_path.read_text(encoding="utf-8")
if old_text not in text: return f"Error: text not found in {path}"
file_path.write_text(text.replace(old_text, new_text, 1))
return f"Edited {path}"
@@ -501,7 +501,7 @@ def persist_large(tid, out):
if len(out) <= PERSIST_THRESHOLD: return out
TOOL_RESULTS_DIR.mkdir(parents=True, exist_ok=True)
p = TOOL_RESULTS_DIR / f"{tid}.txt"
- if not p.exists(): p.write_text(out)
+ if not p.exists(): p.write_text(out, encoding="utf-8")
return f"\nFull: {p}\nPreview:\n{out[:2000]}\n"
def tool_result_budget(msgs, mx=200_000):
diff --git a/s10_system_prompt/code.py b/s10_system_prompt/code.py
index 723adc6df..946656dbd 100644
--- a/s10_system_prompt/code.py
+++ b/s10_system_prompt/code.py
@@ -104,7 +104,8 @@ def safe_path(p: str) -> Path:
def run_bash(command: str) -> str:
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, timeout=120,
+ encoding="utf-8", errors="replace")
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -113,7 +114,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -125,7 +126,7 @@ def run_write(path: str, content: str) -> str:
try:
file_path = safe_path(path)
file_path.parent.mkdir(parents=True, exist_ok=True)
- file_path.write_text(content)
+ file_path.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -157,7 +158,7 @@ def update_context(context: dict, messages: list) -> dict:
"""Derive context from real state: which tools exist, whether memory files exist."""
memories = ""
if MEMORY_INDEX.exists():
- content = MEMORY_INDEX.read_text().strip()
+ content = MEMORY_INDEX.read_text(encoding="utf-8").strip()
if content:
memories = content
return {
diff --git a/s11_error_recovery/code.py b/s11_error_recovery/code.py
index 8a4862b8e..df61fefdb 100644
--- a/s11_error_recovery/code.py
+++ b/s11_error_recovery/code.py
@@ -111,7 +111,7 @@ def safe_path(p: str) -> Path:
def run_bash(command: str) -> str:
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -120,7 +120,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -132,7 +132,7 @@ def run_write(path: str, content: str) -> str:
try:
file_path = safe_path(path)
file_path.parent.mkdir(parents=True, exist_ok=True)
- file_path.write_text(content)
+ file_path.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -250,7 +250,7 @@ def update_context(context: dict, messages: list) -> dict:
"""Derive context from real state: which tools exist, whether memory files exist."""
memories = ""
if MEMORY_INDEX.exists():
- content = MEMORY_INDEX.read_text().strip()
+ content = MEMORY_INDEX.read_text(encoding="utf-8").strip()
if content:
memories = content
return {
diff --git a/s12_task_system/code.py b/s12_task_system/code.py
index 7f442ebf2..f4768d106 100644
--- a/s12_task_system/code.py
+++ b/s12_task_system/code.py
@@ -82,11 +82,11 @@ def save_task(task: Task):
def load_task(task_id: str) -> Task:
- return Task(**json.loads(_task_path(task_id).read_text()))
+ return Task(**json.loads(_task_path(task_id).read_text(encoding="utf-8")))
def list_tasks() -> list[Task]:
- return [Task(**json.loads(p.read_text()))
+ return [Task(**json.loads(p.read_text(encoding="utf-8")))
for p in sorted(TASKS_DIR.glob("task_*.json"))]
@@ -185,7 +185,7 @@ def safe_path(p: str) -> Path:
def run_bash(command: str) -> str:
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -194,7 +194,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -206,7 +206,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -311,7 +311,7 @@ def update_context(context: dict, messages: list) -> dict:
"""Derive context from real state."""
memories = ""
if MEMORY_INDEX.exists():
- content = MEMORY_INDEX.read_text().strip()
+ content = MEMORY_INDEX.read_text(encoding="utf-8").strip()
if content:
memories = content
return {
diff --git a/s13_background_tasks/code.py b/s13_background_tasks/code.py
index a96eabcac..dda0d0b1a 100644
--- a/s13_background_tasks/code.py
+++ b/s13_background_tasks/code.py
@@ -81,11 +81,11 @@ def save_task(task: Task):
def load_task(task_id: str) -> Task:
- return Task(**json.loads(_task_path(task_id).read_text()))
+ return Task(**json.loads(_task_path(task_id).read_text(encoding="utf-8")))
def list_tasks() -> list[Task]:
- return [Task(**json.loads(p.read_text()))
+ return [Task(**json.loads(p.read_text(encoding="utf-8")))
for p in sorted(TASKS_DIR.glob("task_*.json"))]
@@ -185,7 +185,7 @@ def run_bash(command: str, run_in_background: bool = False) -> str:
# run_in_background is handled by agent_loop dispatch, not here
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -194,7 +194,7 @@ def run_bash(command: str, run_in_background: bool = False) -> str:
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -206,7 +206,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -395,7 +395,7 @@ def update_context(context: dict, messages: list) -> dict:
"""Derive context from real state."""
memories = ""
if MEMORY_INDEX.exists():
- content = MEMORY_INDEX.read_text().strip()
+ content = MEMORY_INDEX.read_text(encoding="utf-8").strip()
if content:
memories = content
return {
diff --git a/s14_cron_scheduler/code.py b/s14_cron_scheduler/code.py
index 7fd36324c..6358971a1 100644
--- a/s14_cron_scheduler/code.py
+++ b/s14_cron_scheduler/code.py
@@ -83,11 +83,11 @@ def save_task(task: Task):
def load_task(task_id: str) -> Task:
- return Task(**json.loads(_task_path(task_id).read_text()))
+ return Task(**json.loads(_task_path(task_id).read_text(encoding="utf-8")))
def list_tasks() -> list[Task]:
- return [Task(**json.loads(p.read_text()))
+ return [Task(**json.loads(p.read_text(encoding="utf-8")))
for p in sorted(TASKS_DIR.glob("task_*.json"))]
@@ -188,7 +188,7 @@ def run_bash(command: str, run_in_background: bool = False) -> str:
# run_in_background is handled by agent_loop dispatch, not here
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -197,7 +197,7 @@ def run_bash(command: str, run_in_background: bool = False) -> str:
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -209,7 +209,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -470,7 +470,7 @@ def load_durable_jobs():
if not DURABLE_PATH.exists():
return
try:
- jobs = json.loads(DURABLE_PATH.read_text())
+ jobs = json.loads(DURABLE_PATH.read_text(encoding="utf-8"))
for j in jobs:
job = CronJob(**j)
err = validate_cron(job.cron)
@@ -668,7 +668,7 @@ def update_context(context: dict, messages: list) -> dict:
"""Derive context from real state."""
memories = ""
if MEMORY_INDEX.exists():
- content = MEMORY_INDEX.read_text().strip()
+ content = MEMORY_INDEX.read_text(encoding="utf-8").strip()
if content:
memories = content
return {
diff --git a/s15_agent_teams/code.py b/s15_agent_teams/code.py
index 143a73e82..d32703b11 100644
--- a/s15_agent_teams/code.py
+++ b/s15_agent_teams/code.py
@@ -81,11 +81,11 @@ def save_task(task: Task):
def load_task(task_id: str) -> Task:
- return Task(**json.loads(_task_path(task_id).read_text()))
+ return Task(**json.loads(_task_path(task_id).read_text(encoding="utf-8")))
def list_tasks() -> list[Task]:
- return [Task(**json.loads(p.read_text()))
+ return [Task(**json.loads(p.read_text(encoding="utf-8")))
for p in sorted(TASKS_DIR.glob("task_*.json"))]
@@ -187,7 +187,7 @@ def run_bash(command: str, run_in_background: bool = False) -> str:
# run_in_background is handled by agent_loop dispatch, not here
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -196,7 +196,7 @@ def run_bash(command: str, run_in_background: bool = False) -> str:
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -208,7 +208,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -477,7 +477,7 @@ def load_durable_jobs():
if not DURABLE_PATH.exists():
return
try:
- jobs = json.loads(DURABLE_PATH.read_text())
+ jobs = json.loads(DURABLE_PATH.read_text(encoding="utf-8"))
for j in jobs:
job = CronJob(**j)
err = validate_cron(job.cron)
@@ -619,7 +619,7 @@ def read_inbox(self, agent: str) -> list[dict]:
inbox = MAILBOX_DIR / f"{agent}.jsonl"
if not inbox.exists():
return []
- msgs = [json.loads(line) for line in inbox.read_text().splitlines()
+ msgs = [json.loads(line) for line in inbox.read_text(encoding="utf-8").splitlines()
if line.strip()]
inbox.unlink() # consume: read + delete
return msgs
@@ -843,7 +843,7 @@ def update_context(context: dict, messages: list) -> dict:
"""Derive context from real state."""
memories = ""
if MEMORY_INDEX.exists():
- content = MEMORY_INDEX.read_text().strip()
+ content = MEMORY_INDEX.read_text(encoding="utf-8").strip()
if content:
memories = content
return {
diff --git a/s16_team_protocols/code.py b/s16_team_protocols/code.py
index d7993fb9e..119106d9a 100644
--- a/s16_team_protocols/code.py
+++ b/s16_team_protocols/code.py
@@ -85,11 +85,11 @@ def save_task(task: Task):
def load_task(task_id: str) -> Task:
- return Task(**json.loads(_task_path(task_id).read_text()))
+ return Task(**json.loads(_task_path(task_id).read_text(encoding="utf-8")))
def list_tasks() -> list[Task]:
- return [Task(**json.loads(p.read_text()))
+ return [Task(**json.loads(p.read_text(encoding="utf-8")))
for p in sorted(TASKS_DIR.glob("task_*.json"))]
@@ -191,7 +191,7 @@ def run_bash(command: str, run_in_background: bool = False) -> str:
# run_in_background is handled by agent_loop dispatch, not here
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -200,7 +200,7 @@ def run_bash(command: str, run_in_background: bool = False) -> str:
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -212,7 +212,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -357,7 +357,7 @@ def read_inbox(self, agent: str) -> list[dict]:
inbox = MAILBOX_DIR / f"{agent}.jsonl"
if not inbox.exists():
return []
- msgs = [json.loads(line) for line in inbox.read_text().splitlines()
+ msgs = [json.loads(line) for line in inbox.read_text(encoding="utf-8").splitlines()
if line.strip()]
inbox.unlink() # consume: read + delete
return msgs
@@ -791,7 +791,7 @@ def update_context(context: dict, messages: list) -> dict:
"""Derive context from real state."""
memories = ""
if MEMORY_INDEX.exists():
- content = MEMORY_INDEX.read_text().strip()
+ content = MEMORY_INDEX.read_text(encoding="utf-8").strip()
if content:
memories = content
return {
diff --git a/s17_autonomous_agents/code.py b/s17_autonomous_agents/code.py
index 71d97bebe..851885590 100644
--- a/s17_autonomous_agents/code.py
+++ b/s17_autonomous_agents/code.py
@@ -78,11 +78,11 @@ def save_task(task: Task):
def load_task(task_id: str) -> Task:
- return Task(**json.loads(_task_path(task_id).read_text()))
+ return Task(**json.loads(_task_path(task_id).read_text(encoding="utf-8")))
def list_tasks() -> list[Task]:
- return [Task(**json.loads(p.read_text()))
+ return [Task(**json.loads(p.read_text(encoding="utf-8")))
for p in sorted(TASKS_DIR.glob("task_*.json"))]
@@ -183,7 +183,7 @@ def safe_path(p: str) -> Path:
def run_bash(command: str) -> str:
try:
r = subprocess.run(command, shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -192,7 +192,7 @@ def run_bash(command: str) -> str:
def run_read(path: str, limit: int | None = None) -> str:
try:
- lines = safe_path(path).read_text().splitlines()
+ lines = safe_path(path).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -204,7 +204,7 @@ def run_write(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -232,7 +232,7 @@ def read_inbox(self, agent: str) -> list[dict]:
inbox = MAILBOX_DIR / f"{agent}.jsonl"
if not inbox.exists():
return []
- msgs = [json.loads(line) for line in inbox.read_text().splitlines()
+ msgs = [json.loads(line) for line in inbox.read_text(encoding="utf-8").splitlines()
if line.strip()]
inbox.unlink()
return msgs
@@ -293,7 +293,7 @@ def scan_unclaimed_tasks() -> list[dict]:
"""Find pending, unowned tasks with all dependencies completed."""
unclaimed = []
for f in sorted(TASKS_DIR.glob("task_*.json")):
- task = json.loads(f.read_text())
+ task = json.loads(f.read_text(encoding="utf-8"))
if (task.get("status") == "pending"
and not task.get("owner")
and can_start(task["id"])):
@@ -744,7 +744,7 @@ def run_check_inbox() -> str:
def update_context(context: dict, messages: list) -> dict:
memories = ""
if MEMORY_INDEX.exists():
- memories = MEMORY_INDEX.read_text()[:2000]
+ memories = MEMORY_INDEX.read_text(encoding="utf-8")[:2000]
return {"memories": memories}
diff --git a/s18_worktree_isolation/code.py b/s18_worktree_isolation/code.py
index c00fcf449..170d173cd 100644
--- a/s18_worktree_isolation/code.py
+++ b/s18_worktree_isolation/code.py
@@ -86,11 +86,11 @@ def save_task(task: Task):
def load_task(task_id: str) -> Task:
- return Task(**json.loads(_task_path(task_id).read_text()))
+ return Task(**json.loads(_task_path(task_id).read_text(encoding="utf-8")))
def list_tasks() -> list[Task]:
- return [Task(**json.loads(p.read_text()))
+ return [Task(**json.loads(p.read_text(encoding="utf-8")))
for p in sorted(TASKS_DIR.glob("task_*.json"))]
@@ -169,7 +169,7 @@ def run_git(args: list[str]) -> tuple[bool, str]:
"""Run git command. Return (ok, output)."""
try:
r = subprocess.run(["git"] + args, cwd=WORKDIR,
- capture_output=True, text=True, timeout=30)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=30)
out = (r.stdout + r.stderr).strip()
out = out[:5000] if out else "(no output)"
return r.returncode == 0, out
@@ -216,10 +216,10 @@ def _count_worktree_changes(path: Path) -> tuple[int, int]:
"""Count uncommitted files and commits in a worktree."""
try:
r1 = subprocess.run(["git", "status", "--porcelain"],
- cwd=path, capture_output=True, text=True, timeout=10)
+ cwd=path, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10)
files = len([l for l in r1.stdout.strip().splitlines() if l.strip()])
r2 = subprocess.run(["git", "log", "@{push}..HEAD", "--oneline"],
- cwd=path, capture_output=True, text=True, timeout=10)
+ cwd=path, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10)
commits = len([l for l in r2.stdout.strip().splitlines() if l.strip()])
return files, commits
except Exception:
@@ -311,7 +311,7 @@ def safe_path(p: str, cwd: Path = None) -> Path:
def run_bash(command: str, cwd: Path = None) -> str:
try:
r = subprocess.run(command, shell=True, cwd=cwd or WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -320,7 +320,7 @@ def run_bash(command: str, cwd: Path = None) -> str:
def run_read(path: str, limit: int | None = None, cwd: Path = None) -> str:
try:
- lines = safe_path(path, cwd).read_text().splitlines()
+ lines = safe_path(path, cwd).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -332,7 +332,7 @@ def run_write(path: str, content: str, cwd: Path = None) -> str:
try:
fp = safe_path(path, cwd)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -360,7 +360,7 @@ def read_inbox(self, agent: str) -> list[dict]:
inbox = MAILBOX_DIR / f"{agent}.jsonl"
if not inbox.exists():
return []
- msgs = [json.loads(line) for line in inbox.read_text().splitlines()
+ msgs = [json.loads(line) for line in inbox.read_text(encoding="utf-8").splitlines()
if line.strip()]
inbox.unlink()
return msgs
@@ -431,7 +431,7 @@ def scan_unclaimed_tasks() -> list[dict]:
"""Find pending, unowned tasks with all dependencies completed."""
unclaimed = []
for f in sorted(TASKS_DIR.glob("task_*.json")):
- task = json.loads(f.read_text())
+ task = json.loads(f.read_text(encoding="utf-8"))
if (task.get("status") == "pending"
and not task.get("owner")
and can_start(task["id"])):
@@ -929,7 +929,7 @@ def run_check_inbox() -> str:
def update_context(context: dict, messages: list) -> dict:
memories = ""
if MEMORY_INDEX.exists():
- memories = MEMORY_INDEX.read_text()[:2000]
+ memories = MEMORY_INDEX.read_text(encoding="utf-8")[:2000]
return {"memories": memories}
diff --git a/s19_mcp_plugin/code.py b/s19_mcp_plugin/code.py
index eed5acecb..c840bb828 100644
--- a/s19_mcp_plugin/code.py
+++ b/s19_mcp_plugin/code.py
@@ -81,11 +81,11 @@ def save_task(task: Task):
def load_task(task_id: str) -> Task:
- return Task(**json.loads(_task_path(task_id).read_text()))
+ return Task(**json.loads(_task_path(task_id).read_text(encoding="utf-8")))
def list_tasks() -> list[Task]:
- return [Task(**json.loads(p.read_text()))
+ return [Task(**json.loads(p.read_text(encoding="utf-8")))
for p in sorted(TASKS_DIR.glob("task_*.json"))]
@@ -161,7 +161,7 @@ def validate_worktree_name(name: str) -> str | None:
def run_git(args: list[str]) -> tuple[bool, str]:
try:
r = subprocess.run(["git"] + args, cwd=WORKDIR,
- capture_output=True, text=True, timeout=30)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=30)
out = (r.stdout + r.stderr).strip()
return r.returncode == 0, out[:5000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -202,10 +202,10 @@ def bind_task_to_worktree(task_id: str, worktree_name: str):
def _count_worktree_changes(path: Path) -> tuple[int, int]:
try:
r1 = subprocess.run(["git", "status", "--porcelain"],
- cwd=path, capture_output=True, text=True, timeout=10)
+ cwd=path, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10)
files = len([l for l in r1.stdout.strip().splitlines() if l.strip()])
r2 = subprocess.run(["git", "log", "@{push}..HEAD", "--oneline"],
- cwd=path, capture_output=True, text=True, timeout=10)
+ cwd=path, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10)
commits = len([l for l in r2.stdout.strip().splitlines() if l.strip()])
return files, commits
except Exception:
@@ -283,7 +283,7 @@ def safe_path(p: str, cwd: Path = None) -> Path:
def run_bash(command: str, cwd: Path = None) -> str:
try:
r = subprocess.run(command, shell=True, cwd=cwd or WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -292,7 +292,7 @@ def run_bash(command: str, cwd: Path = None) -> str:
def run_read(path: str, limit: int | None = None, cwd: Path = None) -> str:
try:
- lines = safe_path(path, cwd).read_text().splitlines()
+ lines = safe_path(path, cwd).read_text(encoding="utf-8").splitlines()
if limit and limit < len(lines):
lines = lines[:limit] + [f"... ({len(lines) - limit} more lines)"]
return "\n".join(lines)
@@ -304,7 +304,7 @@ def run_write(path: str, content: str, cwd: Path = None) -> str:
try:
fp = safe_path(path, cwd)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -332,7 +332,7 @@ def read_inbox(self, agent: str) -> list[dict]:
inbox = MAILBOX_DIR / f"{agent}.jsonl"
if not inbox.exists():
return []
- msgs = [json.loads(line) for line in inbox.read_text().splitlines()
+ msgs = [json.loads(line) for line in inbox.read_text(encoding="utf-8").splitlines()
if line.strip()]
inbox.unlink()
return msgs
@@ -393,7 +393,7 @@ def consume_lead_inbox(route_protocol=True) -> list[dict]:
def scan_unclaimed_tasks() -> list[dict]:
unclaimed = []
for f in sorted(TASKS_DIR.glob("task_*.json")):
- task = json.loads(f.read_text())
+ task = json.loads(f.read_text(encoding="utf-8"))
if (task.get("status") == "pending"
and not task.get("owner")
and can_start(task["id"])):
@@ -953,7 +953,7 @@ def run_connect_mcp(name: str) -> str:
def update_context(context: dict, messages: list) -> dict:
memories = ""
if MEMORY_INDEX.exists():
- memories = MEMORY_INDEX.read_text()[:2000]
+ memories = MEMORY_INDEX.read_text(encoding="utf-8")[:2000]
return {"memories": memories}
diff --git a/s20_comprehensive/code.py b/s20_comprehensive/code.py
index 722aba153..e7398af25 100644
--- a/s20_comprehensive/code.py
+++ b/s20_comprehensive/code.py
@@ -109,11 +109,11 @@ def save_task(task: Task):
def load_task(task_id: str) -> Task:
- return Task(**json.loads(_task_path(task_id).read_text()))
+ return Task(**json.loads(_task_path(task_id).read_text(encoding="utf-8")))
def list_tasks() -> list[Task]:
- return [Task(**json.loads(p.read_text()))
+ return [Task(**json.loads(p.read_text(encoding="utf-8")))
for p in sorted(TASKS_DIR.glob("task_*.json"))]
@@ -193,7 +193,7 @@ def validate_worktree_name(name: str) -> str | None:
def run_git(args: list[str]) -> tuple[bool, str]:
try:
r = subprocess.run(["git"] + args, cwd=WORKDIR,
- capture_output=True, text=True, timeout=30)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=30)
out = (r.stdout + r.stderr).strip()
return r.returncode == 0, out[:5000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -241,10 +241,10 @@ def bind_task_to_worktree(task_id: str, worktree_name: str):
def _count_worktree_changes(path: Path) -> tuple[int, int]:
try:
r1 = subprocess.run(["git", "status", "--porcelain"],
- cwd=path, capture_output=True, text=True, timeout=10)
+ cwd=path, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10)
files = len([l for l in r1.stdout.strip().splitlines() if l.strip()])
r2 = subprocess.run(["git", "log", "@{push}..HEAD", "--oneline"],
- cwd=path, capture_output=True, text=True, timeout=10)
+ cwd=path, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=10)
commits = len([l for l in r2.stdout.strip().splitlines() if l.strip()])
return files, commits
except Exception:
@@ -310,7 +310,7 @@ def scan_skills():
manifest = directory / "SKILL.md"
if not manifest.exists():
continue
- raw = manifest.read_text()
+ raw = manifest.read_text(encoding="utf-8")
meta, _ = _parse_frontmatter(raw)
name = meta.get("name", directory.name)
desc = meta.get("description", raw.split("\n")[0].lstrip("#").strip())
@@ -391,7 +391,7 @@ def run_bash(command: str, cwd: Path = None,
# run_in_background is consumed by the dispatcher; direct execution ignores it.
try:
r = subprocess.run(command, shell=True, cwd=cwd or WORKDIR,
- capture_output=True, text=True, timeout=120)
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
@@ -401,7 +401,7 @@ def run_bash(command: str, cwd: Path = None,
def run_read(path: str, limit: int | None = None,
offset: int = 0, cwd: Path = None) -> str:
try:
- lines = safe_path(path, cwd).read_text().splitlines()
+ lines = safe_path(path, cwd).read_text(encoding="utf-8").splitlines()
offset = max(int(offset or 0), 0)
limit = int(limit) if limit is not None else None
lines = lines[offset:]
@@ -416,7 +416,7 @@ def run_write(path: str, content: str, cwd: Path = None) -> str:
try:
fp = safe_path(path, cwd)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
return f"Error: {e}"
@@ -426,7 +426,7 @@ def run_edit(path: str, old_text: str, new_text: str,
cwd: Path = None) -> str:
try:
fp = safe_path(path, cwd)
- text = fp.read_text()
+ text = fp.read_text(encoding="utf-8")
if old_text not in text:
return f"Error: text not found in {path}"
fp.write_text(text.replace(old_text, new_text, 1))
@@ -511,7 +511,7 @@ def read_inbox(self, agent: str) -> list[dict]:
inbox = MAILBOX_DIR / f"{agent}.jsonl"
if not inbox.exists():
return []
- msgs = [json.loads(line) for line in inbox.read_text().splitlines()
+ msgs = [json.loads(line) for line in inbox.read_text(encoding="utf-8").splitlines()
if line.strip()]
inbox.unlink()
return msgs
@@ -574,7 +574,7 @@ def consume_lead_inbox(route_protocol=True) -> list[dict]:
def scan_unclaimed_tasks() -> list[dict]:
unclaimed = []
for f in sorted(TASKS_DIR.glob("task_*.json")):
- task = json.loads(f.read_text())
+ task = json.loads(f.read_text(encoding="utf-8"))
if (task.get("status") == "pending"
and not task.get("owner")
and can_start(task["id"])):
@@ -1101,7 +1101,7 @@ def persist_large_output(tool_use_id: str, output: str) -> str:
TOOL_RESULTS_DIR.mkdir(parents=True, exist_ok=True)
path = TOOL_RESULTS_DIR / f"{tool_use_id}.txt"
if not path.exists():
- path.write_text(output)
+ path.write_text(output, encoding="utf-8")
return (f"\nFull output: {path}\n"
f"Preview:\n{output[:2000]}\n")
@@ -1440,7 +1440,7 @@ def load_durable_jobs():
if not DURABLE_PATH.exists():
return
try:
- for item in json.loads(DURABLE_PATH.read_text()):
+ for item in json.loads(DURABLE_PATH.read_text(encoding="utf-8")):
job = CronJob(**item)
if not validate_cron(job.cron):
scheduled_jobs[job.id] = job
@@ -1899,7 +1899,7 @@ def run_connect_mcp(name: str) -> str:
def update_context(context: dict, messages: list) -> dict:
memories = ""
if MEMORY_INDEX.exists():
- memories = MEMORY_INDEX.read_text()[:2000]
+ memories = MEMORY_INDEX.read_text(encoding="utf-8")[:2000]
return {
"memories": memories,
"connected_mcp": list(mcp_clients.keys()),
diff --git a/skills/agent-builder/references/minimal-agent.py b/skills/agent-builder/references/minimal-agent.py
index 9eae11d6f..ad6ba0c5a 100644
--- a/skills/agent-builder/references/minimal-agent.py
+++ b/skills/agent-builder/references/minimal-agent.py
@@ -70,7 +70,7 @@ def execute_tool(name: str, args: dict) -> str:
try:
r = subprocess.run(
args["command"], shell=True, cwd=WORKDIR,
- capture_output=True, text=True, timeout=60
+ capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=60
)
return (r.stdout + r.stderr).strip() or "(empty)"
except subprocess.TimeoutExpired:
@@ -78,7 +78,7 @@ def execute_tool(name: str, args: dict) -> str:
if name == "read_file":
try:
- return (WORKDIR / args["path"]).read_text()[:50000]
+ return (WORKDIR / args["path"]).read_text(encoding="utf-8")[:50000]
except Exception as e:
return f"Error: {e}"
diff --git a/skills/agent-builder/references/tool-templates.py b/skills/agent-builder/references/tool-templates.py
index 952cd698f..587b297c4 100644
--- a/skills/agent-builder/references/tool-templates.py
+++ b/skills/agent-builder/references/tool-templates.py
@@ -168,7 +168,7 @@ def run_bash(command: str) -> str:
shell=True,
cwd=WORKDIR,
capture_output=True,
- text=True,
+ text=True, encoding="utf-8", errors="replace",
timeout=60
)
output = (result.stdout + result.stderr).strip()
@@ -190,7 +190,7 @@ def run_read_file(path: str, limit: int = None) -> str:
- Output truncated to 50KB
"""
try:
- text = safe_path(path).read_text()
+ text = safe_path(path).read_text(encoding="utf-8")
lines = text.splitlines()
if limit and limit < len(lines):
@@ -215,7 +215,7 @@ def run_write_file(path: str, content: str) -> str:
try:
fp = safe_path(path)
fp.parent.mkdir(parents=True, exist_ok=True)
- fp.write_text(content)
+ fp.write_text(content, encoding="utf-8")
return f"Wrote {len(content)} bytes to {path}"
except Exception as e:
@@ -233,13 +233,13 @@ def run_edit_file(path: str, old_text: str, new_text: str) -> str:
"""
try:
fp = safe_path(path)
- content = fp.read_text()
+ content = fp.read_text(encoding="utf-8")
if old_text not in content:
return f"Error: Text not found in {path}"
new_content = content.replace(old_text, new_text, 1)
- fp.write_text(new_content)
+ fp.write_text(new_content, encoding="utf-8")
return f"Edited {path}"
except Exception as e:
diff --git a/skills/agent-builder/scripts/init_agent.py b/skills/agent-builder/scripts/init_agent.py
index 2f401157e..4a6888927 100644
--- a/skills/agent-builder/scripts/init_agent.py
+++ b/skills/agent-builder/scripts/init_agent.py
@@ -63,7 +63,7 @@ def run(prompt, history=[]):
if b.type == "tool_use":
print(f"> {{b.input['command']}}")
try:
- out = subprocess.run(b.input["command"], shell=True, capture_output=True, text=True, timeout=60)
+ out = subprocess.run(b.input["command"], shell=True, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=60)
output = (out.stdout + out.stderr).strip() or "(empty)"
except Exception as e:
output = f"Error: {{e}}"
@@ -133,7 +133,7 @@ def execute(name: str, args: dict) -> str:
if any(d in args["command"] for d in dangerous):
return "Error: Dangerous command blocked"
try:
- r = subprocess.run(args["command"], shell=True, cwd=WORKDIR, capture_output=True, text=True, timeout=60)
+ r = subprocess.run(args["command"], shell=True, cwd=WORKDIR, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=60)
return (r.stdout + r.stderr).strip()[:50000] or "(empty)"
except subprocess.TimeoutExpired:
return "Error: Timeout (60s)"
@@ -142,7 +142,7 @@ def execute(name: str, args: dict) -> str:
if name == "read_file":
try:
- return safe_path(args["path"]).read_text()[:50000]
+ return safe_path(args["path"]).read_text(encoding="utf-8")[:50000]
except Exception as e:
return f"Error: {{e}}"
@@ -158,7 +158,7 @@ def execute(name: str, args: dict) -> str:
if name == "edit_file":
try:
p = safe_path(args["path"])
- content = p.read_text()
+ content = p.read_text(encoding="utf-8")
if args["old_text"] not in content:
return f"Error: Text not found in {{args['path']}}"
p.write_text(content.replace(args["old_text"], args["new_text"], 1))
@@ -235,7 +235,7 @@ def create_agent(name: str, level: int, output_dir: Path):
# Write .env.example
env_file = agent_dir / ".env.example"
- env_file.write_text(ENV_TEMPLATE)
+ env_file.write_text(ENV_TEMPLATE, encoding="utf-8")
print(f"Created: {env_file}")
# Write .gitignore