@nathan33/nao-skill 0.7.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/checklists/pm.md +8 -6
- package/.agents/checklists/rd-infra.md +50 -0
- package/.agents/common/intercom-protocol.md +9 -6
- package/.agents/prompts/infra-engineer.md +65 -0
- package/.agents/prompts/product-manager.md +29 -89
- package/.agents/roles.yaml +4 -0
- package/.agents/scripts/nao-fleet.sh +347 -78
- package/.agents/scripts/qq-notify +199 -0
- package/.agents/skills/pm-operations.md +92 -0
- package/.agents/skills/pm-routing.md +31 -0
- package/.agents/skills/qq-notify.md +87 -0
- package/.agents/skills/research.md +1 -1
- package/.agents/skills/test-design.md +9 -0
- package/README.md +19 -5
- package/bin/nao-skill.js +4 -4
- package/package.json +1 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
//bin/true; exec node "$0" "$@"
|
|
3
|
+
//
|
|
4
|
+
// qq-notify —— QQ 官方机器人「主动消息」推送器(舰队可选能力)
|
|
5
|
+
//
|
|
6
|
+
// 用法(三种形式等价:./qq-notify · node qq-notify · sh qq-notify)
|
|
7
|
+
// ./qq-notify "文本" 发给配置中的 ownerOpenId(默认收件人)
|
|
8
|
+
// ./qq-notify --to OPENID "文本" 发给指定 openid
|
|
9
|
+
// ./qq-notify --dry-run "文本" 只取 token、不发送(连通性自检)
|
|
10
|
+
// ./qq-notify --config PATH "文本" 指定配置文件
|
|
11
|
+
// ./qq-notify --help 显示帮助
|
|
12
|
+
// stdin 管道 无参数时从标准输入读取文本
|
|
13
|
+
//
|
|
14
|
+
// 配置优先级:--config PATH > 环境变量 QQ_NOTIFY_CONFIG > ~/.pi/agent/pi-agent-qqbot.json
|
|
15
|
+
// 退出码:0 成功 · 1 参数/配置错 · 2 取 token 失败 · 3 发送失败
|
|
16
|
+
// 安全:绝不打印/落盘 clientSecret 与 access_token;错误输出截断。
|
|
17
|
+
//
|
|
18
|
+
// ⛔ 禁用 `bash <本脚本>` 这类「用 shell 逐行解释 Node 脚本」的调用方式:
|
|
19
|
+
// shell 会把注释里的示例当命令执行(管道、重定向、glob),可能触发意外发送
|
|
20
|
+
// (本仓历史上正因此误发过一条真实消息)。正确形式:./qq-notify 或 node qq-notify。
|
|
21
|
+
// 第 2 行的 exec 守卫已使 sh/bash 调用自动转交 node,但仍不建议依赖该行为。
|
|
22
|
+
//
|
|
23
|
+
// 实现说明:本文件无扩展名,需同时兼容宿主项目 package.json 的
|
|
24
|
+
// "type": "module" 与 CommonJS 两种解析;因此只用动态 import()(两种格式都合法)
|
|
25
|
+
// 并包在 async IIFE 中,避免静态 import / 顶层 await / require。
|
|
26
|
+
;(async () => {
|
|
27
|
+
const fs = await import('node:fs')
|
|
28
|
+
const os = await import('node:os')
|
|
29
|
+
const path = await import('node:path')
|
|
30
|
+
|
|
31
|
+
const EXIT = { OK: 0, ARG: 1, TOKEN: 2, SEND: 3 }
|
|
32
|
+
const DEFAULT_CFG = path.join(os.homedir(), '.pi/agent/pi-agent-qqbot.json')
|
|
33
|
+
const TOKEN_URL = 'https://bots.qq.com/app/getAppAccessToken'
|
|
34
|
+
const BODY_LIMIT = 300
|
|
35
|
+
|
|
36
|
+
const USAGE = [
|
|
37
|
+
'qq-notify —— QQ 官方机器人「主动消息」推送器',
|
|
38
|
+
'',
|
|
39
|
+
'用法',
|
|
40
|
+
' qq-notify "文本" 发给配置中的 ownerOpenId',
|
|
41
|
+
' qq-notify --to <openid> "文本" 发给指定 openid',
|
|
42
|
+
' echo "文本" | qq-notify 从 stdin 读取文本',
|
|
43
|
+
' qq-notify --dry-run "文本" 只取 token、不发送(连通性自检)',
|
|
44
|
+
' qq-notify --config <path> ... 指定配置文件',
|
|
45
|
+
' qq-notify -h | --help 显示本帮助',
|
|
46
|
+
'',
|
|
47
|
+
'配置(优先级从高到低)',
|
|
48
|
+
' 1) --config <path>',
|
|
49
|
+
' 2) 环境变量 QQ_NOTIFY_CONFIG',
|
|
50
|
+
' 3) ' + DEFAULT_CFG,
|
|
51
|
+
' 字段:{ "appId": "...", "clientSecret": "...", "ownerOpenId": "...", "sandbox": true }',
|
|
52
|
+
'',
|
|
53
|
+
'退出码',
|
|
54
|
+
' 0 成功 1 参数/配置错 2 取 token 失败 3 发送失败',
|
|
55
|
+
'',
|
|
56
|
+
'说明',
|
|
57
|
+
' · 主动消息受 QQ 开放平台权限与月度配额限制;sandbox 与 prod 权限不通用',
|
|
58
|
+
' · 绝不打印或落盘 clientSecret / access_token',
|
|
59
|
+
].join('\n')
|
|
60
|
+
|
|
61
|
+
const trunc = (v, n = BODY_LIMIT) => {
|
|
62
|
+
const s = String(v ?? '')
|
|
63
|
+
return s.length > n ? s.slice(0, n) + '…(截断)' : s
|
|
64
|
+
}
|
|
65
|
+
// 从任何即将打印的文本中抹去已知凭证值(客户端密钥 / access_token)
|
|
66
|
+
const redact = (v, secrets = [], n = BODY_LIMIT) => {
|
|
67
|
+
let s = String(v ?? '')
|
|
68
|
+
for (const sec of secrets) {
|
|
69
|
+
if (sec && String(sec).length >= 6) s = s.split(String(sec)).join('***')
|
|
70
|
+
}
|
|
71
|
+
return trunc(s, n)
|
|
72
|
+
}
|
|
73
|
+
const maskId = (v) => (v ? String(v).slice(0, 6) + '…' : '<无>')
|
|
74
|
+
const fail = (code, msg, hint) => {
|
|
75
|
+
process.stderr.write('FAIL: ' + msg + '\n')
|
|
76
|
+
if (hint) process.stderr.write(' → ' + hint + '\n')
|
|
77
|
+
process.exit(code)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ---- 参数解析 -----------------------------------------------------------
|
|
81
|
+
const argv = process.argv.slice(2)
|
|
82
|
+
let to = null
|
|
83
|
+
let cfgPath = null
|
|
84
|
+
let dry = false
|
|
85
|
+
let text = null
|
|
86
|
+
let endOpts = false
|
|
87
|
+
|
|
88
|
+
for (let i = 0; i < argv.length; i++) {
|
|
89
|
+
const a = argv[i]
|
|
90
|
+
if (endOpts) {
|
|
91
|
+
if (text === null) { text = a; continue }
|
|
92
|
+
fail(EXIT.ARG, '多余的位置参数: ' + trunc(a, 60), '文本请用引号括起为一个参数')
|
|
93
|
+
}
|
|
94
|
+
if (a === '-h' || a === '--help') { process.stdout.write(USAGE + '\n'); process.exit(EXIT.OK) }
|
|
95
|
+
else if (a === '--dry-run') dry = true
|
|
96
|
+
else if (a === '--') endOpts = true
|
|
97
|
+
else if (a === '--to') {
|
|
98
|
+
const v = argv[++i]
|
|
99
|
+
if (!v || v.startsWith('--')) fail(EXIT.ARG, '--to 缺少 openid', '用法:qq-notify --to <openid> "文本"')
|
|
100
|
+
to = v
|
|
101
|
+
} else if (a.startsWith('--to=')) {
|
|
102
|
+
to = a.slice(5)
|
|
103
|
+
if (!to) fail(EXIT.ARG, '--to 缺少 openid', '用法:qq-notify --to <openid> "文本"')
|
|
104
|
+
} else if (a === '--config') {
|
|
105
|
+
const v = argv[++i]
|
|
106
|
+
if (!v || v.startsWith('--')) fail(EXIT.ARG, '--config 缺少路径', '用法:qq-notify --config <path> "文本"')
|
|
107
|
+
cfgPath = v
|
|
108
|
+
} else if (a.startsWith('--config=')) {
|
|
109
|
+
cfgPath = a.slice(9)
|
|
110
|
+
if (!cfgPath) fail(EXIT.ARG, '--config 缺少路径', '用法:qq-notify --config <path> "文本"')
|
|
111
|
+
} else if (a.startsWith('-') && a !== '-') {
|
|
112
|
+
fail(EXIT.ARG, '未知参数: ' + trunc(a, 60), '查看帮助:qq-notify --help')
|
|
113
|
+
} else {
|
|
114
|
+
if (text === null) text = a
|
|
115
|
+
else fail(EXIT.ARG, '多余的位置参数: ' + trunc(a, 60), '文本请用引号括起为一个参数')
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!text && !process.stdin.isTTY) {
|
|
120
|
+
try {
|
|
121
|
+
const piped = fs.readFileSync(0, 'utf8').trim()
|
|
122
|
+
if (piped) text = piped
|
|
123
|
+
} catch { /* 无 stdin:忽略 */ }
|
|
124
|
+
}
|
|
125
|
+
if (!text) fail(EXIT.ARG, '缺少推送文本', '用法:qq-notify "文本" 或 echo "文本" | qq-notify')
|
|
126
|
+
|
|
127
|
+
// ---- 配置 ---------------------------------------------------------------
|
|
128
|
+
const cfgFile = cfgPath || process.env.QQ_NOTIFY_CONFIG || DEFAULT_CFG
|
|
129
|
+
let cfg
|
|
130
|
+
try {
|
|
131
|
+
cfg = JSON.parse(fs.readFileSync(cfgFile, 'utf8'))
|
|
132
|
+
} catch (e) {
|
|
133
|
+
if (e && e.code === 'ENOENT') {
|
|
134
|
+
fail(EXIT.ARG, '读不到配置:' + cfgFile,
|
|
135
|
+
'创建该 JSON(appId / clientSecret / ownerOpenId / sandbox),或用 --config <path> / QQ_NOTIFY_CONFIG=<path> 指定')
|
|
136
|
+
}
|
|
137
|
+
fail(EXIT.ARG, '配置解析失败:' + cfgFile + ':' + trunc(e && e.message, 160), '确认该文件是合法 JSON')
|
|
138
|
+
}
|
|
139
|
+
if (!cfg || typeof cfg !== 'object') fail(EXIT.ARG, '配置内容非法:' + cfgFile, '应为 JSON 对象')
|
|
140
|
+
if (!cfg.appId || !cfg.clientSecret) fail(EXIT.ARG, '配置缺少 appId 或 clientSecret', '检查 ' + cfgFile)
|
|
141
|
+
|
|
142
|
+
const target = to || cfg.ownerOpenId
|
|
143
|
+
if (!target) {
|
|
144
|
+
fail(EXIT.ARG, '没有收件人:配置缺 ownerOpenId,且未传 --to',
|
|
145
|
+
'在配置中填 ownerOpenId,或 qq-notify --to <openid> "文本"')
|
|
146
|
+
}
|
|
147
|
+
const env = cfg.sandbox ? 'sandbox' : 'prod'
|
|
148
|
+
const base = cfg.sandbox ? 'https://sandbox.api.sgroup.qq.com' : 'https://api.sgroup.qq.com'
|
|
149
|
+
|
|
150
|
+
// ---- 取 access token(绝不打日志) --------------------------------------
|
|
151
|
+
let tokenRes
|
|
152
|
+
try {
|
|
153
|
+
tokenRes = await fetch(TOKEN_URL, {
|
|
154
|
+
method: 'POST',
|
|
155
|
+
headers: { 'content-type': 'application/json' },
|
|
156
|
+
body: JSON.stringify({ appId: cfg.appId, clientSecret: cfg.clientSecret }),
|
|
157
|
+
})
|
|
158
|
+
} catch (e) {
|
|
159
|
+
fail(EXIT.TOKEN, '取 token 网络错误:' + trunc(e && e.message, 160), '检查网络/代理后重试')
|
|
160
|
+
}
|
|
161
|
+
let tokenJson = {}
|
|
162
|
+
try { tokenJson = await tokenRes.json() } catch { /* 非 JSON 响应 */ }
|
|
163
|
+
if (!tokenRes.ok || !tokenJson.access_token) {
|
|
164
|
+
fail(EXIT.TOKEN, '取 token 失败:status=' + tokenRes.status + ' body=' + redact(JSON.stringify(tokenJson), [cfg.clientSecret]),
|
|
165
|
+
'核对 appId/clientSecret,并确认 QQ 开放平台应用已开通机器人能力')
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (dry) {
|
|
169
|
+
process.stdout.write('OK (dry-run) 取 token 成功 · env=' + env + ' · to=' + maskId(target) + '\n')
|
|
170
|
+
process.exit(EXIT.OK)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ---- 发送主动消息(不带 msg_id) ----------------------------------------
|
|
174
|
+
let res
|
|
175
|
+
try {
|
|
176
|
+
res = await fetch(base + '/v2/users/' + encodeURIComponent(target) + '/messages', {
|
|
177
|
+
method: 'POST',
|
|
178
|
+
headers: {
|
|
179
|
+
'content-type': 'application/json',
|
|
180
|
+
authorization: 'QQBot ' + tokenJson.access_token,
|
|
181
|
+
},
|
|
182
|
+
body: JSON.stringify({ content: text, msg_type: 0 }),
|
|
183
|
+
})
|
|
184
|
+
} catch (e) {
|
|
185
|
+
fail(EXIT.SEND, '发送网络错误:' + trunc(e && e.message, 160), '检查网络后重试')
|
|
186
|
+
}
|
|
187
|
+
const body = await res.text().catch(() => '')
|
|
188
|
+
if (res.ok) {
|
|
189
|
+
process.stdout.write('OK env=' + env + ' to=' + maskId(target) + ' ' + redact(body, [cfg.clientSecret, tokenJson.access_token], 120) + '\n')
|
|
190
|
+
process.exit(EXIT.OK)
|
|
191
|
+
}
|
|
192
|
+
fail(EXIT.SEND, '发送失败:status=' + res.status + ' body=' + redact(body, [cfg.clientSecret, tokenJson.access_token]),
|
|
193
|
+
'核对收件人 openid、主动消息权限与月度配额;sandbox 与 prod 权限不通用')
|
|
194
|
+
})().catch((e) => {
|
|
195
|
+
// 兜底:任何未预期错误都不抛栈(此处不依赖 IIFE 内作用域)
|
|
196
|
+
const msg = String((e && e.message) ? e.message : e)
|
|
197
|
+
process.stderr.write('FAIL: 未预期错误:' + (msg.length > 200 ? msg.slice(0, 200) + '…(截断)' : msg) + '\n')
|
|
198
|
+
process.exit(1)
|
|
199
|
+
})
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 按需技能——PM 运行手册(调度细则 / 开工确认卡 / 会话生命周期 / AGENTS.md 治理 / 交付归档)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# PM 运行手册
|
|
6
|
+
|
|
7
|
+
> 触发:**调度与运营操作**(拉起会话、派发、重开、建/改 `AGENTS.md`、归档)时读取;身份/边界/红线常驻在角色卡。
|
|
8
|
+
> 角色路由与会话登记在 @.agents/skills/pm-routing.md;需求分支与 PR 流水线在 @.agents/skills/github-flow.md。
|
|
9
|
+
|
|
10
|
+
## 一、多会话调度细则
|
|
11
|
+
|
|
12
|
+
- **准入判定**:先 `intercom status` + `list`;不可用立即降级不重试。
|
|
13
|
+
- **会话登记**:用户提供名称**及角色标注**(`fe-dev`/`be-dev`/`qa`/`infra`);**未标注禁止猜测**,必须补问。
|
|
14
|
+
- **舰队启动**:不在线先拉起再 `list` 验证(`ask` 仅对在线会话):`bash .agents/scripts/nao-fleet.sh ensure <别名>[@<repo目录>]`;项目独立于 nao-skills 仓库时用 `bash "$NAO_SKILLS/.agents/scripts/nao-fleet.sh" ...`。
|
|
15
|
+
- **环境自检(开工确认后、派发前)**:`bash .agents/scripts/nao-fleet.sh check`——默认单行摘要(完整报告不进 PM 上下文),`warn>0` 或失败加 `-v`;**exit code 非 0(硬错误)→ 停止派发**(白名单 / CodeGraph 处置见 intercom-protocol「开工前自检」)。
|
|
16
|
+
- **架构评审闸门**:PRD 涉及架构/NFR/选型时,开工确认后、派发前 `send` arch-designer 评审。纯 CRUD 跳过。
|
|
17
|
+
- **派发协议(省 token)**:一条消息给 `编号 + 范围 + 引用路径(docs/...md#section)+ AC 编号 + 回执级别 + 需求分支(feat/<issue-id>-<slug>)`;**不贴 PRD/方案全文、不用 `attachments` 传全文**(worker 自己读文件,PM 上下文不留副本);worker `ask` → PM `reply`。
|
|
18
|
+
- **忙闲闸门**:`list` 见目标在线**且 `idle`** 才派完整任务;忙碌(`thinking`/`tool:*`)→ **不投递任务内容**,记入待派发队列(`docs/tasks-state.md`),转 `idle` 再派;**紧急**才抢占(交互会话可 steer 注入并注明 `紧急抢占:…回执须报告挂起任务状态`;非交互会话只能排队)。
|
|
19
|
+
- **任务派生会话(并行隔离)**:`ensure <role>@<repo> --task <编号>` → `--name <角色>-<编号>`(如 `rd-be-T1`);**验收通过后回收**:`nao-fleet.sh close --task <编号> <别名>`(工具内置「在跑 turn / tasks-state 未推进」双闸门,后者防验收未过就回收丢返工上下文);回执 role 写派生名。
|
|
20
|
+
- **状态外部化 + 接续快照(可恢复)**:任务状态落盘 `docs/tasks-state.md`(五栏 + 顶部「PM 接续快照」;骨架见 @.agents/templates/tasks-state.md.example),每次派发/回执/验收/抢占后更新;重开后**先读该文件重建状态**,不依赖历史消息。
|
|
21
|
+
- **终态回执闸门**:每次派发(含架构评审)必须收到 `[编号] done` 回执(两级:默认 `done(lite)`;有阻塞/风险/需决策 `done(full)`,模板见 @.agents/checklists/comm-templates.md);未见回执(含 arch 静默)→ 主动追讨,必要时上报用户,**不默认成功**。
|
|
22
|
+
- **残留自检**:`nao-fleet.sh status` 标出派生会话残留(tasks-state 已归档 / 无记录);发现即核对状态并 `close --task <编号> <别名>` 回收。
|
|
23
|
+
- **会话回收与收窗**:派生会话验收通过即 `close`;**常驻会话不回收**(保留复用,上下文变长时 `ensure --force` 重开);手工关窗前按 checklists/pm.md「会话回收 / 收窗核对」逐项核对(工具不替代回执与产物核对)。
|
|
24
|
+
- **角色提示词加载**:fleet 拉起的会话已启动期注入,派发只要求回执 `已按 <role> 角色执行`;仅对用户手工开、未注入的会话才指示加载 `@.agents/prompts/<role>.md`。
|
|
25
|
+
- **验收闭环(省 token)**:核对 AC 五覆盖 + 回执「清单」字段(未核对则打回);验收 = 核对回执**全量门禁精确数字** + 读变更文件核对 AC + **异常才复跑/抽查**(**不可改码**、**不重复跑 worker 已跑的门禁**);读码用 `codegraph node --file <f> --offset <n> --limit <m>` 行段,**禁 cat 全文**;依赖 QA 先行用例。
|
|
26
|
+
- **合并闸门(PM 控制合并时间点)**:工作期 RD 只在需求分支 `feat/<issue-id>-<slug>`(降级 `nao/<批次-slug>`)提交(`wip(<编号>):` 检查点、路径级暂存、禁 `-A`)。**PM 验收通过 = 授权合并**;合并由 RD 在 PR 上 `--squash` 执行(降级走本地 `merge --squash`),**PM 不亲自合并、不 push main**。验收核对:PR 标题/信息**用户可读** + main 上本需求**恰好 1 条提交且无 `wip()`** + 工作区干净。特例白名单与流水线见 @.agents/skills/github-flow.md。
|
|
27
|
+
|
|
28
|
+
## 二、开工确认卡(强制闸门)
|
|
29
|
+
|
|
30
|
+
表格输出,末尾询问「是否开工?会话/角色是否如上?」:
|
|
31
|
+
|
|
32
|
+
| 任务编号 | 目标会话 | 角色 | 任务概要 | 对应 AC |
|
|
33
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
34
|
+
| T1 | rd-be | 后端 | ... | AC1/AC3 |
|
|
35
|
+
|
|
36
|
+
分支:`feat/<issue-id>-<slug>`(降级 `nao/<批次-slug>`)· PR owner:<角色> · Reviewer:<arch-designer / 无> · 预览环境:<URL / 无>
|
|
37
|
+
|
|
38
|
+
建议顺序:**QA 先行用例 → 前后端并行**。
|
|
39
|
+
|
|
40
|
+
用户指定模型时在开工确认卡另起一行注明(如 `rd-be: deepseek-v4-flash:high`);**未指定禁止传 `--model`、禁止继承 PM 模型**(白名单由 `nao-fleet.sh` 校验,PM 只需如实转达)。
|
|
41
|
+
|
|
42
|
+
## 三、PM 会话生命周期(重开纪律,PM 专属)
|
|
43
|
+
|
|
44
|
+
PM 是舰队唯一常驻长寿会话:上下文过长时丢的是**纪律**(红线/闸门遗漏且无感),因此不靠「记得住」,靠**批次边界重开 + 落盘接续**。**三个触发点,任一命中即 `ensure --force pm`**:
|
|
45
|
+
|
|
46
|
+
1. 交付归档完成(批次终态,默认触发);
|
|
47
|
+
2. 用户切入新需求、上一需求已闭环;
|
|
48
|
+
3. `/session` 显示 contextTokens 超上下文窗口 40%(主动交接,不等自动压缩)。
|
|
49
|
+
|
|
50
|
+
- **重开前**:更新 `docs/tasks-state.md` 顶部「PM 接续快照」(当前阶段 / 当前 PRD / 未决决策 / 待用户回答 / 下次唤醒条件 / 会话体检)。
|
|
51
|
+
- **重开后**:读 tasks-state(含接续快照)+ `docs/prds/README.md` 索引 → **向用户回读确认 3 行**(当前阶段 · 未决决策 · 口头约束),确认后方可调度;**禁止凭残缺记忆调度**。
|
|
52
|
+
- **自动压缩只作兜底**:LLM 摘要**有损且不可审计**(决策点最易丢)、额外花 token、禁用该次 prompt-cache 写——它救的是溢出,不是记忆。
|
|
53
|
+
|
|
54
|
+
## 四、AGENTS.md 项目上下文治理(PM 独有职责)
|
|
55
|
+
|
|
56
|
+
项目根 `AGENTS.md` 是**项目级单一事实来源**(pi 自动加载进所有会话上下文,与 CLAUDE.md 兼容)。PM 负责建立与维护——让任何角色进入项目即获得一致的项目属性与约束。
|
|
57
|
+
|
|
58
|
+
**内容范围(只放项目级;它常驻上下文,必须精简)**
|
|
59
|
+
|
|
60
|
+
- 项目属性:仓库结构 / 技术栈与版本 / 构建·测试·lint 命令 / 运行方式 / 环境变量 / 目录约定。
|
|
61
|
+
- 项目约束:领域红线、依赖与命令纪律、与 nao 机制衔接(CodeGraph 索引位置、UI tokens/ux-playbook 路径、checklists 位置)。
|
|
62
|
+
- **指针而非复制**:角色级/团队级内容留在 nao-skills(roles.yaml / common / checklists),AGENTS.md 只写「在哪里」。
|
|
63
|
+
|
|
64
|
+
**接入已有 AGENTS.md(项目已存在且非空时)**
|
|
65
|
+
|
|
66
|
+
- **合并不覆盖**:原文件是项目既有资产,PM 无权单方面改写;先读全文判断来源(手写规范 / 工具生成)。
|
|
67
|
+
- **在 pi 实际加载的文件里合并**:pi 按 `AGENTS.override.md → AGENTS.md → CLAUDE.md` 只取第一个存在者;在**被加载的那个文件**里追加,而非新建遮蔽文件。
|
|
68
|
+
- **保留原文 + 追加接入区块**:原内容原样保留,末尾追加 `## nao 舰队接入(YYYY-MM-DD)` 区块,含:角色清单指针(`$NAO_SKILLS/.agents/roles.yaml`)、机制衔接(CodeGraph / checklists / UI tokens)、命令纪律;区块加注释标记便于未来移除(可回退)。
|
|
69
|
+
- **同主题冲突**:原文件条目优先(它是项目既定决策);nao 需要的补充以接入区块承载,不混改原文措辞。
|
|
70
|
+
- **删除/改写原文须用户确认**:与零代码边界同源——PM 只增不改既有资产,除非用户明确授权。
|
|
71
|
+
- **验收**:合并后 `nao-fleet.sh check` 通过;任意会话进项目可复述 AGENTS.md 中的项目约束。
|
|
72
|
+
|
|
73
|
+
**更新时机**
|
|
74
|
+
|
|
75
|
+
- 项目初始化(首个 PRD 定稿后**当日建立**)。
|
|
76
|
+
- **用户口头约束即时落盘**:用户的任何约束性表述(如「这个项目不许用 ORM」)**当场**写入 `AGENTS.md`(项目级)或 PRD「变更治理」(需求级),**不等归档**——不落盘则 PM 重开后无法恢复。
|
|
77
|
+
- 关键技术决策(ADR 归档时同步一条)。
|
|
78
|
+
- 约束/命令/结构变化随改随更;交付归档时顺带核对(见本文件 §五)。
|
|
79
|
+
|
|
80
|
+
**自查(PM 交付前)**
|
|
81
|
+
|
|
82
|
+
- [ ] AGENTS.md 掺入角色级/团队级内容?(应留在 nao-skills)
|
|
83
|
+
- [ ] AGENTS.md 超长(>120 行)或含易变细节?(应精简/移除)
|
|
84
|
+
- [ ] 技术决策或约束变化后未同步 AGENTS.md?
|
|
85
|
+
|
|
86
|
+
## 五、交付归档
|
|
87
|
+
|
|
88
|
+
- 时机:PRD 完成且交付闭环后**当日**归档。
|
|
89
|
+
- 落盘:`docs/prds/YYYY-MM-DD-<主题>.md`。
|
|
90
|
+
- 内容:背景(5 Whys 摘要) → 目标指标与埋点 → 9 模块要点 → 决策留痕(RICE/取舍/回滚) → 参考来源(`docs/research/` 路径) → 派发记录(含需求分支、PR 编号、main 合并 commit hash) → 验收结果 → 变更记录 → 遗留项。
|
|
91
|
+
- 首次归档创建 `docs/prds/README.md` 索引(日期 | 主题 | 交付 | 终签状态)。
|
|
92
|
+
- 归档同时:更新 `docs/tasks-state.md` 接续快照;「派发记录」末行记**会话体检**(`contextTokens / 压缩次数 / cacheRead 占比`,取自 `/session`);随后按本文件 §三「PM 会话生命周期」重开 PM 会话(归档 = 批次终态)。
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 按需技能——PM 路由与派单纪律(角色路由口诀 / 技术调研边界)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# PM 路由与派单纪律
|
|
6
|
+
|
|
7
|
+
> 触发:**派发前**(决定派给谁)与**调研前**(判断是否越界)读取;其余时候不加载。
|
|
8
|
+
> 角色清单与别名以 `.agents/roles.yaml` 为唯一事实来源;`rd-infra` 边界见 @.agents/prompts/infra-engineer.md §一。
|
|
9
|
+
|
|
10
|
+
## 一、路由口诀
|
|
11
|
+
|
|
12
|
+
> **改「业务行为」⇒ `rd-fe`/`rd-be`;改「怎么构建 / 怎么验 / 怎么发 / 怎么治理」⇒ `rd-infra`。**
|
|
13
|
+
|
|
14
|
+
- **工程基座类 ⇒ `rd-infra`,不再由 `rd-be` 兜底**:CI 与流水线 · 构建/发布工具 · 脚本与守卫(`guard:*`、fleet 脚本) · 依赖与安全告警升级 · 仓库治理(分支/保护/清理/baseline 合并执行) · lint 与格式化工具链 · 跨项目安装器与分发 · 开发环境排障。
|
|
15
|
+
- **兜底纪律(无匹配角色时)**:**显式声明 + 选最接近专精角色 + 临时附加清单 + 台账登记**——**不许默默兜底**。
|
|
16
|
+
- 三条例外(避免口诀被滥用):① 架构取舍(选型/分层/契约)先经 `arch-designer`,`rd-infra` 只落地已定方案;② 验收口径与断言归属 `qa`/`pm`,`rd-infra` **不自验自己的守卫**;③ 工程基座改动**触碰业务语义**时拆单,业务部分回 `rd-fe`/`rd-be`。
|
|
17
|
+
|
|
18
|
+
## 二、技术调研边界
|
|
19
|
+
|
|
20
|
+
> 依据:TASK-23 越界调研真实案例——PM 亲自读仓储实现 / sweep 机制 / 服务端 Go 源码 / 限流桶与排序语义,据此形成**错误前提**(误判服务端字段不可用),用户在错误前提下多做一轮选择,最终由 arch 以更硬理由拉回。**越界调研的实际成本 = 无效决策轮次。**
|
|
21
|
+
|
|
22
|
+
- **PM 可自做(简略调研)**:① 粗略分析任务设计的**技术深度**(供范围划分与派发,不下结论);② UI/文案/样式/交互的**落点确认**(组件在哪、有无可复用件、i18n 键、插槽/属性支持);③ 读 `docs/prds` / `docs/adr` / `docs/tasks-state.md` 等**决策留痕**;④ 判断任务编号与范围划分;⑤ **产品形态业界调研**(同类功能有哪些功能点 / 属性 / 组织结构 / 视图 / 协作 / 生命周期;清单与检索纪律见 @.agents/skills/research.md)——只作**参考**,不得据此下技术结论。
|
|
23
|
+
- **必须转角色(任一命中即转,PM 不自行下结论)**:
|
|
24
|
+
1. 出现「哪个方案更好」的**取舍**;
|
|
25
|
+
2. 需读 `packages/domain-*` / `packages/infrastructure` / 服务端 / 构建配置 / 同步链;
|
|
26
|
+
3. 结论会**改变 PRD 范围或 AC**;
|
|
27
|
+
4. 影响面跨 **>1 个包或 >1 个端**;
|
|
28
|
+
5. 涉及性能量级、限流、契约字段语义、枚举/排序语义;
|
|
29
|
+
6. 任何以外部文章/竞品做法为依据的**技术结论**(该用哪个方案/库/中间件)。
|
|
30
|
+
- **转发对象**:技术选型与影响面 → `arch-designer`;验收策略与用例 → `qa`;实现细节 → `rd-fe` / `rd-be`;**工程基座(CI/构建/发布/脚本守卫/依赖安全/仓库治理)→ `rd-infra`**。
|
|
31
|
+
- **判定口径**:不确定是否越界时**按越界处理**(转角色),不以「省一轮」为由自行下结论。
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 按需技能——QQ 主动推送(关键节点通知;PM 在里程碑/阻塞时读取)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# QQ 主动推送(qq-notify)
|
|
6
|
+
|
|
7
|
+
## 触发时机
|
|
8
|
+
|
|
9
|
+
**仅关键节点**由 PM 使用,读本文件确认纪律后调用;日常轮次不加载、不发送。
|
|
10
|
+
本能力**可选**:未配置 `pi-agent-qqbot` 时不要使用,按普通回执继续交付。
|
|
11
|
+
|
|
12
|
+
## 定位与能力边界
|
|
13
|
+
|
|
14
|
+
- 把「关键节点」用 QQ 官方机器人的**主动消息**推给 owner,让用户离开工位也能收到交付信号。
|
|
15
|
+
- **主动消息**(不带 `msg_id`)受 **QQ 开放平台账号权限与月度配额**限制:配额用尽或未开通对应权限时发送会失败(exit 3),**不得重试轰炸**。
|
|
16
|
+
- 只做**单向推送**,不做接收/被动回复;不做定时调度、不做退避重发(这些是平台与宿主的事)。
|
|
17
|
+
- **默认 sandbox**:`pi-agent-qqbot.json` 中 `sandbox: true` 走测试环境,`false` 才走 `prod`。**两者权限与收件人不通用**,切换 prod 前先确认机器人已在正式环境配置好。
|
|
18
|
+
|
|
19
|
+
## 何时用(四类节点,仅此四类)
|
|
20
|
+
|
|
21
|
+
| 节点 | 典型内容 |
|
|
22
|
+
| :--- | :--- |
|
|
23
|
+
| 批次进度汇总 | 一个批次开始/收尾时的整体进度(不是每个子任务) |
|
|
24
|
+
| 验收结论 | PM 验收通过 / 驳回,含 PR 编号与一句话结论 |
|
|
25
|
+
| 发版或合并完成 | tag / Release / squash 合并完成,含版本号 |
|
|
26
|
+
| 异常阻塞 | 需要用户决策、外部依赖卡住、门禁长期红 |
|
|
27
|
+
|
|
28
|
+
## 纪律(禁刷屏)
|
|
29
|
+
|
|
30
|
+
- **一个节点最多一条**;同一批次不逐任务推送。
|
|
31
|
+
- 推文用**一句话**:`[编号] 结论 + 关键数字/链接`,不贴长正文(正文留 `docs/`)。
|
|
32
|
+
- 用户未回应不重复推;阻塞未解除才允许在**跨天**时补一条。
|
|
33
|
+
- **默认 sandbox**,仅在正式发布通知用户时考虑 prod(须用户明确知情)。
|
|
34
|
+
|
|
35
|
+
## 调用禁令(⛔ 重要:曾因此误发过真实消息)
|
|
36
|
+
|
|
37
|
+
本脚本是 **Node 程序**,文件头部的注释里写着用法示例。⛔ **禁止用 shell 把它当 shell 脚本逐行解释**:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
⛔ bash .agents/scripts/qq-notify "文本" ← 错:shell 会执行注释里的示例
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
原因(实测):`bash <本脚本>` 会把注释当命令执行——注释里的 `*` 触发 **glob 展开**(把 `AGENTS.md apps docs …` 当命令跑)、`--to <openid>` 变成 **重定向**、`echo "文本" | qq-notify` 变成 **真实管道**(PATH 命中同名脚本即**真发**)——本仓历史上正因此误发过一条真实 QQ 消息。
|
|
44
|
+
|
|
45
|
+
**正确形式(三者等价,均 rc=0 且输出一致)**:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
.agents/scripts/qq-notify "文本" # ./ 直接执行(推荐)
|
|
49
|
+
node .agents/scripts/qq-notify "文本" # 显式用 node
|
|
50
|
+
sh .agents/scripts/qq-notify "文本" # 已加固:头部 exec 守卫自动转交 node
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> 加固方式(v0.9.2):第 1 行 `#!/bin/sh` + 第 2 行 `//bin/true; exec node "$0" "$@"`。
|
|
54
|
+
> `sh/bash` 执行时第 2 行即 `exec node`(**自动转交**,注释不会再被 shell 解析);`node` 执行时第 2 行是 JS 注释。
|
|
55
|
+
> `nao-fleet.sh check` 已增加该守卫的存在性校验(缺失 ⇒ rc=1)。
|
|
56
|
+
|
|
57
|
+
## 用法
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# 安装后位于当前项目:.agents/scripts/qq-notify
|
|
61
|
+
.agents/scripts/qq-notify "[T301] 验收通过 · PR #4 已合并 · v0.8.0"
|
|
62
|
+
.agents/scripts/qq-notify --dry-run "连通性自检" # 只取 token、不发送
|
|
63
|
+
echo "文本" | .agents/scripts/qq-notify # 从 stdin 读
|
|
64
|
+
.agents/scripts/qq-notify --to <openid> "文本" # 指定收件人
|
|
65
|
+
.agents/scripts/qq-notify -h # 帮助
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- 配置优先级:`--config <path>` > 环境变量 `QQ_NOTIFY_CONFIG` > `~/.pi/agent/pi-agent-qqbot.json`
|
|
69
|
+
- 配置文件字段:`{ "appId", "clientSecret", "ownerOpenId", "sandbox" }`;**无此文件即视为未启用本能力**(exit 1)。
|
|
70
|
+
|
|
71
|
+
## 退出码与失败回报
|
|
72
|
+
|
|
73
|
+
| 退出码 | 含义 | 处理 |
|
|
74
|
+
| :--- | :--- | :--- |
|
|
75
|
+
| `0` | 发送成功(或 `--dry-run` 取 token 成功) | 正常,可在回执记一行 `notify=ok` |
|
|
76
|
+
| `1` | 参数或配置错(缺文本 / 读不到配置 / 缺 `ownerOpenId`) | 按 stderr 的 `→` 提示修复;**不要重试**,先补配置 |
|
|
77
|
+
| `2` | 取 access token 失败(网络 / 凭证错误) | 报出 `status` 与截断错误体;不重试轰炸 |
|
|
78
|
+
| `3` | 发送失败(权限 / 配额 / openid 非法) | 报出 `status` 与截断错误体;主动消息受权限与配额限制 |
|
|
79
|
+
|
|
80
|
+
- 失败时**不抛栈**:脚本输出 `FAIL: …` 与一行可执行修复提示,**非 0 退出码 + 截断错误体**即为回报内容。
|
|
81
|
+
- 推送失败**不阻断交付**:把 `notify=fail(exit=<n>)` 写进回执的风险项,继续原流程。
|
|
82
|
+
- **安全**:脚本绝不打印/落盘 `clientSecret` 与 `access_token`;回执中也不要粘贴这两者。
|
|
83
|
+
|
|
84
|
+
## 跨项目用法
|
|
85
|
+
|
|
86
|
+
安装后脚本固定位于 **`<项目>/.agents/scripts/qq-notify`**(随 `nao-skill install/update` 整目录分发)。
|
|
87
|
+
在项目里直接用相对路径调用;从别的仓库操作时用绝对路径 `.agents/scripts/qq-notify`(可配 `NAO_SKILLS`)。
|
|
@@ -16,7 +16,7 @@ description: 按需技能——外部调研协议(产品形态可查;技术
|
|
|
16
16
|
| **RD** | 具体库/API/SDK 用法、报错与版本事实 | 自定架构方案(转 arch) |
|
|
17
17
|
| **qa** | 测试策略、工具与业界用例惯例 | 替代 RD 定实现 |
|
|
18
18
|
|
|
19
|
-
- PM 命中「不可调研」任一项 → **停止检索,转 arch**(见
|
|
19
|
+
- PM 命中「不可调研」任一项 → **停止检索,转 arch**(见 @.agents/skills/pm-routing.md §二)。
|
|
20
20
|
- 不确定是否越界 → **按越界处理**。
|
|
21
21
|
- 外部做法一律是**参考与候选**,不是既定前提;**技术前提只能由 arch 以 ADR 定**。
|
|
22
22
|
|
|
@@ -12,6 +12,15 @@ description: 按需技能——用例设计、缺陷管理、性能安全、DDD
|
|
|
12
12
|
- **场景法**:主路径 + 异常 + 边界
|
|
13
13
|
- **错误推测**:并发、超时、断网、重复提交、空值、超长输入
|
|
14
14
|
|
|
15
|
+
## 反事实守卫(把静态推断升级为实测结论)
|
|
16
|
+
|
|
17
|
+
当要验证的是「实现/契约的静态推断」而非现成行为时,用**两段式探针**,避免只在单侧证据上下结论:
|
|
18
|
+
|
|
19
|
+
- **S1 实际链路**:用**真实现 + 真编排**(如真 repo + 真 SyncService)跑端到端;mock 只复现**外部系统**(服务端/网络)的**真源语义**——引源码行号,不复制逻辑;有源码仓时优先临时单测直调真源函数(如 Go converter),测后删除。
|
|
20
|
+
- **S2 反事实分支**:独立断言「假设推断成立」时的后果。两段合读才能区分:**命题为假** / **命题为真** / **命题的前置在上游先行被阻断**(最常见误区:推断的上游前提不成立)。
|
|
21
|
+
- **守卫化**:S2 保留为**反事实守卫**(当前绿)——修复后它应**转红**,届时改为**正向不变量**(如「往返后必须命中」)并在文件头注明;放 `__tests__/**`,红/绿分列说明。
|
|
22
|
+
- **绑定版本**:结论显式绑定被测外部系统版本(commit);版本一变结论可能翻转(如服务端对齐修法)。
|
|
23
|
+
|
|
15
24
|
## DDD 分层对齐表
|
|
16
25
|
|
|
17
26
|
| 层 | 测试方式 | 覆盖内容 |
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 给你的 AI 编程助手配一支「开发小队」:你提需求,它自己找人干活、自己验收交付。
|
|
4
4
|
|
|
5
|
-
nao-skills 是一套 **角色卡 + 协作规矩 + 小工具**。装上以后,你的项目里会有
|
|
5
|
+
nao-skills 是一套 **角色卡 + 协作规矩 + 小工具**。装上以后,你的项目里会有 6 个分工明确的 AI 会话——产品经理负责拆需求、派活、验收;前端、后端、测试各自干活;工程基座管构建发布与治理。你只做两件事:**回答问题**、**点确认**。
|
|
6
6
|
|
|
7
7
|
基于 [pi](https://pi.dev)。Made by [Nathan Lee](https://github.com/nathan)。
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ nao-skills 是一套 **角色卡 + 协作规矩 + 小工具**。装上以后,
|
|
|
12
12
|
|
|
13
13
|
| 毛病 | nao-skills 的做法 |
|
|
14
14
|
| --- | --- |
|
|
15
|
-
| 一个会话什么都干,聊到后面就糊涂了 | 拆成
|
|
15
|
+
| 一个会话什么都干,聊到后面就糊涂了 | 拆成 6 个专业角色,各管一段,互不干扰 |
|
|
16
16
|
| 需求没问清楚就开写,写完发现不对 | PM 先追问、写清 PRD 和目标,你点头才开工 |
|
|
17
17
|
| 干完不知道算不算干完 | 每个任务必须「回执」并按清单自查,PM 逐条验收才归档 |
|
|
18
18
|
|
|
@@ -33,7 +33,7 @@ nao-skill plugins install-all # 装协作插件(intercom 等),多会话
|
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
35
|
.agents/scripts/nao-fleet.sh check # 体检:一行 OK 就能开工
|
|
36
|
-
.agents/scripts/nao-fleet.sh ensure pm arch rd-fe rd-be qa # 拉起
|
|
36
|
+
.agents/scripts/nao-fleet.sh ensure pm arch rd-fe rd-be qa rd-infra # 拉起 6 个角色会话
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
然后**在 PM 会话里说需求**就行,剩下的它自己流转。大概长这样:
|
|
@@ -49,17 +49,29 @@ PM:在 PR 核 AC 并评论 → 验收通过(授权合并)→ rd-be squash
|
|
|
49
49
|
|
|
50
50
|
每个任务完成后都能在 `docs/` 里找到痕迹(PRD、任务状态、回报),不依赖聊天记录——所以会话聊久了可以直接重开,不会丢事。
|
|
51
51
|
|
|
52
|
-
##
|
|
52
|
+
## 六个角色
|
|
53
53
|
|
|
54
54
|
| 角色 | 帮你干什么 |
|
|
55
55
|
| --- | --- |
|
|
56
56
|
| 产品经理 | 澄清需求、**调研同类产品形态**、写 PRD、定优先级、派活、验收、归档、**管 Issue 与发版**(PR 验收评论、tag/release,优先 `gh`;唯一的调度者,不写代码、不合并) |
|
|
57
57
|
| 架构师 | 技术选型与方案评审(**对照业界成熟模式**并写明为何不采纳),出 ADR,评影响面 |
|
|
58
58
|
| 前端 / 后端研发 | 各自实现,写完自己跑测试再回执 |
|
|
59
|
+
| 工程基座 | CI 与流水线、构建/发布工具、脚本与守卫、依赖与安全告警升级、仓库治理 |
|
|
59
60
|
| 测试工程师 | 按验收标准出用例、跑门禁、报缺陷 |
|
|
60
61
|
|
|
61
62
|
不想要哪个角色,删掉对应文件即可;只留 PM + 一个研发角色也能跑。
|
|
62
63
|
|
|
64
|
+
## 可选:关键节点 QQ 主动推送
|
|
65
|
+
|
|
66
|
+
装上 [`pi-agent-qqbot`](https://github.com/Nathan3303) 并配好 `~/.pi/agent/pi-agent-qqbot.json`(`appId` / `clientSecret` / `ownerOpenId` / `sandbox`)后,PM 可把**关键节点**主动推到你 QQ——批次进度汇总、验收结论、发版或合并完成、异常阻塞:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
.agents/scripts/qq-notify "[T301] 验收通过 · PR #4 已合并 · v0.8.0"
|
|
70
|
+
.agents/scripts/qq-notify --dry-run "连通性自检" # 只取 token、不发送
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**为什么默认不主动打扰**:这是**可选能力**——没配 `pi-agent-qqbot` 就自动视为未启用;四类节点之外不发,一个节点最多一条(禁刷屏);推送失败只记回执行风险项,**不阻断交付**。默认走 sandbox(测试环境),正式发布通知才考虑 prod。能力边界、退出码与纪律见 `.agents/skills/qq-notify.md`。
|
|
74
|
+
|
|
63
75
|
## 为什么它省 token(这是本项目的核心追求)
|
|
64
76
|
|
|
65
77
|
和 AI 聊天,钱花在「它每轮要读多少字」。所以:
|
|
@@ -81,6 +93,8 @@ PM:在 PR 核 AC 并评论 → 验收通过(授权合并)→ rd-be squash
|
|
|
81
93
|
|
|
82
94
|
**跑完的会话要留着吗?** 后台派生会话验收完可以回收:`nao-fleet.sh close --task T1 rd-be`;常驻角色留着复用。`nao-fleet.sh status` 会提示哪些是残留。
|
|
83
95
|
|
|
96
|
+
**终端窗口太挤怎么办?** tmux 宿主支持三种布局:默认 `main-row2`(主会话占左,其余往右分列)、`main-col`(其余在右列竖排)、`grid`(等大网格)。窗口太窄时 `main-row2` 会**自动回退** `main-col`、仍不够再退 `grid`,并打印带数字的提示(不中断拉起);用 `NAO_TMUX_LAYOUT` / `NAO_TMUX_MAIN_WIDTH` / `NAO_TMUX_MIN_PANE_WIDTH` 调整,详见 `bash .agents/scripts/nao-fleet.sh --help`。
|
|
97
|
+
|
|
84
98
|
**它会自己上网查吗?** 会,但有分工:产品经理查同类功能的**产品形态**(有哪些功能点、属性、视图、协作方式),架构师对照**业界成熟的架构模式**。两者都必须把来源和「适不适用本项目」落成文件(`docs/research/`),抓来的网页内容不会堆进聊天记录;技术选型仍然只由架构师定。
|
|
85
99
|
|
|
86
100
|
**装完之后项目里多了什么?**
|
|
@@ -90,7 +104,7 @@ PM:在 PR 核 AC 并评论 → 验收通过(授权合并)→ rd-be squash
|
|
|
90
104
|
├── AGENTS.md # 项目级约定(PM 维护,AI 每次都会读)
|
|
91
105
|
├── .github/ # PR 模板(PM 建,GitHub Flow 用)
|
|
92
106
|
└── .agents/
|
|
93
|
-
├── prompts/ #
|
|
107
|
+
├── prompts/ # 6 张角色卡
|
|
94
108
|
├── common/ # 协作规矩、输出与回执规范
|
|
95
109
|
├── checklists/ # 各角色红线 / 交付清单 / 回执模板(按需读)
|
|
96
110
|
├── skills/ # 按需技能(DDD 细节、代码定位、提交规范…)
|
package/bin/nao-skill.js
CHANGED
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
* - 目标项目无 .agents/ → 完整复制
|
|
12
12
|
* - 已有 .agents/ → 合并:同名条目保留项目既有(--force 则备份后覆盖)
|
|
13
13
|
* - 无 AGENTS.md → 由模板生成(项目级上下文,PM 维护)
|
|
14
|
-
* - 已有 AGENTS.md → 提示按 PM 卡 §七 合并规则手动追加,不自动覆盖
|
|
14
|
+
* - 已有 AGENTS.md → 提示按 PM 卡 §七 / skills/pm-operations.md §四 合并规则手动追加,不自动覆盖
|
|
15
15
|
*
|
|
16
16
|
* 使用(安装后)
|
|
17
|
-
* bash .agents/scripts/nao-fleet.sh check / ensure arch rd-fe
|
|
17
|
+
* bash .agents/scripts/nao-fleet.sh check / ensure arch rd-fe rd-be qa rd-infra
|
|
18
18
|
* 角色卡经 fleet 拉起时 --append-system-prompt 注入;checklists 按需读取
|
|
19
19
|
*/
|
|
20
20
|
import { execFileSync } from 'node:child_process';
|
|
@@ -174,14 +174,14 @@ function install(target, force, verbose) {
|
|
|
174
174
|
writeFileSync(agentsMd, tpl);
|
|
175
175
|
log('已生成 AGENTS.md(项目级上下文,PM 维护;保持精简 <120 行)。');
|
|
176
176
|
} else {
|
|
177
|
-
warn('AGENTS.md 已存在:按 PM 卡 §七 合并规则手动追加「nao 舰队接入」区块,勿覆盖原文。');
|
|
177
|
+
warn('AGENTS.md 已存在:按 PM 卡 §七 / skills/pm-operations.md §四 合并规则手动追加「nao 舰队接入」区块,勿覆盖原文。');
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
log('');
|
|
181
181
|
log('✔ 安装完成。下一步:');
|
|
182
182
|
log(' bash .agents/scripts/nao-fleet.sh check # 体检');
|
|
183
183
|
log(' bash .agents/scripts/nao-fleet.sh status # 在线状态');
|
|
184
|
-
log(' bash .agents/scripts/nao-fleet.sh ensure arch rd-fe rd-be qa # 拉起舰队');
|
|
184
|
+
log(' bash .agents/scripts/nao-fleet.sh ensure arch rd-fe rd-be qa rd-infra # 拉起舰队');
|
|
185
185
|
log(' 角色卡:.agents/prompts/ · 技能:.agents/skills/ · 交付清单:.agents/checklists/');
|
|
186
186
|
}
|
|
187
187
|
|