Newer
Older
aicoding / yarn / config / AGENTS.md
# 全局规则

交互语言

  • 所有交互一律使用中文(包括问题回答、代码解释、错误说明等)
  • 代码注释使用中文
  • 变量名、函数名等代码标识符使用英文(遵循编程惯例)

代码风格

  • 保持代码简洁、易读
  • 遵循项目现有的代码规范和约定
  • 不要添加不必要的注释或解释
  • 字符串排序(如日期 YYYY-MM-DD)用纯比较运算符 a > b ? 1 : a < b ? -1 : 0,不用 localeCompare —— 后者走 ICU 规则,与字典序不严格等价

observer 使用规范

observer 必须跟随 export,而非组件定义:

// ✅ 正确:定义与导出分离
const MyComponent = () => { ... };
export default observer(MyComponent);

// ✅ 正确:定义与导出合一时
export const MyComponent = observer(() => { ... });

// ❌ 错误:observer 包裹定义,单独导出  
const MyComponent = observer(() => { ... });
export default MyComponent;

工作原则

  • 优先理解用户意图,必要时主动提问澄清
  • 修改代码前先了解项目结构和现有模式
  • 完成修改后运行测试验证(如果项目有测试)
  • 除非用户明确要求,否则不要提交代码变更
  • 代码查阅确认机制:当需要阅读代码来确认 API 细节或具体用法时,必须先向用户说明:
    1. 为什么要阅读代码(目的)
    2. 准备阅读哪些代码(范围)
    3. 等待用户决定:
      • 同意按方案继续阅读
      • 直接告知具体用法
      • 修正阅读范围,指定应该阅读哪些代码

Git commit message 生成规范

生成 commit message 前,必须过滤非实质性变更:

  • 先用 git diff --ignore-all-space --stat 检查是否有实质性变更
  • 使用 git diff --ignore-cr-at-eol 忽略行尾符(CRLF/LF)变化
  • 使用 git diff --word-diff 检查单词级别的实质性变更
  • 若忽略空白字符后无实质性变更,不应描述为"代码格式化"或"风格调整"
  • 优先关注:新增/删除的代码逻辑、函数签名变更、配置项修改等

代码查阅原则

  • 当 AGENTS.md 中已提供了完整的代码模板和规范时(如 Entity、Dao、Service、MyBatis XML 等模板), 必须直接按照模板和规范编写代码,不得先去阅读现有代码来"学习风格"
  • 只有在以下情况才允许阅读现有代码:
    1. 确认某个框架 API 的具体用法或参数
    2. 了解某个模块的具体业务逻辑或实现细节
    3. 模板和规范中未覆盖的特殊场景
  • 简单说:规范已覆盖的内容 → 直接按规范写;规范未覆盖的内容 → 再去看代码
  • 禁止在规划阶段启动 agent 或工具去阅读现有代码。 规划必须仅基于两份 AGENTS.md 中的信息进行。 只有在进入实现阶段、且需要确认模板未覆盖的框架 API 细节时, 才允许按「代码查阅确认机制」经用户同意后阅读代码。

项目初始化规则

当用户要求"了解项目"、"熟悉规范"或类似指令时:

必须做的:

  1. 阅读并理解全局 AGENTS.md(/root/.config/opencode/AGENTS.md,即本文件)和项目级 AGENTS.md(/workspace/AGENTS.md,位于项目根目录)中的所有规范
  2. 基于两份 AGENTS.md 中的信息回答项目相关问题
  3. 直接使用两份 AGENTS.md 中的代码模板和规范进行开发

禁止做的:

  1. 不要主动阅读实际代码文件来"学习风格"
  2. 不要查看现有模块来"理解代码组织"
  3. 不要假设 AGENTS.md 中的信息不够用
  4. 不要只阅读其中一份 AGENTS.md 而忽略另一份——两份都必须阅读

标准回复:

