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