adspecs 0.1.19 → 0.1.21

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 (74) hide show
  1. package/.adspecs/feature.json +16 -16
  2. package/.adspecs/feature.yml +28 -28
  3. package/.adspecs/paths.json +17 -17
  4. package/.adspecs/templates/04-/345/211/215/347/253/257/345/212/237/350/203/275/350/256/276/350/256/241/346/250/241/346/235/277.md +1 -1
  5. package/.adspecs/templates/05-/345/220/216/347/253/257/344/273/273/345/212/241/346/270/205/345/215/225/346/250/241/346/235/277.md +613 -724
  6. package/.adspecs/templates/05b-/345/211/215/347/253/257/344/273/273/345/212/241/346/270/205/345/215/225/346/250/241/346/235/277.md +51 -51
  7. package/.claude-plugin/marketplace.json +23 -23
  8. package/.claude-plugin/plugin.json +18 -18
  9. package/.qoder-plugin/plugin.json +31 -31
  10. package/CLAUDE.md +1 -5
  11. package/INSTALL.md +3 -3
  12. package/README.md +395 -395
  13. package/bin/adspecs.js +129 -129
  14. package/hooks/commit-queue.js +245 -245
  15. package/hooks/hooks.json +63 -63
  16. package/hooks/session-start.js +44 -44
  17. package/hooks/wiki-queue.js +127 -127
  18. package/package.json +61 -61
  19. package/references/ant6-front-standard/index.md +99 -99
  20. package/references/antd-front-demo/public/mockServiceWorker.js +361 -361
  21. package/references/ecp-end-standard/index.md +63 -63
  22. package/references/python-end-standard/01-Python/345/220/216/347/253/257/347/274/226/347/240/201/350/247/204/350/214/203.md +372 -372
  23. package/references/python-end-standard/02-/346/225/260/346/215/256/345/272/223/350/256/276/350/256/241/344/270/216/344/275/277/347/224/250/350/247/204/350/214/203.md +226 -226
  24. package/references/python-end-standard/03-Celery/345/274/202/346/255/245/344/273/273/345/212/241/350/247/204/350/214/203.md +237 -237
  25. package/references/python-end-standard/04-Redis/344/275/277/347/224/250/350/247/204/350/214/203.md +231 -231
  26. package/scripts/postinstall.js +107 -107
  27. package/scripts/sync-version.js +105 -105
  28. package/skills/.claude/.wiki-update-queue +26 -26
  29. package/skills/adspecs-constitution/SKILL.md +157 -0
  30. package/skills/adspecs-export-word/SKILL.md +498 -498
  31. package/skills/adspecs-export-word/references/md-to-docx.js +862 -862
  32. package/skills/adspecs-export-word/references/package-lock.json +220 -220
  33. package/skills/adspecs-export-word/references/package.json +10 -10
  34. package/skills/adspecs-front-prototype/SKILL.md +405 -405
  35. package/skills/adspecs-front-spec/SKILL.md +4 -4
  36. package/skills/adspecs-front-tasks/SKILL.md +213 -173
  37. package/skills/adspecs-plan/SKILL.md +59 -69
  38. package/skills/adspecs-prd/SKILL.md +13 -5
  39. package/skills/adspecs-prd-to-demo/SKILL.md +532 -0
  40. package/skills/adspecs-tasks/SKILL.md +175 -204
  41. package/skills/adspecs-update-status/SKILL.md +382 -382
  42. package/skills/adspecs-utest/SKILL.md +107 -116
  43. package/skills/grill-me/SKILL.md +7 -0
  44. package/skills/grill-me/agents/openai.yaml +5 -0
  45. package/skills/playwright-cli/SKILL.md +420 -0
  46. package/skills/playwright-cli/references/element-attributes.md +23 -0
  47. package/skills/playwright-cli/references/playwright-tests.md +39 -0
  48. package/skills/playwright-cli/references/request-mocking.md +87 -0
  49. package/skills/playwright-cli/references/running-code.md +241 -0
  50. package/skills/playwright-cli/references/session-management.md +225 -0
  51. package/skills/playwright-cli/references/storage-state.md +275 -0
  52. package/skills/playwright-cli/references/test-generation.md +433 -0
  53. package/skills/playwright-cli/references/tracing.md +139 -0
  54. package/skills/playwright-cli/references/video-recording.md +143 -0
  55. package/skills/playwright-trace/SKILL.md +171 -0
  56. package/skills/project-init/SKILL.md +93 -22
  57. package/skills/project-init/references/front-demo/.claude/settings.local.json +9 -0
  58. package/skills/wiki-update/SKILL.md +232 -232
  59. package/src/commands/doctor.js +197 -197
  60. package/src/commands/init.js +83 -83
  61. package/src/commands/plugin.js +165 -165
  62. package/src/commands/update.js +87 -87
  63. package/src/lib/area-scanner.js +129 -129
  64. package/src/lib/copier.js +104 -104
  65. package/src/lib/dir-utils.js +161 -133
  66. package/src/lib/json-merge.js +114 -114
  67. package/src/lib/paths-defaults.js +37 -37
  68. package/src/lib/prompts.js +428 -347
  69. package/src/lib/readme-gen.js +143 -143
  70. package/src/lib/report.js +338 -327
  71. package/src/lib/scaffolder.js +551 -518
  72. package/src/lib/short-name.js +36 -36
  73. package/src/utils.js +80 -80
  74. package/references/antd-front-demo/.env +0 -15
