Wingman:ai agent 小帮手
自己 ai 使用了已经很长一段时间了,确实非常好用,写代码快了很多(但是活一点没少) ,用了这么久发现 ai 有时候实在乱改,有些事情要强调好多遍,项目进度太赶的话我自己也记不清之前这里为什么要这么改,为此开发了一个 agent 插件 wingman。
目前主要有几类能力:
1. Memory 长期记忆
ai 用久了会有几个问题:
- 我一个月前都让 ai 做过啥?
- 前几天明明 ai 做对过的东西,在另一个地方再做一遍怎么做错了。
- 明明项目里已经有类似组件/工具函数,它又重新写一套。
- 当初为什么要让 ai 这么做?
- 前几天强调过的地方,现在又要强调一遍。
为此开发了一个 memory 系统,把项目里比较稳定的规则、上下文、历史决策分开记录。
使用方法:
text
/memory-setup
初始化记忆,会在仓库本地生成一个 .wingman 目录。
然后在做比较大的修改前,可以让 Agent 先加载记忆:
text
/memory-load 搜索下这个模块之前的逻辑,当时为什么要这么写
完成有价值的修改后,再同步记忆:
text
/memory-sync 整理下对话,更新记忆
初始化仓库后,load 和 sync 可以自动触发,记忆文件也会自动更新,无需手动维护。前提是对话要在 agent 里,当然手动调用 skill 更保险一点。
示例:
arduino
/memory-setup
会创建以下目录结构:
bash
.wingman/memory/
brief.md
context.md
brief.md 大概如下:
md
# 记忆简报 (Memory Brief)
## 0. 记忆设置 (Memory Settings)
- **语言 (Language)**: `zh-CN`
## 1. 架构决策 (ADR - 全局规则)
> 记录影响整个项目的全局规则。每条规则必须包含 `[WHY]`(原因)。
## 2. 领域注册表 (Domain Registry)
| 领域 (Domain) | 何时读取 (Read When) | 当前文件 (Current File) | 历史记录 (History) | 别名 (Aliases) | 相关领域 (Related Domains) | 状态 (Status) |
| --- | --- | --- | --- | --- | --- | --- |
## 3. 记忆布局 (Memory Layout)
- `brief.md`: 全局规则、架构决策(ADRs)、记忆设置以及领域注册表。
- `context.md`: 当前任务、待办工作以及近期的高价值日志。
- `domains/`: 当前持久化的领域真相,按需创建。
- `history/`: 建立索引的事件历史,按需创建,默认不读取。
## 4. 权威顺位 (Authority Order)
当前规则会覆盖历史事件(优先级从高到低):
1. `brief.md`:全局规则和当前的架构决策(ADRs)。
2. `domains/`:当前的领域真相。
3. `context.md`:热点工作上下文。
4. `history/`:历史事件和审计轨迹。
context.md 大概如下:
md
# 记忆上下文 (Memory Context)
## 待办任务 (Pending Tasks)
- [ ] 待计划
## 当前工作 (Current Work)
- 暂无。
---
## 近期日志 (Recent Logs)
### [Init] Wingman 记忆已启用
- **目标 (Goal)**: 启用仓库级别的 Wingman 记忆,以便 AI 智能体在工作前加载当前项目上下文,并在工作后同步重要的产出。
- **核心文件 (Core Files)**:
- `.wingman/memory/brief.md`: 存储全局 ADRs、记忆设置和领域注册表。
- `.wingman/memory/context.md`: 存储短期进度、待办任务和近期工作上下文。
- **备注 (Notes)**: `domains/` 和 `history/` 仅在出现持久化的领域真相或历史事件时才会创建。
memory-sync 会记录什么
比如你刚确认了一条业务规则:
go
订单列表里不能用 payment_status 推导 order.status。
`status` 表示订单生命周期,`payment_status` 只表示支付状态。
你可以让 Agent 同步:
go
/memory-sync 订单列表里不能用 payment_status 推导 order.status。
`status` 表示订单生命周期,`payment_status` 只表示支付状态。
如果这是长期规则,Wingman 可能会创建:
bash
.wingman/memory/domains/order.md
内容类似:
markdown
# 订单领域 (Order Domain)
## 何时读取此领域 (When To Read This Domain)
- 修改订单列表、订单详情、订单状态展示、支付状态展示时
- 对接订单 API、订单 DTO、订单前端 ViewModel 时
## 当前真相 (Current Truths)
- 绝不能将 `Order.status` 和 `Order.payment_status` 互相替代使用。[WHY]:`status` 描述的是订单生命周期,而 `payment_status` 描述的是支付结算状态。
- **证据 (Evidence)**: 在对接订单列表时确认的 API 契约决策
- **适用场景 (Applies When)**: 未来将订单 API 数据映射到 UI、领域模型、过滤器、徽章或工作流状态时
- **状态 (Status)**: `current` (现行有效)
- **起始时间 (Since)**: `2026-05-26`
- **替代 (Supersedes)**: `None` (无)
- **相关领域 (Related Domains)**: `payment` (支付)
- **历史记录 (History)**: `None` (无)
同时,brief.md 的领域注册表(Domain Registry)会多出一行,让后续的 Agent 知道什么时候该读取这个领域规则:
markdown
| Order | 订单 API, 订单列表, 订单详情, 状态映射 | domains/order.md | None | 订单, order status | payment | current |
如果这次改动对后续的几次会话也有帮助,context.md 还可能会添加一条近期日志:
markdown
### [2026-05-26] 订单 API 状态契约已对齐
- **目标 (Goal)**: 保持订单生命周期状态和支付结算状态之间的语义独立性。
- **核心文件 (Core Files)**:
- `src/features/orders/orderMapper.ts`: 集中处理 API DTO 到订单视图模型的映射,并保持 `status` 与 `paymentStatus` 的分离。
- `src/features/orders/OrderStatusBadge.tsx`: 仅读取生命周期状态,不再通过支付状态推导订单状态。
- **备注 (Notes)**: 未来涉及订单 UI 的工作,在修改状态映射前,都应先读取 `.wingman/memory/domains/order.md`。
2. align-contracts 数据对接
最开始做这个 skill 主要是为了约束让 ai 对接后端接口字段的时候不要乱改,后来把这个概念扩散到当 A 系统的数据和 B 系统的数据发生交接时,ai 该如何处理。
几个常见的问题:
开发阶段前端使用的是 userName,让 ai 对接后端接口后,为了把后端的字段映射成 UI 组件期望的字段(比如把 user_name 转成 userName,把 image 转成 img),写了很多无意义的转换,因为 ai 不知道之前的代码是在调试阶段,不会轻易改之前的代码,比如
ts
// 数据源:[{ id: 1, user_name: "Alex", image: "xxxxx" }, ...]
export function UserList({ userListApiData }) {
return (
<ul className="user-list">
{userListApiData.map((user) => {
// 在 map 内部执行 Scenario A:轻量级解构映射
const {
id,
user_name: userName,
image: img
} = user;
return (
<li key={id} className="list-item">
<span className="name">{userName}</span>
<span >{img}</span>
</li>
);
})}
</ul>
);
}
现有的 UI 组件 ProductCard 原本期望传入 title, img 和 points。但新的 API 返回的却是 product_name, image_url,而且有时候还会漏掉 price 字段。
ts
// 错误的做法:
// 父组件 (Parent Component)
export function ProductList({ apiData }) {
return (
<div>
{apiData.map(item => (
<ProductCard
// 错误示范:在这里做"翻译"和"填坑"
title={item.product_name} // 把 product_name 翻译成 title
img={item.image_url} // 把 image_url 翻译成 img
points={item.price ? item.price : 0} // 缺字段?在这里硬补一个 0
/>
))}
</div>
);
}
// 正确的做法:
// 子组件:ProductCard.tsx
// ✅ 动作 1 直接修改组件的 Interface,拥抱后端的命名
interface ProductCardProps {
product_name: string; // 以前叫 title,现在直接叫 product_name
image_url: string; // 以前叫 img,现在直接叫 image_url
price?: number; // ✅ 打上问号,承认后端有时候会不传这个字段
}
// 组件入参直接使用后端字段
export function ProductCard({ product_name, image_url, price }: ProductCardProps) {
// ✅ 动作 2 遇到没传 price 时,别报错,给个默认值 0,并留下 FIXME 证据
const displayPrice = price ?? 0; // TODO: Field [price] missing in API
return (
<div className="product-card">
<img src={image_url} alt={product_name} />
<h3>{product_name}</h3>
<p>价格: ${displayPrice}</p>
</div>
);
}
// 改完之后的父组件 (Parent Component)
export function ProductList({ apiData }) {
return (
<div>
{apiData.map(item => (
// ✅ 完美:没有任何翻译代码,直接把后端的一整条数据扔进去!
<ProductCard {...item} />
))}
</div>
);
}
乱改 ui,我就想不明白了,对接个接口 ai 改什么样式
还有些别的用法,但是功能还不是特别稳定,各位大佬可以调用 /using-wingman 自行查阅
安装
很遗憾目前 cursor 和 codex 我都没发上架到官方插件市场,codex 入口都没有,辛苦各位大佬手动安装下
Codex 我写了一个一个脚本
bash
curl -fsSLO https://raw.githubusercontent.com/lsshym/wingman.ai/main/scripts/install-codex-wingman.sh
bash install-codex-wingman.sh --self-delete
安装后重启 Codex。
Cursor 用户安装非常简单,在 agent 对话框
text
/add-plugin wingman@https://github.com/lsshym/wingman.ai
仓库
GitHub:https://github.com/lsshym/wingman.ai
现在还是比较早期的版本,还在边用边改,希望大佬们提提建议,我也需要更多的真实反馈,看哪里边界问题没做好,也希望大佬们点点 star。