scrum-skills 0.1.0 → 0.1.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.
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scrum-meta
|
|
3
|
+
description: 维护与演进 .agents/skills/ 下的敏捷 skill 体系本身。当用户要求修改 skill 规则、引入外部 skill(如 superpowers)、调整工作流纪律时使用。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# scrum-meta:skill 体系自身的演进
|
|
7
|
+
|
|
8
|
+
## 何时使用
|
|
9
|
+
|
|
10
|
+
用户要求:改某个 skill 的规则/工作流、从外部 skill 合集(superpowers、openspec 等)吸收能力、新增或删除 skill 时。
|
|
11
|
+
|
|
12
|
+
## 核心纪律(对 skill 文件本身也用敏捷的规矩)
|
|
13
|
+
|
|
14
|
+
1. **先说清动机再动手**:改 skill 前用 1~2 轮对话确认要解决的问题是什么(brainstorming 同款:一次问一个问题)。没有真实痛点不动 skill,禁止为改而改。
|
|
15
|
+
2. **修改必须最小且可追溯**:每个改动能回答「这解决什么问题」;改动后向用户复述改了哪些文件哪些节。
|
|
16
|
+
3. **引入外部 skill 前先做映射判断**:
|
|
17
|
+
- 现有 skill(planning/sprint/tdd/report/meta)已覆盖的能力 → 不引入,说明理由即可
|
|
18
|
+
- 有增量价值且与「数据落库、状态机、TDD」纪律兼容 → 融入对应 skill 的相应小节(不新建文件),标注出处
|
|
19
|
+
- 需要独立工作流的才新建 skill 文件
|
|
20
|
+
4. **skill 不膨胀**:每个 SKILL.md 控制在可一屏读完;超了就往 references/ 拆细节。
|
|
21
|
+
5. **同步索引**:改动影响能力边界时,同步更新 `AGENTS.md` 的 skill 索引行和该 skill 的 frontmatter description。
|
|
22
|
+
|
|
23
|
+
## 工作流
|
|
24
|
+
|
|
25
|
+
1. 读当前各 skill + `AGENTS.md`,确认现状
|
|
26
|
+
2. 与用户确认动机与落点(哪个 skill 哪一节 / 新建)
|
|
27
|
+
3. 实施修改(局部编辑,禁止整文件重写)
|
|
28
|
+
4. 若修改了敏捷数据规范(schema/状态机),运行 `node tools/validate.mjs` 确认未破坏
|
|
29
|
+
5. 复述:改动清单 + 每项对应的动机 + 落索引情况
|
|
30
|
+
|
|
31
|
+
## 反模式(禁止)
|
|
32
|
+
|
|
33
|
+
- 不经确认大改 skill 工作流
|
|
34
|
+
- 照抄外部 skill 全文(只吸收方法,用本项目语言重写,标注出处)
|
|
35
|
+
- 新增 skill 却不更新 AGENTS.md 索引
|
|
36
|
+
- 把 skill 改成与本体系冲突的纪律(如允许对话结论不落库)
|
|
@@ -12,9 +12,20 @@ description: 将用户与 AI 的需求对话约束为结构化敏捷数据(Epi
|
|
|
12
12
|
## 核心纪律
|
|
13
13
|
|
|
14
14
|
1. **禁止只聊天**。凡是确认要做的事,必须落成 `agile/backlog/` 下的 JSON 文件(schema 见 `references/schema.md`)。对话中的口头结论不算数,文件才算数。
|
|
15
|
-
2. **禁止凭空拆解**。写 story
|
|
15
|
+
2. **禁止凭空拆解**。写 story 前先走「澄清」流程(见下节),澄清结果写进
|
|
16
|
+
`narrative` 和 `acceptance`,不许留在对话记录里。
|
|
16
17
|
3. **数据是唯一事实来源**。后续实施只读文件不读对话记忆。AI 每次接手必须先读 `agile/` 下相关 JSON,再决定下一步。
|
|
17
18
|
|
|
19
|
+
## 需求澄清(brainstorming,源自 superpowers)
|
|
20
|
+
|
|
21
|
+
在写任何 story 之前执行,把模糊想法聊成清晰需求:
|
|
22
|
+
|
|
23
|
+
1. **一次只问一个问题**,一次抛一堆问题等于没问。优先问影响验收的:这东西做完了怎么算成功?谁在用?边界在哪?
|
|
24
|
+
2. **不替用户做主**:澄清中给出选项和各自的代价,让用户拍板;用户的原话是最高优先级输入。
|
|
25
|
+
3. **至少考虑两条路**:写 story 前在内心比对过至少两种实现/拆解方案,被放弃的那条如果用户可能关心,用一句话带过取舍理由(可写进 narrative 或讨论中)。
|
|
26
|
+
4. **适时收敛**:问题问清了就停,不许无限澄清;宁可拆成两个 story 也不做一个大而全的。
|
|
27
|
+
5. **结论即时落库**:每澄清出一个结论,当场更新到对应 story JSON(`narrative`/`acceptance`/`points`),禁止聊完再统一补。
|
|
28
|
+
|
|
18
29
|
## 工作流
|
|
19
30
|
|
|
20
31
|
1. **定位层级**:判断这是 Epic(多迭代的大目标)还是 Story(一个迭代内可交付的用户价值)。拿不准就先建 Epic,Story 留空壳。
|
|
@@ -37,11 +37,21 @@ description: 以 red-green-refactor TDD 循环驱动任务实施,并把每一
|
|
|
37
37
|
2. 有实质重构才写 journal entry(`phase:"refactor"`),零碎小改可并入 green entry
|
|
38
38
|
3. task 状态 → `refactored`(无实质重构可直接 `green → done`)
|
|
39
39
|
|
|
40
|
-
### ✅ Done
|
|
40
|
+
### ✅ Done(交工前真实验证,源自 superpowers 的 verification-before-completion)
|
|
41
41
|
|
|
42
42
|
1. 最后一次全量测试通过,journal 末条 `result` 为 `pass`
|
|
43
|
-
2.
|
|
44
|
-
3.
|
|
43
|
+
2. **不许凭断言关单**:「测试过了应该没问题」不算验证。有可运行入口的(页面/命令/接口)要真正跑起来按 acceptance 逐条核一遍;跑不了的也要向用户说明「验证到了哪一步、哪一步没法验」
|
|
44
|
+
3. task 状态 → `done`,`files` 补齐本次触碰的文件
|
|
45
|
+
4. story 的所有 task 都 done → 提醒用户进入 review(scrum-sprint skill)
|
|
46
|
+
|
|
47
|
+
## 排障纪律(源自 superpowers 的 systematic-debugging)
|
|
48
|
+
|
|
49
|
+
测试意外失败、行为不符预期、bug 排查时:
|
|
50
|
+
|
|
51
|
+
1. **先读代码找根因,禁止试错式盲改**——不许靠猜着改再跑一遍碰运气
|
|
52
|
+
2. 四步走:读相关代码形成假设 → 加观测(日志/断点/最小复现)验证假设 → 确认根因 → 做最小修复
|
|
53
|
+
3. 修 bug 也走 TDD:先写一个复现该 bug 的失败测试(red),修复让它转绿(green)
|
|
54
|
+
4. 根因和假设写进 journal 的 `note`(为什么改这里),修复过程照常落 entry
|
|
45
55
|
|
|
46
56
|
## Journal 纪律
|
|
47
57
|
|
|
@@ -55,3 +65,4 @@ description: 以 red-green-refactor TDD 循环驱动任务实施,并把每一
|
|
|
55
65
|
- 测试骗不过状态机:`done` 的 task journal 末条必须 pass
|
|
56
66
|
- 不许为让测试通过而删除/跳过/弱化测试;确需改测试时在 journal 的 note 里说明理由
|
|
57
67
|
- 一次只做一个 task 的一个阶段,禁止批量推进多个 task 状态
|
|
68
|
+
- 交工前没做真实验证不许把 task 置 `done`(跑不起入口的须在 journal 里写明验证边界)
|
package/bin/scrum-skills.mjs
CHANGED
|
@@ -5,20 +5,26 @@
|
|
|
5
5
|
* scrum-skills init [dir] [--force] 初始化敏捷开发文件到目标文件夹(默认当前目录)
|
|
6
6
|
* 拷贝 .agents/skills/、AGENTS.md、tools/validate.mjs,建 agile/ 目录骨架
|
|
7
7
|
* 已存在 AGENTS.md 时拒绝,--force 覆盖(备份为 AGENTS.md.bak)
|
|
8
|
+
* scrum-skills update [dir] [--dry-run] 更新已初始化项目里的 skill 文件(.agents/、AGENTS.md、tools/validate.mjs)
|
|
9
|
+
* 只刷新框架文件,绝不碰 agile/ 数据;每个被覆盖的文件先备份为 *.bak
|
|
10
|
+
* 内容相同的不动;--dry-run 只报告不写入
|
|
8
11
|
* scrum-skills serve [dir] [port] 启动可视化 server(默认当前目录,端口 4173)
|
|
9
12
|
* scrum-skills validate [dir] 校验敏捷数据(默认当前目录)
|
|
10
13
|
*/
|
|
11
|
-
import { cpSync, mkdirSync, existsSync, writeFileSync, readFileSync } from 'node:fs';
|
|
12
|
-
import { join, resolve } from 'node:path';
|
|
14
|
+
import { cpSync, mkdirSync, existsSync, writeFileSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
15
|
+
import { join, resolve, dirname } from 'node:path';
|
|
13
16
|
import { spawnSync } from 'node:child_process';
|
|
14
17
|
import { fileURLToPath } from 'node:url';
|
|
15
18
|
|
|
16
19
|
const PKG_ROOT = fileURLToPath(new URL('..', import.meta.url));
|
|
20
|
+
const VERSION = JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf8')).version;
|
|
17
21
|
const HELP = `用法:
|
|
18
22
|
scrum-skills init [dir] [--force] 初始化敏捷开发文件到目标文件夹(默认当前目录)
|
|
23
|
+
scrum-skills update [dir] [--dry-run] 更新已初始化项目的 skill 框架文件(不碰 agile/ 数据)
|
|
19
24
|
scrum-skills serve [dir] [port] 启动可视化 server(默认当前目录,端口 4173)
|
|
20
25
|
scrum-skills validate [dir] 校验敏捷数据(默认当前目录)
|
|
21
|
-
scrum-skills help
|
|
26
|
+
scrum-skills help 显示本帮助
|
|
27
|
+
scrum-skills -v / --version 显示版本号`;
|
|
22
28
|
|
|
23
29
|
const [cmd, ...args] = process.argv.slice(2);
|
|
24
30
|
const fail = msg => { console.error(`✘ ${msg}`); process.exit(1); };
|
|
@@ -55,6 +61,60 @@ function init() {
|
|
|
55
61
|
3. 看板/燃尽图: 在目标项目目录下运行 scrum-skills serve`);
|
|
56
62
|
}
|
|
57
63
|
|
|
64
|
+
function walkFiles(dir, base = '') {
|
|
65
|
+
const out = [];
|
|
66
|
+
for (const name of readdirSync(dir)) {
|
|
67
|
+
const abs = join(dir, name);
|
|
68
|
+
const rel = base ? `${base}/${name}` : name;
|
|
69
|
+
if (statSync(abs).isDirectory()) out.push(...walkFiles(abs, rel));
|
|
70
|
+
else out.push({ abs, rel });
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** 收集要更新的框架文件:相对路径 → 源绝对路径 */
|
|
76
|
+
function frameworkFiles() {
|
|
77
|
+
const out = [];
|
|
78
|
+
for (const f of walkFiles(join(PKG_ROOT, '.agents')))
|
|
79
|
+
out.push({ rel: `.agents/${f.rel}`, src: f.abs });
|
|
80
|
+
out.push({ rel: 'AGENTS.md', src: join(PKG_ROOT, 'templates/AGENTS.md') });
|
|
81
|
+
out.push({ rel: 'tools/validate.mjs', src: join(PKG_ROOT, 'tools/validate.mjs') });
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function update() {
|
|
86
|
+
const dryRun = args.includes('--dry-run');
|
|
87
|
+
const dir = resolve(args.find(a => !a.startsWith('--')) || process.cwd());
|
|
88
|
+
if (!existsSync(dir)) fail(`目录不存在: ${dir}`);
|
|
89
|
+
if (!existsSync(join(dir, '.agents')) && !existsSync(join(dir, 'AGENTS.md')))
|
|
90
|
+
fail(`${dir} 未初始化(找不到 .agents/ 与 AGENTS.md)。请先运行: scrum-skills init`);
|
|
91
|
+
|
|
92
|
+
const added = [], changed = [], same = [];
|
|
93
|
+
for (const { rel, src } of frameworkFiles()) {
|
|
94
|
+
const dest = join(dir, rel);
|
|
95
|
+
const srcText = readFileSync(src, 'utf8');
|
|
96
|
+
if (!existsSync(dest)) { added.push(rel); if (!dryRun) {
|
|
97
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
98
|
+
writeFileSync(dest, srcText);
|
|
99
|
+
} continue; }
|
|
100
|
+
if (readFileSync(dest, 'utf8') === srcText) { same.push(rel); continue; }
|
|
101
|
+
changed.push(rel);
|
|
102
|
+
if (!dryRun) {
|
|
103
|
+
cpSync(dest, `${dest}.bak`); // 覆盖前备份,本地自定义不丢
|
|
104
|
+
writeFileSync(dest, srcText);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const verb = dryRun ? '(dry-run,未写入)' : '';
|
|
109
|
+
console.log(`✔ skill 框架文件更新完成${verb} — ${dir}
|
|
110
|
+
- 新增 ${added.length} 个${added.length ? `: ${added.join(', ')}` : ''}
|
|
111
|
+
- 更新 ${changed.length} 个(旧文件备份为 *.bak)${changed.length ? `: ${changed.join(', ')}` : ''}
|
|
112
|
+
- 未变 ${same.length} 个
|
|
113
|
+
- agile/ 数据目录未做任何改动
|
|
114
|
+
|
|
115
|
+
${changed.length ? '提示: 更新会覆盖目标项目里对框架文件的本地修改(已备份 *.bak)。若目标项目自定义过 skill,请 diff 对比后手动合并。' : '已是最新,无需操作。'}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
58
118
|
function runTool(script, needPort) {
|
|
59
119
|
const positional = args.filter(a => !a.startsWith('--'));
|
|
60
120
|
const dir = resolve(positional[0] || process.cwd());
|
|
@@ -68,10 +128,13 @@ function runTool(script, needPort) {
|
|
|
68
128
|
|
|
69
129
|
switch (cmd) {
|
|
70
130
|
case 'init': init(); break;
|
|
131
|
+
case 'update': update(); break;
|
|
71
132
|
case 'serve': runTool('serve.mjs', true); break;
|
|
72
133
|
case 'validate': runTool('validate.mjs', false); break;
|
|
73
134
|
case 'help': case '-h': case '--help': case undefined:
|
|
74
135
|
console.log(HELP); break;
|
|
136
|
+
case '-v': case '--version': case 'version':
|
|
137
|
+
console.log(VERSION); break;
|
|
75
138
|
default:
|
|
76
139
|
console.error(`未知命令: ${cmd}\n`);
|
|
77
140
|
console.log(HELP);
|