diff --git a/README.md b/README.md
index 04c621c..f524551 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@ PhoneWork uses a **Router + Host Client** architecture that supports both single
| Module | Purpose |
|--------|---------|
| `standalone.py` | Single-process entry point: runs router + host client together |
-| `router/main.py` | FastAPI app factory, mounts `/ws/node` endpoint, can be run directly |
+| `router/main.py` | FastAPI app factory, mounts `//ws/node` endpoint, can be run directly |
| `shared/protocol.py` | Wire protocol for router-host communication |
| `router/nodes.py` | Node registry, connection management, user-to-node mapping |
| `router/ws.py` | WebSocket endpoint for host clients, heartbeat, message routing |
@@ -51,7 +51,7 @@ PhoneWork uses a **Router + Host Client** architecture that supports both single
| `host_client/config.py` | Host client configuration loader |
| `bot/handler.py` | Receives Feishu events via long-connection WebSocket |
| `bot/feishu.py` | Sends text/file replies back to Feishu |
-| `bot/commands.py` | Slash command handler (`/new`, `/status`, `/shell`, `/remind`, `/tasks`, `/nodes`, `/node`) |
+| `bot/commands.py` | Slash command handler (`//new`, `//status`, `//shell`, `//remind`, `//tasks`, `//nodes`, `//node`) |
| `orchestrator/agent.py` | LangChain agent with per-user history + direct/smart mode + direct Q&A |
| `orchestrator/tools.py` | Tools: session management, shell, file ops, web search, scheduler, task status |
| `agent/manager.py` | Session registry with persistence, idle timeout, and auto-background tasks |
@@ -281,30 +281,30 @@ GET /health
| Command | Description |
|---------|-------------|
-| `/new
[msg]` | Create a new Claude Code session in `` |
-| `/new [msg] --timeout N` | Create with custom CC timeout (seconds) |
-| `/new [msg] --idle N` | Create with custom idle timeout (seconds) |
-| `/status` | Show your sessions and current mode |
-| `/switch ` | Switch active session to number `` from `/status` |
-| `/close [n]` | Close active session (or session ``) |
-| `/direct` | Direct mode: messages go straight to Claude Code (no LLM overhead) |
-| `/smart` | Smart mode: messages go through LLM for intelligent routing (default) |
-| `/shell ` | Run a shell command directly (bypasses LLM) |
-| `/remind ` | Set a reminder (e.g., `/remind 10m check build`) |
-| `/tasks` | List background tasks with status |
-| `/nodes` | List connected host nodes (multi-host mode) |
-| `/node ` | Switch active node (multi-host mode) |
-| `/help` | Show command reference |
+| `//new [msg]` | Create a new Claude Code session in `` |
+| `//new [msg] --timeout N` | Create with custom CC timeout (seconds) |
+| `//new [msg] --idle N` | Create with custom idle timeout (seconds) |
+| `//status` | Show your sessions and current mode |
+| `//switch ` | Switch active session to number `` from `//status` |
+| `//close [n]` | Close active session (or session ``) |
+| `//direct` | Direct mode: messages go straight to Claude Code (no LLM overhead) |
+| `//smart` | Smart mode: messages go through LLM for intelligent routing (default) |
+| `//shell ` | Run a shell command directly (bypasses LLM) |
+| `//remind ` | Set a reminder (e.g., `//remind 10m check build`) |
+| `//tasks` | List background tasks with status |
+| `//nodes` | List connected host nodes (multi-host mode) |
+| `//node ` | Switch active node (multi-host mode) |
+| `//help` | Show command reference |
### Message Routing Modes
**Smart mode (default):** Messages are analyzed by the LLM, which decides whether to create a new session, send to an existing one, or ask for clarification. Useful when you want the bot to understand natural language requests.
-**Direct mode:** Messages go straight to the active Claude Code session, bypassing the LLM. Faster and more predictable, but requires an active session. Use `/direct` to enable.
+**Direct mode:** Messages go straight to the active Claude Code session, bypassing the LLM. Faster and more predictable, but requires an active session. Use `//direct` to enable.
### Claude Code Commands
-Claude Code slash commands (like `/help`, `/clear`, `/compact`, `/cost`) are passed through to Claude Code when you have an active session. Bot commands (`/new`, `/status`, `/switch`, etc.) are handled by the bot first.
+Claude Code slash commands (like `//help`, `//clear`, `//compact`, `//cost`) are passed through to Claude Code when you have an active session. Bot commands (`//new`, `//status`, `//switch`, etc.) are handled by the bot first.
---
@@ -321,13 +321,13 @@ Claude Code slash commands (like `/help`, `/clear`, `/compact`, `/cost`) are pas
#### Better Interaction
-- **Slash commands** - Direct control via `/new`, `/status`, `/switch`, `/close`, `/direct`, `/smart`
+- **Slash commands** - Direct control via `//new`, `//status`, `//switch`, `//close`, `//direct`, `//smart`
- **Multi-session switching** - Multiple projects open simultaneously, switch between them
- **Interactive cards** - Session status displayed in Feishu message cards
#### Operational Quality
-- **Health checks** - `/health` endpoint shows WebSocket status and can test Claude Code connectivity
+- **Health checks** - `//health` endpoint shows WebSocket status and can test Claude Code connectivity
- **Auto-reconnection** - WebSocket automatically reconnects if the connection drops
- **Configurable timeouts** - Each session can have custom idle and execution timeout settings
- **Audit logging** - All conversations logged to files for debugging and accountability
@@ -346,7 +346,7 @@ Claude Code slash commands (like `/help`, `/clear`, `/compact`, `/cost`) are pas
- Automatic detection of question-like messages
#### Shell Access
-- Execute shell commands remotely via `/shell` or through the LLM
+- Execute shell commands remotely via `//shell` or through the LLM
- Safety guards block destructive commands (`rm -rf /`, `sudo rm`, `mkfs`, etc.)
- Configurable timeout (max 120 seconds)
@@ -361,7 +361,7 @@ Claude Code slash commands (like `/help`, `/clear`, `/compact`, `/cost`) are pas
- Long-running tasks (timeout > 60s) automatically run in background
- Immediate acknowledgment with task ID
- Feishu notification on completion
-- Track task status with `/tasks` command
+- Track task status with `//tasks` command
#### Web Search
- Search the web via 秘塔AI Search (requires `METASO_API_KEY`)
@@ -370,7 +370,7 @@ Claude Code slash commands (like `/help`, `/clear`, `/compact`, `/cost`) are pas
- Supports multiple scopes: webpage, paper, document, video, podcast
#### Scheduling & Reminders
-- Set one-time reminders: `/remind 10m check the build`
+- Set one-time reminders: `//remind 10m check the build`
- Schedule recurring reminders
- Notifications delivered to Feishu
- Persistent across server restarts
@@ -400,8 +400,8 @@ python -m host_client.main
Connects to router via WebSocket, runs full mailboy stack locally.
#### Node Management
-- `/nodes` — View all connected host nodes with status
-- `/node ` — Switch active node for your user
+- `//nodes` — View all connected host nodes with status
+- `//node ` — Switch active node for your user
- Automatic routing: LLM decides which node handles each message
- Health monitoring: Router tracks node heartbeats
- Reconnection: Host clients auto-reconnect on disconnect
diff --git a/ROADMAP.md b/ROADMAP.md
index 5a8704d..b9c6af3 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -40,7 +40,7 @@ returns: {stdout, stderr, exit_code}
approved by the user (raise a confirmation request)
- Timeout hard cap: 120 s; for longer tasks see M2.4
-**New slash command:** `/shell ` (bypasses LLM; runs directly)
+**New slash command:** `//shell ` (bypasses LLM; runs directly)
---
@@ -86,7 +86,7 @@ fire-and-forget with completion notification.
**New tool:** `run_background` — explicitly submits any shell command or CC prompt as a
background task and returns `task_id` immediately.
-**New slash command:** `/tasks` — list running/completed background tasks with status.
+**New slash command:** `//tasks` — list running/completed background tasks with status.
**New tool:** `task_status` — check status of a specific `task_id`, optionally get output so far.
@@ -145,7 +145,7 @@ args: action ("remind" | "repeat"), delay_seconds (int), interval_seconds (int),
message (str), conv_id (str, optional — if set, forward to that CC session)
```
-**New slash command:** `/remind m|h|s ` — set a reminder without LLM
+**New slash command:** `//remind m|h|s ` — set a reminder without LLM
---
@@ -168,7 +168,7 @@ args: action ("remind" | "repeat"), delay_seconds (int), interval_seconds (int),
| `orchestrator/tools.py` | Add `ShellTool`, `FileOpsTool`, `WebTool`, `TaskStatusTool`, `SchedulerTool` |
| `agent/task_runner.py` | New — `TaskRunner` singleton, `BackgroundTask` dataclass |
| `agent/scheduler.py` | New — `schedule_once`, `schedule_recurring` |
-| `bot/commands.py` | Add `/shell`, `/tasks`, `/remind` commands |
+| `bot/commands.py` | Add `//shell`, `//tasks`, `//remind` commands |
| `bot/feishu.py` | Add `chat_id` context var for file send from tool |
| `bot/handler.py` | Pass `chat_id` into context var alongside `user_id` |
| `requirements.txt` | Add `httpx` (if not already present as transitive dep) |
@@ -183,10 +183,10 @@ args: action ("remind" | "repeat"), delay_seconds (int), interval_seconds (int),
- [x] M2.3: Send "show me the last 50 lines of audit/abc123.jsonl" — file content returned
- [x] M2.3: Send "send me the sessions.json file" — file arrives in Feishu chat
- [x] M2.4: Start a long CC task (e.g. `--timeout 120`) — bot replies immediately, notifies on finish
-- [x] M2.4: `/tasks` — lists running task with elapsed time
+- [x] M2.4: `//tasks` — lists running task with elapsed time
- [x] M2.5: "Python 3.13 有哪些新特性?" — `web ask` returns RAG answer from metaso
- [x] M2.5: "帮我读取这个URL: https://example.com" — page content extracted as markdown
-- [x] M2.6: `/remind 10m deploy check` — 10 min later, message arrives in Feishu
+- [x] M2.6: `//remind 10m deploy check` — 10 min later, message arrives in Feishu
---
---
@@ -342,7 +342,7 @@ node to forward each message to.
`display_name`, `connected_at`, `last_heartbeat`
- `get_nodes_for_user(open_id) -> list[NodeConnection]` — may return multiple
- `get_active_node(user_id) -> NodeConnection | None` — per-user active node preference
-- `set_active_node(user_id, node_id)` — updated by router LLM or `/node` command
+- `set_active_node(user_id, node_id)` — updated by router LLM or `//node` command
**Router LLM** (`router/routing_agent.py`):
@@ -383,7 +383,7 @@ Authorization: Bearer
- On `ForwardResponse`, resolves Future with `reply` or raises on `error`
**Modified files:**
-- `main.py` → mounts `/ws/node`, starts `NodeRegistry`
+- `main.py` → mounts `//ws/node`, starts `NodeRegistry`
- `bot/handler.py` → after allowlist check, calls `routing_agent.route(user_id, chat_id, text)`
instead of `agent.run(user_id, text)` directly
- `config.py` → adds `ROUTER_SECRET`, `ROUTER_LLM_*` (can be same or different model)
@@ -437,7 +437,7 @@ all LLM/CC config from it. User only maintains one config file.
### M3.5 — Node Health + User-Facing Status
-**`/nodes` slash command** (handled at router, before forwarding):
+**`//nodes` slash command** (handled at router, before forwarding):
```
Connected Nodes:
→ home-pc [ACTIVE] sessions=2 online 3h
@@ -446,9 +446,9 @@ Connected Nodes:
Use "/node " to switch active node.
```
-**`/node ` slash command** — sets active node for user.
+**`//node ` slash command** — sets active node for user.
-**Router `/health` updates:**
+**Router `//health` updates:**
```json
{
"nodes": [
@@ -513,7 +513,7 @@ PhoneWork/
2. **M3.2** — Host client daemon (wrap existing mailboy + agent stack)
3. **M3.3** — Router (node registry, WS, routing LLM, refactor handler)
4. **M3.4** — Standalone script
-5. **M3.5** — Node health, `/nodes`, `/node` commands
+5. **M3.5** — Node health, `//nodes`, `//node` commands
---
@@ -522,8 +522,8 @@ PhoneWork/
- [x] `python standalone.py` — works identically to current `python main.py`
- [x] Router starts, host client connects, registration logged
- [x] Feishu message → routing LLM selects node → forwarded → reply returned
-- [x] `/nodes` shows all connected nodes with active marker
-- [x] `/node work-server` — switches active node, confirmed in next message
+- [x] `//nodes` shows all connected nodes with active marker
+- [x] `//node work-server` — switches active node, confirmed in next message
- [x] Two nodes serving same user — message routed to active node
- [x] Kill host client → router marks offline, user sees "Node home-pc is offline"
- [x] Host client reconnects → re-registered, messages flow again
@@ -543,8 +543,8 @@ The current `commands.py` calls `agent._active_conv`, `manager.list_sessions()`,
`task_runner.list_tasks()`, `scheduler` — all of which move to the host client in M3.
**Resolution:** At the router, `bot/commands.py` is reduced to two commands:
-`/nodes` and `/node `. All other slash commands (`/new`, `/status`, `/close`,
-`/switch`, `/direct`, `/smart`, `/shell`, `/tasks`, `/remind`) are forwarded to the
+`//nodes` and `//node `. All other slash commands (`//new`, `//status`, `//close`,
+`//switch`, `//direct`, `//smart`, `//shell`, `//tasks`, `//remind`) are forwarded to the
active node as-is — the node's mailboy handles them using its local `commands.py`.
The node's command handler remains unchanged from M2.
diff --git a/bot/commands.py b/bot/commands.py
index 030fdcc..839ece9 100644
--- a/bot/commands.py
+++ b/bot/commands.py
@@ -202,8 +202,8 @@ async def _cmd_status(user_id: str) -> str:
perm = _perm_label(s.get("permission_mode", DEFAULT_PERMISSION_MODE))
lines.append(f"{marker}{i}. `{s['conv_id']}` - `{s['cwd']}` [{perm}]")
lines.append(f"\n**Mode:** {mode}")
- lines.append("Use `/switch ` to activate a session.")
- lines.append("Use `/direct` or `/smart` to change mode.")
+ lines.append("Use `//switch ` to activate a session.")
+ lines.append("Use `//direct` or `//smart` to change mode.")
return "\n".join(lines)
@@ -247,7 +247,7 @@ async def _cmd_close(user_id: str, args: str) -> str:
else:
conv_id = agent.get_active_conv(user_id)
if not conv_id:
- return "No active session. Use `/close ` or `/close `."
+ return "No active session. Use `//close ` or `//close `."
try:
success = await manager.close(conv_id, user_id=user_id)
@@ -302,7 +302,7 @@ async def _cmd_perm(user_id: str, args: str) -> str:
return f"Unknown mode '{alias}'. Valid: bypass, accept, plan"
if not conv_id:
- return "No active session. Use `/perm ` or activate a session first."
+ return "No active session. Use `//perm ` or activate a session first."
try:
manager.set_permission_mode(conv_id, permission_mode, user_id=user_id)
@@ -323,7 +323,7 @@ def _cmd_direct(user_id: str) -> str:
"""Enable direct mode - messages go straight to Claude Code."""
conv = agent.get_active_conv(user_id)
if not conv:
- return "No active session. Use `/new` or `/switch` first."
+ return "No active session. Use `//new` or `//switch` first."
agent.set_passthrough(user_id, True)
return f"✓ Direct mode ON. Messages go directly to session `{conv}`."
@@ -430,7 +430,7 @@ async def _cmd_nodes(user_id: str, args: str) -> str:
marker = "→ " if n["node_id"] == active_node_id else " "
status = "🟢" if n["status"] == "online" else "🔴"
lines.append(f"{marker}{n['display_name']} {status} sessions={n['sessions']}")
- lines.append("\nUse `/node ` to switch active node.")
+ lines.append("\nUse `//node ` to switch active node.")
return "\n".join(lines)