1. 项目概述一个AI编码工具的“中央控制台”如果你和我一样日常开发中同时用着Cursor、Claude Code可能还有GitHub Copilot或者一些本地部署的模型工具那你一定对下面这个场景不陌生为了给某个项目配置AI助手的行为你需要在项目根目录下创建一个.cursorrules文件同时为了确保Claude Code在同一个项目里也能遵循类似的规范你又得去维护一个CLAUDE.md。这还没完你可能会在~/.cursor目录下放一些全局的钩子脚本在~/.config/claude里存一些自定义技能模板。几天后当你切换到另一个项目或者想复用某个配置时记忆就开始模糊了——那个好用的代码审查规则模板到底放在哪个工具的哪个目录下了这种配置的碎片化和管理上的心智负担正是我开发Relay这个macOS应用的初衷。Relay本质上是一个本地的、图形化的“配置管理中心”它自动扫描并聚合了你系统中所有主流AI编码工具目前主要支持Cursor、Claude Code及其相关生态的配置文件。无论是项目级的规则文件如.cursorrules还是用户全局的配置、钩子、技能定义它都能在一个简洁的三栏视图中呈现给你。你不用再记忆路径不用在终端里反复cd和cat更不用手动在不同工具间同步相似的配置内容。它的目标很单纯让你管理AI助手的“知识”和“行为准则”像管理代码本身一样清晰、高效。Relay完全免费并开源基于Swift和SwiftUI构建这确保了它在macOS上的原生体验和性能。它不收集任何数据所有操作都在本地完成你的配置隐私得到充分保障。接下来我会详细拆解它的设计思路、实现细节并分享在开发这样一个“元工具”管理工具的工具过程中积累的经验和踩过的坑。2. 核心设计思路与架构解析2.1 解决的核心痛点配置散落与认知断层在深入代码之前我们先明确问题域。现代AI编码工具通常采用一种“配置即约定”的模式其配置层级大致可分为全局配置位于用户主目录下的隐藏文件夹中如~/.cursor、~/.config/claude。这里存放着影响该工具在所有项目中行为的设置、自定义命令、全局钩子脚本等。项目级配置位于具体项目根目录下的特定文件如.cursorrules、CLAUDE.md、AGENTS.md。这些文件定义了AI在该项目上下文中的行为规范、代码风格、禁止操作等。模板与预设用户可能会积累一些常用的规则模板例如“严格的代码审查模式”、“快速原型开发模式”。这些模板目前缺乏统一的管理往往以文本片段的形式散落在各处。痛点由此产生配置分散导致查找困难格式相似但工具独立导致同步成本高在Cursor里改了个规则可能忘了在Claude的配置里也更新缺乏全局视角使得你很难回答“我当前所有AI工具加起来被赋予了哪些知识和限制”。Relay的设计哲学是“聚合与可视化”。它不试图取代任何AI工具本身的配置系统而是作为一个只读或辅助编辑的“仪表盘”将这些分散的配置源聚合起来提供一个统一的查看、编辑与管理入口。这类似于IDE将不同语言、不同构建工具的配置文件在一个项目视图中统一管理。2.2 技术选型为什么是Swift/SwiftUI作为一个专注macOS的桌面应用选型几乎是必然的Swift/SwiftUI这是开发现代macOS原生应用的标杆组合。Swift提供了高性能和内存安全SwiftUI则用声明式语法极大地简化了UI构建。对于Relay这种数据驱动、视图相对标准的工具类应用SwiftUI的List、NavigationSplitView用于实现三栏布局、TextEditor等组件能事半功倍。更重要的是它能天然地适配macOS的系统外观深色/浅色模式、响应手势并提供流畅的动画。原生而非跨平台虽然Electron等框架可以跨平台但它们带来的内存开销和非原生体验与Relay追求的“轻量、快捷”背道而驰。Relay的目标是成为一个即开即用、毫无存在感的效率工具原生开发能确保其启动速度、响应速度和系统集成度如文件系统监听、菜单栏扩展潜力达到最佳。开源与社区选择Swift生态也便于吸引macOS开发者社区的关注和贡献。依赖管理使用Swift Package Manager (SPM)项目结构清晰任何人都可以轻松地git clone后打开Relay.xcodeproj进行编译或修改。2.3 应用架构概览Relay的架构遵循了典型的macOS应用模型并清晰分层Relay App ├── 表示层 (Presentation Layer) │ ├── Views (SwiftUI): MainView, SidebarView, DetailView, EditorView │ └── ViewModels: 连接模型层与视图处理UI逻辑 ├── 业务逻辑层 (Business Logic Layer) │ ├── ConfigScanner: 核心扫描器负责发现和解析配置 │ ├── RuleFileManager: 管理规则文件的CRUD操作 │ └── TemplateManager: 处理预设模板的保存与应用 ├── 模型层 (Model Layer) │ ├── ConfigItem: 配置项的数据模型类型、路径、内容、所属工具 │ ├── AITool: 枚举定义支持的AI工具Cursor, Claude等 │ └── Project: 已注册的项目文件夹信息 └── 持久层 (Persistence Layer) ├── UserDefaults: 存储用户偏好如注册的项目路径、窗口状态 └── File System: 所有配置文件的真实存储Relay本身不另存副本关键的设计在于ConfigScanner。它不是一个简单的文件遍历器。它在启动时以及受用户操作如刷新或系统事件如监听的文件变更触发时执行以下任务定位配置源根据预定义的知识如~/.cursor是Cursor的全局配置目录结合用户手动注册的项目路径构建待扫描的目录列表。模式匹配在每个目录中使用文件扩展名和文件名模式如*.json,.cursorrules,CLAUDE.md来识别潜在的配置文件。解析与分类读取文件内容对于文本文件或解析其结构对于JSON/YAML将其封装成统一的ConfigItem模型。同时根据文件路径和命名规则判断该配置项属于哪个AITool以及它是“全局配置”还是“项目级规则”。通知更新将扫描结果传递给ViewModel驱动UI更新。这个架构保证了核心的扫描逻辑与UI展示解耦便于未来扩展支持新的AI工具只需在AITool枚举和ConfigScanner的识别逻辑中添加新条目。3. 关键功能实现细节与实操3.1 三栏主界面信息架构的设计主界面采用NavigationSplitView实现三栏布局这是macOS文件管理器Finder和很多专业应用如Xcode的经典布局符合用户直觉。第一栏侧边栏作为导航的“锚点”提供了两个主要视角。工具视角按AI工具如Cursor, Claude分组。选择某个工具后第二栏会显示该工具下的所有配置类型全局设置、规则文件、钩子等。项目视角显示所有用户手动“注册”的项目文件夹。选择某个项目后第二栏会显示在该项目根目录下发现的所有AI工具配置文件例如同时显示该项目下的.cursorrules和CLAUDE.md。这种双视角设计至关重要它允许用户既可以从“工具维度”去管理某一类AI的所有配置也可以从“项目维度”去统一查看和编辑某个项目下的所有AI约束。第二栏内容列表根据第一栏的选择动态列出相关的配置项列表。每个列表项显示文件名、所在路径的简短表示以及一个图标根据文件类型。这里的一个细节是对于项目下的文件我们会显示相对于项目根目录的路径对于全局文件则显示从用户主目录~开始的路径以增强可读性。第三栏详情/编辑区这是与配置内容交互的核心区域。当在第二栏选中一个文件后第三栏会呈现以下两种视图之一预览模式对于文本文件Markdown, YAML, JSON等会提供一个语法高亮的只读预览。这对于快速浏览、确认内容非常方便避免了误操作。编辑模式用户点击“编辑”按钮后界面会切换为一个功能完整的文本编辑器基于TextEditor并集成了一些如Monaco Editor的简单语法高亮逻辑。编辑完成后保存操作会直接写入原文件。Relay本身不存储文件副本它只是一个“通道”。实操心得文件系统监听为了让应用状态与真实文件系统同步我们实现了文件系统事件监听使用DispatchSourceFileSystemObject或第三方库如FileWatcher。当用户通过Relay或其他方式如终端Vim修改了某个正在被管理的配置文件时Relay能实时感知到变化并更新UI中的预览内容。这个功能看似细小但对维持用户“信任感”至关重要——用户必须确信他在Relay里看到的就是磁盘上最新的内容。实现时要注意性能避免对同一文件的频繁修改导致事件风暴通常需要做一个短时间的防抖Debounce处理。3.2 配置的自动发现与解析逻辑ConfigScanner的自动发现能力是Relay的“魔法”所在。其逻辑如下构建扫描路径库固定全局路径硬编码已知的AI工具全局配置目录如let globalPaths: [AITool: [String]] [ .cursor: [~/.cursor, ~/Library/Application Support/Cursor], .claude: [~/.config/claude, ~/Library/Application Support/Claude], ]用户注册的项目路径从UserDefaults中读取用户之前通过“添加项目文件夹”操作注册的路径列表。执行扫描对上述每一个路径异步执行文件遍历。使用FileManager的enumerator方法并设置skipsHiddenFiles为false因为很多配置是隐藏文件同时skipsPackageDescendants为true避免进入.app等包内部。智能识别对于遍历到的每一个文件通过其路径和名称进行匹配func classifyFile(at path: String) - ConfigItem? { let url URL(fileURLWithPath: path) let fileName url.lastPathComponent // 识别工具 var tool: AITool? nil if path.contains(.cursor) { tool .cursor } else if path.contains(claude) || fileName CLAUDE.md { tool .claude } // ... 其他工具识别逻辑 // 识别类型 var type: ConfigType .unknown if fileName.hasPrefix(.) fileName.contains(rules) { type .projectRule } else if fileName.hasSuffix(.md) (fileName AGENTS.md || fileName CLAUDE.md) { type .projectRule } else if url.deletingLastPathComponent().lastPathComponent hooks { type .hook } else if fileName settings.json { type .globalSetting } // ... 其他类型识别逻辑 guard let tool tool, type ! .unknown else { return nil } return ConfigItem(tool: tool, type: type, fileURL: url) }这个过程允许一定程度的误报但核心是“宁可多扫不可漏扫”。在UI列表中可以允许用户手动隐藏某些误识别的文件。内容提取与索引对于识别出的文件尝试读取其内容。对于文本文件读取全文对于大型或二进制文件可能只读取元数据。这些内容会被缓存在内存中用于快速预览和搜索。3.3 预设模板功能实现“配置即代码”的复用这是Relay提升效率的关键功能。用户可以将一个编辑好的规则文件例如一个精心调校的代码审查规则集保存为“预设模板”。实现机制TemplateManager负责处理模板的存储。模板本身以纯文本文件的形式保存在一个固定的应用支持目录下如~/Library/Application Support/com.yourname.Relay/Templates/每个模板一个文件。同时用一个templates.json的索引文件来记录模板的元数据名称、描述、适用的原始工具、创建时间等。应用模板当用户在一个项目或全局配置目录中想要创建新规则文件或覆盖现有文件时可以从模板列表中选择。操作实质上是文件内容的复制。但这里有一个进阶处理Relay会解析模板内容中可能存在的“占位符变量”例如{{project_name}}并在应用时将其替换为当前项目的实际名称。这借鉴了基础设施即代码IaC工具的思想让配置更具动态性。版本管理的联想虽然Relay本身不提供复杂的版本管理但因为它管理的文件本身就是纯文本且位于你的项目目录或标准配置目录中它们天然就可以被Git等版本控制系统管理。Relay的模板功能可以看作是你的AI配置的“代码片段库”而Git则是这个库的版本历史记录者。我强烈建议用户将重要的规则模板文件也纳入Git管理。4. 开发历程中的挑战与解决方案4.1 挑战一沙盒限制与文件系统访问macOS应用商店Mac App Store的应用默认运行在沙盒中这极大地限制了其对用户文件系统的自由访问。Relay需要扫描用户主目录和任意项目文件夹这与沙盒的严格权限模型冲突。解决方案我们选择了非沙盒化分发。这意味着Relay主要通过GitHub等渠道分发用户下载的是经过公证Notarized的.dmg或直接是.app包。这样应用可以获得必要的文件系统访问权限。为了获得这些权限应用需要在首次访问用户指定目录时通过系统的NSOpenPanel请求明确的用户授权。一旦用户授予了对某个目录的访问权限系统会通过“安全范围书签”Security-Scoped Bookmark技术允许应用在后续启动时再次访问该目录而无需重复请求。我们在UserDefaults中安全地存储这些书签数据。注意事项书签的持久化与恢复处理安全范围书签需要非常小心。书签数据是二进制Data必须使用UserDefaults的set(_:forKey:)方法存储。在应用启动时需要尝试解析这些书签数据来恢复访问权限。这个过程可能会因为文件系统变动如文件夹被移动而失败代码中必须有健全的错误处理逻辑在失败时优雅地降级例如清除无效书签并等待用户重新选择目录。4.2 挑战二支持多样化的文件格式与语法高亮AI工具的配置文件格式五花八门有简单的Markdown.cursorrules有JSONsettings.json有YAML某些工具的配置甚至可能是自定义格式。解决方案我们采用了分层的处理策略。文本编辑对于所有文本文件核心编辑器使用SwiftUI的TextEditor。这是一个基础但可靠的组件。为了提升编辑体验我们集成了一个轻量级的语法高亮库例如通过封装Highlightr它基于JavaScript的highlight.js。在编辑模式下根据文件扩展名选择对应的语法高亮规则。内容预览在预览模式我们不仅要高亮还要对结构化数据JSON, YAML进行格式化pretty-print。我们使用了Foundation框架的JSONSerialization来验证和格式化JSON。对于YAML则引入了一个轻量的Swift YAML解析库如Yams。未知格式对于无法识别的二进制或特殊格式文件Relay会显示一个警告图标并提供一个“在Finder中显示”和“用默认应用打开”的按钮将控制权交还给操作系统和用户熟悉的工具。4.3 挑战三保持响应式与性能当用户注册了包含成千上万个文件的大型项目目录如整个Monorepo时启动时的全量扫描可能会阻塞UI线程导致应用卡顿。解决方案彻底的异步化与增量更新。ConfigScanner的所有文件遍历和读取操作都放在后台队列DispatchQueue.global(qos: .userInitiated)中执行。扫描结果通过MainActor属性包装器或DispatchQueue.main.async安全地传回主线程更新UI。实现增量扫描逻辑。应用启动后ConfigScanner会为每个被监控的目录创建一个文件系统监听器。当目录内发生文件创建、修改、删除事件时只会重新扫描该目录下受影响的部分而不是全量扫描从而极大地提升了响应速度。对于文件内容的预览采用懒加载策略。列表视图只加载文件的元数据名称、路径、大小。只有当用户点击某个文件进入详情预览时才触发该文件内容的读取操作。5. 构建、分发与开源协作5.1 使用Swift Package Manager管理依赖Relay的依赖非常精简这符合其轻量化的定位。Package.swift文件清晰定义了依赖关系dependencies: [ .package(url: https://github.com/johnsundell/files, from: 4.0.0), // 更优雅的文件操作 .package(url: https://github.com/sharplet/FileWatcher, from: 0.3.0), // 文件系统监听 .package(url: https://github.com/jpsim/Yams, from: 5.0.0), // YAML解析 // 可选语法高亮库 ],使用SPM使得项目设置非常简单任何开发者克隆仓库后Xcode会自动解析并获取依赖。5.2 打包与公证为了能在macOS上顺利分发特别是避免Gatekeeper警告必须对应用进行公证。归档Archive在Xcode中选择“Any Mac (Apple Silicon, Intel)”作为目标进行归档。导出从归档管理器中选择“Distribute App” - “Developer ID”方式导出这会创建一个.app包。公证Notarize使用xcrun notarytool命令行工具或Xcode的“Distribute App”流程自动完成。你需要一个有效的Apple开发者账号。公证过程会将你的应用提交给Apple服务器进行安全扫描。打票Staple公证成功后将票据ticket钉到应用包上xcrun stapler staple YourApp.app。这一步至关重要它确保了应用在离线环境下也能通过Gatekeeper验证。创建DMG使用create-dmg等工具创建一个美观的磁盘映像文件将.app拖进去并创建一个指向“应用程序”文件夹的快捷方式方便用户安装。5.3 开源与社区反馈将项目开源在GitHub上不仅仅是发布代码更是开启了一个协作循环。清晰的READMEREADME是项目的门面。我花了大量时间撰写一份详细的README包括功能特性截图、快速开始指南、开发环境搭建说明、技术栈介绍以及明确的贡献指南如何提交Issue如何发起Pull Request。Issue模板在GitHub仓库设置中配置了Bug报告和功能请求的Issue模板引导用户提供必要的信息如macOS版本、Relay版本、复现步骤这能极大提高沟通效率。处理反馈早期用户反馈非常宝贵。有人提出希望支持Windsurf的配置有人发现对某些符号链接Symlink目录的扫描有问题还有人希望编辑器的字体可以自定义。我根据反馈的优先级和普遍性逐步将它们纳入开发路线图。例如支持新工具往往只需要在AITool枚举和ConfigScanner的识别规则中添加新条目架构的可扩展性在这里得到了验证。6. 总结与未来可能的演进开发Relay的过程是一个典型的“为自己造轮子最终惠及他人”的故事。它解决了我个人工作流中的一个具体痛点而Swift和SwiftUI的成熟生态让这个想法的实现变得异常顺畅。目前Relay处于一个“好用”的1.0状态。但它还有很长的进化路径。一些正在考虑或社区呼吁的方向包括更多AI工具集成除了Cursor和Claude像GitHub Copilot其配置可能在VSCode或全局的~/.config/github-copilot中、Windsurf、甚至是本地运行的CodeLLM如配置Ollama的模型参数都可以成为聚合目标。配置同步与共享探索通过简单的Git仓库或加密云存储如iCloud在用户的多台Mac间同步“预设模板”甚至是被标记为“收藏”的配置项。更智能的编辑辅助基于现有规则文件利用AI ironic, isn‘t it?来提供规则编写的建议、补全或者检查规则之间的冲突。规则有效性测试提供一个沙盒环境可以针对一段示例代码快速测试当前项目的规则文件是否会被AI正确解读和执行。这个项目的核心价值在于它承认了“管理AI行为”正在成为软件开发中一个日益重要的子任务。Relay试图将这个任务工具化、可视化让它变得更可控、更可积累。如果你也受困于多个AI编码助手的配置混乱不妨试试Relay或者基于它的开源代码构建属于你自己的“控制中心”。
Relay:聚合管理Cursor、Claude等AI编码工具配置的macOS原生应用
1. 项目概述一个AI编码工具的“中央控制台”如果你和我一样日常开发中同时用着Cursor、Claude Code可能还有GitHub Copilot或者一些本地部署的模型工具那你一定对下面这个场景不陌生为了给某个项目配置AI助手的行为你需要在项目根目录下创建一个.cursorrules文件同时为了确保Claude Code在同一个项目里也能遵循类似的规范你又得去维护一个CLAUDE.md。这还没完你可能会在~/.cursor目录下放一些全局的钩子脚本在~/.config/claude里存一些自定义技能模板。几天后当你切换到另一个项目或者想复用某个配置时记忆就开始模糊了——那个好用的代码审查规则模板到底放在哪个工具的哪个目录下了这种配置的碎片化和管理上的心智负担正是我开发Relay这个macOS应用的初衷。Relay本质上是一个本地的、图形化的“配置管理中心”它自动扫描并聚合了你系统中所有主流AI编码工具目前主要支持Cursor、Claude Code及其相关生态的配置文件。无论是项目级的规则文件如.cursorrules还是用户全局的配置、钩子、技能定义它都能在一个简洁的三栏视图中呈现给你。你不用再记忆路径不用在终端里反复cd和cat更不用手动在不同工具间同步相似的配置内容。它的目标很单纯让你管理AI助手的“知识”和“行为准则”像管理代码本身一样清晰、高效。Relay完全免费并开源基于Swift和SwiftUI构建这确保了它在macOS上的原生体验和性能。它不收集任何数据所有操作都在本地完成你的配置隐私得到充分保障。接下来我会详细拆解它的设计思路、实现细节并分享在开发这样一个“元工具”管理工具的工具过程中积累的经验和踩过的坑。2. 核心设计思路与架构解析2.1 解决的核心痛点配置散落与认知断层在深入代码之前我们先明确问题域。现代AI编码工具通常采用一种“配置即约定”的模式其配置层级大致可分为全局配置位于用户主目录下的隐藏文件夹中如~/.cursor、~/.config/claude。这里存放着影响该工具在所有项目中行为的设置、自定义命令、全局钩子脚本等。项目级配置位于具体项目根目录下的特定文件如.cursorrules、CLAUDE.md、AGENTS.md。这些文件定义了AI在该项目上下文中的行为规范、代码风格、禁止操作等。模板与预设用户可能会积累一些常用的规则模板例如“严格的代码审查模式”、“快速原型开发模式”。这些模板目前缺乏统一的管理往往以文本片段的形式散落在各处。痛点由此产生配置分散导致查找困难格式相似但工具独立导致同步成本高在Cursor里改了个规则可能忘了在Claude的配置里也更新缺乏全局视角使得你很难回答“我当前所有AI工具加起来被赋予了哪些知识和限制”。Relay的设计哲学是“聚合与可视化”。它不试图取代任何AI工具本身的配置系统而是作为一个只读或辅助编辑的“仪表盘”将这些分散的配置源聚合起来提供一个统一的查看、编辑与管理入口。这类似于IDE将不同语言、不同构建工具的配置文件在一个项目视图中统一管理。2.2 技术选型为什么是Swift/SwiftUI作为一个专注macOS的桌面应用选型几乎是必然的Swift/SwiftUI这是开发现代macOS原生应用的标杆组合。Swift提供了高性能和内存安全SwiftUI则用声明式语法极大地简化了UI构建。对于Relay这种数据驱动、视图相对标准的工具类应用SwiftUI的List、NavigationSplitView用于实现三栏布局、TextEditor等组件能事半功倍。更重要的是它能天然地适配macOS的系统外观深色/浅色模式、响应手势并提供流畅的动画。原生而非跨平台虽然Electron等框架可以跨平台但它们带来的内存开销和非原生体验与Relay追求的“轻量、快捷”背道而驰。Relay的目标是成为一个即开即用、毫无存在感的效率工具原生开发能确保其启动速度、响应速度和系统集成度如文件系统监听、菜单栏扩展潜力达到最佳。开源与社区选择Swift生态也便于吸引macOS开发者社区的关注和贡献。依赖管理使用Swift Package Manager (SPM)项目结构清晰任何人都可以轻松地git clone后打开Relay.xcodeproj进行编译或修改。2.3 应用架构概览Relay的架构遵循了典型的macOS应用模型并清晰分层Relay App ├── 表示层 (Presentation Layer) │ ├── Views (SwiftUI): MainView, SidebarView, DetailView, EditorView │ └── ViewModels: 连接模型层与视图处理UI逻辑 ├── 业务逻辑层 (Business Logic Layer) │ ├── ConfigScanner: 核心扫描器负责发现和解析配置 │ ├── RuleFileManager: 管理规则文件的CRUD操作 │ └── TemplateManager: 处理预设模板的保存与应用 ├── 模型层 (Model Layer) │ ├── ConfigItem: 配置项的数据模型类型、路径、内容、所属工具 │ ├── AITool: 枚举定义支持的AI工具Cursor, Claude等 │ └── Project: 已注册的项目文件夹信息 └── 持久层 (Persistence Layer) ├── UserDefaults: 存储用户偏好如注册的项目路径、窗口状态 └── File System: 所有配置文件的真实存储Relay本身不另存副本关键的设计在于ConfigScanner。它不是一个简单的文件遍历器。它在启动时以及受用户操作如刷新或系统事件如监听的文件变更触发时执行以下任务定位配置源根据预定义的知识如~/.cursor是Cursor的全局配置目录结合用户手动注册的项目路径构建待扫描的目录列表。模式匹配在每个目录中使用文件扩展名和文件名模式如*.json,.cursorrules,CLAUDE.md来识别潜在的配置文件。解析与分类读取文件内容对于文本文件或解析其结构对于JSON/YAML将其封装成统一的ConfigItem模型。同时根据文件路径和命名规则判断该配置项属于哪个AITool以及它是“全局配置”还是“项目级规则”。通知更新将扫描结果传递给ViewModel驱动UI更新。这个架构保证了核心的扫描逻辑与UI展示解耦便于未来扩展支持新的AI工具只需在AITool枚举和ConfigScanner的识别逻辑中添加新条目。3. 关键功能实现细节与实操3.1 三栏主界面信息架构的设计主界面采用NavigationSplitView实现三栏布局这是macOS文件管理器Finder和很多专业应用如Xcode的经典布局符合用户直觉。第一栏侧边栏作为导航的“锚点”提供了两个主要视角。工具视角按AI工具如Cursor, Claude分组。选择某个工具后第二栏会显示该工具下的所有配置类型全局设置、规则文件、钩子等。项目视角显示所有用户手动“注册”的项目文件夹。选择某个项目后第二栏会显示在该项目根目录下发现的所有AI工具配置文件例如同时显示该项目下的.cursorrules和CLAUDE.md。这种双视角设计至关重要它允许用户既可以从“工具维度”去管理某一类AI的所有配置也可以从“项目维度”去统一查看和编辑某个项目下的所有AI约束。第二栏内容列表根据第一栏的选择动态列出相关的配置项列表。每个列表项显示文件名、所在路径的简短表示以及一个图标根据文件类型。这里的一个细节是对于项目下的文件我们会显示相对于项目根目录的路径对于全局文件则显示从用户主目录~开始的路径以增强可读性。第三栏详情/编辑区这是与配置内容交互的核心区域。当在第二栏选中一个文件后第三栏会呈现以下两种视图之一预览模式对于文本文件Markdown, YAML, JSON等会提供一个语法高亮的只读预览。这对于快速浏览、确认内容非常方便避免了误操作。编辑模式用户点击“编辑”按钮后界面会切换为一个功能完整的文本编辑器基于TextEditor并集成了一些如Monaco Editor的简单语法高亮逻辑。编辑完成后保存操作会直接写入原文件。Relay本身不存储文件副本它只是一个“通道”。实操心得文件系统监听为了让应用状态与真实文件系统同步我们实现了文件系统事件监听使用DispatchSourceFileSystemObject或第三方库如FileWatcher。当用户通过Relay或其他方式如终端Vim修改了某个正在被管理的配置文件时Relay能实时感知到变化并更新UI中的预览内容。这个功能看似细小但对维持用户“信任感”至关重要——用户必须确信他在Relay里看到的就是磁盘上最新的内容。实现时要注意性能避免对同一文件的频繁修改导致事件风暴通常需要做一个短时间的防抖Debounce处理。3.2 配置的自动发现与解析逻辑ConfigScanner的自动发现能力是Relay的“魔法”所在。其逻辑如下构建扫描路径库固定全局路径硬编码已知的AI工具全局配置目录如let globalPaths: [AITool: [String]] [ .cursor: [~/.cursor, ~/Library/Application Support/Cursor], .claude: [~/.config/claude, ~/Library/Application Support/Claude], ]用户注册的项目路径从UserDefaults中读取用户之前通过“添加项目文件夹”操作注册的路径列表。执行扫描对上述每一个路径异步执行文件遍历。使用FileManager的enumerator方法并设置skipsHiddenFiles为false因为很多配置是隐藏文件同时skipsPackageDescendants为true避免进入.app等包内部。智能识别对于遍历到的每一个文件通过其路径和名称进行匹配func classifyFile(at path: String) - ConfigItem? { let url URL(fileURLWithPath: path) let fileName url.lastPathComponent // 识别工具 var tool: AITool? nil if path.contains(.cursor) { tool .cursor } else if path.contains(claude) || fileName CLAUDE.md { tool .claude } // ... 其他工具识别逻辑 // 识别类型 var type: ConfigType .unknown if fileName.hasPrefix(.) fileName.contains(rules) { type .projectRule } else if fileName.hasSuffix(.md) (fileName AGENTS.md || fileName CLAUDE.md) { type .projectRule } else if url.deletingLastPathComponent().lastPathComponent hooks { type .hook } else if fileName settings.json { type .globalSetting } // ... 其他类型识别逻辑 guard let tool tool, type ! .unknown else { return nil } return ConfigItem(tool: tool, type: type, fileURL: url) }这个过程允许一定程度的误报但核心是“宁可多扫不可漏扫”。在UI列表中可以允许用户手动隐藏某些误识别的文件。内容提取与索引对于识别出的文件尝试读取其内容。对于文本文件读取全文对于大型或二进制文件可能只读取元数据。这些内容会被缓存在内存中用于快速预览和搜索。3.3 预设模板功能实现“配置即代码”的复用这是Relay提升效率的关键功能。用户可以将一个编辑好的规则文件例如一个精心调校的代码审查规则集保存为“预设模板”。实现机制TemplateManager负责处理模板的存储。模板本身以纯文本文件的形式保存在一个固定的应用支持目录下如~/Library/Application Support/com.yourname.Relay/Templates/每个模板一个文件。同时用一个templates.json的索引文件来记录模板的元数据名称、描述、适用的原始工具、创建时间等。应用模板当用户在一个项目或全局配置目录中想要创建新规则文件或覆盖现有文件时可以从模板列表中选择。操作实质上是文件内容的复制。但这里有一个进阶处理Relay会解析模板内容中可能存在的“占位符变量”例如{{project_name}}并在应用时将其替换为当前项目的实际名称。这借鉴了基础设施即代码IaC工具的思想让配置更具动态性。版本管理的联想虽然Relay本身不提供复杂的版本管理但因为它管理的文件本身就是纯文本且位于你的项目目录或标准配置目录中它们天然就可以被Git等版本控制系统管理。Relay的模板功能可以看作是你的AI配置的“代码片段库”而Git则是这个库的版本历史记录者。我强烈建议用户将重要的规则模板文件也纳入Git管理。4. 开发历程中的挑战与解决方案4.1 挑战一沙盒限制与文件系统访问macOS应用商店Mac App Store的应用默认运行在沙盒中这极大地限制了其对用户文件系统的自由访问。Relay需要扫描用户主目录和任意项目文件夹这与沙盒的严格权限模型冲突。解决方案我们选择了非沙盒化分发。这意味着Relay主要通过GitHub等渠道分发用户下载的是经过公证Notarized的.dmg或直接是.app包。这样应用可以获得必要的文件系统访问权限。为了获得这些权限应用需要在首次访问用户指定目录时通过系统的NSOpenPanel请求明确的用户授权。一旦用户授予了对某个目录的访问权限系统会通过“安全范围书签”Security-Scoped Bookmark技术允许应用在后续启动时再次访问该目录而无需重复请求。我们在UserDefaults中安全地存储这些书签数据。注意事项书签的持久化与恢复处理安全范围书签需要非常小心。书签数据是二进制Data必须使用UserDefaults的set(_:forKey:)方法存储。在应用启动时需要尝试解析这些书签数据来恢复访问权限。这个过程可能会因为文件系统变动如文件夹被移动而失败代码中必须有健全的错误处理逻辑在失败时优雅地降级例如清除无效书签并等待用户重新选择目录。4.2 挑战二支持多样化的文件格式与语法高亮AI工具的配置文件格式五花八门有简单的Markdown.cursorrules有JSONsettings.json有YAML某些工具的配置甚至可能是自定义格式。解决方案我们采用了分层的处理策略。文本编辑对于所有文本文件核心编辑器使用SwiftUI的TextEditor。这是一个基础但可靠的组件。为了提升编辑体验我们集成了一个轻量级的语法高亮库例如通过封装Highlightr它基于JavaScript的highlight.js。在编辑模式下根据文件扩展名选择对应的语法高亮规则。内容预览在预览模式我们不仅要高亮还要对结构化数据JSON, YAML进行格式化pretty-print。我们使用了Foundation框架的JSONSerialization来验证和格式化JSON。对于YAML则引入了一个轻量的Swift YAML解析库如Yams。未知格式对于无法识别的二进制或特殊格式文件Relay会显示一个警告图标并提供一个“在Finder中显示”和“用默认应用打开”的按钮将控制权交还给操作系统和用户熟悉的工具。4.3 挑战三保持响应式与性能当用户注册了包含成千上万个文件的大型项目目录如整个Monorepo时启动时的全量扫描可能会阻塞UI线程导致应用卡顿。解决方案彻底的异步化与增量更新。ConfigScanner的所有文件遍历和读取操作都放在后台队列DispatchQueue.global(qos: .userInitiated)中执行。扫描结果通过MainActor属性包装器或DispatchQueue.main.async安全地传回主线程更新UI。实现增量扫描逻辑。应用启动后ConfigScanner会为每个被监控的目录创建一个文件系统监听器。当目录内发生文件创建、修改、删除事件时只会重新扫描该目录下受影响的部分而不是全量扫描从而极大地提升了响应速度。对于文件内容的预览采用懒加载策略。列表视图只加载文件的元数据名称、路径、大小。只有当用户点击某个文件进入详情预览时才触发该文件内容的读取操作。5. 构建、分发与开源协作5.1 使用Swift Package Manager管理依赖Relay的依赖非常精简这符合其轻量化的定位。Package.swift文件清晰定义了依赖关系dependencies: [ .package(url: https://github.com/johnsundell/files, from: 4.0.0), // 更优雅的文件操作 .package(url: https://github.com/sharplet/FileWatcher, from: 0.3.0), // 文件系统监听 .package(url: https://github.com/jpsim/Yams, from: 5.0.0), // YAML解析 // 可选语法高亮库 ],使用SPM使得项目设置非常简单任何开发者克隆仓库后Xcode会自动解析并获取依赖。5.2 打包与公证为了能在macOS上顺利分发特别是避免Gatekeeper警告必须对应用进行公证。归档Archive在Xcode中选择“Any Mac (Apple Silicon, Intel)”作为目标进行归档。导出从归档管理器中选择“Distribute App” - “Developer ID”方式导出这会创建一个.app包。公证Notarize使用xcrun notarytool命令行工具或Xcode的“Distribute App”流程自动完成。你需要一个有效的Apple开发者账号。公证过程会将你的应用提交给Apple服务器进行安全扫描。打票Staple公证成功后将票据ticket钉到应用包上xcrun stapler staple YourApp.app。这一步至关重要它确保了应用在离线环境下也能通过Gatekeeper验证。创建DMG使用create-dmg等工具创建一个美观的磁盘映像文件将.app拖进去并创建一个指向“应用程序”文件夹的快捷方式方便用户安装。5.3 开源与社区反馈将项目开源在GitHub上不仅仅是发布代码更是开启了一个协作循环。清晰的READMEREADME是项目的门面。我花了大量时间撰写一份详细的README包括功能特性截图、快速开始指南、开发环境搭建说明、技术栈介绍以及明确的贡献指南如何提交Issue如何发起Pull Request。Issue模板在GitHub仓库设置中配置了Bug报告和功能请求的Issue模板引导用户提供必要的信息如macOS版本、Relay版本、复现步骤这能极大提高沟通效率。处理反馈早期用户反馈非常宝贵。有人提出希望支持Windsurf的配置有人发现对某些符号链接Symlink目录的扫描有问题还有人希望编辑器的字体可以自定义。我根据反馈的优先级和普遍性逐步将它们纳入开发路线图。例如支持新工具往往只需要在AITool枚举和ConfigScanner的识别规则中添加新条目架构的可扩展性在这里得到了验证。6. 总结与未来可能的演进开发Relay的过程是一个典型的“为自己造轮子最终惠及他人”的故事。它解决了我个人工作流中的一个具体痛点而Swift和SwiftUI的成熟生态让这个想法的实现变得异常顺畅。目前Relay处于一个“好用”的1.0状态。但它还有很长的进化路径。一些正在考虑或社区呼吁的方向包括更多AI工具集成除了Cursor和Claude像GitHub Copilot其配置可能在VSCode或全局的~/.config/github-copilot中、Windsurf、甚至是本地运行的CodeLLM如配置Ollama的模型参数都可以成为聚合目标。配置同步与共享探索通过简单的Git仓库或加密云存储如iCloud在用户的多台Mac间同步“预设模板”甚至是被标记为“收藏”的配置项。更智能的编辑辅助基于现有规则文件利用AI ironic, isn‘t it?来提供规则编写的建议、补全或者检查规则之间的冲突。规则有效性测试提供一个沙盒环境可以针对一段示例代码快速测试当前项目的规则文件是否会被AI正确解读和执行。这个项目的核心价值在于它承认了“管理AI行为”正在成为软件开发中一个日益重要的子任务。Relay试图将这个任务工具化、可视化让它变得更可控、更可积累。如果你也受困于多个AI编码助手的配置混乱不妨试试Relay或者基于它的开源代码构建属于你自己的“控制中心”。