⚠️ AI Agent 注意:对任何源文件做修改后,必须先执行npm run typecheck确认无类型错误,再提交结果。这是项目的基本质量门禁。
- 缩进统一 2 空格,禁止 Tab
- 使用
oxfmt进行格式化,命令:npm run format - 每次修改后必须执行
npm run typecheck确认类型正确
- 文件名统一 kebab-case(如
chat-input.tsx、agent-bridge.ts、session-state-reducer.ts) - React 组件文件以功能命名,按页面结构放入
components/对应的模块目录下(如session/、session/chat/、settings/、skills/等) - 消息气泡按角色拆分到
components/chat-bubbles/ - 多模态消息内容块按类型拆分到
components/content-blocks/ - UI primitives 保持
components/ui/,避免跨目录重复实现 - Agent 会话逻辑(框架无关)放在
src/agents/,不耦合 Electron 或 React
- 优先使用
unknown而非any,强制在使用前进行类型检查 - 优先使用
satisfies操作符而非类型断言(as),以保留精确的类型推导并确保类型兼容性 - 避免过度使用类型断言,尽量让 TS 自动推导类型
- 判别联合类型用于多态实体(如
AgentInfo = StdioAgentInfo | ApiAgentInfo) - 共享类型定义集中在
src/shared/schema.ts
- 优先使用函数组件与具名导出
- 组件职责保持单一:容器组件负责数据流,展示组件负责渲染
- 复杂交互使用
useCallback/useMemo/useRef控制重渲染与副作用 - 涉及订阅(事件、监听器)必须在
useEffect中成对注册/清理
- 全局状态统一走
useAppStore - 与会话相关的状态必须按
sessionId隔离在sessionStates中 - 与项目相关的状态必须按
projectId隔离在projectStates中 - 不在组件中散落维护重复业务状态,优先通过 store mutator 更新
- 流式消息结束时统一调用
reduceFlushStreaming收尾,保证消息状态一致 - 全局共享状态(如 iLink 状态、主题、语言)挂载于 store 根层级
- 协议遵循:所有功能开发必须遵循 ACP(Agent Client Protocol)协议规范(基于
@agentclientprotocol/sdk)。如果发现功能需求与 ACP 协议冲突,必须提出质疑并进行讨论,禁止强行绕过或违背协议。 - ID 映射规范:与底层 ACP 服务(Agent 进程)交互时,必须使用
session.resumeId而非session.id。由于 ACP 接口声明中常将参数命名为sessionId,极易与 Fello 自身的session.id混淆。牢记规则:ACP 侧的sessionId=== Fello 侧的session.resumeId。 - ACP 更新事件统一进入
reduceSessionNotification(sessionId, currentState, notification),主进程在接收到这些事件时,对于 Stdio Agent 会先通过appendSessionMessage持久化到messages.jsonl文件 - API Agent 的历史消息由
OpenaiCompatibleAgent直接管理并持久化到history.jsonl - 历史回放和实时流式事件共用同一 Reducer 处理逻辑,避免行为分叉
- 切换/恢复会话前先
resetSessionState,避免历史与旧状态混叠 - tool call 状态更新必须同时同步到
activeToolCalls与messages
- Agent 实现须满足 ACP
Agent接口(src/agents/openai-compatible-agent.ts) - Agent 进程 spawner 须实现
AgentProcess接口(src/backend/agent/base-agent.ts) - API Agent 会话状态(modelId、allowedToolKinds)须通过
src/agents/storage.ts持久化 - 权限记忆通过
src/agents/permission.ts管理,支持"始终允许"并持久化
- 所有主渲染请求/事件类型定义集中在
src/shared/schema.ts - 路径处理:为了保证跨平台(特别是 Windows)的一致性,除
getSystemFilePath接口专门用于返回操作系统原生路径格式外,其他所有 IPC 接口的输入和输出(如searchFiles,readDir,fs-changed等)涉及的项目内相对路径,均必须统一使用 POSIX 风格路径(即正斜杠/分隔) - 主进程通过
ipcMain.handle注册由src/backend提供的请求式 API - 渲染层只通过
window.fello.invoke/on/off与主进程交互 - 渲染业务组件应使用
src/mainview/backend.ts的request/subscribe,不直接触达window.fello
- 文件系统、终端、系统对话框、原生菜单必须在主进程执行
- 渲染进程禁止直接访问 Node 能力,依赖 preload 暴露的受限 API
- 退出流程需要清理 Agent 进程(含子进程组)、iLink 连接与 PTY,避免僵尸进程
- iLink 微信凭证文件须设置 0o600 权限
- 优先复用现有 shadcn/base-ui 组件,不重复造轮子
- 统一使用语义化 token 类名(如
bg-background、text-foreground) - 图标统一使用
lucide-react - 所有用户可见文本必须支持多语言(使用
react-i18next的t()函数),并且在locales/目录下维护对应的翻译文件。默认语言环境提供英文(en.json)和简体中文(zh-CN.json)
- 异常信息尽量标准化为可读 message,再反馈给 UI
- 全局未捕获组件渲染异常统一由
ErrorBoundary组件拦截并提供用户友好的反馈界面 - 面向用户的提示与交互(Alert/Confirm/Prompt/Toast)必须统一通过
useMessageHook 调起,避免直接使用原生或散落的 Dialog 组件 - 关键异步流程应有
try/catch/finally,避免 loading 状态悬挂 - 面向用户的错误优先通过
useMessage的toast.error等方式提示,不静默吞错 - API Agent 的标题生成等非关键功能失败时应静默降级,不影响主流程