sumulige-claude 1.5.1 → 1.5.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.
- package/.claude/hooks/hook-registry.json +0 -15
- package/.claude/rules/coding-style.md +18 -7
- package/.claude/rules/hooks.md +15 -4
- package/.claude/rules/performance.md +15 -5
- package/.claude/rules/security.md +140 -4
- package/.claude/rules/testing.md +138 -9
- package/.claude/rules/web-design-standard.md +16 -5
- package/.claude/skills/algorithmic-art/metadata.yaml +28 -0
- package/.claude/skills/api-tester/SKILL.md +61 -0
- package/.claude/skills/api-tester/examples/basic.md +3 -0
- package/.claude/skills/api-tester/metadata.yaml +30 -0
- package/.claude/skills/api-tester/templates/default.md +3 -0
- package/.claude/skills/brand-guidelines/metadata.yaml +26 -0
- package/.claude/skills/canvas-design/metadata.yaml +27 -0
- package/.claude/skills/code-reviewer-123/SKILL.md +61 -0
- package/.claude/skills/code-reviewer-123/examples/basic.md +3 -0
- package/.claude/skills/code-reviewer-123/metadata.yaml +30 -0
- package/.claude/skills/code-reviewer-123/templates/default.md +3 -0
- package/.claude/skills/doc-coauthoring/metadata.yaml +27 -0
- package/.claude/skills/docx/metadata.yaml +30 -0
- package/.claude/skills/frontend-design/metadata.yaml +28 -0
- package/.claude/skills/internal-comms/metadata.yaml +28 -0
- package/.claude/skills/mcp-builder/metadata.yaml +26 -0
- package/.claude/skills/my-skill/SKILL.md +61 -0
- package/.claude/skills/my-skill/examples/basic.md +3 -0
- package/.claude/skills/my-skill/metadata.yaml +30 -0
- package/.claude/skills/my-skill/templates/default.md +3 -0
- package/.claude/skills/pdf/metadata.yaml +29 -0
- package/.claude/skills/pptx/metadata.yaml +29 -0
- package/.claude/skills/react-best-practices/metadata.yaml +26 -0
- package/.claude/skills/react-node-practices/SKILL.md +409 -0
- package/.claude/skills/react-node-practices/metadata.yaml +56 -0
- package/.claude/skills/skill-creator/metadata.yaml +25 -0
- package/.claude/skills/slack-gif-creator/metadata.yaml +28 -0
- package/.claude/skills/test-skill-name/SKILL.md +61 -0
- package/.claude/skills/test-skill-name/examples/basic.md +3 -0
- package/.claude/skills/test-skill-name/metadata.yaml +30 -0
- package/.claude/skills/test-skill-name/templates/default.md +3 -0
- package/.claude/skills/test-workflow/metadata.yaml +32 -0
- package/.claude/skills/theme-factory/metadata.yaml +26 -0
- package/.claude/skills/threejs-fundamentals/metadata.yaml +27 -0
- package/.claude/skills/web-artifacts-builder/metadata.yaml +30 -0
- package/.claude/skills/web-design-guidelines/metadata.yaml +26 -0
- package/.claude/skills/webapp-testing/metadata.yaml +26 -0
- package/.claude/skills/xlsx/metadata.yaml +29 -0
- package/LICENSE +21 -0
- package/cli.js +1 -1
- package/package.json +25 -3
- package/.claude/.kickoff-hint.txt +0 -52
- package/.claude/.sumulige-claude-version +0 -1
- package/.claude/.version +0 -1
- package/.claude/AGENTS.md +0 -42
- package/.claude/ANCHORS.md +0 -40
- package/.claude/CLAUDE.md +0 -138
- package/.claude/MEMORY.md +0 -69
- package/.claude/PROJECT_LOG.md +0 -101
- package/.claude/THINKING_CHAIN_GUIDE.md +0 -287
- package/.claude/USAGE.md +0 -175
- package/.claude/boris-optimizations.md +0 -167
- package/.claude/handoffs/INDEX.md +0 -21
- package/.claude/handoffs/LATEST.md +0 -76
- package/.claude/handoffs/handoff_2026-01-22T13-07-04-757Z.md +0 -76
- package/.claude/quality-gate.json +0 -82
- package/.claude/rag/skill-index.json +0 -135
- package/.claude/settings.json +0 -99
- package/.claude/settings.local.json +0 -175
- package/.claude/templates/PROJECT_KICKOFF.md +0 -89
- package/.claude/templates/PROJECT_PROPOSAL.md +0 -227
- package/.claude/templates/TASK_PLAN.md +0 -121
- package/.claude/templates/hooks/README.md +0 -302
- package/.claude/templates/hooks/hook.sh.template +0 -94
- package/.claude/templates/hooks/user-prompt-submit.cjs.template +0 -116
- package/.claude/templates/hooks/user-response-submit.cjs.template +0 -94
- package/.claude/templates/hooks/validate.js +0 -173
- package/.claude/templates/tasks/develop.md +0 -69
- package/.claude/templates/tasks/research.md +0 -64
- package/.claude/templates/tasks/test.md +0 -96
- package/.claude/thinking-routes/.last-sync +0 -1
- package/.claude/thinking-routes/QUICKREF.md +0 -98
- package/.claude/workflow/document-scanner.js +0 -426
- package/.claude/workflow/knowledge-engine.js +0 -941
- package/.claude/workflow/notebooklm/browser.js +0 -1028
- package/.claude/workflow/phases/phase1-research.js +0 -578
- package/.claude/workflow/phases/phase1-research.ts +0 -465
- package/.claude/workflow/phases/phase2-approve.js +0 -722
- package/.claude/workflow/phases/phase3-plan.js +0 -1200
- package/.claude/workflow/phases/phase4-develop.js +0 -894
- package/.claude/workflow/search-cache.js +0 -230
- package/.claude/workflow/templates/approval.md +0 -315
- package/.claude/workflow/templates/development.md +0 -377
- package/.claude/workflow/templates/planning.md +0 -328
- package/.claude/workflow/templates/research.md +0 -250
- package/.claude/workflow/types.js +0 -37
- package/.claude/workflow/web-search.js +0 -278
- package/.claude-plugin/marketplace.json +0 -71
- package/.github/workflows/sync-skills.yml +0 -74
- package/.versionrc +0 -25
- package/AGENTS.md +0 -580
- package/CHANGELOG.md +0 -481
- package/CLAUDE-template.md +0 -114
- package/DEV_TOOLS_GUIDE.md +0 -190
- package/PROJECT_STRUCTURE.md +0 -266
- package/Q&A.md +0 -325
- package/config/defaults.json +0 -34
- package/config/official-skills.json +0 -183
- package/config/quality-gate.json +0 -67
- package/config/skill-categories.json +0 -40
- package/config/version-manifest.json +0 -85
- package/demos/power-3d-scatter.html +0 -683
- package/development/cache/web-search/search_1193d605f8eb364651fc2f2041b58a31.json +0 -36
- package/development/cache/web-search/search_3798bf06960edc125f744a1abb5b72c5.json +0 -36
- package/development/cache/web-search/search_37c7d4843a53f0d83f1122a6f908a2a3.json +0 -36
- package/development/cache/web-search/search_44166fa0153709ee168485a22aa0ab40.json +0 -36
- package/development/cache/web-search/search_4deaebb1f77e86a8ca066dc5a49c59fd.json +0 -36
- package/development/cache/web-search/search_94da91789466070a7f545612e73c7372.json +0 -36
- package/development/cache/web-search/search_dd5de8491b8b803a3cb01339cd210fb0.json +0 -36
- package/development/knowledge-base/.index.clean.json +0 -1
- package/development/knowledge-base/.index.json +0 -486
- package/development/knowledge-base/test-best-practices.md +0 -29
- package/development/projects/proj_mkh1pazz_ixmt1/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4jvnb_z7rwf/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4jxkd_ewz5a/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4k84n_ni73k/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4wfyd_u9w88/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4wsbo_iahvf/development/projects/proj_mkh4xbpg_4na5w/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4wsbo_iahvf/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4xulg_1ka8x/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4xwhj_gch8j/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4y2qk_9lm8z/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4y2qk_9lm8z/phase2/requirements.md +0 -226
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/PRD.md +0 -345
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/TASK_PLAN.md +0 -284
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/prototype/README.md +0 -14
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/DEVELOPMENT_LOG.md +0 -35
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/TASKS.md +0 -34
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/.env.example +0 -5
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/README.md +0 -60
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/package.json +0 -25
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/index.js +0 -70
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/routes/index.js +0 -48
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/health.test.js +0 -20
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/jest.config.js +0 -21
- package/development/projects/proj_mkh7veqg_3lypc/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh7veqg_3lypc/phase2/requirements.md +0 -226
- package/development/projects/proj_mkh7veqg_3lypc/phase3/PRD.md +0 -345
- package/development/projects/proj_mkh7veqg_3lypc/phase3/TASK_PLAN.md +0 -284
- package/development/projects/proj_mkh7veqg_3lypc/phase3/prototype/README.md +0 -14
- package/development/projects/proj_mkh8k8fo_rmqn5/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh8xyhy_1vshq/phase1/feasibility-report.md +0 -178
- package/development/projects/proj_mkh8zddd_dhamf/phase1/feasibility-report.md +0 -377
- package/development/projects/proj_mkh8zddd_dhamf/phase2/requirements.md +0 -442
- package/development/projects/proj_mkh8zddd_dhamf/phase3/api-design.md +0 -800
- package/development/projects/proj_mkh8zddd_dhamf/phase3/architecture.md +0 -625
- package/development/projects/proj_mkh8zddd_dhamf/phase3/data-model.md +0 -830
- package/development/projects/proj_mkh8zddd_dhamf/phase3/risks.md +0 -957
- package/development/projects/proj_mkh8zddd_dhamf/phase3/wbs.md +0 -381
- package/development/todos/.state.json +0 -19
- package/development/todos/INDEX.md +0 -63
- package/development/todos/active/_README.md +0 -49
- package/development/todos/archived/_README.md +0 -11
- package/development/todos/backlog/_README.md +0 -11
- package/development/todos/backlog/mcp-integration.md +0 -35
- package/development/todos/completed/_README.md +0 -11
- package/development/todos/completed/boris-optimizations.md +0 -39
- package/development/todos/completed/develop/local-knowledge-index.md +0 -85
- package/development/todos/completed/develop/todo-system.md +0 -47
- package/development/todos/completed/develop/web-search-integration.md +0 -83
- package/development/todos/completed/test/phase1-e2e-test.md +0 -103
- package/docs/DEVELOPMENT.md +0 -461
- package/docs/MARKETPLACE.md +0 -352
- package/docs/RELEASE.md +0 -93
- package/jest.config.js +0 -63
- package/lib/commands.js +0 -3588
- package/lib/config-manager.js +0 -441
- package/lib/config-schema.js +0 -408
- package/lib/config-validator.js +0 -330
- package/lib/config.js +0 -122
- package/lib/errors.js +0 -305
- package/lib/incremental-sync.js +0 -274
- package/lib/marketplace.js +0 -487
- package/lib/migrations.js +0 -154
- package/lib/permission-audit.js +0 -255
- package/lib/quality-gate.js +0 -431
- package/lib/quality-rules.js +0 -373
- package/lib/utils.js +0 -150
- package/lib/version-check.js +0 -169
- package/lib/version-manifest.js +0 -171
- package/project-paradigm.md +0 -313
- package/prompts/how-to-find.md +0 -163
- package/prompts/linus-architect.md +0 -71
- package/prompts/software-architect.md +0 -173
- package/prompts/web-designer.md +0 -249
- package/scripts/fix-hooks.mjs +0 -97
- package/scripts/sync-external.mjs +0 -298
- package/scripts/sync-to-home.sh +0 -108
- package/scripts/update-registry.mjs +0 -325
- package/sources.yaml +0 -83
- package/tests/README.md +0 -263
- package/tests/commands.test.js +0 -1086
- package/tests/config-manager.test.js +0 -677
- package/tests/config-schema.test.js +0 -425
- package/tests/config-validator.test.js +0 -436
- package/tests/config.test.js +0 -100
- package/tests/errors.test.js +0 -477
- package/tests/manual/phase1-e2e.sh +0 -389
- package/tests/manual/phase2-test-cases.md +0 -311
- package/tests/manual/phase3-test-cases.md +0 -309
- package/tests/manual/phase4-test-cases.md +0 -414
- package/tests/manual/test-cases.md +0 -417
- package/tests/marketplace.test.js +0 -420
- package/tests/migrations.test.js +0 -187
- package/tests/quality-gate.test.js +0 -679
- package/tests/quality-rules.test.js +0 -619
- package/tests/sync-external.test.js +0 -214
- package/tests/update-registry.test.js +0 -251
- package/tests/utils.test.js +0 -171
- package/tests/version-check.test.js +0 -75
- package/tests/web-search.test.js +0 -392
- package/thinkinglens-silent.md +0 -138
|
@@ -1,420 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Marketplace 模块单元测试 - 扩展版
|
|
3
|
-
* 测试技能市场管理和 YAML 解析功能
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { parseSimpleYaml } = require('../lib/marketplace');
|
|
7
|
-
|
|
8
|
-
describe('Marketplace Module', () => {
|
|
9
|
-
|
|
10
|
-
// ============================================================================
|
|
11
|
-
// parseSimpleYaml 单元测试
|
|
12
|
-
// ============================================================================
|
|
13
|
-
|
|
14
|
-
describe('parseSimpleYaml', () => {
|
|
15
|
-
it('should parse version number', () => {
|
|
16
|
-
const yaml = `version: 1
|
|
17
|
-
skills:
|
|
18
|
-
- name: test-skill`;
|
|
19
|
-
expect(parseSimpleYaml(yaml).version).toBe(1);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('should parse version as integer', () => {
|
|
23
|
-
const yaml = `version: 2
|
|
24
|
-
skills:`;
|
|
25
|
-
expect(typeof parseSimpleYaml(yaml).version).toBe('number');
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should handle empty YAML', () => {
|
|
29
|
-
expect(parseSimpleYaml('')).toEqual({ skills: [] });
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should handle skills section with no skills', () => {
|
|
33
|
-
const yaml = `version: 1
|
|
34
|
-
skills:`;
|
|
35
|
-
expect(parseSimpleYaml(yaml).skills).toEqual([]);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should parse single skill name only', () => {
|
|
39
|
-
const yaml = `skills:
|
|
40
|
-
- name: test-skill`;
|
|
41
|
-
const result = parseSimpleYaml(yaml);
|
|
42
|
-
expect(result.skills).toHaveLength(1);
|
|
43
|
-
expect(result.skills[0].name).toBe('test-skill');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should parse multiple skill names', () => {
|
|
47
|
-
const yaml = `skills:
|
|
48
|
-
- name: skill-one
|
|
49
|
-
- name: skill-two
|
|
50
|
-
- name: skill-three`;
|
|
51
|
-
const result = parseSimpleYaml(yaml);
|
|
52
|
-
expect(result.skills).toHaveLength(3);
|
|
53
|
-
expect(result.skills[0].name).toBe('skill-one');
|
|
54
|
-
expect(result.skills[1].name).toBe('skill-two');
|
|
55
|
-
expect(result.skills[2].name).toBe('skill-three');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('should parse nested source object', () => {
|
|
59
|
-
const yaml = `skills:
|
|
60
|
-
- name: test
|
|
61
|
-
source:
|
|
62
|
-
repo: owner/repo
|
|
63
|
-
ref: main`;
|
|
64
|
-
const result = parseSimpleYaml(yaml);
|
|
65
|
-
expect(result.skills[0].source).toEqual({ repo: 'owner/repo', ref: 'main' });
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should parse nested target object', () => {
|
|
69
|
-
const yaml = `skills:
|
|
70
|
-
- name: test
|
|
71
|
-
target:
|
|
72
|
-
category: tools
|
|
73
|
-
path: /path/to/skill`;
|
|
74
|
-
const result = parseSimpleYaml(yaml);
|
|
75
|
-
expect(result.skills[0].target).toEqual({ category: 'tools', path: '/path/to/skill' });
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should parse nested author object', () => {
|
|
79
|
-
const yaml = `skills:
|
|
80
|
-
- name: test
|
|
81
|
-
author:
|
|
82
|
-
name: Test Author
|
|
83
|
-
github: testauthor`;
|
|
84
|
-
const result = parseSimpleYaml(yaml);
|
|
85
|
-
expect(result.skills[0].author).toEqual({ name: 'Test Author', github: 'testauthor' });
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should parse nested sync object', () => {
|
|
89
|
-
const yaml = `skills:
|
|
90
|
-
- name: test
|
|
91
|
-
sync:
|
|
92
|
-
include: *.md
|
|
93
|
-
exclude: node_modules`;
|
|
94
|
-
const result = parseSimpleYaml(yaml);
|
|
95
|
-
expect(result.skills[0].sync).toEqual({ include: '*.md', exclude: 'node_modules' });
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('should parse boolean true in nested properties', () => {
|
|
99
|
-
const yaml = `skills:
|
|
100
|
-
- name: test
|
|
101
|
-
sync:
|
|
102
|
-
enabled: true`;
|
|
103
|
-
const result = parseSimpleYaml(yaml);
|
|
104
|
-
expect(result.skills[0].sync.enabled).toBe(true);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should parse boolean false in nested properties', () => {
|
|
108
|
-
const yaml = `skills:
|
|
109
|
-
- name: test
|
|
110
|
-
sync:
|
|
111
|
-
enabled: false`;
|
|
112
|
-
const result = parseSimpleYaml(yaml);
|
|
113
|
-
expect(result.skills[0].sync.enabled).toBe(false);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('should parse empty array in nested properties', () => {
|
|
117
|
-
const yaml = `skills:
|
|
118
|
-
- name: test
|
|
119
|
-
sync:
|
|
120
|
-
tags: []`;
|
|
121
|
-
const result = parseSimpleYaml(yaml);
|
|
122
|
-
expect(result.skills[0].sync.tags).toEqual([]);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('should handle properties with hyphens', () => {
|
|
126
|
-
const yaml = `skills:
|
|
127
|
-
- name: my-skill
|
|
128
|
-
author:
|
|
129
|
-
github-url: https://github.com/test`;
|
|
130
|
-
const result = parseSimpleYaml(yaml);
|
|
131
|
-
expect(result.skills[0].author['github-url']).toBe('https://github.com/test');
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should handle properties with underscores', () => {
|
|
135
|
-
const yaml = `skills:
|
|
136
|
-
- name: test
|
|
137
|
-
author:
|
|
138
|
-
github_user: test_user`;
|
|
139
|
-
const result = parseSimpleYaml(yaml);
|
|
140
|
-
expect(result.skills[0].author.github_user).toBe('test_user');
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('should skip comments at start', () => {
|
|
144
|
-
const yaml = `# Comment at start
|
|
145
|
-
skills:
|
|
146
|
-
- name: test`;
|
|
147
|
-
const result = parseSimpleYaml(yaml);
|
|
148
|
-
expect(result.skills[0].name).toBe('test');
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('should skip comments in skills section', () => {
|
|
152
|
-
const yaml = `skills:
|
|
153
|
-
# Inline comment
|
|
154
|
-
- name: test`;
|
|
155
|
-
const result = parseSimpleYaml(yaml);
|
|
156
|
-
expect(result.skills[0].name).toBe('test');
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('should skip empty lines', () => {
|
|
160
|
-
const yaml = `skills:
|
|
161
|
-
|
|
162
|
-
- name: test
|
|
163
|
-
|
|
164
|
-
- name: test2`;
|
|
165
|
-
const result = parseSimpleYaml(yaml);
|
|
166
|
-
expect(result.skills).toHaveLength(2);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it('should handle complex real-world YAML', () => {
|
|
170
|
-
const yaml = `version: 1
|
|
171
|
-
skills:
|
|
172
|
-
- name: dev-browser
|
|
173
|
-
description: Browser automation
|
|
174
|
-
native: true
|
|
175
|
-
target:
|
|
176
|
-
category: tools
|
|
177
|
-
path: template/.claude/skills/tools/dev-browser
|
|
178
|
-
- name: gastown
|
|
179
|
-
source:
|
|
180
|
-
repo: SawyerHood/gastown
|
|
181
|
-
path: skills/gastown
|
|
182
|
-
ref: main
|
|
183
|
-
target:
|
|
184
|
-
category: tools
|
|
185
|
-
path: template/.claude/skills/tools/gastown
|
|
186
|
-
author:
|
|
187
|
-
name: SawyerHood
|
|
188
|
-
github: SawyerHood
|
|
189
|
-
license: MIT
|
|
190
|
-
verified: false`;
|
|
191
|
-
const result = parseSimpleYaml(yaml);
|
|
192
|
-
expect(result.version).toBe(1);
|
|
193
|
-
expect(result.skills).toHaveLength(2);
|
|
194
|
-
expect(result.skills[0].name).toBe('dev-browser');
|
|
195
|
-
expect(result.skills[0].native).toBe(true);
|
|
196
|
-
expect(result.skills[0].target.category).toBe('tools');
|
|
197
|
-
expect(result.skills[1].name).toBe('gastown');
|
|
198
|
-
expect(result.skills[1].source.repo).toBe('SawyerHood/gastown');
|
|
199
|
-
expect(result.skills[1].author.github).toBe('SawyerHood');
|
|
200
|
-
expect(result.skills[1].verified).toBe(false);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('should handle multiple nested objects in one skill', () => {
|
|
204
|
-
const yaml = `skills:
|
|
205
|
-
- name: complex-skill
|
|
206
|
-
source:
|
|
207
|
-
repo: owner/repo
|
|
208
|
-
ref: main
|
|
209
|
-
target:
|
|
210
|
-
category: workflow
|
|
211
|
-
path: /path
|
|
212
|
-
author:
|
|
213
|
-
name: Author
|
|
214
|
-
github: author
|
|
215
|
-
sync:
|
|
216
|
-
include: *.md
|
|
217
|
-
exclude: node_modules`;
|
|
218
|
-
const result = parseSimpleYaml(yaml);
|
|
219
|
-
expect(result.skills[0].source.repo).toBe('owner/repo');
|
|
220
|
-
expect(result.skills[0].target.category).toBe('workflow');
|
|
221
|
-
expect(result.skills[0].author.name).toBe('Author');
|
|
222
|
-
expect(result.skills[0].sync.include).toBe('*.md');
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it('should parse license property', () => {
|
|
226
|
-
const yaml = `skills:
|
|
227
|
-
- name: test
|
|
228
|
-
license: MIT`;
|
|
229
|
-
const result = parseSimpleYaml(yaml);
|
|
230
|
-
expect(result.skills[0].license).toBe('MIT');
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it('should parse homepage property', () => {
|
|
234
|
-
const yaml = `skills:
|
|
235
|
-
- name: test
|
|
236
|
-
homepage: https://example.com`;
|
|
237
|
-
const result = parseSimpleYaml(yaml);
|
|
238
|
-
expect(result.skills[0].homepage).toBe('https://example.com');
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('should parse verified property', () => {
|
|
242
|
-
const yaml = `skills:
|
|
243
|
-
- name: test
|
|
244
|
-
verified: false`;
|
|
245
|
-
const result = parseSimpleYaml(yaml);
|
|
246
|
-
expect(result.skills[0].verified).toBe(false);
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
it('should handle quoted strings', () => {
|
|
250
|
-
const yaml = `skills:
|
|
251
|
-
- name: test
|
|
252
|
-
description: "A quoted description"`;
|
|
253
|
-
const result = parseSimpleYaml(yaml);
|
|
254
|
-
expect(result.skills[0].description).toBe('A quoted description');
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it('should handle single-quoted strings', () => {
|
|
258
|
-
const yaml = `skills:
|
|
259
|
-
- name: test
|
|
260
|
-
description: 'A single-quoted description'`;
|
|
261
|
-
const result = parseSimpleYaml(yaml);
|
|
262
|
-
expect(result.skills[0].description).toBe('A single-quoted description');
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it('should handle URLs with special characters', () => {
|
|
266
|
-
const yaml = `skills:
|
|
267
|
-
- name: test
|
|
268
|
-
homepage: https://github.com/owner/repo/tree/main/skills`;
|
|
269
|
-
const result = parseSimpleYaml(yaml);
|
|
270
|
-
expect(result.skills[0].homepage).toBe('https://github.com/owner/repo/tree/main/skills');
|
|
271
|
-
});
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
// ============================================================================
|
|
275
|
-
// 市场常量定义测试
|
|
276
|
-
// ============================================================================
|
|
277
|
-
|
|
278
|
-
describe('Marketplace constants', () => {
|
|
279
|
-
it('should define SOURCES_FILE path', () => {
|
|
280
|
-
const path = require('path');
|
|
281
|
-
const sourcesPath = path.join(__dirname, '..', 'sources.yaml');
|
|
282
|
-
expect(sourcesPath).toContain('sources.yaml');
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
it('should define MARKETPLACE_FILE path', () => {
|
|
286
|
-
const path = require('path');
|
|
287
|
-
const marketplacePath = path.join(__dirname, '..', '.claude-plugin', 'marketplace.json');
|
|
288
|
-
expect(marketplacePath).toContain('marketplace.json');
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
it('should define CATEGORIES_FILE path', () => {
|
|
292
|
-
const path = require('path');
|
|
293
|
-
const categoriesPath = path.join(__dirname, '..', 'config', 'skill-categories.json');
|
|
294
|
-
expect(categoriesPath).toContain('skill-categories.json');
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
// ============================================================================
|
|
299
|
-
// YAML 格式验证测试
|
|
300
|
-
// ============================================================================
|
|
301
|
-
|
|
302
|
-
describe('YAML format validation', () => {
|
|
303
|
-
it('should recognize skills section header', () => {
|
|
304
|
-
const yaml = `skills:`;
|
|
305
|
-
const lines = yaml.split('\n');
|
|
306
|
-
const hasSkillsSection = lines.some(l => l.trim() === 'skills:');
|
|
307
|
-
expect(hasSkillsSection).toBe(true);
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
it('should recognize version field', () => {
|
|
311
|
-
const yaml = `version: 1`;
|
|
312
|
-
const lines = yaml.split('\n');
|
|
313
|
-
const hasVersion = lines.some(l => l.trim().startsWith('version:'));
|
|
314
|
-
expect(hasVersion).toBe(true);
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
it('should recognize skill entry marker', () => {
|
|
318
|
-
const yaml = ` - name: test-skill`;
|
|
319
|
-
const isSkillEntry = yaml.trim().startsWith('- name:');
|
|
320
|
-
expect(isSkillEntry).toBe(true);
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
it('should validate indent pattern for skill entries', () => {
|
|
324
|
-
const line = ` - name: test`;
|
|
325
|
-
const indent = line.search(/\S/);
|
|
326
|
-
expect(indent).toBe(2);
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
it('should validate indent pattern for nested properties', () => {
|
|
330
|
-
const line = ` repo: owner/repo`;
|
|
331
|
-
const indent = line.search(/\S/);
|
|
332
|
-
expect(indent).toBe(4);
|
|
333
|
-
});
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
// ============================================================================
|
|
337
|
-
// 模块导出测试
|
|
338
|
-
// ============================================================================
|
|
339
|
-
|
|
340
|
-
describe('exports', () => {
|
|
341
|
-
it('should export marketplaceCommands object', () => {
|
|
342
|
-
const marketplace = require('../lib/marketplace');
|
|
343
|
-
expect(marketplace.marketplaceCommands).toBeDefined();
|
|
344
|
-
expect(typeof marketplace.marketplaceCommands).toBe('object');
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
it('should export all marketplace commands', () => {
|
|
348
|
-
const marketplace = require('../lib/marketplace');
|
|
349
|
-
const commands = marketplace.marketplaceCommands;
|
|
350
|
-
|
|
351
|
-
expect(commands['marketplace:list']).toBeDefined();
|
|
352
|
-
expect(commands['marketplace:install']).toBeDefined();
|
|
353
|
-
expect(commands['marketplace:sync']).toBeDefined();
|
|
354
|
-
expect(commands['marketplace:add']).toBeDefined();
|
|
355
|
-
expect(commands['marketplace:remove']).toBeDefined();
|
|
356
|
-
expect(commands['marketplace:status']).toBeDefined();
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
it('should have exactly 6 commands', () => {
|
|
360
|
-
const marketplace = require('../lib/marketplace');
|
|
361
|
-
const commands = marketplace.marketplaceCommands;
|
|
362
|
-
|
|
363
|
-
expect(Object.keys(commands).length).toBe(6);
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
it('should export functions with correct types', () => {
|
|
367
|
-
const marketplace = require('../lib/marketplace');
|
|
368
|
-
const commands = marketplace.marketplaceCommands;
|
|
369
|
-
|
|
370
|
-
Object.values(commands).forEach(cmd => {
|
|
371
|
-
expect(typeof cmd).toBe('function');
|
|
372
|
-
});
|
|
373
|
-
});
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
// ============================================================================
|
|
377
|
-
// 命令功能测试
|
|
378
|
-
// ============================================================================
|
|
379
|
-
|
|
380
|
-
describe('command features', () => {
|
|
381
|
-
it('should parse repo format correctly', () => {
|
|
382
|
-
const repo = 'owner/repo-name';
|
|
383
|
-
const match = repo.match(/^([^/]+)\/(.+)$/);
|
|
384
|
-
expect(match).toBeTruthy();
|
|
385
|
-
expect(match[1]).toBe('owner');
|
|
386
|
-
expect(match[2]).toBe('repo-name');
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
it('should reject invalid repo format', () => {
|
|
390
|
-
const repo = 'invalid-format';
|
|
391
|
-
const match = repo.match(/^([^/]+)\/(.+)$/);
|
|
392
|
-
expect(match).toBeNull();
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
it('should normalize skill name to kebab-case', () => {
|
|
396
|
-
const name = 'Owner/Repo_Name';
|
|
397
|
-
const skillName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
398
|
-
expect(skillName).toBe('owner-repo-name');
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
it('should detect duplicate skills in sources list', () => {
|
|
402
|
-
const sources = [
|
|
403
|
-
{ name: 'skill-one' },
|
|
404
|
-
{ name: 'skill-two' },
|
|
405
|
-
{ name: 'skill-three' }
|
|
406
|
-
];
|
|
407
|
-
const exists = sources.some(s => s.name === 'skill-two');
|
|
408
|
-
expect(exists).toBe(true);
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
it('should not find non-existent skill', () => {
|
|
412
|
-
const sources = [
|
|
413
|
-
{ name: 'skill-one' },
|
|
414
|
-
{ name: 'skill-two' }
|
|
415
|
-
];
|
|
416
|
-
const exists = sources.some(s => s.name === 'skill-three');
|
|
417
|
-
expect(exists).toBe(false);
|
|
418
|
-
});
|
|
419
|
-
});
|
|
420
|
-
});
|
package/tests/migrations.test.js
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Migrations 模块单元测试
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const mockFs = require('mock-fs');
|
|
8
|
-
|
|
9
|
-
// 在 mock 之前加载模块
|
|
10
|
-
const migrations = require('../lib/migrations');
|
|
11
|
-
|
|
12
|
-
describe('Migrations Module', () => {
|
|
13
|
-
let originalFs = { ...fs };
|
|
14
|
-
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
// Mock 文件系统,保留一些必要的路径
|
|
17
|
-
mockFs({
|
|
18
|
-
'/test-project': {
|
|
19
|
-
'.claude': {}
|
|
20
|
-
}
|
|
21
|
-
}, {
|
|
22
|
-
// 不 mock 项目的 lib 目录
|
|
23
|
-
[path.join(__dirname, '../lib')]: true
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
afterEach(() => {
|
|
28
|
-
mockFs.restore();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe('getProjectVersion', () => {
|
|
32
|
-
it('should return 1.0.0 when version file does not exist', () => {
|
|
33
|
-
const version = migrations.getProjectVersion('/test-project');
|
|
34
|
-
expect(version).toBe('1.0.0');
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should read version from file', () => {
|
|
38
|
-
const versionFile = path.join('/test-project', '.claude', '.version');
|
|
39
|
-
fs.writeFileSync(versionFile, '1.0.5\n');
|
|
40
|
-
|
|
41
|
-
const version = migrations.getProjectVersion('/test-project');
|
|
42
|
-
expect(version).toBe('1.0.5');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should trim whitespace from version', () => {
|
|
46
|
-
const versionFile = path.join('/test-project', '.claude', '.version');
|
|
47
|
-
fs.writeFileSync(versionFile, ' 1.0.8 \n');
|
|
48
|
-
|
|
49
|
-
const version = migrations.getProjectVersion('/test-project');
|
|
50
|
-
expect(version).toBe('1.0.8');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should return 1.0.0 on read error', () => {
|
|
54
|
-
const version = migrations.getProjectVersion('/nonexistent');
|
|
55
|
-
expect(version).toBe('1.0.0');
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe('setProjectVersion', () => {
|
|
60
|
-
it('should write version to file', () => {
|
|
61
|
-
migrations.setProjectVersion('/test-project', '1.0.7');
|
|
62
|
-
|
|
63
|
-
const versionFile = path.join('/test-project', '.claude', '.version');
|
|
64
|
-
expect(fs.existsSync(versionFile)).toBe(true);
|
|
65
|
-
|
|
66
|
-
const content = fs.readFileSync(versionFile, 'utf-8');
|
|
67
|
-
expect(content.trim()).toBe('1.0.7');
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should add newline after version', () => {
|
|
71
|
-
migrations.setProjectVersion('/test-project', '1.0.9');
|
|
72
|
-
|
|
73
|
-
const versionFile = path.join('/test-project', '.claude', '.version');
|
|
74
|
-
const content = fs.readFileSync(versionFile, 'utf-8');
|
|
75
|
-
expect(content).toBe('1.0.9\n');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should create .claude directory if not exists', () => {
|
|
79
|
-
// 注意:setProjectVersion 不会自动创建目录
|
|
80
|
-
// 需要先创建目录
|
|
81
|
-
fs.mkdirSync(path.join('/new-project', '.claude'), { recursive: true });
|
|
82
|
-
migrations.setProjectVersion('/new-project', '1.0.1');
|
|
83
|
-
|
|
84
|
-
const versionFile = path.join('/new-project', '.claude', '.version');
|
|
85
|
-
expect(fs.existsSync(versionFile)).toBe(true);
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
describe('compareVersions', () => {
|
|
90
|
-
it('should return -1 when v1 < v2', () => {
|
|
91
|
-
expect(migrations.compareVersions('1.0.0', '1.0.1')).toBe(-1);
|
|
92
|
-
expect(migrations.compareVersions('1.0.0', '1.1.0')).toBe(-1);
|
|
93
|
-
expect(migrations.compareVersions('1.0.0', '2.0.0')).toBe(-1);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should return 1 when v1 > v2', () => {
|
|
97
|
-
expect(migrations.compareVersions('1.0.1', '1.0.0')).toBe(1);
|
|
98
|
-
expect(migrations.compareVersions('1.1.0', '1.0.0')).toBe(1);
|
|
99
|
-
expect(migrations.compareVersions('2.0.0', '1.0.0')).toBe(1);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should return 0 when v1 === v2', () => {
|
|
103
|
-
expect(migrations.compareVersions('1.0.0', '1.0.0')).toBe(0);
|
|
104
|
-
expect(migrations.compareVersions('1.5.10', '1.5.10')).toBe(0);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should handle version comparison correctly', () => {
|
|
108
|
-
// Patch version comparison
|
|
109
|
-
expect(migrations.compareVersions('1.0.5', '1.0.10')).toBe(-1);
|
|
110
|
-
expect(migrations.compareVersions('1.0.10', '1.0.5')).toBe(1);
|
|
111
|
-
|
|
112
|
-
// Minor version comparison
|
|
113
|
-
expect(migrations.compareVersions('1.5.0', '1.10.0')).toBe(-1);
|
|
114
|
-
|
|
115
|
-
// Major version comparison
|
|
116
|
-
expect(migrations.compareVersions('2.0.0', '10.0.0')).toBe(-1);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('should handle version strings with different formats', () => {
|
|
120
|
-
expect(migrations.compareVersions('1', '1.0.0')).toBe(0);
|
|
121
|
-
expect(migrations.compareVersions('1.0', '1.0.0')).toBe(0);
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
describe('runMigrations', () => {
|
|
126
|
-
it('should return success when already at latest version', () => {
|
|
127
|
-
// 设置当前版本为最新
|
|
128
|
-
migrations.setProjectVersion('/test-project', migrations.TEMPLATE_VERSION);
|
|
129
|
-
|
|
130
|
-
const result = migrations.runMigrations('/test-project', true);
|
|
131
|
-
|
|
132
|
-
expect(result.success).toBe(true);
|
|
133
|
-
expect(result.migrations).toEqual([]);
|
|
134
|
-
expect(result.currentVersion).toBe(migrations.TEMPLATE_VERSION);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it('should return success when version is newer than template', () => {
|
|
138
|
-
// 设置一个未来版本
|
|
139
|
-
migrations.setProjectVersion('/test-project', '2.0.0');
|
|
140
|
-
|
|
141
|
-
const result = migrations.runMigrations('/test-project', true);
|
|
142
|
-
|
|
143
|
-
expect(result.success).toBe(true);
|
|
144
|
-
expect(result.migrations).toEqual([]);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('should return correct structure when pending migrations exist', () => {
|
|
148
|
-
// 设置旧版本
|
|
149
|
-
migrations.setProjectVersion('/test-project', '1.0.0');
|
|
150
|
-
|
|
151
|
-
// 创建需要迁移的 settings.json
|
|
152
|
-
const settingsFile = path.join('/test-project', '.claude', 'settings.json');
|
|
153
|
-
fs.writeFileSync(
|
|
154
|
-
settingsFile,
|
|
155
|
-
JSON.stringify({ matcher: {}, hooks: [] }, null, 2)
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
const result = migrations.runMigrations('/test-project', true);
|
|
159
|
-
|
|
160
|
-
expect(result).toBeDefined();
|
|
161
|
-
expect(typeof result.success).toBe('boolean');
|
|
162
|
-
expect(result.currentVersion).toBeDefined();
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('should handle missing settings.json gracefully', () => {
|
|
166
|
-
migrations.setProjectVersion('/test-project', '1.0.0');
|
|
167
|
-
|
|
168
|
-
const result = migrations.runMigrations('/test-project', true);
|
|
169
|
-
|
|
170
|
-
expect(result.success).toBe(true);
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
describe('exports', () => {
|
|
175
|
-
it('should export TEMPLATE_VERSION constant', () => {
|
|
176
|
-
expect(migrations.TEMPLATE_VERSION).toBeDefined();
|
|
177
|
-
expect(typeof migrations.TEMPLATE_VERSION).toBe('string');
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('should export all functions', () => {
|
|
181
|
-
expect(typeof migrations.getProjectVersion).toBe('function');
|
|
182
|
-
expect(typeof migrations.setProjectVersion).toBe('function');
|
|
183
|
-
expect(typeof migrations.compareVersions).toBe('function');
|
|
184
|
-
expect(typeof migrations.runMigrations).toBe('function');
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
});
|