neo-skill 0.1.21 → 0.1.23
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/data/third_party/indexes/packages.all.json +4 -0
- package/data/third_party/indexes/units.all.json +6 -0
- package/data/third_party/indexes/units.by_ide.json +68 -0
- package/data/third_party/indexes/units.by_keyword.json +96 -0
- package/data/third_party/indexes/units.by_tag.json +60 -0
- package/data/third_party/packages/gh_aider-chat_aider.json +29 -0
- package/data/third_party/packages/gh_nextlevelbuilder_ui-ux-pro-max-skill.json +28 -0
- package/data/third_party/units/gh_aider-chat_aider#code-edit.json +36 -0
- package/data/third_party/units/gh_aider-chat_aider#git-commit.json +31 -0
- package/data/third_party/units/gh_nextlevelbuilder_ui-ux-pro-max-skill#design-system.json +38 -0
- package/data/third_party/units/gh_nextlevelbuilder_ui-ux-pro-max-skill#ui-design.json +57 -0
- package/package.json +2 -1
- package/skills/skill-finder/skillspec.json +10 -8
- package/src/skill_finder/cli.py +94 -8
- package/src/skill_finder/interview.py +61 -38
- package/src/skill_finder/matcher.py +65 -0
- package/src/skill_finder/registry.py +94 -8
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"antigravity": [
|
|
3
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
4
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
5
|
+
],
|
|
6
|
+
"claude": [
|
|
7
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
8
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
9
|
+
],
|
|
10
|
+
"codebuddy": [
|
|
11
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
12
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
13
|
+
],
|
|
14
|
+
"codex": [
|
|
15
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
16
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
17
|
+
],
|
|
18
|
+
"continue": [
|
|
19
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
20
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
21
|
+
],
|
|
22
|
+
"copilot": [
|
|
23
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
24
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
25
|
+
],
|
|
26
|
+
"cursor": [
|
|
27
|
+
"gh:aider-chat/aider#code-edit",
|
|
28
|
+
"gh:aider-chat/aider#git-commit",
|
|
29
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
30
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
31
|
+
],
|
|
32
|
+
"gemini": [
|
|
33
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
34
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
35
|
+
],
|
|
36
|
+
"generic": [
|
|
37
|
+
"gh:aider-chat/aider#code-edit",
|
|
38
|
+
"gh:aider-chat/aider#git-commit",
|
|
39
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
40
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
41
|
+
],
|
|
42
|
+
"kiro": [
|
|
43
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
44
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
45
|
+
],
|
|
46
|
+
"opencode": [
|
|
47
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
48
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
49
|
+
],
|
|
50
|
+
"qoder": [
|
|
51
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
52
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
53
|
+
],
|
|
54
|
+
"roocode": [
|
|
55
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
56
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
57
|
+
],
|
|
58
|
+
"trae": [
|
|
59
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
60
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
61
|
+
],
|
|
62
|
+
"windsurf": [
|
|
63
|
+
"gh:aider-chat/aider#code-edit",
|
|
64
|
+
"gh:aider-chat/aider#git-commit",
|
|
65
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
66
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
67
|
+
]
|
|
68
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ai coding": [
|
|
3
|
+
"gh:aider-chat/aider#code-edit"
|
|
4
|
+
],
|
|
5
|
+
"aider": [
|
|
6
|
+
"gh:aider-chat/aider#code-edit",
|
|
7
|
+
"gh:aider-chat/aider#git-commit"
|
|
8
|
+
],
|
|
9
|
+
"auto commit": [
|
|
10
|
+
"gh:aider-chat/aider#git-commit"
|
|
11
|
+
],
|
|
12
|
+
"code assistant": [
|
|
13
|
+
"gh:aider-chat/aider#code-edit"
|
|
14
|
+
],
|
|
15
|
+
"color palette": [
|
|
16
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system"
|
|
17
|
+
],
|
|
18
|
+
"commit message": [
|
|
19
|
+
"gh:aider-chat/aider#git-commit"
|
|
20
|
+
],
|
|
21
|
+
"components": [
|
|
22
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system"
|
|
23
|
+
],
|
|
24
|
+
"dashboard": [
|
|
25
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
26
|
+
],
|
|
27
|
+
"design": [
|
|
28
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
29
|
+
],
|
|
30
|
+
"design system": [
|
|
31
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system"
|
|
32
|
+
],
|
|
33
|
+
"design tokens": [
|
|
34
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system"
|
|
35
|
+
],
|
|
36
|
+
"edit multiple files": [
|
|
37
|
+
"gh:aider-chat/aider#code-edit"
|
|
38
|
+
],
|
|
39
|
+
"flutter": [
|
|
40
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
41
|
+
],
|
|
42
|
+
"git commit": [
|
|
43
|
+
"gh:aider-chat/aider#git-commit"
|
|
44
|
+
],
|
|
45
|
+
"landing page": [
|
|
46
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
47
|
+
],
|
|
48
|
+
"mobile app": [
|
|
49
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
50
|
+
],
|
|
51
|
+
"nextjs": [
|
|
52
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
53
|
+
],
|
|
54
|
+
"pair programming": [
|
|
55
|
+
"gh:aider-chat/aider#code-edit"
|
|
56
|
+
],
|
|
57
|
+
"react": [
|
|
58
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
59
|
+
],
|
|
60
|
+
"refactor": [
|
|
61
|
+
"gh:aider-chat/aider#code-edit"
|
|
62
|
+
],
|
|
63
|
+
"shadcn": [
|
|
64
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
65
|
+
],
|
|
66
|
+
"spacing": [
|
|
67
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system"
|
|
68
|
+
],
|
|
69
|
+
"style guide": [
|
|
70
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system"
|
|
71
|
+
],
|
|
72
|
+
"swiftui": [
|
|
73
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
74
|
+
],
|
|
75
|
+
"tailwind": [
|
|
76
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
77
|
+
],
|
|
78
|
+
"typography": [
|
|
79
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system"
|
|
80
|
+
],
|
|
81
|
+
"ui": [
|
|
82
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
83
|
+
],
|
|
84
|
+
"ui library": [
|
|
85
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system"
|
|
86
|
+
],
|
|
87
|
+
"ux": [
|
|
88
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
89
|
+
],
|
|
90
|
+
"vue": [
|
|
91
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
92
|
+
],
|
|
93
|
+
"website": [
|
|
94
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
95
|
+
]
|
|
96
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"code-generation": [
|
|
3
|
+
"gh:aider-chat/aider#code-edit"
|
|
4
|
+
],
|
|
5
|
+
"code-refactoring": [
|
|
6
|
+
"gh:aider-chat/aider#code-edit"
|
|
7
|
+
],
|
|
8
|
+
"commit-message": [
|
|
9
|
+
"gh:aider-chat/aider#git-commit"
|
|
10
|
+
],
|
|
11
|
+
"component-library": [
|
|
12
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system"
|
|
13
|
+
],
|
|
14
|
+
"context-aware": [
|
|
15
|
+
"gh:aider-chat/aider#code-edit"
|
|
16
|
+
],
|
|
17
|
+
"design-guidelines": [
|
|
18
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system"
|
|
19
|
+
],
|
|
20
|
+
"design-system": [
|
|
21
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
22
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
23
|
+
],
|
|
24
|
+
"design-tokens": [
|
|
25
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system"
|
|
26
|
+
],
|
|
27
|
+
"frontend": [
|
|
28
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
29
|
+
],
|
|
30
|
+
"git-commit": [
|
|
31
|
+
"gh:aider-chat/aider#git-commit"
|
|
32
|
+
],
|
|
33
|
+
"git-integration": [
|
|
34
|
+
"gh:aider-chat/aider#code-edit"
|
|
35
|
+
],
|
|
36
|
+
"git-workflow": [
|
|
37
|
+
"gh:aider-chat/aider#git-commit"
|
|
38
|
+
],
|
|
39
|
+
"mobile-design": [
|
|
40
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
41
|
+
],
|
|
42
|
+
"multi-file-editing": [
|
|
43
|
+
"gh:aider-chat/aider#code-edit"
|
|
44
|
+
],
|
|
45
|
+
"responsive-design": [
|
|
46
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
47
|
+
],
|
|
48
|
+
"style-guide": [
|
|
49
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system"
|
|
50
|
+
],
|
|
51
|
+
"ui-design": [
|
|
52
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
53
|
+
],
|
|
54
|
+
"ux-design": [
|
|
55
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
56
|
+
],
|
|
57
|
+
"web-design": [
|
|
58
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design"
|
|
59
|
+
]
|
|
60
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"package_id": "gh:aider-chat/aider",
|
|
3
|
+
"name": "Aider",
|
|
4
|
+
"description": "AI pair programming in your terminal - multi-file editing, git integration, LLM-powered coding assistant",
|
|
5
|
+
"source": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"repo": "aider-chat/aider",
|
|
8
|
+
"ref": "main"
|
|
9
|
+
},
|
|
10
|
+
"docs": {
|
|
11
|
+
"readme": "https://github.com/aider-chat/aider/blob/main/README.md",
|
|
12
|
+
"homepage": "https://aider.chat",
|
|
13
|
+
"issues": "https://github.com/aider-chat/aider/issues"
|
|
14
|
+
},
|
|
15
|
+
"supported_ides": ["windsurf", "cursor", "generic"],
|
|
16
|
+
"install": {
|
|
17
|
+
"method": "pip",
|
|
18
|
+
"auto_install_cmd": "pip install aider-chat",
|
|
19
|
+
"manual_install_cmd": "pip install aider-chat",
|
|
20
|
+
"uninstall_cmd": "pip uninstall -y aider-chat",
|
|
21
|
+
"notes": "需要 Python 3.8+;需配置 OPENAI_API_KEY 或其他 LLM provider"
|
|
22
|
+
},
|
|
23
|
+
"units": [
|
|
24
|
+
"gh:aider-chat/aider#code-edit",
|
|
25
|
+
"gh:aider-chat/aider#git-commit"
|
|
26
|
+
],
|
|
27
|
+
"trust_level": "trusted",
|
|
28
|
+
"notes": "成熟开源项目,活跃维护"
|
|
29
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"package_id": "gh:nextlevelbuilder/ui-ux-pro-max-skill",
|
|
3
|
+
"name": "UI UX Pro Max",
|
|
4
|
+
"description": "AI skill that provides design intelligence for building professional UI/UX across multiple platforms and frameworks. Includes 67 UI styles, 96 color palettes, 57 font pairings, and 100 reasoning rules for intelligent design system generation.",
|
|
5
|
+
"source": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"repo": "nextlevelbuilder/ui-ux-pro-max-skill",
|
|
8
|
+
"ref": "main"
|
|
9
|
+
},
|
|
10
|
+
"docs": {
|
|
11
|
+
"readme": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill/blob/main/README.md",
|
|
12
|
+
"homepage": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill"
|
|
13
|
+
},
|
|
14
|
+
"supported_ides": ["claude", "windsurf", "cursor", "antigravity", "copilot", "kiro", "codex", "qoder", "roocode", "gemini", "trae", "opencode", "continue", "codebuddy", "generic"],
|
|
15
|
+
"install": {
|
|
16
|
+
"method": "npm",
|
|
17
|
+
"auto_install_cmd": "npm install -g uipro-cli && uipro init --ai windsurf",
|
|
18
|
+
"manual_install_cmd": "npm install -g uipro-cli\nuipro init --ai <your-ai-assistant>",
|
|
19
|
+
"uninstall_cmd": "npm uninstall -g uipro-cli",
|
|
20
|
+
"notes": "需要 Node.js 16+;安装后需要在项目目录中运行 uipro init 命令指定 AI 助手类型"
|
|
21
|
+
},
|
|
22
|
+
"units": [
|
|
23
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design",
|
|
24
|
+
"gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system"
|
|
25
|
+
],
|
|
26
|
+
"trust_level": "trusted",
|
|
27
|
+
"notes": "成熟开源项目,支持 13 种技术栈,包含 100 条行业特定推理规则"
|
|
28
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"unit_id": "gh:aider-chat/aider#code-edit",
|
|
3
|
+
"package_id": "gh:aider-chat/aider",
|
|
4
|
+
"name": "Aider Code Editing",
|
|
5
|
+
"description": "AI-powered multi-file code editing with context awareness and git integration",
|
|
6
|
+
"capability_tags": [
|
|
7
|
+
"code-generation",
|
|
8
|
+
"code-refactoring",
|
|
9
|
+
"multi-file-editing",
|
|
10
|
+
"git-integration",
|
|
11
|
+
"context-aware"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"aider",
|
|
15
|
+
"ai coding",
|
|
16
|
+
"pair programming",
|
|
17
|
+
"code assistant",
|
|
18
|
+
"refactor",
|
|
19
|
+
"edit multiple files"
|
|
20
|
+
],
|
|
21
|
+
"ide_support": ["windsurf", "cursor", "generic"],
|
|
22
|
+
"entrypoints": [
|
|
23
|
+
{
|
|
24
|
+
"command": "aider",
|
|
25
|
+
"args": "[files...] [--model MODEL] [--message MSG]",
|
|
26
|
+
"cwd": "项目根目录(需要 git repo)",
|
|
27
|
+
"examples": [
|
|
28
|
+
"aider src/main.py src/utils.py",
|
|
29
|
+
"aider --model gpt-4 --message 'refactor authentication logic'",
|
|
30
|
+
"aider --yes --message 'add error handling to all API calls'"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"usage_notes": "Aider 在终端中运行,支持对话式编辑多个文件。\n- 自动追踪文件依赖\n- 生成 git commit\n- 支持多种 LLM(OpenAI/Anthropic/本地模型)\n- 可通过 --yes 跳过确认直接执行\n\n典型工作流:\n1. cd 到 git repo\n2. 运行 aider <files>\n3. 对话式描述需求\n4. Aider 自动编辑并 commit",
|
|
35
|
+
"conflicts": "与其他自动 commit 工具可能冲突;需要干净的 git 工作区"
|
|
36
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"unit_id": "gh:aider-chat/aider#git-commit",
|
|
3
|
+
"package_id": "gh:aider-chat/aider",
|
|
4
|
+
"name": "Aider Git Commit Generator",
|
|
5
|
+
"description": "AI-generated commit messages based on code changes",
|
|
6
|
+
"capability_tags": [
|
|
7
|
+
"git-commit",
|
|
8
|
+
"commit-message",
|
|
9
|
+
"git-workflow"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"git commit",
|
|
13
|
+
"commit message",
|
|
14
|
+
"auto commit",
|
|
15
|
+
"aider"
|
|
16
|
+
],
|
|
17
|
+
"ide_support": ["windsurf", "cursor", "generic"],
|
|
18
|
+
"entrypoints": [
|
|
19
|
+
{
|
|
20
|
+
"command": "aider --commit",
|
|
21
|
+
"args": "[--message MSG]",
|
|
22
|
+
"cwd": "git repo with staged changes",
|
|
23
|
+
"examples": [
|
|
24
|
+
"aider --commit",
|
|
25
|
+
"aider --commit --message 'feat: add user authentication'"
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"usage_notes": "使用 Aider 的 commit 功能生成语义化 commit message。\n- 分析 staged changes\n- 生成符合 conventional commits 的消息\n- 可手动覆盖\n\n使用场景:\n- 快速生成规范 commit message\n- 团队统一 commit 风格",
|
|
30
|
+
"conflicts": null
|
|
31
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"unit_id": "gh:nextlevelbuilder/ui-ux-pro-max-skill#design-system",
|
|
3
|
+
"package_id": "gh:nextlevelbuilder/ui-ux-pro-max-skill",
|
|
4
|
+
"name": "UI UX Pro Max - Design System Generator",
|
|
5
|
+
"description": "Intelligent design system generation with 100 industry-specific reasoning rules. Automatically generates complete design systems including colors, typography, spacing, and component guidelines based on product type and requirements.",
|
|
6
|
+
"capability_tags": [
|
|
7
|
+
"design-system",
|
|
8
|
+
"design-tokens",
|
|
9
|
+
"style-guide",
|
|
10
|
+
"component-library",
|
|
11
|
+
"design-guidelines"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"design system",
|
|
15
|
+
"design tokens",
|
|
16
|
+
"style guide",
|
|
17
|
+
"color palette",
|
|
18
|
+
"typography",
|
|
19
|
+
"spacing",
|
|
20
|
+
"components",
|
|
21
|
+
"ui library"
|
|
22
|
+
],
|
|
23
|
+
"ide_support": ["claude", "windsurf", "cursor", "antigravity", "copilot", "kiro", "codex", "qoder", "roocode", "gemini", "trae", "opencode", "continue", "codebuddy", "generic"],
|
|
24
|
+
"entrypoints": [
|
|
25
|
+
{
|
|
26
|
+
"command": "/design-system",
|
|
27
|
+
"args": "[product-type] [requirements]",
|
|
28
|
+
"cwd": "项目目录",
|
|
29
|
+
"examples": [
|
|
30
|
+
"/design-system Generate a design system for a SaaS dashboard",
|
|
31
|
+
"/design-system Create design tokens for a healthcare app",
|
|
32
|
+
"/design-system Build a style guide for an e-commerce platform"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"usage_notes": "设计系统生成器使用 100 条行业特定推理规则自动生成完整的设计系统。\n\n**功能特性**:\n- 67 种 UI 样式(Glassmorphism, Claymorphism, Minimalism, Brutalism, Neumorphism, Bento Grid, Dark Mode, AI-Native UI 等)\n- 96 种行业特定色板(SaaS, E-commerce, Healthcare, Fintech, Beauty 等)\n- 57 种字体配对(包含 Google Fonts 导入)\n- 25 种图表类型推荐(用于仪表板和分析)\n- 99 条 UX 指南(最佳实践、反模式、无障碍规则)\n\n**设计系统持久化**(Master + Overrides 模式):\n生成的设计系统可以保存并在项目中复用,支持覆盖特定组件的样式。\n\n**智能推荐**:\n根据产品类型(SaaS/电商/医疗/金融等)和需求自动匹配最佳设计方案。",
|
|
37
|
+
"conflicts": null
|
|
38
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"unit_id": "gh:nextlevelbuilder/ui-ux-pro-max-skill#ui-design",
|
|
3
|
+
"package_id": "gh:nextlevelbuilder/ui-ux-pro-max-skill",
|
|
4
|
+
"name": "UI UX Pro Max - UI Design",
|
|
5
|
+
"description": "Professional UI/UX design and implementation across multiple platforms. Auto-generates design systems with 67 UI styles, 96 color palettes, 57 font pairings, and intelligent recommendations based on product type.",
|
|
6
|
+
"capability_tags": [
|
|
7
|
+
"ui-design",
|
|
8
|
+
"ux-design",
|
|
9
|
+
"design-system",
|
|
10
|
+
"frontend",
|
|
11
|
+
"web-design",
|
|
12
|
+
"mobile-design",
|
|
13
|
+
"responsive-design"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ui",
|
|
17
|
+
"ux",
|
|
18
|
+
"design",
|
|
19
|
+
"landing page",
|
|
20
|
+
"dashboard",
|
|
21
|
+
"website",
|
|
22
|
+
"mobile app",
|
|
23
|
+
"react",
|
|
24
|
+
"nextjs",
|
|
25
|
+
"vue",
|
|
26
|
+
"tailwind",
|
|
27
|
+
"shadcn",
|
|
28
|
+
"swiftui",
|
|
29
|
+
"flutter"
|
|
30
|
+
],
|
|
31
|
+
"ide_support": ["claude", "windsurf", "cursor", "antigravity", "copilot", "kiro", "codex", "qoder", "roocode", "gemini", "trae", "opencode", "continue", "codebuddy", "generic"],
|
|
32
|
+
"entrypoints": [
|
|
33
|
+
{
|
|
34
|
+
"command": "自然语言触发(Skill Mode)",
|
|
35
|
+
"args": "直接描述需求,如 'Build a landing page for my SaaS product'",
|
|
36
|
+
"cwd": "项目目录",
|
|
37
|
+
"examples": [
|
|
38
|
+
"Build a landing page for my SaaS product",
|
|
39
|
+
"Create a dashboard for healthcare analytics",
|
|
40
|
+
"Design a portfolio website with dark mode",
|
|
41
|
+
"Make a mobile app UI for e-commerce",
|
|
42
|
+
"Build a fintech banking app with dark theme"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"command": "/ui-ux-pro-max(Workflow Mode)",
|
|
47
|
+
"args": "描述 UI/UX 需求",
|
|
48
|
+
"cwd": "项目目录",
|
|
49
|
+
"examples": [
|
|
50
|
+
"/ui-ux-pro-max Build a landing page for my SaaS product",
|
|
51
|
+
"/ui-ux-pro-max Create a dashboard for healthcare analytics"
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
"usage_notes": "UI UX Pro Max 提供智能设计系统生成和 UI/UX 实现。\n\n**工作流程**:\n1. 描述需求 - 请求任何 UI/UX 任务\n2. 自动生成设计系统 - AI 使用推理引擎生成完整设计系统\n3. 智能推荐 - 根据产品类型和需求匹配最佳样式、颜色、字体\n4. 代码生成 - 实现 UI 并遵循最佳实践\n5. 交付前检查 - 验证常见 UI/UX 反模式\n\n**支持的技术栈**:\n- Web: HTML+Tailwind, React, Next.js, Vue, Nuxt.js, Svelte, Astro, shadcn/ui, Nuxt UI\n- iOS: SwiftUI\n- Android: Jetpack Compose\n- 跨平台: React Native, Flutter\n\n**触发方式**:\n- Claude/Windsurf/Antigravity 等:自动激活(Skill Mode)\n- Cursor/Kiro/Copilot/Roo Code:使用 /ui-ux-pro-max 命令",
|
|
56
|
+
"conflicts": null
|
|
57
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neo-skill",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"description": "A multi-assistant skill generator (Claude/Windsurf/Cursor/GitHub Skills) driven by a canonical SkillSpec.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"bin/**",
|
|
11
11
|
"src/**",
|
|
12
|
+
"data/**",
|
|
12
13
|
"skills/**",
|
|
13
14
|
".shared/**",
|
|
14
15
|
".claude/**",
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
"primary_target": "windsurf",
|
|
6
6
|
"backward_compat": ["claude", "cursor", "github"],
|
|
7
7
|
"questions": [
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
8
|
+
"你需要什么能力?(一句话描述目标,例如:需要一个开发 UI/UX 的 skill)",
|
|
9
|
+
"你的技术栈/平台是什么?(如 React/Vue/Next.js/SwiftUI/Flutter/HTML+Tailwind;不确定可回车)",
|
|
10
|
+
"硬约束有哪些?(如不可联网/必须离线/不可修改文件等;无则回车)",
|
|
11
|
+
"你使用哪个 IDE?(默认当前 IDE;不确定可回车)"
|
|
12
12
|
],
|
|
13
13
|
"triggers": [
|
|
14
14
|
"找第三方 skill",
|
|
@@ -27,21 +27,23 @@
|
|
|
27
27
|
"title": "两级提问收集需求",
|
|
28
28
|
"kind": "action",
|
|
29
29
|
"commands": [],
|
|
30
|
-
"notes": "
|
|
30
|
+
"notes": "优先少问:先用用户原始请求直接匹配;若已高置信命中则不要反问,直接给出 Top1-3。\n\n若信息不足,仅允许 1 轮补充提问(总问题数<=5),问完立即进入匹配,不要进入第二轮追问。"
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
"id": "match",
|
|
34
34
|
"title": "匹配第三方 unit",
|
|
35
35
|
"kind": "action",
|
|
36
|
-
"commands": [
|
|
37
|
-
|
|
36
|
+
"commands": [
|
|
37
|
+
"python -m skill_finder.cli match --goal \"<goal>\" --ide <ide> --format json"
|
|
38
|
+
],
|
|
39
|
+
"notes": "优先使用内置第三方 registry(packages/units)作为事实来源进行匹配。\n\n匹配策略:\n- 先从用户 goal 里推断 tags/keywords(如 UI/UX/设计系统/原型/React 等),避免过多追问\n- 两阶段:粗筛(倒排索引或扫描 registry 文件降级)+ 精排(tag/keyword 覆盖率 + IDE 支持 + 约束过滤)\n- 置信度 < 60% 必须拒绝并说明原因\n\n当候选数量较少(例如 <=20)且语义需要判断时,可以把候选摘要(unit name/description/tags/keywords/IDE/README)作为输入交给 AI 做最终判定,但不得捏造“缺少 registry/索引”。"
|
|
38
40
|
},
|
|
39
41
|
{
|
|
40
42
|
"id": "present",
|
|
41
43
|
"title": "展示结果(Top 1-3 或拒绝)",
|
|
42
44
|
"kind": "gate",
|
|
43
45
|
"commands": [],
|
|
44
|
-
"notes": "命中:列出 unit + package + 匹配原因 + README 链接 +
|
|
46
|
+
"notes": "命中:列出 unit + package + 匹配原因 + README 链接 + 使用方式。\n未命中:明确拒绝原因分类(no_candidates_by_tag/no_ide_support/incompatible_env/insufficient_info)。\n禁止输出不实诊断(例如声称索引文件缺失),除非你已实际检查过本地文件。"
|
|
45
47
|
},
|
|
46
48
|
{
|
|
47
49
|
"id": "install",
|
package/src/skill_finder/cli.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""主动搜索入口 - CLI 接口"""
|
|
2
2
|
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
3
5
|
import sys
|
|
4
6
|
from typing import Optional
|
|
5
7
|
from .interview import Interview
|
|
@@ -89,17 +91,101 @@ class SkillFinderCLI:
|
|
|
89
91
|
if selected.unit.usage_notes:
|
|
90
92
|
print(f"\n使用说明:\n{selected.unit.usage_notes}")
|
|
91
93
|
|
|
94
|
+
def run_match(
|
|
95
|
+
self,
|
|
96
|
+
goal: str,
|
|
97
|
+
ide: Optional[str] = None,
|
|
98
|
+
env: Optional[str] = None,
|
|
99
|
+
constraints: Optional[str] = None,
|
|
100
|
+
top: int = 3,
|
|
101
|
+
output_format: str = "text",
|
|
102
|
+
min_score: Optional[float] = None,
|
|
103
|
+
):
|
|
104
|
+
if min_score is not None:
|
|
105
|
+
self.matcher.min_score = min_score
|
|
106
|
+
|
|
107
|
+
constraints_list = None
|
|
108
|
+
if constraints:
|
|
109
|
+
constraints_list = [c.strip() for c in constraints.replace(",", ",").split(",") if c.strip()]
|
|
110
|
+
|
|
111
|
+
from .models import SearchQuery
|
|
112
|
+
|
|
113
|
+
query = SearchQuery(goal=goal, ide=ide, env=env, constraints=constraints_list)
|
|
114
|
+
result = self.matcher.match(query)
|
|
115
|
+
|
|
116
|
+
if output_format == "json":
|
|
117
|
+
payload = {
|
|
118
|
+
"query": result.query.model_dump(),
|
|
119
|
+
"matches": [
|
|
120
|
+
{
|
|
121
|
+
"score": m.score,
|
|
122
|
+
"reasons": m.reasons,
|
|
123
|
+
"warnings": m.warnings,
|
|
124
|
+
"unit": m.unit.model_dump(),
|
|
125
|
+
"package": m.package.model_dump(),
|
|
126
|
+
}
|
|
127
|
+
for m in result.matches[: max(0, top)]
|
|
128
|
+
],
|
|
129
|
+
"rejection_reason": result.rejection_reason,
|
|
130
|
+
"rejection_category": result.rejection_category,
|
|
131
|
+
}
|
|
132
|
+
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
print("\n=== 匹配结果 ===")
|
|
136
|
+
if not result.matches:
|
|
137
|
+
print("✗ 未找到匹配的第三方能力\n")
|
|
138
|
+
print(f"拒绝原因: {result.rejection_reason}")
|
|
139
|
+
return
|
|
140
|
+
|
|
141
|
+
print(f"找到 {min(len(result.matches), top)} 个匹配的能力:\n")
|
|
142
|
+
for i, match in enumerate(result.matches[: max(0, top)], 1):
|
|
143
|
+
print(f"【{i}】{match.unit.name} ({match.package.name})")
|
|
144
|
+
print(f"- 描述: {match.unit.description}")
|
|
145
|
+
print(f"- 匹配原因: {'; '.join(match.reasons)}")
|
|
146
|
+
print(f"- 置信度: {match.score:.2f}")
|
|
147
|
+
print(f"- README: {match.package.docs.readme}")
|
|
148
|
+
if match.warnings:
|
|
149
|
+
print(f"- ⚠ 警告: {'; '.join(match.warnings)}")
|
|
150
|
+
print()
|
|
151
|
+
|
|
92
152
|
|
|
93
153
|
def main():
|
|
94
154
|
"""CLI 入口"""
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
155
|
+
parser = argparse.ArgumentParser(prog="skill-finder")
|
|
156
|
+
sub = parser.add_subparsers(dest="cmd")
|
|
157
|
+
|
|
158
|
+
match_p = sub.add_parser("match")
|
|
159
|
+
match_p.add_argument("--goal", required=True)
|
|
160
|
+
match_p.add_argument("--ide", default=None)
|
|
161
|
+
match_p.add_argument("--env", default=None)
|
|
162
|
+
match_p.add_argument("--constraints", default=None)
|
|
163
|
+
match_p.add_argument("--top", type=int, default=3)
|
|
164
|
+
match_p.add_argument("--format", choices=["text", "json"], default="text")
|
|
165
|
+
match_p.add_argument("--min-score", type=float, default=None)
|
|
166
|
+
|
|
167
|
+
sub.add_parser("doctor")
|
|
168
|
+
|
|
169
|
+
args = parser.parse_args(sys.argv[1:])
|
|
170
|
+
cli = SkillFinderCLI()
|
|
171
|
+
|
|
172
|
+
if args.cmd == "doctor":
|
|
173
|
+
print(cli.doctor.show_install_records())
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
if args.cmd == "match":
|
|
177
|
+
cli.run_match(
|
|
178
|
+
goal=args.goal,
|
|
179
|
+
ide=args.ide,
|
|
180
|
+
env=args.env,
|
|
181
|
+
constraints=args.constraints,
|
|
182
|
+
top=args.top,
|
|
183
|
+
output_format=args.format,
|
|
184
|
+
min_score=args.min_score,
|
|
185
|
+
)
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
cli.run()
|
|
103
189
|
|
|
104
190
|
|
|
105
191
|
if __name__ == "__main__":
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""两级提问逻辑"""
|
|
2
2
|
|
|
3
|
+
import re
|
|
3
4
|
from typing import Dict, List, Optional
|
|
4
5
|
from .models import SearchQuery
|
|
5
6
|
|
|
@@ -13,15 +14,9 @@ class Interview:
|
|
|
13
14
|
"text": "你想实现什么目标?(一句话描述任务)",
|
|
14
15
|
"required": True
|
|
15
16
|
},
|
|
16
|
-
{
|
|
17
|
-
"id": "input",
|
|
18
|
-
"text": "输入是什么?(文件/代码/数据/命令等)",
|
|
19
|
-
"required": False,
|
|
20
|
-
"default": "任意"
|
|
21
|
-
},
|
|
22
17
|
{
|
|
23
18
|
"id": "env",
|
|
24
|
-
"text": "
|
|
19
|
+
"text": "技术栈/环境?(如 React/Vue/Next.js/SwiftUI/Flutter/HTML+Tailwind;不确定可回车)",
|
|
25
20
|
"required": False,
|
|
26
21
|
"default": "通用"
|
|
27
22
|
},
|
|
@@ -29,14 +24,19 @@ class Interview:
|
|
|
29
24
|
"id": "ide",
|
|
30
25
|
"text": "使用哪个 IDE/编辑器?(windsurf/cursor/vscode/generic)",
|
|
31
26
|
"required": True
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "constraints",
|
|
30
|
+
"text": "硬约束?(如不可联网/必须离线/不可修改文件等;无则回车)",
|
|
31
|
+
"required": False
|
|
32
32
|
}
|
|
33
33
|
]
|
|
34
34
|
|
|
35
35
|
def collect(self) -> SearchQuery:
|
|
36
|
-
"""
|
|
36
|
+
"""收集需求(最多 1 轮,最多 4 问)"""
|
|
37
37
|
answers = {}
|
|
38
38
|
|
|
39
|
-
print("===
|
|
39
|
+
print("=== 需求收集 ===\n")
|
|
40
40
|
for q in self.LEVEL1_QUESTIONS:
|
|
41
41
|
if q["required"]:
|
|
42
42
|
answer = input(f"{q['text']}: ").strip()
|
|
@@ -47,45 +47,41 @@ class Interview:
|
|
|
47
47
|
default_hint = f" [默认: {q.get('default', '跳过')}]" if q.get('default') else ""
|
|
48
48
|
answer = input(f"{q['text']}{default_hint}: ").strip()
|
|
49
49
|
answers[q["id"]] = answer if answer else q.get("default")
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
detail = input("能否详细描述一下具体要做什么?: ").strip()
|
|
56
|
-
if detail:
|
|
57
|
-
answers["goal"] += f" - {detail}"
|
|
58
|
-
|
|
59
|
-
constraints = input("有什么硬约束吗?(如不可联网/必须离线/不可修改文件等,无则回车): ").strip()
|
|
60
|
-
if constraints:
|
|
61
|
-
answers["constraints"] = [c.strip() for c in constraints.split(",")]
|
|
50
|
+
|
|
51
|
+
constraints_value = answers.get("constraints")
|
|
52
|
+
constraints_list: Optional[List[str]] = None
|
|
53
|
+
if isinstance(constraints_value, str) and constraints_value and constraints_value != "通用":
|
|
54
|
+
constraints_list = [c.strip() for c in re.split(r"[,,]", constraints_value) if c.strip()]
|
|
62
55
|
|
|
63
56
|
return SearchQuery(
|
|
64
57
|
goal=answers["goal"],
|
|
65
|
-
input_type=answers.get("input"),
|
|
66
58
|
env=answers.get("env"),
|
|
67
59
|
ide=answers["ide"],
|
|
68
|
-
constraints=
|
|
60
|
+
constraints=constraints_list,
|
|
69
61
|
tags=self._extract_tags(answers["goal"]),
|
|
70
62
|
keywords=self._extract_keywords(answers["goal"])
|
|
71
63
|
)
|
|
72
64
|
|
|
73
|
-
def _needs_clarification(self, answers: Dict) -> bool:
|
|
74
|
-
"""判断是否需要 Level 2"""
|
|
75
|
-
return len(answers["goal"].split()) < 5
|
|
76
|
-
|
|
77
65
|
def _extract_tags(self, goal: str) -> List[str]:
|
|
78
66
|
"""从 goal 提取 tags(简单关键词映射)"""
|
|
79
67
|
tag_map = {
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
68
|
+
"ui": "ui-design",
|
|
69
|
+
"ux": "ux-design",
|
|
70
|
+
"ui/ux": "ui-design",
|
|
71
|
+
"原型": "ui-design",
|
|
72
|
+
"prototype": "ui-design",
|
|
73
|
+
"landing": "web-design",
|
|
74
|
+
"落地页": "web-design",
|
|
75
|
+
"dashboard": "frontend",
|
|
76
|
+
"仪表板": "frontend",
|
|
77
|
+
"设计系统": "design-system",
|
|
78
|
+
"design system": "design-system",
|
|
79
|
+
"style guide": "style-guide",
|
|
80
|
+
"组件库": "component-library",
|
|
81
|
+
"component library": "component-library",
|
|
82
|
+
"design token": "design-tokens",
|
|
83
|
+
"无障碍": "design-guidelines",
|
|
84
|
+
"accessibility": "design-guidelines",
|
|
89
85
|
}
|
|
90
86
|
tags = []
|
|
91
87
|
goal_lower = goal.lower()
|
|
@@ -97,5 +93,32 @@ class Interview:
|
|
|
97
93
|
|
|
98
94
|
def _extract_keywords(self, goal: str) -> List[str]:
|
|
99
95
|
"""提取关键词"""
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
keywords: List[str] = []
|
|
97
|
+
text = goal.strip()
|
|
98
|
+
|
|
99
|
+
for token in re.findall(r"[A-Za-z0-9][A-Za-z0-9\+\-\./#]*", text):
|
|
100
|
+
for part in re.split(r"[/_]+", token):
|
|
101
|
+
part = part.strip()
|
|
102
|
+
if part and part.lower() not in [k.lower() for k in keywords]:
|
|
103
|
+
keywords.append(part)
|
|
104
|
+
|
|
105
|
+
for token in re.split(r"\s+", text):
|
|
106
|
+
token = token.strip()
|
|
107
|
+
if token and len(token) > 2 and token.lower() not in [k.lower() for k in keywords]:
|
|
108
|
+
keywords.append(token)
|
|
109
|
+
|
|
110
|
+
synonyms = {
|
|
111
|
+
"ui/ux": ["ui", "ux"],
|
|
112
|
+
"用户体验": ["ux"],
|
|
113
|
+
"设计系统": ["design system", "design tokens"],
|
|
114
|
+
"原型": ["prototype"],
|
|
115
|
+
"落地页": ["landing page"],
|
|
116
|
+
}
|
|
117
|
+
lowered = text.lower()
|
|
118
|
+
for k, vals in synonyms.items():
|
|
119
|
+
if k in lowered:
|
|
120
|
+
for v in vals:
|
|
121
|
+
if v.lower() not in [x.lower() for x in keywords]:
|
|
122
|
+
keywords.append(v)
|
|
123
|
+
|
|
124
|
+
return keywords
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""匹配算法实现 - 两阶段匹配"""
|
|
2
2
|
|
|
3
|
+
import re
|
|
3
4
|
from typing import List, Dict, Set
|
|
4
5
|
from .models import SearchQuery, SearchResult, MatchResult
|
|
5
6
|
from .registry import Registry
|
|
@@ -11,9 +12,67 @@ class Matcher:
|
|
|
11
12
|
def __init__(self, registry: Registry, min_score: float = 0.6):
|
|
12
13
|
self.registry = registry
|
|
13
14
|
self.min_score = min_score
|
|
15
|
+
|
|
16
|
+
def _infer_tags_keywords(self, goal: str) -> Dict[str, List[str]]:
|
|
17
|
+
"""从自然语言 goal 粗提取 tags/keywords(用于减少追问)"""
|
|
18
|
+
goal_lower = (goal or "").lower()
|
|
19
|
+
|
|
20
|
+
tag_map = {
|
|
21
|
+
"ui": "ui-design",
|
|
22
|
+
"ux": "ux-design",
|
|
23
|
+
"ui/ux": "ui-design",
|
|
24
|
+
"用户体验": "ux-design",
|
|
25
|
+
"原型": "ui-design",
|
|
26
|
+
"prototype": "ui-design",
|
|
27
|
+
"设计系统": "design-system",
|
|
28
|
+
"design system": "design-system",
|
|
29
|
+
"design token": "design-tokens",
|
|
30
|
+
"design tokens": "design-tokens",
|
|
31
|
+
"style guide": "style-guide",
|
|
32
|
+
"组件库": "component-library",
|
|
33
|
+
"component library": "component-library",
|
|
34
|
+
"landing": "web-design",
|
|
35
|
+
"落地页": "web-design",
|
|
36
|
+
"dashboard": "frontend",
|
|
37
|
+
"仪表板": "frontend",
|
|
38
|
+
"无障碍": "design-guidelines",
|
|
39
|
+
"accessibility": "design-guidelines",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
tags: List[str] = []
|
|
43
|
+
for keyword, tag in tag_map.items():
|
|
44
|
+
if keyword in goal_lower and tag not in tags:
|
|
45
|
+
tags.append(tag)
|
|
46
|
+
|
|
47
|
+
keywords: List[str] = []
|
|
48
|
+
for token in re.findall(r"[A-Za-z0-9][A-Za-z0-9\+\-\./#]*", goal or ""):
|
|
49
|
+
for part in re.split(r"[/_]+", token):
|
|
50
|
+
part = part.strip()
|
|
51
|
+
if part and part.lower() not in [k.lower() for k in keywords]:
|
|
52
|
+
keywords.append(part)
|
|
53
|
+
|
|
54
|
+
synonyms = {
|
|
55
|
+
"ui/ux": ["ui", "ux"],
|
|
56
|
+
"原型": ["prototype"],
|
|
57
|
+
"落地页": ["landing page"],
|
|
58
|
+
"设计系统": ["design system", "design tokens"],
|
|
59
|
+
}
|
|
60
|
+
for k, vals in synonyms.items():
|
|
61
|
+
if k in goal_lower:
|
|
62
|
+
for v in vals:
|
|
63
|
+
if v.lower() not in [x.lower() for x in keywords]:
|
|
64
|
+
keywords.append(v)
|
|
65
|
+
|
|
66
|
+
return {"tags": tags, "keywords": keywords}
|
|
14
67
|
|
|
15
68
|
def match(self, query: SearchQuery) -> SearchResult:
|
|
16
69
|
"""执行两阶段匹配"""
|
|
70
|
+
inferred = self._infer_tags_keywords(query.goal)
|
|
71
|
+
if not query.tags and inferred["tags"]:
|
|
72
|
+
query.tags = inferred["tags"]
|
|
73
|
+
if not query.keywords and inferred["keywords"]:
|
|
74
|
+
query.keywords = inferred["keywords"]
|
|
75
|
+
|
|
17
76
|
candidates = self._coarse_filter(query)
|
|
18
77
|
|
|
19
78
|
if not candidates:
|
|
@@ -54,6 +113,12 @@ class Matcher:
|
|
|
54
113
|
if query.keywords:
|
|
55
114
|
for kw in query.keywords:
|
|
56
115
|
unit_ids.update(self.registry.search_by_keyword(kw))
|
|
116
|
+
|
|
117
|
+
if not unit_ids:
|
|
118
|
+
if query.ide:
|
|
119
|
+
unit_ids = set(self.registry.search_by_ide(query.ide))
|
|
120
|
+
else:
|
|
121
|
+
unit_ids = set(self.registry.get_all_units())
|
|
57
122
|
|
|
58
123
|
if query.ide:
|
|
59
124
|
ide_units = set(self.registry.search_by_ide(query.ide))
|
|
@@ -21,13 +21,24 @@ class Registry:
|
|
|
21
21
|
self._packages_cache: Dict[str, SkillPackage] = {}
|
|
22
22
|
self._units_cache: Dict[str, SkillUnit] = {}
|
|
23
23
|
self.indexes: Dict[str, dict] = {}
|
|
24
|
+
self.indexes_available: bool = False
|
|
24
25
|
|
|
25
26
|
self._load_indexes()
|
|
26
27
|
|
|
27
28
|
def _load_indexes(self):
|
|
28
29
|
"""加载所有索引文件"""
|
|
29
30
|
if not self.indexes_dir.exists():
|
|
30
|
-
|
|
31
|
+
self.indexes_available = False
|
|
32
|
+
self.indexes = {
|
|
33
|
+
"by_tag": {},
|
|
34
|
+
"by_keyword": {},
|
|
35
|
+
"by_ide": {},
|
|
36
|
+
"packages_all": [],
|
|
37
|
+
"units_all": [],
|
|
38
|
+
}
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
self.indexes_available = True
|
|
31
42
|
|
|
32
43
|
index_files = {
|
|
33
44
|
"by_tag": "units.by_tag.json",
|
|
@@ -44,6 +55,44 @@ class Registry:
|
|
|
44
55
|
self.indexes[key] = json.load(f)
|
|
45
56
|
else:
|
|
46
57
|
self.indexes[key] = {} if key != "packages_all" and key != "units_all" else []
|
|
58
|
+
|
|
59
|
+
def _load_all_units_from_disk(self) -> Dict[str, SkillUnit]:
|
|
60
|
+
"""从磁盘加载所有 units(用于索引缺失时的降级查询)"""
|
|
61
|
+
if self._units_cache:
|
|
62
|
+
return self._units_cache
|
|
63
|
+
|
|
64
|
+
if not self.units_dir.exists():
|
|
65
|
+
return self._units_cache
|
|
66
|
+
|
|
67
|
+
for unit_path in self.units_dir.glob("*.json"):
|
|
68
|
+
try:
|
|
69
|
+
with open(unit_path, 'r', encoding='utf-8') as f:
|
|
70
|
+
data = json.load(f)
|
|
71
|
+
unit = SkillUnit(**data)
|
|
72
|
+
self._units_cache[unit.unit_id] = unit
|
|
73
|
+
except Exception as e:
|
|
74
|
+
print(f"加载 unit 文件 {unit_path} 失败: {e}")
|
|
75
|
+
|
|
76
|
+
return self._units_cache
|
|
77
|
+
|
|
78
|
+
def _load_all_packages_from_disk(self) -> Dict[str, SkillPackage]:
|
|
79
|
+
"""从磁盘加载所有 packages(用于索引缺失时的降级查询)"""
|
|
80
|
+
if self._packages_cache:
|
|
81
|
+
return self._packages_cache
|
|
82
|
+
|
|
83
|
+
if not self.packages_dir.exists():
|
|
84
|
+
return self._packages_cache
|
|
85
|
+
|
|
86
|
+
for package_path in self.packages_dir.glob("*.json"):
|
|
87
|
+
try:
|
|
88
|
+
with open(package_path, 'r', encoding='utf-8') as f:
|
|
89
|
+
data = json.load(f)
|
|
90
|
+
package = SkillPackage(**data)
|
|
91
|
+
self._packages_cache[package.package_id] = package
|
|
92
|
+
except Exception as e:
|
|
93
|
+
print(f"加载 package 文件 {package_path} 失败: {e}")
|
|
94
|
+
|
|
95
|
+
return self._packages_cache
|
|
47
96
|
|
|
48
97
|
def get_package(self, package_id: str) -> Optional[SkillPackage]:
|
|
49
98
|
"""获取 package(带缓存)"""
|
|
@@ -54,7 +103,8 @@ class Registry:
|
|
|
54
103
|
package_path = self.packages_dir / f"{sanitized_id}.json"
|
|
55
104
|
|
|
56
105
|
if not package_path.exists():
|
|
57
|
-
|
|
106
|
+
self._load_all_packages_from_disk()
|
|
107
|
+
return self._packages_cache.get(package_id)
|
|
58
108
|
|
|
59
109
|
try:
|
|
60
110
|
with open(package_path, 'r', encoding='utf-8') as f:
|
|
@@ -75,7 +125,8 @@ class Registry:
|
|
|
75
125
|
unit_path = self.units_dir / f"{sanitized_id}.json"
|
|
76
126
|
|
|
77
127
|
if not unit_path.exists():
|
|
78
|
-
|
|
128
|
+
self._load_all_units_from_disk()
|
|
129
|
+
return self._units_cache.get(unit_id)
|
|
79
130
|
|
|
80
131
|
try:
|
|
81
132
|
with open(unit_path, 'r', encoding='utf-8') as f:
|
|
@@ -89,20 +140,55 @@ class Registry:
|
|
|
89
140
|
|
|
90
141
|
def search_by_tag(self, tag: str) -> List[str]:
|
|
91
142
|
"""通过 tag 搜索 unit_id 列表"""
|
|
92
|
-
|
|
143
|
+
by_tag = self.indexes.get("by_tag", {})
|
|
144
|
+
if by_tag:
|
|
145
|
+
return by_tag.get(tag.lower(), [])
|
|
146
|
+
|
|
147
|
+
tag_lower = tag.lower()
|
|
148
|
+
unit_ids: List[str] = []
|
|
149
|
+
for unit in self._load_all_units_from_disk().values():
|
|
150
|
+
if tag_lower in [t.lower() for t in unit.capability_tags]:
|
|
151
|
+
unit_ids.append(unit.unit_id)
|
|
152
|
+
return unit_ids
|
|
93
153
|
|
|
94
154
|
def search_by_keyword(self, keyword: str) -> List[str]:
|
|
95
155
|
"""通过 keyword 搜索 unit_id 列表"""
|
|
96
|
-
|
|
156
|
+
by_keyword = self.indexes.get("by_keyword", {})
|
|
157
|
+
if by_keyword:
|
|
158
|
+
return by_keyword.get(keyword.lower(), [])
|
|
159
|
+
|
|
160
|
+
kw_lower = keyword.lower()
|
|
161
|
+
unit_ids: List[str] = []
|
|
162
|
+
for unit in self._load_all_units_from_disk().values():
|
|
163
|
+
if kw_lower in [k.lower() for k in unit.keywords]:
|
|
164
|
+
unit_ids.append(unit.unit_id)
|
|
165
|
+
return unit_ids
|
|
97
166
|
|
|
98
167
|
def search_by_ide(self, ide: str) -> List[str]:
|
|
99
168
|
"""通过 IDE 搜索 unit_id 列表"""
|
|
100
|
-
|
|
169
|
+
by_ide = self.indexes.get("by_ide", {})
|
|
170
|
+
if by_ide:
|
|
171
|
+
return by_ide.get(ide.lower(), [])
|
|
172
|
+
|
|
173
|
+
ide_lower = ide.lower()
|
|
174
|
+
unit_ids: List[str] = []
|
|
175
|
+
for unit in self._load_all_units_from_disk().values():
|
|
176
|
+
if ide_lower in [i.lower() for i in unit.ide_support]:
|
|
177
|
+
unit_ids.append(unit.unit_id)
|
|
178
|
+
return unit_ids
|
|
101
179
|
|
|
102
180
|
def get_all_packages(self) -> List[str]:
|
|
103
181
|
"""获取所有 package_id"""
|
|
104
|
-
|
|
182
|
+
packages_all = self.indexes.get("packages_all", [])
|
|
183
|
+
if packages_all:
|
|
184
|
+
return packages_all
|
|
185
|
+
|
|
186
|
+
return list(self._load_all_packages_from_disk().keys())
|
|
105
187
|
|
|
106
188
|
def get_all_units(self) -> List[str]:
|
|
107
189
|
"""获取所有 unit_id"""
|
|
108
|
-
|
|
190
|
+
units_all = self.indexes.get("units_all", [])
|
|
191
|
+
if units_all:
|
|
192
|
+
return units_all
|
|
193
|
+
|
|
194
|
+
return list(self._load_all_units_from_disk().keys())
|