"已阅读全局和项目级 AGENTS.md,项目采用 [技术栈],包含 [模块数量] 个模块。 命名规范:[简述]。代码模板已就绪,可以开始开发。"

前端项目开发原则(仅限 React 项目)

适用范围说明: 以下原则仅适用于前端 React 项目开发。对于后端项目(Java、Python 等)或其他类型项目,这些规则不适用。

1. 架构思维

分层计算原则

  • 数据计算按层级划分:原始数据 → 单实体衍生 → 跨实体汇总 → 展示
  • 每一层只做自己该做的事,不越界
  • 体现"关注点分离"的核心思想

响应式数据流

  • 状态变化自动触发相关更新,而非手动调用
  • 使用观察者模式(如 autorun)实现自动响应
  • 体现"声明式编程"优于"命令式编程"的理念

2. 命名哲学

语义优先,避免冗余

  • 命名要清晰表达含义,但不重复上下文
  • 在已知上下文中,省略冗余前缀
  • 嵌套结构表达关联关系(如 compare.scale.daily)
  • 体现"代码即文档"的自解释原则

3. 职责分离

逻辑与展示分离

  • 业务逻辑集中在状态管理层
  • 组件只负责展示和 UI 配置
  • 体现"单一职责原则"

全局与局部分离

  • 全局参数集中管理(日期、汇率等)
  • 业务数据按领域分离
  • 体现"高内聚、低耦合"的设计原则

4. 工程实践

渐进式改进

  • 逐步消除技术债务(如硬编码数据)
  • 暂时不处理的部分明确标注,不阻塞整体进度
  • 体现"持续重构"的工程思维

一致性原则

  • 相关文件集中组织(Store、API、Mock 一一对应)
  • 统一的命名规范和文件结构
  • 体现"约定优于配置"的理念

5. 数据驱动

以最细粒度实体为基础

  • 所有数据围绕最小业务单元组织
  • 汇总数据从细粒度数据派生
  • 体现"数据规范化"的思想

确定性 Mock

  • Mock 数据基于种子生成,保证可重复性
  • 体现"可测试性"和"可预测性"的要求

代码模板

Store 模板

import {action, computed, observable} from "mobx";
import {LocalStore} from "./local.store";  // 如需本地存储继承
import {XxxApi} from "../api";

export class XxxStore extends LocalStore {

  @observable
  list = []

  @observable
  detail = null

  @computed
  get derivedData() {
    // 计算衍生数据
    return this.list.map(item => ({
      ...item,
      computedField: item.field * 2
    }));
  }

  get storeName() {
    return "xxx";  // 本地存储 key
  }

  @action
  fetchData = async (params) => {
    this.list = await XxxApi.getList(params);
  }

  @action
  fetchDetail = async (id) => {
    this.detail = await XxxApi.getDetail(id);
  }

  @action
  reset = () => {
    this.list = [];
    this.detail = null;
  }
}

export default new XxxStore();

API 模板

import request from "../utils/request";
import qs from "query-string";

// GET 请求
export const getList = async (query) => {
  return request.get(qs.stringifyUrl({
    url: '/xxx', query
  }, {skipNull: true, skipEmptyString: true}));
}

// GET 详情
export const getDetail = async (id) => {
  return request.get(`/xxx/${id}`);
}

// POST 创建
export const create = async (data) => {
  return request.post('/xxx', {data});
}

// PUT 更新
export const update = async (id, data) => {
  return request.put(`/xxx/${id}`, {data});
}

// DELETE 删除
export const remove = async (id) => {
  return request.delete(`/xxx/${id}`);
}

Page 组件模板

import React, {useEffect} from "react";
import {observer} from 'mobx-react';
import {PageContainer} from "@ant-design/pro-layout";
import {useStores} from "../../hooks";

const XxxPage = () => {
  const {xxxStore} = useStores();

  useEffect(() => {
    xxxStore.fetchData();
  }, []);

  return (
    <PageContainer>
      {/* 页面内容 */}
    </PageContainer>
  );
}

