Skip to content

Commit f3d36e9

Browse files
lzwjavaclaude
andcommitted
feat: add /read command and refresh README tagline
Adds a local /read <path> command that prints file contents to the terminal with line numbers, so users can inspect files without leaving the REPL. Strips a leading @ to match the @mention UX; rejects missing paths, directories, and binary files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 51a3bc3 commit f3d36e9

4 files changed

Lines changed: 58 additions & 1 deletion

File tree

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
[中文](./README_CN.md)
44

5-
A minimal [openclaw](https://github.com/lzwjava/openclaw) implementation for enterprise-constrained environments. iclaw is a lightweight terminal REPL that provides AI-assisted coding via GitHub Copilot, designed to work where full-featured tools are blocked or restricted — no browser extensions, no IDE plugins, just a plain Python CLI.
5+
A terminal AI agent that codes, searches, and runs commands for you — works on personal machines and locked-down enterprise ones.
6+
7+
A minimal [openclaw](https://github.com/lzwjava/openclaw) implementation, built as a plain Python CLI with no browser extensions or IDE plugins, powered by GitHub Copilot.
68

79
*Disclaimer: All tests with Copilot were performed using my personal subscription and personal account.*
810

@@ -54,6 +56,7 @@ pip install -e .
5456
- `/ca_bundle`: Set CA bundle for HTTPS (usage: `/ca_bundle [path|off]`).
5557
- `/log`: Set log verbosity (usage: `/log [verbose|info]`).
5658
- `/copy`: Copy last response to clipboard.
59+
- `/read`: Print file contents to terminal (usage: `/read <path>`).
5760
- `/clear`: Clear conversation history.
5861
- `/compact`: Compact conversation history using LLM.
5962
- `/export`: Export full conversation history to JSON file.

iclaw/commands/read.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import os
2+
import sys
3+
4+
5+
def _is_binary(data):
6+
return b"\x00" in data
7+
8+
9+
def handle_read_command(arg):
10+
if not arg:
11+
print("Usage: /read <path>", file=sys.stderr)
12+
return
13+
14+
path = arg.strip()
15+
if path.startswith("@"):
16+
path = path[1:]
17+
18+
if not os.path.exists(path):
19+
print(f"No such file: {path}", file=sys.stderr)
20+
return
21+
if os.path.isdir(path):
22+
print(f"Is a directory: {path}", file=sys.stderr)
23+
return
24+
25+
try:
26+
with open(path, "rb") as f:
27+
raw = f.read()
28+
except OSError as e:
29+
print(f"Error reading {path}: {e}", file=sys.stderr)
30+
return
31+
32+
if _is_binary(raw):
33+
print(f"Binary file: {path} ({len(raw)} bytes)", file=sys.stderr)
34+
return
35+
36+
try:
37+
text = raw.decode("utf-8")
38+
except UnicodeDecodeError:
39+
text = raw.decode("utf-8", errors="replace")
40+
41+
lines = text.splitlines()
42+
print(f"\n{path}{len(lines)} lines")
43+
width = len(str(len(lines))) if lines else 1
44+
for i, line in enumerate(lines, 1):
45+
print(f"{i:>{width}} {line}")
46+
print()

iclaw/completer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"/ca_bundle",
1313
"/log",
1414
"/copy",
15+
"/read",
1516
"/status",
1617
"/help",
1718
"/clear",

iclaw/main.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from iclaw.commands.log import handle_log_command
1515
from iclaw.commands.model import handle_model_command, handle_model_provider_command
1616
from iclaw.commands.proxy import handle_ca_bundle_command, handle_proxy_command
17+
from iclaw.commands.read import handle_read_command
1718
from iclaw.commands.search_provider import handle_search_provider_command
1819
from iclaw.commands.utils import handle_copy_command
1920
from iclaw.completer import IclawCompleter
@@ -39,6 +40,7 @@
3940
("/ca_bundle", "Set CA bundle for HTTPS (usage: /ca_bundle [path|off])"),
4041
("/log", "Set log verbosity (usage: /log [verbose|info])"),
4142
("/copy", "Copy last Copilot response to clipboard"),
43+
("/read", "Print file contents to terminal (usage: /read <path>)"),
4244
("/clear", "Clear conversation history"),
4345
("/compact", "Compact conversation history using LLM"),
4446
("/export", "Export full conversation history to JSON file"),
@@ -115,6 +117,11 @@ def main():
115117
if user_input == "/copy":
116118
handle_copy_command(last_reply)
117119
continue
120+
if user_input == "/read" or user_input.startswith("/read "):
121+
parts = user_input.split(maxsplit=1)
122+
arg = parts[1] if len(parts) > 1 else None
123+
handle_read_command(arg)
124+
continue
118125
if user_input == "/provider_model":
119126
p, t = handle_model_provider_command(CONFIG_PATH, model_provider)
120127
if t:

0 commit comments

Comments
 (0)