sumulige-claude 1.5.1 → 1.6.0

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 (223) hide show
  1. package/.claude/hooks/hook-registry.json +0 -15
  2. package/.claude/rules/coding-style.md +18 -7
  3. package/.claude/rules/hooks.md +15 -4
  4. package/.claude/rules/performance.md +15 -5
  5. package/.claude/rules/security.md +140 -4
  6. package/.claude/rules/testing.md +138 -9
  7. package/.claude/rules/web-design-standard.md +16 -5
  8. package/.claude/skills/algorithmic-art/metadata.yaml +28 -0
  9. package/.claude/skills/api-tester/SKILL.md +61 -0
  10. package/.claude/skills/api-tester/examples/basic.md +3 -0
  11. package/.claude/skills/api-tester/metadata.yaml +30 -0
  12. package/.claude/skills/api-tester/templates/default.md +3 -0
  13. package/.claude/skills/brand-guidelines/metadata.yaml +26 -0
  14. package/.claude/skills/canvas-design/metadata.yaml +27 -0
  15. package/.claude/skills/code-reviewer-123/SKILL.md +61 -0
  16. package/.claude/skills/code-reviewer-123/examples/basic.md +3 -0
  17. package/.claude/skills/code-reviewer-123/metadata.yaml +30 -0
  18. package/.claude/skills/code-reviewer-123/templates/default.md +3 -0
  19. package/.claude/skills/doc-coauthoring/metadata.yaml +27 -0
  20. package/.claude/skills/docx/metadata.yaml +30 -0
  21. package/.claude/skills/frontend-design/metadata.yaml +28 -0
  22. package/.claude/skills/internal-comms/metadata.yaml +28 -0
  23. package/.claude/skills/mcp-builder/metadata.yaml +26 -0
  24. package/.claude/skills/my-skill/SKILL.md +61 -0
  25. package/.claude/skills/my-skill/examples/basic.md +3 -0
  26. package/.claude/skills/my-skill/metadata.yaml +30 -0
  27. package/.claude/skills/my-skill/templates/default.md +3 -0
  28. package/.claude/skills/pdf/metadata.yaml +29 -0
  29. package/.claude/skills/pptx/metadata.yaml +29 -0
  30. package/.claude/skills/react-best-practices/metadata.yaml +26 -0
  31. package/.claude/skills/react-node-practices/SKILL.md +409 -0
  32. package/.claude/skills/react-node-practices/metadata.yaml +56 -0
  33. package/.claude/skills/skill-creator/metadata.yaml +25 -0
  34. package/.claude/skills/slack-gif-creator/metadata.yaml +28 -0
  35. package/.claude/skills/test-skill-name/SKILL.md +61 -0
  36. package/.claude/skills/test-skill-name/examples/basic.md +3 -0
  37. package/.claude/skills/test-skill-name/metadata.yaml +30 -0
  38. package/.claude/skills/test-skill-name/templates/default.md +3 -0
  39. package/.claude/skills/test-workflow/metadata.yaml +32 -0
  40. package/.claude/skills/theme-factory/metadata.yaml +26 -0
  41. package/.claude/skills/threejs-fundamentals/metadata.yaml +27 -0
  42. package/.claude/skills/web-artifacts-builder/metadata.yaml +30 -0
  43. package/.claude/skills/web-design-guidelines/metadata.yaml +26 -0
  44. package/.claude/skills/webapp-testing/metadata.yaml +26 -0
  45. package/.claude/skills/xlsx/metadata.yaml +29 -0
  46. package/LICENSE +21 -0
  47. package/README.md +280 -529
  48. package/cli.js +19 -3
  49. package/package.json +29 -3
  50. package/template/.codex/README.md +69 -0
  51. package/template/.codex/config.toml +56 -0
  52. package/template/AGENTS.md +94 -0
  53. package/.claude/.kickoff-hint.txt +0 -52
  54. package/.claude/.sumulige-claude-version +0 -1
  55. package/.claude/.version +0 -1
  56. package/.claude/AGENTS.md +0 -42
  57. package/.claude/ANCHORS.md +0 -40
  58. package/.claude/CLAUDE.md +0 -138
  59. package/.claude/MEMORY.md +0 -69
  60. package/.claude/PROJECT_LOG.md +0 -101
  61. package/.claude/THINKING_CHAIN_GUIDE.md +0 -287
  62. package/.claude/USAGE.md +0 -175
  63. package/.claude/boris-optimizations.md +0 -167
  64. package/.claude/handoffs/INDEX.md +0 -21
  65. package/.claude/handoffs/LATEST.md +0 -76
  66. package/.claude/handoffs/handoff_2026-01-22T13-07-04-757Z.md +0 -76
  67. package/.claude/quality-gate.json +0 -82
  68. package/.claude/rag/skill-index.json +0 -135
  69. package/.claude/settings.json +0 -99
  70. package/.claude/settings.local.json +0 -175
  71. package/.claude/templates/PROJECT_KICKOFF.md +0 -89
  72. package/.claude/templates/PROJECT_PROPOSAL.md +0 -227
  73. package/.claude/templates/TASK_PLAN.md +0 -121
  74. package/.claude/templates/hooks/README.md +0 -302
  75. package/.claude/templates/hooks/hook.sh.template +0 -94
  76. package/.claude/templates/hooks/user-prompt-submit.cjs.template +0 -116
  77. package/.claude/templates/hooks/user-response-submit.cjs.template +0 -94
  78. package/.claude/templates/hooks/validate.js +0 -173
  79. package/.claude/templates/tasks/develop.md +0 -69
  80. package/.claude/templates/tasks/research.md +0 -64
  81. package/.claude/templates/tasks/test.md +0 -96
  82. package/.claude/thinking-routes/.last-sync +0 -1
  83. package/.claude/thinking-routes/QUICKREF.md +0 -98
  84. package/.claude/workflow/document-scanner.js +0 -426
  85. package/.claude/workflow/knowledge-engine.js +0 -941
  86. package/.claude/workflow/notebooklm/browser.js +0 -1028
  87. package/.claude/workflow/phases/phase1-research.js +0 -578
  88. package/.claude/workflow/phases/phase1-research.ts +0 -465
  89. package/.claude/workflow/phases/phase2-approve.js +0 -722
  90. package/.claude/workflow/phases/phase3-plan.js +0 -1200
  91. package/.claude/workflow/phases/phase4-develop.js +0 -894
  92. package/.claude/workflow/search-cache.js +0 -230
  93. package/.claude/workflow/templates/approval.md +0 -315
  94. package/.claude/workflow/templates/development.md +0 -377
  95. package/.claude/workflow/templates/planning.md +0 -328
  96. package/.claude/workflow/templates/research.md +0 -250
  97. package/.claude/workflow/types.js +0 -37
  98. package/.claude/workflow/web-search.js +0 -278
  99. package/.claude-plugin/marketplace.json +0 -71
  100. package/.github/workflows/sync-skills.yml +0 -74
  101. package/.versionrc +0 -25
  102. package/AGENTS.md +0 -580
  103. package/CHANGELOG.md +0 -481
  104. package/CLAUDE-template.md +0 -114
  105. package/DEV_TOOLS_GUIDE.md +0 -190
  106. package/PROJECT_STRUCTURE.md +0 -266
  107. package/Q&A.md +0 -325
  108. package/config/defaults.json +0 -34
  109. package/config/official-skills.json +0 -183
  110. package/config/quality-gate.json +0 -67
  111. package/config/skill-categories.json +0 -40
  112. package/config/version-manifest.json +0 -85
  113. package/demos/power-3d-scatter.html +0 -683
  114. package/development/cache/web-search/search_1193d605f8eb364651fc2f2041b58a31.json +0 -36
  115. package/development/cache/web-search/search_3798bf06960edc125f744a1abb5b72c5.json +0 -36
  116. package/development/cache/web-search/search_37c7d4843a53f0d83f1122a6f908a2a3.json +0 -36
  117. package/development/cache/web-search/search_44166fa0153709ee168485a22aa0ab40.json +0 -36
  118. package/development/cache/web-search/search_4deaebb1f77e86a8ca066dc5a49c59fd.json +0 -36
  119. package/development/cache/web-search/search_94da91789466070a7f545612e73c7372.json +0 -36
  120. package/development/cache/web-search/search_dd5de8491b8b803a3cb01339cd210fb0.json +0 -36
  121. package/development/knowledge-base/.index.clean.json +0 -1
  122. package/development/knowledge-base/.index.json +0 -486
  123. package/development/knowledge-base/test-best-practices.md +0 -29
  124. package/development/projects/proj_mkh1pazz_ixmt1/phase1/feasibility-report.md +0 -160
  125. package/development/projects/proj_mkh4jvnb_z7rwf/phase1/feasibility-report.md +0 -160
  126. package/development/projects/proj_mkh4jxkd_ewz5a/phase1/feasibility-report.md +0 -160
  127. package/development/projects/proj_mkh4k84n_ni73k/phase1/feasibility-report.md +0 -160
  128. package/development/projects/proj_mkh4wfyd_u9w88/phase1/feasibility-report.md +0 -160
  129. package/development/projects/proj_mkh4wsbo_iahvf/development/projects/proj_mkh4xbpg_4na5w/phase1/feasibility-report.md +0 -160
  130. package/development/projects/proj_mkh4wsbo_iahvf/phase1/feasibility-report.md +0 -160
  131. package/development/projects/proj_mkh4xulg_1ka8x/phase1/feasibility-report.md +0 -160
  132. package/development/projects/proj_mkh4xwhj_gch8j/phase1/feasibility-report.md +0 -160
  133. package/development/projects/proj_mkh4y2qk_9lm8z/phase1/feasibility-report.md +0 -160
  134. package/development/projects/proj_mkh4y2qk_9lm8z/phase2/requirements.md +0 -226
  135. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/PRD.md +0 -345
  136. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/TASK_PLAN.md +0 -284
  137. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/prototype/README.md +0 -14
  138. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/DEVELOPMENT_LOG.md +0 -35
  139. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/TASKS.md +0 -34
  140. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/.env.example +0 -5
  141. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/README.md +0 -60
  142. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/package.json +0 -25
  143. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/index.js +0 -70
  144. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/routes/index.js +0 -48
  145. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/health.test.js +0 -20
  146. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/jest.config.js +0 -21
  147. package/development/projects/proj_mkh7veqg_3lypc/phase1/feasibility-report.md +0 -160
  148. package/development/projects/proj_mkh7veqg_3lypc/phase2/requirements.md +0 -226
  149. package/development/projects/proj_mkh7veqg_3lypc/phase3/PRD.md +0 -345
  150. package/development/projects/proj_mkh7veqg_3lypc/phase3/TASK_PLAN.md +0 -284
  151. package/development/projects/proj_mkh7veqg_3lypc/phase3/prototype/README.md +0 -14
  152. package/development/projects/proj_mkh8k8fo_rmqn5/phase1/feasibility-report.md +0 -160
  153. package/development/projects/proj_mkh8xyhy_1vshq/phase1/feasibility-report.md +0 -178
  154. package/development/projects/proj_mkh8zddd_dhamf/phase1/feasibility-report.md +0 -377
  155. package/development/projects/proj_mkh8zddd_dhamf/phase2/requirements.md +0 -442
  156. package/development/projects/proj_mkh8zddd_dhamf/phase3/api-design.md +0 -800
  157. package/development/projects/proj_mkh8zddd_dhamf/phase3/architecture.md +0 -625
  158. package/development/projects/proj_mkh8zddd_dhamf/phase3/data-model.md +0 -830
  159. package/development/projects/proj_mkh8zddd_dhamf/phase3/risks.md +0 -957
  160. package/development/projects/proj_mkh8zddd_dhamf/phase3/wbs.md +0 -381
  161. package/development/todos/.state.json +0 -19
  162. package/development/todos/INDEX.md +0 -63
  163. package/development/todos/active/_README.md +0 -49
  164. package/development/todos/archived/_README.md +0 -11
  165. package/development/todos/backlog/_README.md +0 -11
  166. package/development/todos/backlog/mcp-integration.md +0 -35
  167. package/development/todos/completed/_README.md +0 -11
  168. package/development/todos/completed/boris-optimizations.md +0 -39
  169. package/development/todos/completed/develop/local-knowledge-index.md +0 -85
  170. package/development/todos/completed/develop/todo-system.md +0 -47
  171. package/development/todos/completed/develop/web-search-integration.md +0 -83
  172. package/development/todos/completed/test/phase1-e2e-test.md +0 -103
  173. package/docs/DEVELOPMENT.md +0 -461
  174. package/docs/MARKETPLACE.md +0 -352
  175. package/docs/RELEASE.md +0 -93
  176. package/jest.config.js +0 -63
  177. package/lib/commands.js +0 -3588
  178. package/lib/config-manager.js +0 -441
  179. package/lib/config-schema.js +0 -408
  180. package/lib/config-validator.js +0 -330
  181. package/lib/config.js +0 -122
  182. package/lib/errors.js +0 -305
  183. package/lib/incremental-sync.js +0 -274
  184. package/lib/marketplace.js +0 -487
  185. package/lib/migrations.js +0 -154
  186. package/lib/permission-audit.js +0 -255
  187. package/lib/quality-gate.js +0 -431
  188. package/lib/quality-rules.js +0 -373
  189. package/lib/utils.js +0 -150
  190. package/lib/version-check.js +0 -169
  191. package/lib/version-manifest.js +0 -171
  192. package/project-paradigm.md +0 -313
  193. package/prompts/how-to-find.md +0 -163
  194. package/prompts/linus-architect.md +0 -71
  195. package/prompts/software-architect.md +0 -173
  196. package/prompts/web-designer.md +0 -249
  197. package/scripts/fix-hooks.mjs +0 -97
  198. package/scripts/sync-external.mjs +0 -298
  199. package/scripts/sync-to-home.sh +0 -108
  200. package/scripts/update-registry.mjs +0 -325
  201. package/sources.yaml +0 -83
  202. package/tests/README.md +0 -263
  203. package/tests/commands.test.js +0 -1086
  204. package/tests/config-manager.test.js +0 -677
  205. package/tests/config-schema.test.js +0 -425
  206. package/tests/config-validator.test.js +0 -436
  207. package/tests/config.test.js +0 -100
  208. package/tests/errors.test.js +0 -477
  209. package/tests/manual/phase1-e2e.sh +0 -389
  210. package/tests/manual/phase2-test-cases.md +0 -311
  211. package/tests/manual/phase3-test-cases.md +0 -309
  212. package/tests/manual/phase4-test-cases.md +0 -414
  213. package/tests/manual/test-cases.md +0 -417
  214. package/tests/marketplace.test.js +0 -420
  215. package/tests/migrations.test.js +0 -187
  216. package/tests/quality-gate.test.js +0 -679
  217. package/tests/quality-rules.test.js +0 -619
  218. package/tests/sync-external.test.js +0 -214
  219. package/tests/update-registry.test.js +0 -251
  220. package/tests/utils.test.js +0 -171
  221. package/tests/version-check.test.js +0 -75
  222. package/tests/web-search.test.js +0 -392
  223. package/thinkinglens-silent.md +0 -138
