- New read_file skill: paginated file reading with line ranges, path restricted to /workspace, binary detection, directory listing - Session persistence via SQLite + FTS5: conversation history survives agent restarts, last N messages restored into deque on boot, auto-prune to 1000 messages - Update truncation hint to reference read_file instead of run_command - New scripts/update.sh for patching rootfs + rebuilding snapshot Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
68 lines
1.9 KiB
Python
68 lines
1.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Read a file from /workspace with optional line range."""
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
args = json.loads(sys.stdin.read())
|
|
path = args.get("path", "")
|
|
offset = max(int(args.get("offset", 1)), 1)
|
|
limit = max(int(args.get("limit", 200)), 1)
|
|
|
|
WORKSPACE = os.environ.get("WORKSPACE", "/workspace")
|
|
|
|
# Resolve to absolute and ensure it stays under /workspace
|
|
resolved = os.path.realpath(path)
|
|
if not resolved.startswith(WORKSPACE + "/") and resolved != WORKSPACE:
|
|
print(f"[error: path must be under {WORKSPACE}]")
|
|
sys.exit(0)
|
|
|
|
if not os.path.exists(resolved):
|
|
print(f"[error: file not found: {path}]")
|
|
sys.exit(0)
|
|
|
|
if os.path.isdir(resolved):
|
|
entries = sorted(os.listdir(resolved))
|
|
print(f"Directory listing of {path} ({len(entries)} entries):")
|
|
for entry in entries[:100]:
|
|
full = os.path.join(resolved, entry)
|
|
kind = "dir" if os.path.isdir(full) else "file"
|
|
print(f" {entry} ({kind})")
|
|
if len(entries) > 100:
|
|
print(f" ... and {len(entries) - 100} more")
|
|
sys.exit(0)
|
|
|
|
# Binary detection — check first 512 bytes
|
|
try:
|
|
with open(resolved, "rb") as f:
|
|
chunk = f.read(512)
|
|
if b"\x00" in chunk:
|
|
size = os.path.getsize(resolved)
|
|
print(f"[binary file: {path} ({size} bytes)]")
|
|
sys.exit(0)
|
|
except Exception as e:
|
|
print(f"[error reading file: {e}]")
|
|
sys.exit(0)
|
|
|
|
# Read with line range
|
|
try:
|
|
with open(resolved) as f:
|
|
lines = f.readlines()
|
|
except Exception as e:
|
|
print(f"[error reading file: {e}]")
|
|
sys.exit(0)
|
|
|
|
total = len(lines)
|
|
start_idx = offset - 1 # 0-based
|
|
end_idx = min(start_idx + limit, total)
|
|
|
|
if start_idx >= total:
|
|
print(f"[file has {total} lines — offset {offset} is beyond end of file]")
|
|
sys.exit(0)
|
|
|
|
for i in range(start_idx, end_idx):
|
|
print(f"{i + 1}\t{lines[i]}", end="")
|
|
|
|
if end_idx < total:
|
|
print(f"\n[showing lines {offset}-{end_idx} of {total} — use offset={end_idx + 1} to read more]")
|