mirror of
https://github.com/shareAI-lab/analysis_claude_code.git
synced 2026-09-20 12:13:38 +08:00
fix: use UTF-8 for text file operations
This commit is contained in:
@@ -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,10 +83,10 @@ 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))
|
||||
fp.write_text(content.replace(old_text, new_text, 1), encoding="utf-8")
|
||||
return f"Edited {path}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -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,10 +129,10 @@ 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))
|
||||
fp.write_text(content.replace(old_text, new_text, 1), encoding="utf-8")
|
||||
return f"Edited {path}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -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,10 +85,10 @@ 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))
|
||||
fp.write_text(content.replace(old_text, new_text, 1), encoding="utf-8")
|
||||
return f"Edited {path}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -66,7 +66,7 @@ class SkillLoader:
|
||||
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)}
|
||||
@@ -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,10 +154,10 @@ 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))
|
||||
fp.write_text(content.replace(old_text, new_text, 1), encoding="utf-8")
|
||||
return f"Edited {path}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -104,7 +104,7 @@ def auto_compact(messages: list, focus: str = "") -> list:
|
||||
# Save full transcript to disk
|
||||
TRANSCRIPT_DIR.mkdir(exist_ok=True)
|
||||
transcript_path = TRANSCRIPT_DIR / f"transcript_{int(time.time())}.jsonl"
|
||||
with open(transcript_path, "w") as f:
|
||||
with open(transcript_path, "w", encoding="utf-8") as f:
|
||||
for msg in messages:
|
||||
f.write(json.dumps(msg, default=str) + "\n")
|
||||
print(f"[transcript saved: {transcript_path}]")
|
||||
@@ -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,10 +171,10 @@ 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))
|
||||
fp.write_text(content.replace(old_text, new_text, 1), encoding="utf-8")
|
||||
return f"Edited {path}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -58,11 +58,11 @@ class TaskManager:
|
||||
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"
|
||||
path.write_text(json.dumps(task, indent=2, ensure_ascii=False))
|
||||
path.write_text(json.dumps(task, indent=2, ensure_ascii=False), encoding="utf-8")
|
||||
|
||||
def create(self, subject: str, description: str = "") -> str:
|
||||
task = {
|
||||
@@ -95,7 +95,7 @@ class TaskManager:
|
||||
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 @@ class TaskManager:
|
||||
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 = []
|
||||
@@ -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,10 +161,10 @@ 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))
|
||||
fp.write_text(c.replace(old_text, new_text, 1), encoding="utf-8")
|
||||
return f"Edited {path}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -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,10 +151,10 @@ 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))
|
||||
fp.write_text(c.replace(old_text, new_text, 1), encoding="utf-8")
|
||||
return f"Edited {path}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -93,7 +93,7 @@ class MessageBus:
|
||||
if extra:
|
||||
msg.update(extra)
|
||||
inbox_path = self.dir / f"{to}.jsonl"
|
||||
with open(inbox_path, "a") as f:
|
||||
with open(inbox_path, "a", encoding="utf-8") as f:
|
||||
f.write(json.dumps(msg) + "\n")
|
||||
return f"Sent {msg_type} to {to}"
|
||||
|
||||
@@ -102,11 +102,11 @@ class MessageBus:
|
||||
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))
|
||||
if clear:
|
||||
inbox_path.write_text("")
|
||||
inbox_path.write_text("", encoding="utf-8")
|
||||
return messages
|
||||
|
||||
def broadcast(self, sender: str, content: str, teammates: list) -> str:
|
||||
@@ -132,11 +132,11 @@ class TeammateManager:
|
||||
|
||||
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):
|
||||
self.config_path.write_text(json.dumps(self.config, indent=2))
|
||||
self.config_path.write_text(json.dumps(self.config, indent=2), encoding="utf-8")
|
||||
|
||||
def _find_member(self, name: str) -> dict:
|
||||
for m in self.config["members"]:
|
||||
@@ -277,7 +277,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]
|
||||
@@ -289,7 +289,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}"
|
||||
@@ -298,10 +298,10 @@ 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))
|
||||
fp.write_text(c.replace(old_text, new_text, 1), encoding="utf-8")
|
||||
return f"Edited {path}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -103,7 +103,7 @@ class MessageBus:
|
||||
if extra:
|
||||
msg.update(extra)
|
||||
inbox_path = self.dir / f"{to}.jsonl"
|
||||
with open(inbox_path, "a") as f:
|
||||
with open(inbox_path, "a", encoding="utf-8") as f:
|
||||
f.write(json.dumps(msg) + "\n")
|
||||
return f"Sent {msg_type} to {to}"
|
||||
|
||||
@@ -112,11 +112,11 @@ class MessageBus:
|
||||
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))
|
||||
if clear:
|
||||
inbox_path.write_text("")
|
||||
inbox_path.write_text("", encoding="utf-8")
|
||||
return messages
|
||||
|
||||
def broadcast(self, sender: str, content: str, teammates: list) -> str:
|
||||
@@ -142,11 +142,11 @@ class TeammateManager:
|
||||
|
||||
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):
|
||||
self.config_path.write_text(json.dumps(self.config, indent=2))
|
||||
self.config_path.write_text(json.dumps(self.config, indent=2), encoding="utf-8")
|
||||
|
||||
def _find_member(self, name: str) -> dict:
|
||||
for m in self.config["members"]:
|
||||
@@ -318,7 +318,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]
|
||||
@@ -330,7 +330,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}"
|
||||
@@ -339,10 +339,10 @@ 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))
|
||||
fp.write_text(c.replace(old_text, new_text, 1), encoding="utf-8")
|
||||
return f"Edited {path}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -96,7 +96,7 @@ class MessageBus:
|
||||
if extra:
|
||||
msg.update(extra)
|
||||
inbox_path = self.dir / f"{to}.jsonl"
|
||||
with open(inbox_path, "a") as f:
|
||||
with open(inbox_path, "a", encoding="utf-8") as f:
|
||||
f.write(json.dumps(msg) + "\n")
|
||||
return f"Sent {msg_type} to {to}"
|
||||
|
||||
@@ -105,11 +105,11 @@ class MessageBus:
|
||||
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))
|
||||
if clear:
|
||||
inbox_path.write_text("")
|
||||
inbox_path.write_text("", encoding="utf-8")
|
||||
return messages
|
||||
|
||||
def broadcast(self, sender: str, content: str, teammates: list) -> str:
|
||||
@@ -129,7 +129,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")):
|
||||
@@ -142,7 +142,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 existing_owner := task.get("owner"):
|
||||
return f"Error: Task {task_id} has already been claimed by {existing_owner}"
|
||||
if (status := task.get("status")) != "pending":
|
||||
@@ -151,7 +151,7 @@ def claim_task(task_id: int, owner: str) -> str:
|
||||
return f"Error: Task {task_id} is blocked by other task(s) and cannot be claimed yet"
|
||||
task["owner"] = owner
|
||||
task["status"] = "in_progress"
|
||||
path.write_text(json.dumps(task, indent=2))
|
||||
path.write_text(json.dumps(task, indent=2), encoding="utf-8")
|
||||
return f"Claimed task #{task_id} for {owner}"
|
||||
|
||||
|
||||
@@ -174,11 +174,11 @@ class TeammateManager:
|
||||
|
||||
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):
|
||||
self.config_path.write_text(json.dumps(self.config, indent=2))
|
||||
self.config_path.write_text(json.dumps(self.config, indent=2), encoding="utf-8")
|
||||
|
||||
def _find_member(self, name: str) -> dict:
|
||||
for m in self.config["members"]:
|
||||
@@ -404,7 +404,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]
|
||||
@@ -416,7 +416,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}"
|
||||
@@ -425,10 +425,10 @@ 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))
|
||||
fp.write_text(c.replace(old_text, new_text, 1), encoding="utf-8")
|
||||
return f"Edited {path}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
@@ -570,7 +570,7 @@ if __name__ == "__main__":
|
||||
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}")
|
||||
|
||||
@@ -85,7 +85,7 @@ class EventBus:
|
||||
self.path = event_log_path
|
||||
self.path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if not self.path.exists():
|
||||
self.path.write_text("")
|
||||
self.path.write_text("", encoding="utf-8")
|
||||
|
||||
def emit(
|
||||
self,
|
||||
@@ -141,10 +141,10 @@ class TaskManager:
|
||||
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))
|
||||
self._path(task["id"]).write_text(json.dumps(task, indent=2), encoding="utf-8")
|
||||
|
||||
def create(self, subject: str, description: str = "") -> str:
|
||||
task = {
|
||||
@@ -201,7 +201,7 @@ class TaskManager:
|
||||
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 = []
|
||||
@@ -231,7 +231,7 @@ class WorktreeManager:
|
||||
self.dir.mkdir(parents=True, exist_ok=True)
|
||||
self.index_path = self.dir / "index.json"
|
||||
if not self.index_path.exists():
|
||||
self.index_path.write_text(json.dumps({"worktrees": []}, indent=2))
|
||||
self.index_path.write_text(json.dumps({"worktrees": []}, indent=2), encoding="utf-8")
|
||||
self.git_available = self._is_git_repo()
|
||||
|
||||
def _is_git_repo(self) -> bool:
|
||||
@@ -263,10 +263,10 @@ class WorktreeManager:
|
||||
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))
|
||||
self.index_path.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
||||
|
||||
def _find(self, name: str) -> dict | None:
|
||||
idx = self._load_index()
|
||||
@@ -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,10 +524,10 @@ 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))
|
||||
fp.write_text(c.replace(old_text, new_text, 1), encoding="utf-8")
|
||||
return f"Edited {path}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
@@ -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,10 +110,10 @@ 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))
|
||||
fp.write_text(c.replace(old_text, new_text, 1), encoding="utf-8")
|
||||
return f"Edited {path}"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
@@ -201,7 +201,7 @@ class SkillLoader:
|
||||
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:
|
||||
@@ -243,7 +243,7 @@ def microcompact(messages: list):
|
||||
def auto_compact(messages: list) -> list:
|
||||
TRANSCRIPT_DIR.mkdir(exist_ok=True)
|
||||
path = TRANSCRIPT_DIR / f"transcript_{int(time.time())}.jsonl"
|
||||
with open(path, "w") as f:
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
for msg in messages:
|
||||
f.write(json.dumps(msg, default=str) + "\n")
|
||||
conv_text = json.dumps(messages, default=str)[-80000:]
|
||||
@@ -270,10 +270,10 @@ class TaskManager:
|
||||
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))
|
||||
(TASKS_DIR / f"task_{task['id']}.json").write_text(json.dumps(task, indent=2), encoding="utf-8")
|
||||
|
||||
def create(self, subject: str, description: str = "") -> str:
|
||||
task = {"id": self._next_id(), "subject": subject, "description": description,
|
||||
@@ -291,7 +291,7 @@ class TaskManager:
|
||||
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 @@ class TaskManager:
|
||||
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:
|
||||
@@ -370,15 +370,15 @@ class MessageBus:
|
||||
msg = {"type": msg_type, "from": sender, "content": content,
|
||||
"timestamp": time.time()}
|
||||
if extra: msg.update(extra)
|
||||
with open(INBOX_DIR / f"{to}.jsonl", "a") as f:
|
||||
with open(INBOX_DIR / f"{to}.jsonl", "a", encoding="utf-8") as f:
|
||||
f.write(json.dumps(msg) + "\n")
|
||||
return f"Sent {msg_type} to {to}"
|
||||
|
||||
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]
|
||||
path.write_text("")
|
||||
msgs = [json.loads(l) for l in path.read_text(encoding="utf-8").strip().splitlines() if l]
|
||||
path.write_text("", encoding="utf-8")
|
||||
return msgs
|
||||
|
||||
def broadcast(self, sender: str, content: str, names: list) -> str:
|
||||
@@ -407,11 +407,11 @@ class TeammateManager:
|
||||
|
||||
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):
|
||||
self.config_path.write_text(json.dumps(self.config, indent=2))
|
||||
self.config_path.write_text(json.dumps(self.config, indent=2), encoding="utf-8")
|
||||
|
||||
def _find(self, name: str) -> dict:
|
||||
for m in self.config["members"]:
|
||||
@@ -509,7 +509,7 @@ class TeammateManager:
|
||||
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:
|
||||
|
||||
Reference in New Issue
Block a user