1. 项目概述一个为软件项目工作而生的个人编码操作系统如果你和我一样日常被各种软件项目的琐碎任务缠身——比如代码审查、提交日志梳理、项目文档同步或者只是想为不同的项目建立一个独立、隔离的“工作间”——那么你很可能已经厌倦了那些庞大、复杂且难以定制的通用AI助手平台。这正是我创建retn0claw的初衷。它不是一个面向大众的消费级产品也不是一个功能庞杂的“瑞士军刀”。你可以把它理解为一个专为软件工程师打造的个人编码操作系统一个轻量级的运行时环境核心目标就是让你能清晰理解、自由修改并用它来运行那些围绕项目展开的、需要隔离执行的工作流。retn0claw脱胎于 NanoClaw 项目保留了其精巧的编排器、SQLite状态管理和容器化执行等优秀基因。但我的分叉fork有着更明确的焦点一切围绕软件项目Project-room。这意味着工作单元不再是泛泛的对话而是一个个具体的代码仓库或开发任务。每个“房间”room都是一个独立的沙盒拥有自己的记忆指令集和上下文和执行环境互不干扰。这特别适合需要同时处理多个项目、又希望保持上下文纯净的开发者。为什么选择这条路因为我发现大型平台为了追求通用性往往引入了过多的抽象层和黑盒逻辑当你需要调整一个细微的工作流或者只是想弄明白“它到底是怎么运行的”时会感到无比沮丧。retn0claw的核心哲学是“小到足以审视”。它的代码库保持紧凑架构清晰目的是让作为所有者的你能够完全掌控其行为可以根据自己的习惯去增删功能而不是被工具所限制。2. 核心设计理念与架构拆解2.1 为什么是“项目房间”工作流传统的AI助手交互模式是线性的、无状态的对话。你问一个问题它给一个回答上下文在冗长的对话中逐渐稀释。对于软件开发这种高度结构化、长期持续的活动来说这非常低效。retn0claw采用的“项目房间”Project-room模型是一种心智上的革新。你可以为每个Git仓库、每个功能分支甚至每个实验性想法创建一个独立的“房间”。这个房间拥有独立的记忆体每个房间对应一个CLAUDE.md文件里面定义了该房间的长期记忆、核心指令和上下文。例如你可以为“前端重构项目”房间写入“本项目使用React 18 TypeScript代码风格遵循Airbnb规范重点关注性能优化。” 这些信息会成为该房间内所有交互的默认背景知识。隔离的执行环境房间内的AI代理Agent运行在独立的Linux容器中。它只能访问你明确挂载mount给它的目录通常是项目代码目录。这意味着它无法意外读取或修改你电脑上的其他文件安全性得到保障。序列化的工作队列对于同一个房间或“组”任务会被自动序列化处理。即使你同时丢给它“审查代码”和“运行测试”两个指令它也会一个一个来避免状态混乱。这种设计完美匹配了软件开发的现实任务有边界上下文需隔离执行要可控。它把AI从一个“聊天对象”变成了一个可部署、可管理的“项目成员”。2.2 架构全景数据流与核心组件让我们深入代码看看一条指令是如何走完全程的。整个系统的数据流可以概括为通道 - SQLite - 消息循环 - 组队列 - 容器运行器 - 代理输出。通道Channels这是系统与外界交互的入口。目前内置了Discord和Telegram的适配器。这意味着你可以直接在熟悉的聊天软件里向你的retn0claw实例发送指令比如在Discord的特定频道里输入“/review-latest-commit”。这比打开一个专门的CLI工具要自然得多。通道模块负责接收原始消息并将其格式化为系统内部的标准事件。SQLite数据库这是系统的心脏所有状态都持久化在这里。它存储了消息Messages所有进出系统的消息记录。会话Sessions活跃的交互上下文。任务Tasks包括一次性任务和定时任务的定义与状态。组Groups即我们所说的“项目房间”的逻辑分组。 使用SQLite而非更复杂的数据是为了极致轻量和可移植性。整个系统的状态就是一个.db文件备份、迁移或调试都异常简单。消息循环Message Loop位于src/index.ts的主循环是一个事件驱动的工作泵。它持续监听来自数据库新消息事件和任务调度器的新工作然后将其分派给对应的处理器。组队列Group Queue这是实现“序列化”的关键代码在src/group-queue.ts。它确保同一个组房间内的任务严格按顺序执行。假设你同时向“项目A”房间发送了任务1和任务2组队列会保证任务1彻底完成后才从队列中取出任务2开始执行。这彻底避免了并发执行导致的资源竞争和状态污染。容器运行器Container Runner这是执行隔离的物理实现。核心代码在src/runners/claude/container-runner.ts。当任务需要执行时运行器会根据任务类型和配置决定使用哪个“运行时”目前主要是Claude Code。启动一个全新的Docker容器。将项目代码目录和你指定的其他资源以卷Volume的形式挂载到容器内。在容器内部启动AI代理如Claude Code并将任务指令、房间的CLAUDE.md记忆文件传递给它。捕获代理的所有输出stdout/stderr并在任务完成后自动清理容器。代理输出容器内AI代理的工作结果代码分析报告、生成的文本、执行日志等被运行器捕获后会作为一条新的消息写回SQLite数据库并通过最初发起任务的通道如Discord回复给用户从而形成一个完整的闭环。一个实操心得这种基于队列和容器的架构虽然看起来步骤多但带来了巨大的可靠性优势。即使某个任务中的AI代理发生了崩溃或死循环也只会影响它自己的那个容器。主编排器和其他房间的任务完全不受影响。你可以安全地“杀死”那个容器而系统整体依然健壮。3. 从零开始环境搭建与首次运行3.1 前期准备工具链检查在克隆代码之前请确保你的开发机满足以下基础要求。这些不是“建议”而是系统运行的硬性依赖。操作系统macOS、Linux或者Windows下的WSL 2。系统需要支持完整的容器化运行时。Node.js 20这是后端编排器的运行环境。建议使用nvm等版本管理工具安装LTS版本。Docker / Docker Desktop这是实现隔离执行的基石。请务必安装并确保Docker守护进程正在运行通常docker ps命令能正常执行即表示服务已就绪。Claude Code这是当前默认的AI代理运行时。你需要从官方渠道下载并安装Claude Code CLI工具。安装后在终端输入claude应该能进入其交互界面。注意在macOS上你可能会看到对 Apple Container 的提及。这是一个可选的、针对Apple Silicon优化的容器运行时替代方案但并非默认或必需。对于绝大多数用户使用Docker Desktop即可。3.2 初始化项目与首次对话环境就绪后搭建过程非常直接# 1. 克隆仓库 git clone https://github.com/nbsp1221/retn0claw.git cd retn0claw # 2. 安装项目依赖 npm install # 3. 启动与系统的交互 claude执行claude命令后你会进入Claude Code的专属对话界面。这里有一个关键细节接下来的设置命令不是在普通的系统终端bash/zsh里运行而是在这个claude对话提示符内部。在claude的提示符下输入/setup这个/setup是一个特殊的Claude Code技能命令。它会引导你完成retn0claw实例的初始配置例如设置数据库路径、连接消息通道如Discord Webhook等。根据交互提示完成配置即可。常见踩坑点权限问题如果Docker需要sudo权限而你的Node.js脚本调用Docker时失败可能需要将当前用户加入docker用户组或者配置Docker以非root用户模式运行。Claude Code路径确保claude命令在系统的PATH环境变量中。有时安装程序可能不会自动配置需要手动添加。端口冲突如果系统内嵌了Web服务器或使用了特定端口请检查默认端口如3000是否被占用。3.3 理解项目结构什么该看什么暂可忽略初次打开代码库你可能会觉得文件不少。这里帮你快速梳理重点retn0claw/ ├── src/ # 核心运行时源码 │ ├── index.ts # 主编排循环 │ ├── db.ts # SQLite数据库操作 │ ├── group-queue.ts # 组队列实现 │ ├── task-scheduler.ts # 定时任务调度 │ └── runners/ # 各种代理运行器 │ ├── claude/ # Claude Code 运行器核心 │ └── shared/ # 共享运行器逻辑 ├── groups/ # “项目房间”定义目录 │ └── [group-name]/ # 每个房间一个子目录 │ └── CLAUDE.md # 该房间的长期记忆和指令 ├── docs/ # 当前项目的最新文档 └── 其他遗留的目录和文件作为使用者你最需要关注的是groups/目录。在这里创建你的项目房间。而作为潜在的修改者src/runners/和src/下的核心逻辑文件是你的主要战场。需要特别注意代码库中还存在一些从上游NanoClaw继承下来的文档和“技能”skills目录。这些材料更多地是作为历史参考并不完全代表retn0claw当前的演进方向。最权威的、反映当前设计思想的文档是docs/README.md。在做出任何重大行为假设前建议先查阅此文档。4. 核心工作流实战创建你的第一个项目房间理论说再多不如动手做一遍。让我们创建一个真实的“项目房间”并完成一次代码审查任务。4.1 定义房间编写你的 CLAUDE.md假设你正在开发一个名为my-api-server的Node.js后端项目。你希望retn0claw能专门处理这个项目的事务。在retn0claw/groups/目录下创建一个与新项目同名的文件夹mkdir -p groups/my-api-server在该文件夹内创建最重要的CLAUDE.md文件。这个文件定义了该房间的“人格”和“知识库”。# my-api-server 项目房间 ## 项目概览 这是一个基于 Express.js 和 TypeScript 构建的 RESTful API 服务器目前版本为 0.1.0。主要功能包括用户认证和资源管理。 ## 技术栈与规范 - **运行时**: Node.js 18 - **框架**: Express 4.x - **语言**: TypeScript (严格模式) - **数据库**: PostgreSQL使用 Prisma ORM - **代码风格**: ESLint Prettier规则继承自 typescript-eslint/recommended - **测试**: Jest 和 Supertest 进行单元和集成测试 - **重要目录**: - src/controllers/: 请求处理器 - src/services/: 业务逻辑层 - src/middlewares/: 自定义中间件 - prisma/: 数据库 schema 和迁移文件 ## 你的角色与任务 你是本项目专属的代码助手。你的核心职责是 1. **代码审查**针对提交的代码差异重点检查类型安全、潜在bug、性能问题、是否符合项目规范。 2. **提交摘要**将一段时间的Git提交历史归纳成清晰、面向非技术利益相关者的变更摘要。 3. **文档同步**检查代码改动是否与 README.md 或内联文档的描述保持一致。 4. **回答项目特定问题**基于以上上下文回答关于本项目架构、代码逻辑的疑问。 ## 输出格式要求 - 代码审查结果请以列表形式呈现分为【关键问题】、【建议改进】和【风格提示】。 - 提交摘要请包含【新增功能】、【修复问题】和【技术债务】三个部分。 - 所有输出请使用中文。这个文件的内容会作为系统提示词System Prompt在每次与该房间的AI代理交互时被注入。因此写得越具体、越贴近项目实际代理的表现就越精准。4.2 配置通道让 Discord 成为控制台为了让交互更自然我们配置Discord作为指令通道。在Discord开发者门户创建一个应用并添加一个Bot。获取Bot的Token并邀请它到你的私人服务器或特定频道。在Discord服务器中为你想要的频道启用“开发者模式”然后右键点击频道复制其ID。在retn0claw的配置中通常通过环境变量或/setup命令引导的配置流程设置DISCORD_BOT_TOKEN你的BotToken DISCORD_CHANNEL_ID你的频道ID启动retn0claw主程序例如通过npm start或配置为系统服务。如果配置正确你的Bot会在Discord中上线。4.3 执行第一个任务代码审查现在一切就绪。假设你刚在my-api-server项目里完成了一个功能分支并创建了Pull Request。在配置好的Discord频道里你可以这样发送指令/task-for my-api-server review-pr --pr-url https://github.com/yourname/my-api-server/pull/42这里假设你已经实现了一个名为review-pr的技能或任务。retn0claw的核心是一个框架具体的任务逻辑需要你自行定义或使用社区技能。一个简单的起点是创建一个调用GitHub API获取差异并交给房间AI代理分析的任务。系统会识别到任务目标房间是my-api-server于是将任务放入my-api-server的专属队列。从队列中取出该任务启动一个全新的Docker容器。将你本地的my-api-server代码目录挂载到容器内。在容器中启动Claude Code并将groups/my-api-server/CLAUDE.md的内容和“请审查此PR的代码差异”的指令一并传递给它。Claude Code在隔离的环境中访问挂载的代码分析PR差异生成审查意见。几分钟后审查结果会以该Discord Bot的身份回复到同一个频道中格式大致会遵循你在CLAUDE.md里定义的模板。实操心得挂载卷的权限这是最容易出问题的地方。Docker容器内进程的用户通常是root可能没有权限读写你挂载的宿主主机目录尤其是当宿主目录属于你的个人用户时。解决方案通常有两种一是在运行Docker容器时使用-u参数指定用户ID和组ID匹配你的宿主用户二是在宿主机上调整目录权限但不够安全。我个人的做法是在项目启动脚本中动态获取当前用户的UID和GID并将其作为环境变量传递给容器运行器。5. 深入定制编写你自己的任务与技能retn0claw开箱即用可能只提供基础框架。真正的威力在于根据你的工作流定制专属任务。让我们以“生成每周提交报告”为例看看如何实现一个自定义任务。5.1 任务定义在数据库中注册任务在系统中需要被定义和调度。我们可以通过代码或数据库操作来添加。一个任务通常包含name: 任务标识符如weekly-commit-summary。group: 归属的项目房间如my-api-server。schedule: Cron表达式定义执行时间如0 9 * * 1表示每周一上午9点。instruction: 发送给AI代理的具体指令模板。runner: 指定使用哪个运行器如claude。你可以直接向数据库的tasks表插入记录但更程序化的方式是在系统初始化脚本中创建。例如在某个初始化模块中// 伪代码示例添加一个每周任务 async function createWeeklySummaryTask(db) { await db.run( INSERT OR REPLACE INTO tasks (id, name, group_id, schedule, instruction, runner, enabled) VALUES (?, ?, ?, ?, ?, ?, ?) , [ task_weekly_summary, weekly-commit-summary, my-api-server, // 对应 groups 表中的组ID 0 9 * * 1, // 每周一9点 请分析过去一周从上周一到上周日在本代码库中的所有Git提交。 总结要点包括 1. 新增了哪些主要功能或模块 2. 修复了哪些关键Bug 3. 代码库在结构或依赖上有何重要变化 4. 是否有需要关注的TODO或FIXME标记被引入 请以项目周报的形式输出语言简洁。, claude, 1 // enabled ]); }5.2 技能实现连接外部API任务指令中的“分析Git提交”需要真实数据。这需要实现一个“技能”Skill—— 一段能执行特定操作如调用GitHub API、执行Shell命令的代码。技能通常放在src/skills/目录下如果沿用上游结构。一个获取Git日志的技能可能如下// src/skills/git-log.skill.ts import { exec } from child_process; import { promisify } from util; const execAsync promisify(exec); export interface GitLogSkillInput { repoPath: string; since: string; // e.g., 2024-01-01 until: string; } export async function getGitLog({ repoPath, since, until }: GitLogSkillInput): Promisestring { try { // 安全警告此处直接执行shell命令需确保repoPath是可信路径 const { stdout } await execAsync( git -C ${repoPath} log --since${since} --until${until} --oneline --no-merges, { encoding: utf-8 } ); return stdout; } catch (error) { console.error(Failed to get git log for ${repoPath}:, error); throw new Error(Could not retrieve git history: ${error.message}); } }然后你需要修改任务指令或者修改运行器逻辑使其在执行任务前先调用这个技能获取数据并将数据作为上下文的一部分传递给AI代理。这可能涉及修改container-runner.ts在启动容器前将技能执行结果写入一个临时文件并挂载到容器中或在指令中通过占位符替换。一个重要的架构选择技能可以在主机侧执行如上例也可以在容器内执行。各有优劣主机侧执行技能可以访问宿主机的丰富环境和工具如全局Git、SSH密钥但需要严格防范任意代码执行风险必须对输入进行严格校验和沙盒化。容器内执行更安全、更隔离但需要确保容器镜像包含了执行技能所需的所有工具如Git客户端这可能会增加镜像体积和构建复杂度。对于retn0claw这类强调隔离和个人使用的系统我的建议是与项目代码直接相关的操作如代码分析放在容器内需要访问外部系统凭证或全局配置的操作如调用GitHub API放在主机侧但通过严格的接口进行。6. 运维、调试与故障排查6.1 系统状态监控与日志一个自托管系统可观测性至关重要。数据库检查由于状态全在SQLite你可以直接用sqlite3命令行工具连接数据库文件默认可能在data/retn0claw.db查看tasks,messages,sessions表了解任务历史、消息流转和活跃会话。sqlite3 data/retn0claw.db .tables SELECT * FROM tasks WHERE enabled 1; SELECT group_id, COUNT(*) FROM messages GROUP BY group_id;日志输出确保主程序src/index.ts有详细的日志记录。建议使用结构化的日志库如pino或winston将不同级别INFO, DEBUG, ERROR的日志输出到控制台和文件。关键日志点包括任务入队/出队、容器启动/停止、技能调用开始/结束、消息发送/接收。容器生命周期定期检查是否有“僵尸”容器残留。可以设置一个简单的健康检查端点或定时任务使用docker ps -a --filter statusexited列出已退出的容器并根据标签清理掉旧的retn0claw任务容器。6.2 常见问题与解决方案以下是我在运行过程中遇到的一些典型问题及解决方法问题现象可能原因排查步骤与解决方案任务一直处于“排队中”状态永不执行。1. 组队列死锁。2. 前一个任务执行失败但未正确更新状态。3. 消息循环进程挂起。1. 检查数据库tasks表看是否有任务的status为running但实际已超时。手动将其更新为failed。2. 查看group-queue的日志确认出队逻辑是否正常执行。3. 重启主编排器进程。AI代理在容器内无法读取挂载的代码。1. 挂载路径错误。2. 容器内权限不足。3. 宿主机目录不存在。1. 在container-runner.ts中增加调试日志打印出实际的挂载命令和路径。2. 进入临时创建的调试容器 (docker run -it -v /host/path:/container/path alpine ls -la /container/path)检查文件列表和权限。3. 确保在启动容器时正确传递了宿主用户的UID/GID。Discord/Telegram Bot 不响应消息。1. Token或Channel ID配置错误。2. 网络问题Bot无法连接Discord API。3. 消息格式不符合通道适配器预期。1. 双重检查环境变量或配置文件中的Token和ID确保没有多余空格或换行。2. 查看通道适配器初始化时的日志确认连接是否成功建立。3. 尝试发送最简单的文本消息排除指令解析的问题。定时任务没有在预定时间触发。1. 系统时区设置问题。2. 任务调度器进程未运行或崩溃。3. Cron表达式错误。1. 确保运行retn0claw的服务器时区与你期望的时区一致。2. 检查task-scheduler.ts的日志看它是否在正常轮询。3. 使用在线的Cron表达式验证工具检查你的表达式。Claude Code在容器内启动失败。1. 容器镜像内缺少Claude Code运行时。2. 容器资源内存、CPU不足。3. 许可证或认证问题。1. 确保你的Dockerfile正确安装了Claude Code CLI并且PATH可用。2. 在docker run命令中增加资源限制参数如--memory4g。3. 检查容器内是否有必要的认证文件或环境变量如API Key。6.3 测试策略确保修改的可靠性项目自带的测试套件是你的安全网。理解其结构有助于你自信地修改代码。# 单元测试验证独立函数和模块的逻辑。 npm run test:unit # 契约测试确保核心接口的语义一致性即使内部实现改变对外行为不变。 npm run test:contract # 集成测试验证多个模块协同工作如编排器与通道、运行器的交互。 npm run test:integration # 属性测试PR用用随机但受限的输入测试系统的某些不变性invariants例如“任务永远不会被重复执行”。 npm run test:property:pr # CI流水线测试在拉取请求时运行的完整门禁检查。 npm run test:ci给修改者的建议在添加新功能尤其是新的技能或运行器时优先编写契约测试和集成测试。契约测试定义“它应该做什么”集成测试验证“它能否在真实环境中工作”。单元测试固然重要但对于这种以集成和流程为核心的系统后两者更能防止回归。7. 进阶思考安全、扩展与未来方向7.1 安全边界再审视retn0claw的核心安全模型是容器隔离。但这并非绝对安全你需要清醒地认识到其边界挂载卷是双向的AI代理可以修改挂载目录内的任何文件。务必只挂载必要的、可接受被更改的目录如项目源码。切勿挂载/、/home或包含密钥、配置文件的敏感目录。技能执行权限在主机侧执行的技能拥有与主进程相同的权限。必须对技能代码进行严格的输入验证和沙盒化例如使用vm2等隔离模块绝不允许用户输入直接拼接成Shell命令执行。网络访问默认情况下容器可能可以访问外网。如果你的AI代理或技能需要调用外部API这是必要的。但如果你需要严格的内网环境需要在运行容器时使用--network none或自定义网络。记忆与隐私CLAUDE.md和数据库包含了你的项目上下文和交互历史。请妥善保管retn0claw的数据库文件考虑对其进行加密备份。一个加固的实践是为每个项目房间使用独立的、非root用户的Docker容器并配合AppArmor或Seccomp配置文件来限制容器的系统调用。7.2 如何扩展支持新的AI代理或消息通道retn0claw的架构是模块化的扩展新的组件相对清晰。添加新的AI代理运行器例如想用本地部署的Llama.cpp在src/runners/下创建新目录如llama/。实现一个符合Runner接口的类。这个接口通常需要start(task): PromiseOutput等方法。在新运行器的start方法中实现启动本地Llama模型进程、传递提示词、捕获输出的逻辑。同样考虑是在独立容器中运行还是直接主机运行。在src/runners/shared/runner.ts的调度逻辑中注册你的新运行器使其能根据任务配置被选择。添加新的消息通道例如想用Slack在src/channels/下创建新文件如slack.ts。实现通道的初始化连接Slack WebSocket API、消息接收和消息发送方法。将接收到的Slack消息转化为系统内部的标准事件格式通常包含group,text,user等字段并存入数据库。在主程序初始化时导入并启动这个新的通道适配器。扩展的关键在于理解系统内部的事件总线和数据格式。只要新模块能正确地消费和产生这些事件就能无缝集成。7.3 从个人工具到团队协作的想象目前retn0claw是彻头彻尾的个人工具。但它的“项目房间”范式天然让人联想到团队协作的可能性。例如共享房间一个团队的代码仓库对应一个房间CLAUDE.md由团队共同维护成为项目的“活知识库”。权限模型在通道如Discord中通过角色来控制谁可以向哪个房间发送指令。审计日志所有通过AI代理执行的操作尤其是代码修改都被详细记录便于事后追溯。实现这些需要增加用户管理、更细粒度的权限控制和审计功能。这可能会偏离其“小巧、可审视”的初心但对于小团队来说在现有架构上逐步添加这些功能依然是一个有趣且可行的演进方向。