adspecs 0.1.19 → 0.1.20

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 (67) 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/.claude-plugin/marketplace.json +23 -23
  5. package/.claude-plugin/plugin.json +18 -18
  6. package/.qoder-plugin/plugin.json +31 -31
  7. package/CLAUDE.md +1 -5
  8. package/README.md +395 -395
  9. package/bin/adspecs.js +129 -129
  10. package/hooks/commit-queue.js +245 -245
  11. package/hooks/hooks.json +63 -63
  12. package/hooks/session-start.js +44 -44
  13. package/hooks/wiki-queue.js +127 -127
  14. package/package.json +61 -61
  15. package/references/ant6-front-standard/index.md +99 -99
  16. package/references/antd-front-demo/public/mockServiceWorker.js +361 -361
  17. package/references/ecp-end-standard/index.md +63 -63
  18. 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
  19. 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
  20. 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
  21. package/references/python-end-standard/04-Redis/344/275/277/347/224/250/350/247/204/350/214/203.md +231 -231
  22. package/scripts/postinstall.js +107 -107
  23. package/scripts/sync-version.js +105 -105
  24. package/skills/.claude/.wiki-update-queue +26 -26
  25. package/skills/adspecs-constitution/SKILL.md +157 -0
  26. package/skills/adspecs-export-word/SKILL.md +498 -498
  27. package/skills/adspecs-export-word/references/md-to-docx.js +862 -862
  28. package/skills/adspecs-export-word/references/package-lock.json +220 -220
  29. package/skills/adspecs-export-word/references/package.json +10 -10
  30. package/skills/adspecs-front-prototype/SKILL.md +405 -405
  31. package/skills/adspecs-plan/SKILL.md +59 -69
  32. package/skills/adspecs-prd/SKILL.md +13 -5
  33. package/skills/adspecs-prd-to-demo/SKILL.md +532 -0
  34. package/skills/adspecs-tasks/SKILL.md +175 -204
  35. package/skills/adspecs-update-status/SKILL.md +382 -382
  36. package/skills/adspecs-utest/SKILL.md +107 -116
  37. package/skills/grill-me/SKILL.md +7 -0
  38. package/skills/grill-me/agents/openai.yaml +5 -0
  39. package/skills/playwright-cli/SKILL.md +420 -0
  40. package/skills/playwright-cli/references/element-attributes.md +23 -0
  41. package/skills/playwright-cli/references/playwright-tests.md +39 -0
  42. package/skills/playwright-cli/references/request-mocking.md +87 -0
  43. package/skills/playwright-cli/references/running-code.md +241 -0
  44. package/skills/playwright-cli/references/session-management.md +225 -0
  45. package/skills/playwright-cli/references/storage-state.md +275 -0
  46. package/skills/playwright-cli/references/test-generation.md +433 -0
  47. package/skills/playwright-cli/references/tracing.md +139 -0
  48. package/skills/playwright-cli/references/video-recording.md +143 -0
  49. package/skills/playwright-trace/SKILL.md +171 -0
  50. package/skills/project-init/references/front-demo/.claude/settings.local.json +9 -0
  51. package/skills/wiki-update/SKILL.md +232 -232
  52. package/src/commands/doctor.js +197 -197
  53. package/src/commands/init.js +83 -83
  54. package/src/commands/plugin.js +165 -165
  55. package/src/commands/update.js +87 -87
  56. package/src/lib/area-scanner.js +129 -129
  57. package/src/lib/copier.js +104 -104
  58. package/src/lib/dir-utils.js +133 -133
  59. package/src/lib/json-merge.js +114 -114
  60. package/src/lib/paths-defaults.js +37 -37
  61. package/src/lib/prompts.js +347 -347
  62. package/src/lib/readme-gen.js +143 -143
  63. package/src/lib/report.js +327 -327
  64. package/src/lib/scaffolder.js +518 -518
  65. package/src/lib/short-name.js +36 -36
  66. package/src/utils.js +80 -80
  67. 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
+ };