@haaaiawd/anws 2.2.0 → 2.2.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.
Files changed (28) hide show
  1. package/README.md +180 -343
  2. package/bin/cli.js +112 -112
  3. package/lib/changelog.js +258 -258
  4. package/lib/copy.js +116 -109
  5. package/lib/diff.js +11 -0
  6. package/lib/manifest.js +4 -1
  7. package/lib/update.js +319 -319
  8. package/package.json +4 -3
  9. package/templates/.agents/skills/anws-system/SKILL.md +9 -7
  10. package/templates/.agents/skills/code-reviewer/SKILL.md +102 -327
  11. package/templates/.agents/skills/concept-modeler/SKILL.md +19 -17
  12. package/templates/.agents/skills/craft-authoring/SKILL.md +123 -0
  13. package/templates/.agents/skills/e2e-testing-guide/SKILL.md +59 -0
  14. package/templates/.agents/skills/system-designer/SKILL.md +6 -6
  15. package/templates/.agents/skills/system-designer/references/system-design-template.md +17 -17
  16. package/templates/.agents/skills/task-planner/SKILL.md +113 -113
  17. package/templates/.agents/skills/task-planner/references/TASK_TEMPLATE.md +82 -82
  18. package/templates/.agents/workflows/blueprint.md +284 -284
  19. package/templates/.agents/workflows/challenge.md +450 -491
  20. package/templates/.agents/workflows/change.md +263 -286
  21. package/templates/.agents/workflows/craft.md +243 -664
  22. package/templates/.agents/workflows/design-system.md +624 -624
  23. package/templates/.agents/workflows/explore.md +400 -371
  24. package/templates/.agents/workflows/forge.md +444 -413
  25. package/templates/.agents/workflows/genesis.md +342 -395
  26. package/templates/.agents/workflows/probe.md +21 -16
  27. package/templates/.agents/workflows/quickstart.md +123 -138
  28. package/templates/AGENTS.md +149 -134