export default observer(XxxPage);

Hook 模板

import {useState, useEffect, useCallback} from "react";

export const useXxx = (initialValue) => {
  const [value, setValue] = useState(initialValue);

  const doSomething = useCallback(() => {
    // 逻辑
  }, []);

  return {value, setValue, doSomething};
}

Mock 数据模板

// mock/xxx.js
module.exports = {
  _apis: {
    getList: "GET /api/v1.0/xxx",
    getDetail: "GET /api/v1.0/xxx/:id",
    create: "POST /api/v1.0/xxx",
  },
  
  "GET /api/v1.0/xxx": (req, res) => {
    res.send({
      status: "success",
      body: {
        content: [
          {id: 1, name: "示例1", field: "value1"},
          {id: 2, name: "示例2", field: "value2"},
        ],
        total: 2,
        page: 0,
        size: 20,
        last: true
      },
      success: true
    });
  },

  "GET /api/v1.0/xxx/:id": {
    status: "success",
    body: {id: 1, name: "示例", field: "value"},
    success: true
  },

  "POST /api/v1.0/xxx": {
    status: "success",
    body: {id: 3},
    success: true
  }
}

模板约定说明:

  • _apis:别名 → 路由字符串的映射,供环境变量细粒度屏蔽指定接口(如 REACT_APP_MOCK_IGNORE=xxx.create)。别名必须与 API 层的导出方法名完全一致,聚合入口依赖此对应关系做过滤
  • _getData():返回模块内数据数组,供其他 mock 模块交叉引用(如创建时校验关联实体是否存在)。仅在模块间有引用需求时提供
  • 路由注册顺序:具体路由必须先于参数路由注册,否则 :id 会吞掉字面量路径(如 /xxx/settings 必须先于 /xxx/:id)
  • 聚合入口:若用统一入口(如 mock/index.js)合并多模块,展开时必须剔除非路由键(_apis、_getData),否则同名键会被后注册模块覆盖;模块间交叉引用应直接 require 目标模块,不经聚合对象

图表组件模板

import React from "react";
import {observer} from 'mobx-react';
import {Line, Column, Pie} from "@ant-design/charts";
import {Card, Row, Col, Statistic} from "antd";
import {useStores} from "../../hooks";

const XxxChart = () => {
  const {xxxStore} = useStores();
  const {chartData, summary} = xxxStore;

  // 折线图配置
  const lineConfig = {
    data: chartData.lineData,
    xField: 'date',
    yField: 'value',
    seriesField: 'type',
  };

  // 饼图配置
  const pieConfig = {
    data: chartData.pieData,
    angleField: 'value',
    colorField: 'type',
  };

  return (
    <Row gutter={16}>
      <Col span={8}>
        <Card>
          <Statistic title="指标1" value={summary.value1} />
        </Card>
      </Col>
      <Col span={16}>
        <Card title="趋势图">
          <Line {...lineConfig} />
        </Card>
      </Col>
      <Col span={24}>
        <Card title="分布图">
          <Pie {...pieConfig} />
        </Card>
      </Col>
    </Row>
  );
}

export default observer(XxxChart);

装饰器语法

项目使用 MobX 5,需要以下 Babel 配置:

// craco.config.js
babel: {
  plugins: [
    ['@babel/plugin-proposal-decorators', {legacy: true}],
    ["@babel/plugin-proposal-class-properties", {loose: true}],
  ]
}

常用装饰器:

  • @observable - 声明可观察状态
  • @action - 声明修改状态的方法
  • @computed - 声明计算属性

样式规范

CSS Modules

  • 文件命名:xxx.scss 或 xxx.module.scss
  • 自动启用:src/ 和 @chaosoft/ 下的 .scss 文件
  • 类名转换:kebab-case → camelCase
// component.scss
.container {
  padding: 16px;
}

