diff --git a/AGENTS.md b/AGENTS.md index 2605f78..eefc008 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,7 +27,7 @@ REPL 内的会话管理命令优先使用 slash command。 -`/history`、`/resume`、`/new` 由程序直接处理,不进入 agent loop。 +REPL 内所有 slash command 由程序直接处理,不进入 agent loop。 当前项目支持最小 memory:使用 `/remember` 保存长期信息,使用 `/memory` 查看。 diff --git a/SKILLS/cli-core.md b/SKILLS/cli-core.md index fff60e6..6a295db 100644 --- a/SKILLS/cli-core.md +++ b/SKILLS/cli-core.md @@ -23,7 +23,7 @@ - 搜索文件内容时,优先使用 `Grep`,而不是 `Bash` - 修改已有文件时,优先使用 `Edit`,而不是 `Bash` 或 `Write` - 需要创建或覆盖文件时,优先使用 `Write` 工具,而不是 `Bash` -- 会话管理优先使用 slash command,而不是自然语言或 `Bash` 探查 session 文件 +- 会话与 memory 管理优先使用 slash command,而不是自然语言或 `Bash` 探查对应文件 - 长期有价值的项目约束或偏好,优先使用 `/remember` 保存 - Windows 下先验证 shell 兼容性,再选择命令写法 diff --git a/src/cc_slim/commands.py b/src/cc_slim/commands.py new file mode 100644 index 0000000..48b4033 --- /dev/null +++ b/src/cc_slim/commands.py @@ -0,0 +1,83 @@ +from __future__ import annotations + +from pathlib import Path +from typing import Any + +from rich.console import Console + +from cc_slim.engine import Agent +from cc_slim.memory import MemoryStore +from cc_slim.session import SessionStore + + +def parse_command(text: str) -> dict[str, str] | None: + command = text.strip() + if not command.startswith("/"): + return None + name, _, args = command.partition(" ") + return {"name": name, "args": args.strip()} + + +def handle_command( + text: str, + *, + console: Console, + root: Path, + config: Any, + store: SessionStore, + memory: MemoryStore, + agent: Agent, + build_agent: Any, + render_history: Any, +) -> Agent: + parsed = parse_command(text) + if not parsed: + return agent + + name = parsed["name"] + args = parsed["args"] + + if name == "/help": + console.print("/help - 显示命令帮助") + console.print("/clear - 清空当前上下文并创建新 session") + console.print("/history - 查看历史 session") + console.print("/resume - 恢复指定 session") + console.print("/new - 创建全新 session") + console.print("/remember - 保存长期 memory") + console.print("/memory - 查看当前项目 memory") + return agent + + if name in {"/clear", "/new"}: + session_meta = store.create_session(config.model) + console.print(f"已创建新 session: {session_meta.get('session_id')}") + return build_agent(root, config, store, session_meta, []) + + if name == "/history": + render_history(store) + return agent + + if name == "/resume": + if not args: + console.print("[red]error:[/red] 缺少 resume 目标") + return agent + session_id = store.resolve_session_id(args) + session_meta = store.load_meta(session_id) + restored_history = store.load_messages(session_id) + console.print(f"已恢复 session: {session_meta.get('session_id')}") + return build_agent(root, config, store, session_meta, restored_history) + + if name == "/memory": + content = memory.read() + console.print(content or "当前项目还没有 memory。") + return agent + + if name == "/remember": + if not args: + console.print("[red]error:[/red] 缺少需要保存的 memory 内容") + return agent + path = memory.append(args) + console.print(f"已写入 memory: {path}") + return agent + + console.print(f"[red]error:[/red] 不支持的命令: {name},输入 /help 查看帮助") + return agent diff --git a/src/cc_slim/main.py b/src/cc_slim/main.py index 7cd318a..d08c683 100644 --- a/src/cc_slim/main.py +++ b/src/cc_slim/main.py @@ -7,6 +7,7 @@ import typer from rich.console import Console from rich.table import Table +from cc_slim.commands import handle_command, parse_command from cc_slim.engine import Agent, resolve_config from cc_slim.memory import MemoryStore from cc_slim.session import SessionStore @@ -79,52 +80,6 @@ def build_agent( ) -def handle_repl_command( - user_input: str, - store: SessionStore, - memory: MemoryStore, - config: object, - root: Path, - agent: Agent, -) -> Agent: - command = user_input.strip() - if command in {"--history", "/history"}: - render_history(store) - return agent - - if command == "/new": - session_meta = store.create_session(config.model) - console.print(f"已创建新 session: {session_meta.get('session_id')}") - return build_agent(root, config, store, session_meta, []) - - if command == "/memory": - content = memory.read() - console.print(content or "当前项目还没有 memory。") - return agent - - if command.startswith("/remember "): - text = user_input.split(" ", 1)[1].strip() - if not text: - console.print("[red]error:[/red] 缺少需要保存的 memory 内容") - return agent - path = memory.append(text) - console.print(f"已写入 memory: {path}") - return agent - - if command.startswith("--resume ") or command.startswith("/resume "): - target = command.split(maxsplit=1)[1].strip() - if not target: - console.print("[red]error:[/red] 缺少 resume 目标") - return agent - session_id = store.resolve_session_id(target) - session_meta = store.load_meta(session_id) - restored_history = store.load_messages(session_id) - console.print(f"已恢复 session: {session_meta.get('session_id')}") - return build_agent(root, config, store, session_meta, restored_history) - - return agent - - @app.command() def run( prompt: Optional[str] = typer.Argument(None, help="单次执行的用户输入"), @@ -183,9 +138,19 @@ def run( if not user_input.strip(): continue - if user_input.strip().startswith("/") or user_input.strip().startswith("--history") or user_input.strip().startswith("--resume"): + if parse_command(user_input): try: - agent = handle_repl_command(user_input, store, memory, config, root, agent) + agent = handle_command( + user_input, + console=console, + root=root, + config=config, + store=store, + memory=memory, + agent=agent, + build_agent=build_agent, + render_history=render_history, + ) except Exception as exc: # pragma: no cover console.print(f"[red]error:[/red] {exc}") continue