@@ -1,129 +1,129 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const { hasAnyFile } = require('./dir-utils');
6
-
7
- /**
8
- * 区域状态扫描
9
- *
10
- * 按模式扫描目标目录,返回各区域的存在状态,
11
- * 为冲突决策和计划摘要提供数据基础。
12
- */
13
-
14
- /**
15
- * 各区域的目录名映射
16
- */
17
- const AREA_DIRS = {
18
- adspecs: '.adspecs',
19
- docs: 'docs',
20
- wiki: 'wiki',
21
- webUi: 'web-ui',
22
- service: 'service',
23
- frontDemo: 'front-demo',
24
- };
25
-
26
- /**
27
- * 按模式返回需要处理的区域列表
28
- *
29
- * @param {string} mode - 初始化模式
30
- * @returns {string[]} 区域 key 列表
31
- */
32
- function getAreaListForMode(mode) {
33
- switch (mode) {
34
- case 'simple':
35
- return ['adspecs', 'docs', 'wiki'];
36
- case 'classic':
37
- return ['adspecs', 'docs', 'wiki', 'frontDemo'];
38
- case 'full':
39
- case 'demo':
40
- return ['adspecs', 'docs', 'wiki', 'webUi', 'service', 'frontDemo'];
41
- default:
42
- return ['adspecs', 'docs', 'wiki'];
43
- }
44
- }
45
-
46
- /**
47
- * 按模式扫描目标目录,返回各区域存在状态
48
- *
49
- * @param {string} targetDir - 目标项目根目录
50
- * @param {string} mode - 初始化模式
51
- * @returns {object} 区域状态映射 { areaKey: { exists, hasFiles, dirName } }
52
- */
53
- function scanAreaStates(targetDir, mode) {
54
- const areas = getAreaListForMode(mode);
55
- const states = {};
56
-
57
- for (const areaKey of areas) {
58
- const dirName = AREA_DIRS[areaKey];
59
- const fullPath = path.join(targetDir, dirName);
60
- const exists = fs.existsSync(fullPath);
61
-
62
- states[areaKey] = {
63
- exists,
64
- hasFiles: exists ? hasAnyFile(fullPath) : false,
65
- dirName,
66
- };
67
- }
68
-
69
- return states;
70
- }
71
-
72
- /**
73
- * 将区域 key 转换为中文显示名
74
- *
75
- * @param {string} areaKey
76
- * @returns {string}
77
- */
78
- function areaDisplayName(areaKey) {
79
- const names = {
80
- adspecs: '.adspecs',
81
- docs: 'docs',
82
- wiki: 'wiki',
83
- webUi: 'web-ui',
84
- service: 'service',
85
- frontDemo: 'front-demo',
86
- };
87
- return names[areaKey] || areaKey;
88
- }
89
-
90
- /**
91
- * 将决策转换为中文描述
92
- *
93
- * @param {string} decision - create | recreate | update | skip
94
- * @returns {string}
95
- */
96
- function decisionLabel(decision) {
97
- const labels = {
98
- create: '创建',
99
- recreate: '重新创建',
100
- update: '更新',
101
- skip: '跳过',
102
- };
103
- return labels[decision] || decision;
104
- }
105
-
106
- /**
107
- * 将决策转换为状态图标
108
- *
109
- * @param {string} decision
110
- * @returns {string}
111
- */
112
- function decisionIcon(decision) {
113
- const icons = {
114
- create: '✅',
115
- recreate: '🔄',
116
- update: '🔄',
117
- skip: '⏭',
118
- };
119
- return icons[decision] || '❓';
120
- }
121
-
122
- module.exports = {
123
- AREA_DIRS,
124
- getAreaListForMode,
125
- scanAreaStates,
126
- areaDisplayName,
127
- decisionLabel,
128
- decisionIcon,
129
- };
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { hasAnyFile } = require('./dir-utils');
6
+
7
+ /**
8
+ * 区域状态扫描
9
+ *
10
+ * 按模式扫描目标目录,返回各区域的存在状态,
11
+ * 为冲突决策和计划摘要提供数据基础。
12
+ */
13
+
14
+ /**
15
+ * 各区域的目录名映射
16
+ */
17
+ const AREA_DIRS = {
18
+ adspecs: '.adspecs',
19
+ docs: 'docs',
20
+ wiki: 'wiki',
21
+ webUi: 'web-ui',
22
+ service: 'service',
23
+ frontDemo: 'front-demo',
24
+ };
25
+
26
+ /**
27
+ * 按模式返回需要处理的区域列表
28
+ *
29
+ * @param {string} mode - 初始化模式
30
+ * @returns {string[]} 区域 key 列表
31
+ */
32
+ function getAreaListForMode(mode) {
33
+ switch (mode) {
34
+ case 'simple':
35
+ return ['adspecs', 'docs', 'wiki'];
36
+ case 'classic':
37
+ return ['adspecs', 'docs', 'wiki', 'frontDemo'];
38
+ case 'full':
39
+ case 'demo':
40
+ return ['adspecs', 'docs', 'wiki', 'webUi', 'service', 'frontDemo'];
41
+ default:
42
+ return ['adspecs', 'docs', 'wiki'];
43
+ }
44
+ }
45
+
46
+ /**
47
+ * 按模式扫描目标目录,返回各区域存在状态
48
+ *
49
+ * @param {string} targetDir - 目标项目根目录
50
+ * @param {string} mode - 初始化模式
51
+ * @returns {object} 区域状态映射 { areaKey: { exists, hasFiles, dirName } }
52
+ */
53
+ function scanAreaStates(targetDir, mode) {
54
+ const areas = getAreaListForMode(mode);
55
+ const states = {};
56
+
57
+ for (const areaKey of areas) {
58
+ const dirName = AREA_DIRS[areaKey];
59
+ const fullPath = path.join(targetDir, dirName);
60
+ const exists = fs.existsSync(fullPath);
61
+
62
+ states[areaKey] = {
63
+ exists,
64
+ hasFiles: exists ? hasAnyFile(fullPath) : false,
65
+ dirName,
66
+ };
67
+ }
68
+
69
+ return states;
70
+ }
71
+
72
+ /**
73
+ * 将区域 key 转换为中文显示名
74
+ *
75
+ * @param {string} areaKey
76
+ * @returns {string}
77
+ */
78
+ function areaDisplayName(areaKey) {
79
+ const names = {
80
+ adspecs: '.adspecs',
81
+ docs: 'docs',
82
+ wiki: 'wiki',
83
+ webUi: 'web-ui',
84
+ service: 'service',
85
+ frontDemo: 'front-demo',
86
+ };
87
+ return names[areaKey] || areaKey;
88
+ }
89
+
90
+ /**
91
+ * 将决策转换为中文描述
92
+ *
93
+ * @param {string} decision - create | recreate | update | skip
94
+ * @returns {string}
95
+ */
96
+ function decisionLabel(decision) {
97
+ const labels = {
98
+ create: '创建',
99
+ recreate: '重新创建',
100
+ update: '更新',
101
+ skip: '跳过',
102
+ };
103
+ return labels[decision] || decision;
104
+ }
105
+
106
+ /**
107
+ * 将决策转换为状态图标
108
+ *
109
+ * @param {string} decision
110
+ * @returns {string}
111
+ */
112
+ function decisionIcon(decision) {
113
+ const icons = {
114
+ create: '✅',
115
+ recreate: '🔄',
116
+ update: '🔄',
117
+ skip: '⏭',
118
+ };
119
+ return icons[decision] || '❓';
120
+ }
121
+
122
+ module.exports = {
123
+ AREA_DIRS,
124
+ getAreaListForMode,
125
+ scanAreaStates,
126
+ areaDisplayName,
127
+ decisionLabel,
128
+ decisionIcon,
129
+ };
package/src/lib/copier.js CHANGED
@@ -1,104 +1,104 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
-
6
- /**
7
- * 复制过程中需要跳过的目录名
8
- * 这些目录通常不应出现在目标项目中(体积大 / 与版本控制冲突 / 运行时缓存)
9
- */
10
- const EXCLUDED_DIRS = new Set([
11
- 'node_modules',
12
- '.git',
13
- '.vite',
14
- '.venv',
15
- '__pycache__',
16
- '.next',
17
- 'dist',
18
- 'build',
19
- ]);
20
-
21
- /**
22
- * 递归复制目录,过滤掉不需要的大目录
23
- *
24
- * @param {string} src - 源目录绝对路径
25
- * @param {string} dest - 目标目录绝对路径(不存在会自动创建)
26
- * @param {object} [opts] - 选项
27
- * @param {Set<string>} [opts.exclude] - 额外要排除的目录名
28
- * @param {function} [opts.onProgress] - 进度回调 (filePath: string) => void
29
- * @returns {{ fileCount: number, skippedDirs: string[] }} 复制的文件数和被跳过的目录
30
- */
31
- function copyDirFiltered(src, dest, opts = {}) {
32
- const exclude = opts.exclude || new Set();
33
- const onProgress = opts.onProgress || (() => {});
34
- const skippedDirs = [];
35
-
36
- if (!fs.existsSync(src)) {
37
- return { fileCount: 0, skippedDirs };
38
- }
39
-
40
- fs.mkdirSync(dest, { recursive: true });
41
-
42
- let fileCount = 0;
43
-
44
- function _copy(currentSrc, currentDest) {
45
- const entries = fs.readdirSync(currentSrc, { withFileTypes: true });
46
- for (const entry of entries) {
47
- const srcPath = path.join(currentSrc, entry.name);
48
- const destPath = path.join(currentDest, entry.name);
49
-
50
- if (entry.isDirectory()) {
51
- if (EXCLUDED_DIRS.has(entry.name) || exclude.has(entry.name)) {
52
- skippedDirs.push(entry.name);
53
- continue;
54
- }
55
- fs.mkdirSync(destPath, { recursive: true });
56
- _copy(srcPath, destPath);
57
- } else if (entry.isFile()) {
58
- fs.copyFileSync(srcPath, destPath);
59
- onProgress(destPath);
60
- fileCount++;
61
- }
62
- }
63
- }
64
-
65
- _copy(src, dest);
66
- return { fileCount, skippedDirs };
67
- }
68
-
69
- /**
70
- * 删除目录(如果存在)
71
- * @param {string} dirPath - 目录路径
72
- */
73
- function removeDir(dirPath) {
74
- if (fs.existsSync(dirPath)) {
75
- fs.rmSync(dirPath, { recursive: true, force: true });
76
- }
77
- }
78
-
79
- /**
80
- * 统计目录下的文件数量
81
- * @param {string} dirPath - 目录路径
82
- * @returns {number}
83
- */
84
- function countFiles(dirPath) {
85
- if (!fs.existsSync(dirPath)) return 0;
86
- let count = 0;
87
- const entries = fs.readdirSync(dirPath, { withFileTypes: true });
88
- for (const entry of entries) {
89
- const fullPath = path.join(dirPath, entry.name);
90
- if (entry.isDirectory()) {
91
- count += countFiles(fullPath);
92
- } else if (entry.isFile()) {
93
- count++;
94
- }
95
- }
96
- return count;
97
- }
98
-
99
- module.exports = {
100
- copyDirFiltered,
101
- removeDir,
102
- countFiles,
103
- EXCLUDED_DIRS,
104
- };
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ /**
7
+ * 复制过程中需要跳过的目录名
8
+ * 这些目录通常不应出现在目标项目中(体积大 / 与版本控制冲突 / 运行时缓存)
9
+ */
10
+ const EXCLUDED_DIRS = new Set([
11
+ 'node_modules',
12
+ '.git',
13
+ '.vite',
14
+ '.venv',
15
+ '__pycache__',
16
+ '.next',
17
+ 'dist',
18
+ 'build',
19
+ ]);
20
+
21
+ /**
22
+ * 递归复制目录,过滤掉不需要的大目录
23
+ *
24
+ * @param {string} src - 源目录绝对路径
25
+ * @param {string} dest - 目标目录绝对路径(不存在会自动创建)
26
+ * @param {object} [opts] - 选项
27
+ * @param {Set<string>} [opts.exclude] - 额外要排除的目录名
28
+ * @param {function} [opts.onProgress] - 进度回调 (filePath: string) => void
29
+ * @returns {{ fileCount: number, skippedDirs: string[] }} 复制的文件数和被跳过的目录
30
+ */
31
+ function copyDirFiltered(src, dest, opts = {}) {
32
+ const exclude = opts.exclude || new Set();
33
+ const onProgress = opts.onProgress || (() => {});
34
+ const skippedDirs = [];
35
+
36
+ if (!fs.existsSync(src)) {
37
+ return { fileCount: 0, skippedDirs };
38
+ }
39
+
40
+ fs.mkdirSync(dest, { recursive: true });
41
+
42
+ let fileCount = 0;
43
+
44
+ function _copy(currentSrc, currentDest) {
45
+ const entries = fs.readdirSync(currentSrc, { withFileTypes: true });
46
+ for (const entry of entries) {
47
+ const srcPath = path.join(currentSrc, entry.name);
48
+ const destPath = path.join(currentDest, entry.name);
49
+
50
+ if (entry.isDirectory()) {
51
+ if (EXCLUDED_DIRS.has(entry.name) || exclude.has(entry.name)) {
52
+ skippedDirs.push(entry.name);
53
+ continue;
54
+ }
55
+ fs.mkdirSync(destPath, { recursive: true });
56
+ _copy(srcPath, destPath);
57
+ } else if (entry.isFile()) {
58
+ fs.copyFileSync(srcPath, destPath);
59
+ onProgress(destPath);
60
+ fileCount++;
61
+ }
62
+ }
63
+ }
64
+
65
+ _copy(src, dest);
66
+ return { fileCount, skippedDirs };
67
+ }
68
+
69
+ /**
70
+ * 删除目录(如果存在)
71
+ * @param {string} dirPath - 目录路径
72
+ */
73
+ function removeDir(dirPath) {
74
+ if (fs.existsSync(dirPath)) {
75
+ fs.rmSync(dirPath, { recursive: true, force: true });
76
+ }
77
+ }
78
+
79
+ /**
80
+ * 统计目录下的文件数量
81
+ * @param {string} dirPath - 目录路径
82
+ * @returns {number}
83
+ */
84
+ function countFiles(dirPath) {
85
+ if (!fs.existsSync(dirPath)) return 0;
86
+ let count = 0;
87
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
88
+ for (const entry of entries) {
89
+ const fullPath = path.join(dirPath, entry.name);
90
+ if (entry.isDirectory()) {
91
+ count += countFiles(fullPath);
92
+ } else if (entry.isFile()) {
93
+ count++;
94
+ }
95
+ }
96
+ return count;
97
+ }
98
+
99
+ module.exports = {
100
+ copyDirFiltered,
101
+ removeDir,
102
+ countFiles,
103
+ EXCLUDED_DIRS,
104
+ };