.title-text {
  font-size: 16px;
}
// component.js
import styles from './component.scss';

const Component = () => (
  <div className={styles.container}>
    <span className={styles.titleText}>标题</span>
  </div>
);

VERSION.md 编写规范

文件位置

项目根目录:{project-root}/VERSION.md

版本号规则

采用语义化版本(Semantic Versioning):MAJOR.MINOR.PATCH

部分 说明 示例
MAJOR 不兼容的 API 变更 1.0.0 → 2.0.0
MINOR 向下兼容的功能新增 1.0.0 → 1.1.0
PATCH 向下兼容的问题修复 1.0.0 → 1.0.1

内容结构

# 项目版本记录

## 当前版本
v1.0.0 (2026-09-22)

## 版本历史

| 版本 | 日期 | 说明 |
|------|------|------|
| v1.0.0 | 2026-09-22 | 初始发布版本 |

## 版本明细

### 待发布

**发布日期:** 待定

**新增功能:**
- 无

**修复问题:**
- 无

**变更说明:**
- 无

---

### v1.0.0

**发布日期:** 2026-09-22

**新增功能:**
- 客户管理模块(CRUD)
- 租约管理模块(CRUD)

**修复问题:**
- 无

**变更说明:**
- 无

待发布章节

  • 位置:固定位于「版本明细」最上面,在所有已发布版本章节之上;全文有且仅有一个
  • 结构:与版本章节同构 —— **发布日期:** 待定 + 新增功能 / 修复问题 / 变更说明,以 --- 与下一章节分隔
  • 用途:累积当前未发布周期内用户可感知的净变化,同受「记录范围(产品视角)」与「对比基线(净变化原则)」约束
  • 发版迁移:发版时将其内容整体转为新版本章节(标题改为版本号、发布日期改为实际日期,并同步「当前版本」与「版本历史」表格),随后在其上方重建一个空的「待发布」章节,保证其恒在最上面
  • 常驻保留:即使本周期无待记录内容,也保留空的「待发布」章节(三项均填「无」),不得删除

编写要求

  1. 当前版本:始终显示最新版本号和发布日期
  2. 版本历史表格:按时间倒序排列(最新版本在最上面)
  3. 版本明细:
    • 每个版本一个章节,标题为版本号(如 ### v1.0.0)
    • 包含:发布日期、新增功能、修复问题、变更说明
    • 按时间倒序排列(最新版本在最上面)
    • 「待发布」章节恒置于最上面(在所有已发布版本章节之上),详见「待发布章节」
  4. 更新时机:每次发布新版本时更新此文件
  5. 记录范围(产品视角):只记录用户可感知的产品功能变化,按最终功能归类为新增 / 修复 / 变更
    • ✅ 记录:新增页面与菜单、新增图表与指标卡、功能行为与数据口径变化、界面呈现调整、缺陷修复、交付形态变更
    • ❌ 不记录:技术实现细节(Store / API / 组件拆分、状态管理调整)、代码重构与规范修正、 Mock 数据与辅助文档(AGENTS.md、docs/)调整、构建脚本与依赖变更、 API 数据结构变更(仅后端可感知的字段调整、接口重构等)
    • 判断标准:以最终用户(产品使用者)是否可感知为唯一标准,后端开发者的感知不纳入记录范围
    • 描述以功能名称与用户可见效果表述,不堆砌文件路径、类名、行数等技术标识
  6. 对比基线(净变化原则):所有条目必须是「当前状态 vs 上一个已发布版本」的净变化
    • 同一未发布周期内先新增、后又修复或调整的内容,不单独记录修复/变更,只记录最终功能形态
    • 判断方法:若某缺陷或行为在上一发布版本中不存在(属本周期新增功能自带),则不写入「修复问题」「变更说明」
    • 「新增功能」与「变更说明」不重复描述同一动作
    • 发版前按上一版本 tag 重新逐条核对