@@ -1,214 +0,0 @@
1
- /**
2
- * Sync External Skills 脚本单元测试
3
- * 测试外部技能同步功能
4
- */
5
-
6
- const path = require('path');
7
-
8
- describe('Sync External Skills', () => {
9
- describe('YAML parsing', () => {
10
- it('should parse valid sources.yaml structure', () => {
11
- const yamlStructure = {
12
- version: 1,
13
- skills: [
14
- {
15
- name: 'test-skill',
16
- description: 'A test skill',
17
- native: true
18
- },
19
- {
20
- name: 'external-skill',
21
- description: 'External skill',
22
- source: {
23
- repo: 'owner/repo',
24
- ref: 'main',
25
- path: '/skills/external'
26
- },
27
- target: {
28
- path: 'template/.claude/skills/external-skill'
29
- }
30
- }
31
- ]
32
- };
33
-
34
- expect(yamlStructure.version).toBeDefined();
35
- expect(yamlStructure.skills).toBeInstanceOf(Array);
36
- expect(yamlStructure.skills[0].name).toBe('test-skill');
37
- expect(yamlStructure.skills[0].native).toBe(true);
38
- expect(yamlStructure.skills[1].source?.repo).toBe('owner/repo');
39
- });
40
-
41
- it('should handle skills array', () => {
42
- const skills = [
43
- { name: 'skill1', native: true },
44
- { name: 'skill2', source: { repo: 'owner/repo' } }
45
- ];
46
-
47
- expect(skills).toHaveLength(2);
48
- expect(skills[0].name).toBe('skill1');
49
- expect(skills[1].name).toBe('skill2');
50
- });
51
-
52
- it('should parse boolean native field', () => {
53
- const nativeSkill = { native: true };
54
- const externalSkill = { native: false };
55
-
56
- expect(nativeSkill.native).toBe(true);
57
- expect(externalSkill.native).toBe(false);
58
- });
59
-
60
- it('should parse nested source configuration', () => {
61
- const source = {
62
- repo: 'owner/repo',
63
- ref: 'main',
64
- path: '/path/to/skill'
65
- };
66
-
67
- expect(source.repo).toBe('owner/repo');
68
- expect(source.ref).toBe('main');
69
- expect(source.path).toBe('/path/to/skill');
70
- });
71
-
72
- it('should parse sync configuration', () => {
73
- const sync = {
74
- include: ['*.md', '*.json'],
75
- exclude: ['node_modules', '*.lock']
76
- };
77
-
78
- expect(sync.include).toContain('*.md');
79
- expect(sync.exclude).toContain('node_modules');
80
- });
81
- });
82
-
83
- describe('File pattern matching', () => {
84
- it('should match include patterns', () => {
85
- const include = ['*.md', '*.json'];
86
-
87
- // .md file matches
88
- const mdRegex = new RegExp(include[0].replace('*', '.*'));
89
- expect(mdRegex.test('file.md')).toBe(true);
90
- expect(mdRegex.test('file.txt')).toBe(false);
91
-
92
- // .json file matches
93
- const jsonRegex = new RegExp(include[1].replace('*', '.*'));
94
- expect(jsonRegex.test('file.json')).toBe(true);
95
- expect(jsonRegex.test('file.txt')).toBe(false);
96
- });
97
-
98
- it('should match exclude patterns', () => {
99
- const exclude = ['node_modules', '*.lock'];
100
-
101
- // node_modules exclusion
102
- const nmRegex = new RegExp(exclude[0].replace('*', '.*'));
103
- expect(nmRegex.test('node_modules')).toBe(true);
104
- expect(nmRegex.test('src')).toBe(false);
105
-
106
- // .lock exclusion
107
- const lockRegex = new RegExp(exclude[1].replace('*', '.*'));
108
- expect(lockRegex.test('package-lock.json')).toBe(true);
109
- expect(lockRegex.test('package.json')).toBe(false);
110
- });
111
- });
112
-
113
- describe('Git operations', () => {
114
- it('should construct correct git clone command', () => {
115
- const repo = 'owner/repo';
116
- const ref = 'main';
117
- const targetDir = '/tmp/test';
118
-
119
- const expectedUrl = `https://github.com/${repo}.git`;
120
- const command = `git clone --depth 1 --branch ${ref} ${expectedUrl} ${targetDir}`;
121
-
122
- expect(command).toContain('git clone');
123
- expect(command).toContain('--depth 1');
124
- expect(command).toContain('--branch main');
125
- expect(command).toContain('https://github.com/owner/repo.git');
126
- });
127
- });
128
-
129
- describe('Source JSON structure', () => {
130
- it('should create valid JSON structure', () => {
131
- const skill = {
132
- name: 'test-skill',
133
- description: 'A test skill',
134
- source: {
135
- repo: 'owner/repo',
136
- path: '/skills/test',
137
- ref: 'main'
138
- },
139
- author: 'Test Author',
140
- license: 'MIT',
141
- homepage: 'https://example.com',
142
- verified: true
143
- };
144
-
145
- const expectedFields = ['name', 'description', 'source', 'author', 'license', 'homepage', 'verified'];
146
- expectedFields.forEach(field => {
147
- expect(skill).toHaveProperty(field);
148
- });
149
- });
150
-
151
- it('should include synced_at timestamp', () => {
152
- const sourceJson = {
153
- name: 'test-skill',
154
- source: {
155
- repo: 'owner/repo',
156
- ref: 'main',
157
- synced_at: new Date().toISOString()
158
- }
159
- };
160
-
161
- expect(sourceJson.source.synced_at).toBeDefined();
162
- expect(new Date(sourceJson.source.synced_at)).toBeInstanceOf(Date);
163
- });
164
- });
165
-
166
- describe('Sync logic', () => {
167
- it('should skip native skills', () => {
168
- const nativeSkill = { name: 'native-skill', native: true };
169
- const shouldSkip = nativeSkill.native === true;
170
-
171
- expect(shouldSkip).toBe(true);
172
- });
173
-
174
- it('should require source.repo for external skills', () => {
175
- const externalSkill = { name: 'external-skill' };
176
- const hasRepo = externalSkill.source?.repo;
177
-
178
- expect(hasRepo).toBeUndefined();
179
- });
180
-
181
- it('should validate required fields', () => {
182
- const validSkill = {
183
- name: 'test',
184
- source: { repo: 'owner/repo' },
185
- target: { path: '/path' }
186
- };
187
-
188
- expect(validSkill.name).toBeDefined();
189
- expect(validSkill.source?.repo).toBeDefined();
190
- expect(validSkill.target?.path).toBeDefined();
191
- });
192
- });
193
-
194
- describe('File paths', () => {
195
- it('should have correct sources.yaml path', () => {
196
- const sourcesPath = path.join(__dirname, '..', 'sources.yaml');
197
- expect(sourcesPath).toContain('sources.yaml');
198
- });
199
-
200
- it('should have correct skills directory path', () => {
201
- const skillsDir = path.join(__dirname, '..', 'template', '.claude', 'skills');
202
- expect(skillsDir).toContain('template');
203
- expect(skillsDir).toContain('.claude');
204
- expect(skillsDir).toContain('skills');
205
- });
206
-
207
- it('should construct correct temp directory path', () => {
208
- const skillName = 'test-skill';
209
- const tmpDir = path.join(__dirname, '..', '.tmp', skillName);
210
- expect(tmpDir).toContain('.tmp');
211
- expect(tmpDir).toContain('test-skill');
212
- });
213
- });
214
- });
@@ -1,251 +0,0 @@
1
- /**
2
- * Update Registry 脚本单元测试
3
- * 测试市场注册表生成功能
4
- */
5
-
6
- const path = require('path');
7
-
8
- describe('Update Registry Script', () => {
9
- describe('findSkillDirs', () => {
10
- it('should identify skill directories with SKILL.md', () => {
11
- const hasSkillFile = 'SKILL.md';
12
- expect(hasSkillFile).toBe('SKILL.md');
13
- });
14
-
15
- it('should identify skill directories with metadata.yaml', () => {
16
- const hasMetadata = 'metadata.yaml';
17
- expect(hasMetadata).toBe('metadata.yaml');
18
- });
19
-
20
- it('should identify skill directories with .source.json', () => {
21
- const hasSourceJson = '.source.json';
22
- expect(hasSourceJson).toBe('.source.json');
23
- });
24
-
25
- it('should skip template directory', () => {
26
- const skipDirs = ['template', 'examples', '__tests__'];
27
- expect(skipDirs).toContain('template');
28
- expect(skipDirs).toContain('examples');
29
- expect(skipDirs).toContain('__tests__');
30
- });
31
-
32
- it('should not skip regular skill directories', () => {
33
- const skipDirs = ['template', 'examples', '__tests__'];
34
- expect(skipDirs).not.toContain('my-skill');
35
- expect(skipDirs).not.toContain('code-reviewer');
36
- });
37
- });
38
-
39
- describe('parseMetadata', () => {
40
- it('should parse simple key-value pairs', () => {
41
- const yamlContent = `
42
- name: test-skill
43
- description: A test skill
44
- version: 1.0.0
45
- `;
46
-
47
- expect(yamlContent).toContain('name:');
48
- expect(yamlContent).toContain('description:');
49
- expect(yamlContent).toContain('version:');
50
- });
51
-
52
- it('should parse boolean values', () => {
53
- const yamlTrue = 'enabled: true';
54
- const yamlFalse = 'enabled: false';
55
-
56
- expect(yamlTrue).toContain('true');
57
- expect(yamlFalse).toContain('false');
58
- });
59
-
60
- it('should parse array values', () => {
61
- const yamlArray = 'tags: [tag1, tag2, tag3]';
62
- const yamlEmptyArray = 'tags: []';
63
-
64
- expect(yamlArray).toContain('[');
65
- expect(yamlEmptyArray).toBe('tags: []');
66
- });
67
-
68
- it('should skip comments', () => {
69
- const yamlWithComments = `
70
- # This is a comment
71
- name: test-skill
72
- # Another comment
73
- description: Test
74
- `;
75
-
76
- const lines = yamlWithComments.split('\n').filter(l => l.trim() && !l.trim().startsWith('#'));
77
- expect(lines.length).toBe(2);
78
- });
79
-
80
- it('should handle empty YAML', () => {
81
- const emptyYaml = '';
82
- expect(emptyYaml).toBe('');
83
- });
84
- });
85
-
86
- describe('parseSourceJson', () => {
87
- it('should parse valid JSON', () => {
88
- const json = {
89
- name: 'test-skill',
90
- source: {
91
- repo: 'owner/repo',
92
- ref: 'main'
93
- },
94
- synced_at: '2024-01-01T00:00:00.000Z'
95
- };
96
-
97
- expect(json.name).toBe('test-skill');
98
- expect(json.source.repo).toBe('owner/repo');
99
- expect(json.source.ref).toBe('main');
100
- });
101
-
102
- it('should handle missing .source.json', () => {
103
- const missing = null;
104
- expect(missing).toBeNull();
105
- });
106
- });
107
-
108
- describe('getSkillDescription', () => {
109
- it('should extract first non-heading paragraph', () => {
110
- const content = `
111
- # Test Skill
112
-
113
- This is the description of the skill.
114
-
115
- More details here.
116
- `;
117
-
118
- const lines = content.split('\n').filter(l => l.trim() && !l.trim().startsWith('#'));
119
- expect(lines[0].trim()).toBe('This is the description of the skill.');
120
- });
121
-
122
- it('should skip HTML comments and headings', () => {
123
- const content = `
124
- <!-- This is a comment -->
125
- # Title
126
-
127
- This is real content.
128
- `;
129
-
130
- const lines = content.split('\n').filter(l => {
131
- const trimmed = l.trim();
132
- return trimmed && !trimmed.startsWith('#') && !trimmed.startsWith('>') && !trimmed.startsWith('<!--');
133
- });
134
- expect(lines[0]).toContain('This is real content');
135
- });
136
-
137
- it('should handle empty content', () => {
138
- const emptyContent = '';
139
- const lines = emptyContent.split('\n').filter(l => l.trim());
140
- expect(lines.length).toBe(0);
141
- });
142
- });
143
-
144
- describe('getSkillCategory', () => {
145
- it('should return default category when no match', () => {
146
- const pathParts = ['some', 'random', 'path'];
147
- const categories = { tools: { name: 'Tools' } };
148
-
149
- const found = pathParts.some(part => categories[part]);
150
- expect(found).toBe(false);
151
- });
152
-
153
- it('should find category in path', () => {
154
- const pathParts = ['tools', 'skill-name'];
155
- const categories = { tools: { name: 'Tools' } };
156
-
157
- const found = pathParts.find(part => categories[part]);
158
- expect(found).toBe('tools');
159
- });
160
-
161
- it('should default to tools category', () => {
162
- const defaultCategory = 'tools';
163
- expect(defaultCategory).toBe('tools');
164
- });
165
- });
166
-
167
- describe('generateRegistry structure', () => {
168
- it('should have required top-level fields', () => {
169
- const registry = {
170
- name: 'smc-skills',
171
- description: 'Test registry',
172
- homepage: 'https://github.com/test/test',
173
- owner: { name: 'test' },
174
- plugins: [],
175
- metadata: {}
176
- };
177
-
178
- expect(registry).toHaveProperty('name');
179
- expect(registry).toHaveProperty('description');
180
- expect(registry).toHaveProperty('homepage');
181
- expect(registry).toHaveProperty('owner');
182
- expect(registry).toHaveProperty('plugins');
183
- expect(registry).toHaveProperty('metadata');
184
- });
185
-
186
- it('should create plugin with skills array', () => {
187
- const plugin = {
188
- name: 'smc-skills',
189
- description: 'Test plugin',
190
- source: './',
191
- skills: ['./skills/skill1', './skills/skill2'],
192
- skill_list: [
193
- { name: 'skill1', path: './skills/skill1' },
194
- { name: 'skill2', path: './skills/skill2' }
195
- ]
196
- };
197
-
198
- expect(plugin.skills).toHaveLength(2);
199
- expect(plugin.skill_list).toHaveLength(2);
200
- });
201
-
202
- it('should include metadata with generated_at timestamp', () => {
203
- const metadata = {
204
- version: '1.0.0',
205
- generated_at: new Date().toISOString(),
206
- skill_count: 10,
207
- categories: {}
208
- };
209
-
210
- expect(metadata.generated_at).toBeDefined();
211
- expect(new Date(metadata.generated_at)).toBeInstanceOf(Date);
212
- });
213
- });
214
-
215
- describe('File paths', () => {
216
- it('should have correct marketplace.json path', () => {
217
- const marketplacePath = path.join(__dirname, '..', '.claude-plugin', 'marketplace.json');
218
- expect(marketplacePath).toContain('.claude-plugin');
219
- expect(marketplacePath).toContain('marketplace.json');
220
- });
221
-
222
- it('should have correct skills directory path', () => {
223
- const skillsDir = path.join(__dirname, '..', 'template', '.claude', 'skills');
224
- expect(skillsDir).toContain('template');
225
- expect(skillsDir).toContain('.claude');
226
- expect(skillsDir).toContain('skills');
227
- });
228
-
229
- it('should have correct categories file path', () => {
230
- const categoriesPath = path.join(__dirname, '..', 'config', 'skill-categories.json');
231
- expect(categoriesPath).toContain('config');
232
- expect(categoriesPath).toContain('skill-categories.json');
233
- });
234
- });
235
-
236
- describe('getPackageVersion', () => {
237
- it('should return version from package.json', () => {
238
- const pkg = {
239
- name: 'test-package',
240
- version: '1.2.3'
241
- };
242
-
243
- expect(pkg.version).toBe('1.2.3');
244
- });
245
-
246
- it('should return default version when not found', () => {
247
- const defaultVersion = '1.0.0';
248
- expect(defaultVersion).toBe('1.0.0');
249
- });
250
- });
251
- });
@@ -1,171 +0,0 @@
1
- /**
2
- * Utils 模块单元测试
3
- */
4
-
5
- const fs = require('fs');
6
- const path = require('path');
7
- const os = require('os');
8
-
9
- const utils = require('../lib/utils');
10
-
11
- describe('Utils Module', () => {
12
- describe('copyRecursive', () => {
13
- let tempSrc, tempDest;
14
-
15
- beforeEach(() => {
16
- // 创建临时测试目录
17
- tempSrc = path.join(os.tmpdir(), 'test-src-' + Date.now());
18
- tempDest = path.join(os.tmpdir(), 'test-dest-' + Date.now());
19
- fs.mkdirSync(tempSrc, { recursive: true });
20
- });
21
-
22
- afterEach(() => {
23
- // 清理临时目录
24
- if (fs.existsSync(tempSrc)) {
25
- fs.rmSync(tempSrc, { recursive: true, force: true });
26
- }
27
- if (fs.existsSync(tempDest)) {
28
- fs.rmSync(tempDest, { recursive: true, force: true });
29
- }
30
- });
31
-
32
- it('should return 0 when source does not exist', () => {
33
- const nonExistent = path.join(os.tmpdir(), 'non-existent-' + Date.now());
34
- const result = utils.copyRecursive(nonExistent, tempDest);
35
-
36
- expect(result).toEqual({ copied: 0, skipped: 0, backedup: 0 });
37
- });
38
-
39
- it('should copy files recursively', () => {
40
- // 创建测试文件结构
41
- fs.mkdirSync(path.join(tempSrc, 'subdir'));
42
- fs.writeFileSync(path.join(tempSrc, 'file1.txt'), 'content1');
43
- fs.writeFileSync(path.join(tempSrc, 'subdir', 'file2.txt'), 'content2');
44
-
45
- const result = utils.copyRecursive(tempSrc, tempDest);
46
-
47
- expect(result.copied).toBe(2);
48
- expect(result.skipped).toBe(0);
49
- expect(result.backedup).toBe(0);
50
- expect(fs.existsSync(path.join(tempDest, 'file1.txt'))).toBe(true);
51
- expect(fs.existsSync(path.join(tempDest, 'subdir', 'file2.txt'))).toBe(true);
52
- });
53
-
54
- it('should set execute permission for script files', () => {
55
- fs.writeFileSync(path.join(tempSrc, 'test.sh'), '#!/bin/bash\necho test');
56
- fs.writeFileSync(path.join(tempSrc, 'test.cjs'), 'console.log("test");');
57
- fs.writeFileSync(path.join(tempSrc, 'test.txt'), 'plain text');
58
-
59
- utils.copyRecursive(tempSrc, tempDest);
60
-
61
- // 检查 .sh 文件权限
62
- const shStats = fs.statSync(path.join(tempDest, 'test.sh'));
63
- const cjsStats = fs.statSync(path.join(tempDest, 'test.cjs'));
64
- const txtStats = fs.statSync(path.join(tempDest, 'test.txt'));
65
-
66
- // 检查执行权限 (0o755 的执行位)
67
- expect(shStats.mode & 0o111).toBeTruthy();
68
- expect(cjsStats.mode & 0o111).toBeTruthy();
69
- // txt 文件不应该有执行权限
70
- expect(txtStats.mode & 0o111).toBeFalsy();
71
- });
72
-
73
- it('should not overwrite when overwrite=false', () => {
74
- fs.writeFileSync(path.join(tempSrc, 'file.txt'), 'new content');
75
- fs.mkdirSync(tempDest, { recursive: true });
76
- fs.writeFileSync(path.join(tempDest, 'file.txt'), 'old content');
77
-
78
- const result = utils.copyRecursive(tempSrc, tempDest, false);
79
-
80
- expect(result.copied).toBe(0);
81
- expect(result.skipped).toBe(1);
82
- const content = fs.readFileSync(path.join(tempDest, 'file.txt'), 'utf-8');
83
- expect(content).toBe('old content');
84
- });
85
-
86
- it('should overwrite when overwrite=true', () => {
87
- fs.writeFileSync(path.join(tempSrc, 'file.txt'), 'new content');
88
- fs.mkdirSync(tempDest, { recursive: true });
89
- fs.writeFileSync(path.join(tempDest, 'file.txt'), 'old content');
90
-
91
- const result = utils.copyRecursive(tempSrc, tempDest, true);
92
-
93
- expect(result.copied).toBe(1);
94
- expect(result.skipped).toBe(0);
95
- const content = fs.readFileSync(path.join(tempDest, 'file.txt'), 'utf-8');
96
- expect(content).toBe('new content');
97
- });
98
- });
99
-
100
- describe('ensureDir', () => {
101
- it('should create directory if not exists', () => {
102
- const tempDir = path.join(os.tmpdir(), 'test-ensure-' + Date.now());
103
-
104
- utils.ensureDir(tempDir);
105
-
106
- expect(fs.existsSync(tempDir)).toBe(true);
107
-
108
- // 清理
109
- fs.rmSync(tempDir, { recursive: true, force: true });
110
- });
111
-
112
- it('should not error if directory already exists', () => {
113
- const tempDir = path.join(os.tmpdir(), 'test-ensure-' + Date.now());
114
- fs.mkdirSync(tempDir, { recursive: true });
115
-
116
- expect(() => utils.ensureDir(tempDir)).not.toThrow();
117
-
118
- // 清理
119
- fs.rmSync(tempDir, { recursive: true, force: true });
120
- });
121
-
122
- it('should create nested directories', () => {
123
- const tempDir = path.join(os.tmpdir(), 'test-nested-' + Date.now(), 'level1', 'level2', 'level3');
124
-
125
- utils.ensureDir(tempDir);
126
-
127
- expect(fs.existsSync(tempDir)).toBe(true);
128
-
129
- // 清理
130
- fs.rmSync(path.join(os.tmpdir(), 'test-nested-' + Date.now()), { recursive: true, force: true });
131
- });
132
- });
133
-
134
- describe('toTitleCase', () => {
135
- it('should convert string to title case', () => {
136
- expect(utils.toTitleCase('hello world')).toBe('Hello World');
137
- expect(utils.toTitleCase('foo-bar')).toBe('Foo-Bar');
138
- });
139
-
140
- it('should handle single word', () => {
141
- expect(utils.toTitleCase('hello')).toBe('Hello');
142
- });
143
-
144
- it('should handle empty string', () => {
145
- expect(utils.toTitleCase('')).toBe('');
146
- });
147
-
148
- it('should handle already capitalized string', () => {
149
- expect(utils.toTitleCase('Hello World')).toBe('Hello World');
150
- });
151
-
152
- it('should handle strings with multiple spaces', () => {
153
- expect(utils.toTitleCase('hello world')).toBe('Hello World');
154
- });
155
-
156
- it('should preserve special characters', () => {
157
- // 下划线 _ 是单词字符(\w),所以 _t 中的 t 前面没有单词边界
158
- expect(utils.toTitleCase('hello-world_test')).toBe('Hello-World_test');
159
- // 连字符 - 不是单词字符,所以后面的字母会被大写
160
- expect(utils.toTitleCase('hello-world test')).toBe('Hello-World Test');
161
- });
162
- });
163
-
164
- describe('exports', () => {
165
- it('should export all functions', () => {
166
- expect(typeof utils.copyRecursive).toBe('function');
167
- expect(typeof utils.ensureDir).toBe('function');
168
- expect(typeof utils.toTitleCase).toBe('function');
169
- });
170
- });
171
- });