package/lib/changelog.js CHANGED
@@ -1,258 +1,258 @@
1
- 'use strict';
2
-
3
- const fs = require('node:fs/promises');
4
- const path = require('node:path');
5
- const { groupChanges } = require('./diff');
6
-
7
- const README_CONTENT = `# ⚠️ 重要:请勿删除此目录!
8
-
9
- ## 这个目录是做什么的?
10
-
11
- \`.anws/changelog/\` 存放每次 \`anws update\` 生成的升级记录。
12
-
13
- ## 为什么不能删除?
14
-
15
- 1. **AI 升级判断依赖此目录**
16
- - AI 需要读取历史升级记录来判断变更级别
17
- - 删除后将导致 AI 无法准确判断升级影响
18
-
19
- 2. **业务文档升级的依据**
20
- - 升级记录包含详细的变更详情
21
- - AI 根据这些记录升级你的业务文档
22
-
23
- 3. **回滚依据**
24
- - 如果升级出现问题,可以参考历史记录回滚
25
-
26
- ## 文件命名规则
27
-
28
- - \`v1.4.0.md\` - v1.4.0 的变更记录
29
- - \`v1.5.0.md\` - v1.5.0 的变更记录
30
-
31
- ## 可以删除单个文件吗?
32
-
33
- **不建议**。每个文件都是升级历史的一部分,删除会影响 AI 的判断能力。
34
- `;
35
-
36
- async function ensureChangelogDir(cwd) {
37
- const changelogDir = path.join(cwd, '.anws', 'changelog');
38
- await fs.mkdir(changelogDir, { recursive: true });
39
- await fs.writeFile(path.join(changelogDir, '.gitkeep'), '', 'utf8');
40
- await fs.writeFile(path.join(changelogDir, 'README.md'), README_CONTENT, 'utf8');
41
- return changelogDir;
42
- }
43
-
44
- function compareSemver(a, b) {
45
- const aParts = String(a).split('.').map((item) => Number(item));
46
- const bParts = String(b).split('.').map((item) => Number(item));
47
-
48
- for (let index = 0; index < 3; index += 1) {
49
- const left = aParts[index] || 0;
50
- const right = bParts[index] || 0;
51
- if (left > right) return 1;
52
- if (left < right) return -1;
53
- }
54
-
55
- return 0;
56
- }
57
-
58
- function normalizeText(text) {
59
- return String(text || '').replace(/\r\n/g, '\n');
60
- }
61
-
62
- function buildMergedChangeKey(item) {
63
- const canonicalPath = item.source || item.file;
64
- const summaryKey = JSON.stringify(item.summary || []);
65
- const contentKey = item.type === 'modified'
66
- ? summaryKey
67
- : `${normalizeText(item.oldContent)}\n<<<ANWS_CHANGE_SPLIT>>>\n${normalizeText(item.newContent)}`;
68
-
69
- return `${item.type}::${canonicalPath}::${item.resourceId || canonicalPath}::${contentKey}`;
70
- }
71
-
72
- function mergeChanges(changes) {
73
- const grouped = new Map();
74
-
75
- for (const item of changes) {
76
- const canonicalPath = item.source || item.file;
77
- const key = buildMergedChangeKey(item);
78
- const affectedFile = {
79
- file: item.file,
80
- targetId: item.targetId || null,
81
- targetLabel: item.targetLabel || null
82
- };
83
-
84
- if (!grouped.has(key)) {
85
- grouped.set(key, {
86
- ...item,
87
- canonicalPath,
88
- affectedFiles: [affectedFile]
89
- });
90
- continue;
91
- }
92
-
93
- const current = grouped.get(key);
94
- current.affectedFiles.push(affectedFile);
95
- }
96
-
97
- return Array.from(grouped.values()).map((item) => ({
98
- ...item,
99
- affectedFiles: item.affectedFiles.sort((left, right) => left.file.localeCompare(right.file))
100
- }));
101
- }
102
-
103
- function formatAffectedTargetList(item) {
104
- const targets = Array.from(new Set(
105
- item.affectedFiles
106
- .filter((entry) => entry.targetId && entry.targetLabel)
107
- .map((entry) => `${entry.targetLabel} (${entry.targetId})`)
108
- ));
109
-
110
- return targets.length > 0 ? targets.join(', ') : '无';
111
- }
112
-
113
- function formatFileList(title, items) {
114
- const lines = [`### ${title}`];
115
- if (items.length === 0) {
116
- lines.push('- 无');
117
- return lines.join('\n');
118
- }
119
-
120
- for (const item of items) {
121
- lines.push(`- \`${item.canonicalPath}\` — 影响 Targets: ${formatAffectedTargetList(item)}`);
122
- for (const affectedFile of item.affectedFiles) {
123
- lines.push(` - \`${affectedFile.file}\``);
124
- }
125
- }
126
-
127
- return lines.join('\n');
128
- }
129
-
130
- function formatDetail(item) {
131
- if (item.type === 'added') {
132
- return [
133
- `### \`${item.canonicalPath}\``,
134
- `- **影响 Targets**: ${formatAffectedTargetList(item)}`,
135
- '- **影响文件**:',
136
- ...item.affectedFiles.map((entry) => ` - \`${entry.file}\``),
137
- '- **新增文件**',
138
- '- **说明**: 该文件在旧版本中不存在,因此无前后逐行对比。'
139
- ].join('\n');
140
- }
141
-
142
- if (item.type === 'deleted') {
143
- return [
144
- `### \`${item.canonicalPath}\``,
145
- `- **影响 Targets**: ${formatAffectedTargetList(item)}`,
146
- '- **影响文件**:',
147
- ...item.affectedFiles.map((entry) => ` - \`${entry.file}\``),
148
- '- **删除文件**',
149
- '- **说明**: 该文件在新版本中不存在,因此无前后逐行对比。'
150
- ].join('\n');
151
- }
152
-
153
- if (item.summary.length === 0) {
154
- return [
155
- `### \`${item.canonicalPath}\``,
156
- `- **影响 Targets**: ${formatAffectedTargetList(item)}`,
157
- '- **影响文件**:',
158
- ...item.affectedFiles.map((entry) => ` - \`${entry.file}\``),
159
- '- **说明**: 检测到内容变更,但未能提取到摘要。'
160
- ].join('\n');
161
- }
162
-
163
- return [
164
- `### \`${item.canonicalPath}\``,
165
- `- **影响 Targets**: ${formatAffectedTargetList(item)}`,
166
- '- **影响文件**:',
167
- ...item.affectedFiles.map((entry) => ` - \`${entry.file}\``),
168
- '```diff',
169
- ...item.summary.flatMap((pair) => [
170
- `- [old:${pair.oldLineNumber === null ? '-' : pair.oldLineNumber}] ${pair.oldText}`,
171
- `+ [new:${pair.newLineNumber === null ? '-' : pair.newLineNumber}] ${pair.newText}`
172
- ]),
173
- '```'
174
- ].join('\n');
175
- }
176
-
177
- async function generateChangelog({ cwd, version, changes, targetSummary = null }) {
178
- const changelogDir = await ensureChangelogDir(cwd);
179
- const mergedChanges = mergeChanges(changes);
180
- const grouped = groupChanges(mergedChanges);
181
- const now = new Date();
182
- const timestamp = now.toISOString().replace('T', ' ').slice(0, 19);
183
- const filePath = path.join(changelogDir, `v${version}.md`);
184
-
185
- const content = [
186
- `# 升级记录: v${version}`,
187
- '',
188
- '> ⚠️ **此文件由 `anws update` 自动生成,请勿删除!**',
189
- '> 删除后将导致 AI 无法获取历史升级信息,影响后续升级判断。',
190
- '',
191
- '## 元信息',
192
- `- **升级版本**: ${version}`,
193
- `- 升级时间: ${timestamp}`,
194
- '- **升级类型**: 由 `/upgrade` 工作流判断 (Minor/Major)',
195
- ...(targetSummary
196
- ? [
197
- `- **成功 Targets**: ${targetSummary.successfulTargets.length > 0 ? targetSummary.successfulTargets.join(', ') : '无'}`,
198
- `- **失败 Targets**: ${targetSummary.failedTargets.length > 0 ? targetSummary.failedTargets.join(', ') : '无'}`
199
- ]
200
- : []),
201
- '',
202
- '## 变更摘要',
203
- `- 新增文件: ${grouped.added.length}`,
204
- `- 修改文件: ${grouped.modified.length}`,
205
- `- 删除文件: ${grouped.deleted.length}`,
206
- '',
207
- '## 文件级变更清单',
208
- '',
209
- formatFileList('新增文件', grouped.added),
210
- '',
211
- formatFileList('修改文件', grouped.modified),
212
- '',
213
- formatFileList('删除文件', grouped.deleted),
214
- '',
215
- '## 内容级变更详情',
216
- '',
217
- ...(mergedChanges.length > 0 ? mergedChanges.map(formatDetail).flatMap((section) => [section, '']) : ['- 无变更', ''])
218
- ].join('\n');
219
-
220
- await fs.writeFile(filePath, content, 'utf8');
221
- return filePath;
222
- }
223
-
224
- async function detectUpgrade({ cwd, version }) {
225
- const changelogDir = path.join(cwd, '.anws', 'changelog');
226
-
227
- try {
228
- const files = await fs.readdir(changelogDir);
229
- const latest = files
230
- .filter((name) => /^v\d+\.\d+\.\d+\.md$/.test(name))
231
- .sort((left, right) => {
232
- const leftVersion = left.slice(1, -3);
233
- const rightVersion = right.slice(1, -3);
234
- return compareSemver(leftVersion, rightVersion);
235
- })
236
- .pop();
237
-
238
- if (!latest) {
239
- return { needUpgrade: true, fromVersion: null, toVersion: version, latestVersion: null };
240
- }
241
-
242
- const fromVersion = latest.slice(1, -3);
243
- return {
244
- needUpgrade: compareSemver(fromVersion, version) !== 0,
245
- fromVersion,
246
- toVersion: version,
247
- latestVersion: fromVersion
248
- };
249
- } catch {
250
- return { needUpgrade: true, fromVersion: null, toVersion: version, latestVersion: null };
251
- }
252
- }
253
-
254
- module.exports = {
255
- detectUpgrade,
256
- ensureChangelogDir,
257
- generateChangelog
258
- };
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { groupChanges } = require('./diff');
6
+
7
+ const README_CONTENT = `# ⚠️ 重要:请勿删除此目录!
8
+
9
+ ## 这个目录是做什么的?
10
+
11
+ \`.anws/changelog/\` 存放每次 \`anws update\` 生成的升级记录。
12
+
13
+ ## 为什么不能删除?
14
+
15
+ 1. **AI 升级判断依赖此目录**
16
+ - AI 需要读取历史升级记录来判断变更级别
17
+ - 删除后将导致 AI 无法准确判断升级影响
18
+
19
+ 2. **业务文档升级的依据**
20
+ - 升级记录包含详细的变更详情
21
+ - AI 根据这些记录升级你的业务文档
22
+
23
+ 3. **回滚依据**
24
+ - 如果升级出现问题,可以参考历史记录回滚
25
+
26
+ ## 文件命名规则
27
+
28
+ - \`v1.4.0.md\` - v1.4.0 的变更记录
29
+ - \`v1.5.0.md\` - v1.5.0 的变更记录
30
+
31
+ ## 可以删除单个文件吗?
32
+
33
+ **不建议**。每个文件都是升级历史的一部分,删除会影响 AI 的判断能力。
34
+ `;
35
+
36
+ async function ensureChangelogDir(cwd) {
37
+ const changelogDir = path.join(cwd, '.anws', 'changelog');
38
+ await fs.mkdir(changelogDir, { recursive: true });
39
+ await fs.writeFile(path.join(changelogDir, '.gitkeep'), '', 'utf8');
40
+ await fs.writeFile(path.join(changelogDir, 'README.md'), README_CONTENT, 'utf8');
41
+ return changelogDir;
42
+ }
43
+
44
+ function compareSemver(a, b) {
45
+ const aParts = String(a).split('.').map((item) => Number(item));
46
+ const bParts = String(b).split('.').map((item) => Number(item));
47
+
48
+ for (let index = 0; index < 3; index += 1) {
49
+ const left = aParts[index] || 0;
50
+ const right = bParts[index] || 0;
51
+ if (left > right) return 1;
52
+ if (left < right) return -1;
53
+ }
54
+
55
+ return 0;
56
+ }
57
+
58
+ function normalizeText(text) {
59
+ return String(text || '').replace(/\r\n/g, '\n');
60
+ }
61
+
62
+ function buildMergedChangeKey(item) {
63
+ const canonicalPath = item.source || item.file;
64
+ const summaryKey = JSON.stringify(item.summary || []);
65
+ const contentKey = item.type === 'modified'
66
+ ? summaryKey
67
+ : `${normalizeText(item.oldContent)}\n<<<ANWS_CHANGE_SPLIT>>>\n${normalizeText(item.newContent)}`;
68
+
69
+ return `${item.type}::${canonicalPath}::${item.resourceId || canonicalPath}::${contentKey}`;
70
+ }
71
+
72
+ function mergeChanges(changes) {
73
+ const grouped = new Map();
74
+
75
+ for (const item of changes) {
76
+ const canonicalPath = item.source || item.file;
77
+ const key = buildMergedChangeKey(item);
78
+ const affectedFile = {
79
+ file: item.file,
80
+ targetId: item.targetId || null,
81
+ targetLabel: item.targetLabel || null
82
+ };
83
+
84
+ if (!grouped.has(key)) {
85
+ grouped.set(key, {
86
+ ...item,
87
+ canonicalPath,
88
+ affectedFiles: [affectedFile]
89
+ });
90
+ continue;
91
+ }
92
+
93
+ const current = grouped.get(key);
94
+ current.affectedFiles.push(affectedFile);
95
+ }
96
+
97
+ return Array.from(grouped.values()).map((item) => ({
98
+ ...item,
99
+ affectedFiles: item.affectedFiles.sort((left, right) => left.file.localeCompare(right.file))
100
+ }));
101
+ }
102
+
103
+ function formatAffectedTargetList(item) {
104
+ const targets = Array.from(new Set(
105
+ item.affectedFiles
106
+ .filter((entry) => entry.targetId && entry.targetLabel)
107
+ .map((entry) => `${entry.targetLabel} (${entry.targetId})`)
108
+ ));
109
+
110
+ return targets.length > 0 ? targets.join(', ') : '无';
111
+ }
112
+
113
+ function formatFileList(title, items) {
114
+ const lines = [`### ${title}`];
115
+ if (items.length === 0) {
116
+ lines.push('- 无');
117
+ return lines.join('\n');
118
+ }
119
+
120
+ for (const item of items) {
121
+ lines.push(`- \`${item.canonicalPath}\` — 影响 Targets: ${formatAffectedTargetList(item)}`);
122
+ for (const affectedFile of item.affectedFiles) {
123
+ lines.push(` - \`${affectedFile.file}\``);
124
+ }
125
+ }
126
+
127
+ return lines.join('\n');
128
+ }
129
+
130
+ function formatDetail(item) {
131
+ if (item.type === 'added') {
132
+ return [
133
+ `### \`${item.canonicalPath}\``,
134
+ `- **影响 Targets**: ${formatAffectedTargetList(item)}`,
135
+ '- **影响文件**:',
136
+ ...item.affectedFiles.map((entry) => ` - \`${entry.file}\``),
137
+ '- **新增文件**',
138
+ '- **说明**: 该文件在旧版本中不存在,因此无前后逐行对比。'
139
+ ].join('\n');
140
+ }
141
+
142
+ if (item.type === 'deleted') {
143
+ return [
144
+ `### \`${item.canonicalPath}\``,
145
+ `- **影响 Targets**: ${formatAffectedTargetList(item)}`,
146
+ '- **影响文件**:',
147
+ ...item.affectedFiles.map((entry) => ` - \`${entry.file}\``),
148
+ '- **删除文件**',
149
+ '- **说明**: 该文件在新版本中不存在,因此无前后逐行对比。'
150
+ ].join('\n');
151
+ }
152
+
153
+ if (item.summary.length === 0) {
154
+ return [
155
+ `### \`${item.canonicalPath}\``,
156
+ `- **影响 Targets**: ${formatAffectedTargetList(item)}`,
157
+ '- **影响文件**:',
158
+ ...item.affectedFiles.map((entry) => ` - \`${entry.file}\``),
159
+ '- **说明**: 检测到内容变更,但未能提取到摘要。'
160
+ ].join('\n');
161
+ }
162
+
163
+ return [
164
+ `### \`${item.canonicalPath}\``,
165
+ `- **影响 Targets**: ${formatAffectedTargetList(item)}`,
166
+ '- **影响文件**:',
167
+ ...item.affectedFiles.map((entry) => ` - \`${entry.file}\``),
168
+ '```diff',
169
+ ...item.summary.flatMap((pair) => [
170
+ `- [old:${pair.oldLineNumber === null ? '-' : pair.oldLineNumber}] ${pair.oldText}`,
171
+ `+ [new:${pair.newLineNumber === null ? '-' : pair.newLineNumber}] ${pair.newText}`
172
+ ]),
173
+ '```'
174
+ ].join('\n');
175
+ }
176
+
177
+ async function generateChangelog({ cwd, version, changes, targetSummary = null }) {
178
+ const changelogDir = await ensureChangelogDir(cwd);
179
+ const mergedChanges = mergeChanges(changes);
180
+ const grouped = groupChanges(mergedChanges);
181
+ const now = new Date();
182
+ const timestamp = now.toISOString().replace('T', ' ').slice(0, 19);
183
+ const filePath = path.join(changelogDir, `v${version}.md`);
184
+
185
+ const content = [
186
+ `# 升级记录: v${version}`,
187
+ '',
188
+ '> ⚠️ **此文件由 `anws update` 自动生成,请勿删除!**',
189
+ '> 删除后将导致 AI 无法获取历史升级信息,影响后续升级判断。',
190
+ '',
191
+ '## 元信息',
192
+ `- **升级版本**: ${version}`,
193
+ `- 升级时间: ${timestamp}`,
194
+ '- **升级类型**: 由 `/upgrade` 工作流判断 (Minor/Major)',
195
+ ...(targetSummary
196
+ ? [
197
+ `- **成功 Targets**: ${targetSummary.successfulTargets.length > 0 ? targetSummary.successfulTargets.join(', ') : '无'}`,
198
+ `- **失败 Targets**: ${targetSummary.failedTargets.length > 0 ? targetSummary.failedTargets.join(', ') : '无'}`
199
+ ]
200
+ : []),
201
+ '',
202
+ '## 变更摘要',
203
+ `- 新增文件: ${grouped.added.length}`,
204
+ `- 修改文件: ${grouped.modified.length}`,
205
+ `- 删除文件: ${grouped.deleted.length}`,
206
+ '',
207
+ '## 文件级变更清单',
208
+ '',
209
+ formatFileList('新增文件', grouped.added),
210
+ '',
211
+ formatFileList('修改文件', grouped.modified),
212
+ '',
213
+ formatFileList('删除文件', grouped.deleted),
214
+ '',
215
+ '## 内容级变更详情',
216
+ '',
217
+ ...(mergedChanges.length > 0 ? mergedChanges.map(formatDetail).flatMap((section) => [section, '']) : ['- 无变更', ''])
218
+ ].join('\n');
219
+
220
+ await fs.writeFile(filePath, content, 'utf8');
221
+ return filePath;
222
+ }
223
+
224
+ async function detectUpgrade({ cwd, version }) {
225
+ const changelogDir = path.join(cwd, '.anws', 'changelog');
226
+
227
+ try {
228
+ const files = await fs.readdir(changelogDir);
229
+ const latest = files
230
+ .filter((name) => /^v\d+\.\d+\.\d+\.md$/.test(name))
231
+ .sort((left, right) => {
232
+ const leftVersion = left.slice(1, -3);
233
+ const rightVersion = right.slice(1, -3);
234
+ return compareSemver(leftVersion, rightVersion);
235
+ })
236
+ .pop();
237
+
238
+ if (!latest) {
239
+ return { needUpgrade: true, fromVersion: null, toVersion: version, latestVersion: null };
240
+ }
241
+
242
+ const fromVersion = latest.slice(1, -3);
243
+ return {
244
+ needUpgrade: compareSemver(fromVersion, version) !== 0,
245
+ fromVersion,
246
+ toVersion: version,
247
+ latestVersion: fromVersion
248
+ };
249
+ } catch {
250
+ return { needUpgrade: true, fromVersion: null, toVersion: version, latestVersion: null };
251
+ }
252
+ }
253
+
254
+ module.exports = {
255
+ detectUpgrade,
256
+ ensureChangelogDir,
257
+ generateChangelog
258
+ };