skillcaddy 0.27.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 (99) hide show
  1. package/CHANGELOG.md +497 -0
  2. package/CONTEXT.md +149 -0
  3. package/LICENSE +21 -0
  4. package/README.md +499 -0
  5. package/README_CN.md +482 -0
  6. package/collection-metadata/github/mattpocock.json +16 -0
  7. package/lib/canonicalJson.js +11 -0
  8. package/lib/claudeStore.js +135 -0
  9. package/lib/collectionEnablement.js +115 -0
  10. package/lib/collectionSetup.js +199 -0
  11. package/lib/enablePlan.js +116 -0
  12. package/lib/enablementScope.js +8 -0
  13. package/lib/hermesStore.js +21 -0
  14. package/lib/libraryImage.js +1785 -0
  15. package/lib/managerInstall.js +109 -0
  16. package/lib/managerSourceWorkflow.js +234 -0
  17. package/lib/projectActions.js +24 -0
  18. package/lib/projectPath.js +51 -0
  19. package/lib/roots.js +99 -0
  20. package/lib/skillLinkStore.js +68 -0
  21. package/lib/skillMetadata.js +191 -0
  22. package/lib/skillStore.js +541 -0
  23. package/lib/sourceAcquisition.js +475 -0
  24. package/lib/sourceAcquisitionAdapter.js +47 -0
  25. package/lib/sourceAcquisitionError.js +18 -0
  26. package/lib/sourceAcquisitionPolicy.js +139 -0
  27. package/lib/sourceArchive.js +363 -0
  28. package/lib/sourceArchiveWorkspace.js +36 -0
  29. package/lib/sourceGit.js +95 -0
  30. package/lib/sourceGitAcquisitionAdapter.js +145 -0
  31. package/lib/sourceGitBatchUpdate.js +168 -0
  32. package/lib/sourceGitRepair.js +227 -0
  33. package/lib/sourceGitStaging.js +139 -0
  34. package/lib/sourceGitUpgradeAdapter.js +583 -0
  35. package/lib/sourceGitUrl.js +170 -0
  36. package/lib/sourceHttp.js +302 -0
  37. package/lib/sourceLocal.js +200 -0
  38. package/lib/sourceLocalAcquisitionAdapter.js +107 -0
  39. package/lib/sourceManager.js +242 -0
  40. package/lib/sourceMigration.js +172 -0
  41. package/lib/sourceMigrationIssue.js +6 -0
  42. package/lib/sourcePath.js +6 -0
  43. package/lib/sourcePolicy.js +53 -0
  44. package/lib/sourceRecord.js +295 -0
  45. package/lib/sourceRegistry.js +155 -0
  46. package/lib/sourceRemoteArchiveAcquisitionAdapter.js +147 -0
  47. package/lib/sourceRemoteFileAcquisitionAdapter.js +95 -0
  48. package/lib/sourceRemoteFilePolicy.js +62 -0
  49. package/lib/sourceRemoteFileWorkspace.js +55 -0
  50. package/lib/sourceSkillDiscovery.js +117 -0
  51. package/lib/sourceTree.js +185 -0
  52. package/lib/sourceUpgrade.js +596 -0
  53. package/lib/sourceUpgradeAdapter.js +41 -0
  54. package/lib/sourceUpgradePolicy.js +170 -0
  55. package/lib/sourceUpgradeTransaction.js +202 -0
  56. package/lib/sourceValidation.js +120 -0
  57. package/lib/sourceWorkspace.js +37 -0
  58. package/lib/specGate.js +235 -0
  59. package/lib/tuiActions.js +169 -0
  60. package/lib/tuiInstall.js +133 -0
  61. package/lib/tuiLayout.js +94 -0
  62. package/lib/version.js +17 -0
  63. package/lib/webManager.js +370 -0
  64. package/package.json +74 -0
  65. package/public/agentsUi.js +138 -0
  66. package/public/app.js +1431 -0
  67. package/public/claude.css +2 -0
  68. package/public/claudeUi.js +119 -0
  69. package/public/emptyState.js +12 -0
  70. package/public/enabled.css +303 -0
  71. package/public/favicon.svg +5 -0
  72. package/public/index.html +199 -0
  73. package/public/skillcaddy_CN.png +0 -0
  74. package/public/skillcaddy_EN.png +0 -0
  75. package/public/styles.css +1611 -0
  76. package/scripts/cli.js +647 -0
  77. package/scripts/cron-sync-update-git.sh +128 -0
  78. package/scripts/pull-github.sh +22 -0
  79. package/scripts/source.js +638 -0
  80. package/scripts/tui.js +1058 -0
  81. package/server.js +196 -0
  82. package/skills/skillcaddy-manager/SKILL.md +93 -0
  83. package/skills/skillcaddy-manager/agents/openai.yaml +7 -0
  84. package/skills/skillcaddy-manager/references/METADATA.md +75 -0
  85. package/skills/skillcaddy-manager/references/OPERATIONS.md +315 -0
  86. package/skills/skillcaddy-manager/references/RECOMMENDATION_GUIDE.md +51 -0
  87. package/skills/skillcaddy-manager/references/featured-skills.json +564 -0
  88. package/skills/skillcaddy-manager/references/skill-platforms.json +48 -0
  89. package/skills/skillcaddy-manager/scripts/check-conflicts.cjs +117 -0
  90. package/skills/skillcaddy-manager/scripts/check-global-skills.cjs +169 -0
  91. package/skills/skillcaddy-manager/scripts/translate-skill-notes.cjs +297 -0
  92. package/skills/skillcaddy-manager/scripts/version-manager.cjs +146 -0
  93. package/skills/skillcaddy-manager/scripts/view-recommendations.cjs +279 -0
  94. package/skills/skillcaddy-manager/skillcaddy.json +9 -0
  95. package/skills/spec-pipeline/spec-direction-gate/SKILL.md +119 -0
  96. package/skills/spec-pipeline/spec-direction-gate/check-approval.py +110 -0
  97. package/skills/spec-pipeline/spec-draft-intake/SKILL.md +116 -0
  98. package/skills/spec-pipeline/spec-role-contract/SKILL.md +113 -0
  99. package/skills/spec-pipeline/spec-role-contract/check-roles.py +108 -0
@@ -0,0 +1,2 @@
1
+ /* Claude Code 列改用统一的 .enabled 组件(见 enabled.css)。该文件保留仅为兼容
2
+ 历史引用与第三方加载顺序;样式集中在 enabled.css。 */
@@ -0,0 +1,119 @@
1
+ import { emptyState } from './emptyState.js';
2
+
3
+ // Claude Code 段复用与 .agents/skills 完全相同的 .enabled 组件。
4
+ // 唯一差异:Claude Code 段有「同步」动作(项目 .agents/skills → Claude Code 软链),
5
+ // 因此额外接收 syncEnabled 回调,并在列头由 index.html 提供按钮。
6
+ export function renderClaudeStatus({ claude, skills, elements, onUnlink, onSync, isPreview = false, query = '' }) {
7
+ const normalizedQuery = query.trim().toLowerCase();
8
+ elements.unlinkClaude.disabled = isPreview || !claude || !claude.exists || claude.skills.length === 0;
9
+ if (isPreview) {
10
+ elements.unlinkClaude.title = '预览模式只读:先在下方读取你自己的项目再操作';
11
+ elements.syncClaude.disabled = true;
12
+ elements.syncClaude.title = '预览模式只读:先在下方读取你自己的项目再操作';
13
+ } else {
14
+ elements.syncClaude.disabled = !claude || !claude.exists;
15
+ elements.syncClaude.title = '把项目已启用的 skill 同步到 Claude Code';
16
+ }
17
+ if (onSync) elements.syncClaude.onclick = onSync;
18
+ if (!claude || !claude.exists) {
19
+ elements.claudeResultCount.hidden = true;
20
+ elements.claudeResultCount.textContent = '';
21
+ // 此前这里直接清空,整栏渲染成一片空白:用户既不知道这栏是什么,
22
+ // 也不知道同栏标题里的「同步」就是填充它的入口。
23
+ elements.claudeSkillList.replaceChildren(emptyState(
24
+ '还没有 Claude Code 入口',
25
+ '点这一栏标题右侧的「同步」,把 .agents/skills 已启用的 skill 同步到 Claude Code。'
26
+ ));
27
+ return 0;
28
+ }
29
+
30
+ const filtered = claude.skills.filter((skill) => matchesEnabledQuery(skill, skills, normalizedQuery));
31
+ elements.claudeResultCount.hidden = !normalizedQuery;
32
+ elements.claudeResultCount.textContent = normalizedQuery ? `显示 ${filtered.length}/${claude.skills.length}` : '';
33
+ if (filtered.length === 0 && claude.skills.length > 0) {
34
+ elements.claudeSkillList.replaceChildren(emptyState('没有匹配的 skill', '换一个名称或来源,或清空上方搜索。'));
35
+ return 0;
36
+ }
37
+
38
+ renderClaudeSkills({ skills: filtered, sourceSkills: skills, elements, onUnlink, isPreview });
39
+ return filtered.length;
40
+ }
41
+
42
+ function matchesEnabledQuery(skill, sourceSkills, query) {
43
+ if (!query) return true;
44
+ const sourceSkill = sourceSkills.find((candidate) => candidate.name === skill.alias);
45
+ const fields = [
46
+ skill.alias,
47
+ skill.targetPath,
48
+ sourceSkill?.name,
49
+ sourceSkill?.source,
50
+ sourceSkill?.collection,
51
+ sourceSkill?.description,
52
+ sourceSkill?.note,
53
+ ...(sourceSkill?.tags || [])
54
+ ];
55
+ return fields.some((value) => String(value || '').toLowerCase().includes(query));
56
+ }
57
+
58
+ function renderClaudeSkills({ skills, sourceSkills, elements, onUnlink, isPreview = false }) {
59
+ elements.claudeSkillList.replaceChildren();
60
+ if (skills.length === 0) {
61
+ elements.claudeSkillList.append(emptyState(
62
+ 'Claude Code 还没有同步任何 skill',
63
+ '点这一栏标题右侧的「同步」,把 .agents/skills 已启用的 skill 同步过来。'
64
+ ));
65
+ return;
66
+ }
67
+
68
+ skills.forEach((skill) => {
69
+ const item = document.createElement('article');
70
+ // 与 .agents/skills / 全局 / Hermes 同款结构:name + 来源徽标 + 路径 + 移除按钮。
71
+ item.className = 'enabled';
72
+ item.dataset.focusScope = '';
73
+ item.innerHTML = `
74
+ <div>
75
+ <div class="enabled-head">
76
+ <strong class="name"></strong>
77
+ <span class="tag-pill enabled-source"></span>
78
+ </div>
79
+ <p class="path"></p>
80
+ </div>
81
+ <div class="actions"></div>
82
+ `;
83
+ item.querySelector('.name').textContent = skill.alias;
84
+ item.querySelector('.path').textContent = skill.targetPath || (skill.isSymlink ? '断开的软链接' : '非软链接条目');
85
+
86
+ // Claude Code 软链的目标是某个 skill 原件;按 alias 找到来源后挂上 source 徽标,
87
+ // 视觉上与 .agents/skills 列表对齐(同源同名条目也能靠徽标区分)。
88
+ const sourceSkill = sourceSkills.find((s) => s.name === skill.alias);
89
+ const sourceChip = item.querySelector('.enabled-source');
90
+ if (sourceSkill) {
91
+ sourceChip.textContent = sourceSkill.source;
92
+ sourceChip.title = `${sourceSkill.collection} 来源`;
93
+ const description = sourceSkill.note || sourceSkill.description;
94
+ if (description) item.title = description;
95
+ } else {
96
+ sourceChip.hidden = true;
97
+ }
98
+
99
+ const button = document.createElement('button');
100
+ button.className = 'secondary danger';
101
+ button.type = 'button';
102
+ // 与 agents 列表一致:单条用「移除」。
103
+ button.textContent = '移除';
104
+ button.dataset.focusKey = `claude-remove:${skill.alias}`;
105
+ button.dataset.focusFallbackSelector = '#claudeSkillList [data-focus-key^="claude-remove:"]:not(:disabled)';
106
+ button.dataset.focusFallbackKey = 'enabled-search';
107
+ button.setAttribute('aria-label', `从 Claude Code 移除 ${skill.alias}`);
108
+ button.disabled = isPreview || !skill.isSymlink;
109
+ if (!skill.isSymlink) {
110
+ button.textContent = '不可移除';
111
+ button.title = '非软链接条目无法移除';
112
+ button.setAttribute('aria-label', `${skill.alias} 不是软链接,无法移除`);
113
+ }
114
+ if (isPreview) button.title = '预览模式只读:先在下方读取你自己的项目再操作';
115
+ button.addEventListener('click', () => onUnlink(skill.alias));
116
+ item.querySelector('.actions').append(button);
117
+ elements.claudeSkillList.append(item);
118
+ });
119
+ }
@@ -0,0 +1,12 @@
1
+ // 空态统一在这里构造:先说明这是什么,再给下一步。
2
+ // 只写「没有内容」的空态会让用户以为出了错,而不是知道该做什么。
3
+ export function emptyState(title, detail) {
4
+ const element = document.createElement('div');
5
+ element.className = 'empty';
6
+
7
+ const heading = document.createElement('strong');
8
+ heading.textContent = title;
9
+ element.append(heading, document.createTextNode(detail));
10
+
11
+ return element;
12
+ }
@@ -0,0 +1,303 @@
1
+ /* 四个作用域是四个 agent 通道:项目级 Agents/Claude 并列,跨项目的全局与
2
+ 按需出现的 Hermes 各自占满下一行。列表限高,确保全局通道不会被长列表推走。 */
3
+ .enabled-grid {
4
+ display: grid;
5
+ grid-template-columns: repeat(2, minmax(0, 1fr));
6
+ gap: 12px;
7
+ background: var(--surface-soft);
8
+ padding: 12px;
9
+ }
10
+
11
+ .enabled-column {
12
+ display: flex;
13
+ min-width: 0;
14
+ flex-direction: column;
15
+ overflow: hidden;
16
+ border: 1px solid var(--scope-line, var(--hairline));
17
+ border-radius: var(--radius-md);
18
+ background: var(--scope-surface, var(--canvas));
19
+ }
20
+
21
+ .scope-agents {
22
+ --scope-surface: var(--scope-agents-surface);
23
+ --scope-line: var(--scope-agents-line);
24
+ --scope-ink: var(--scope-agents-ink);
25
+ }
26
+
27
+ .scope-claude {
28
+ --scope-surface: var(--scope-claude-surface);
29
+ --scope-line: var(--scope-claude-line);
30
+ --scope-ink: var(--scope-claude-ink);
31
+ }
32
+
33
+ .scope-global,
34
+ .scope-hermes {
35
+ grid-column: 1 / -1;
36
+ }
37
+
38
+ .scope-global {
39
+ --scope-surface: var(--scope-global-surface);
40
+ --scope-line: var(--scope-global-line);
41
+ --scope-ink: var(--scope-global-ink);
42
+ }
43
+
44
+ .scope-hermes {
45
+ --scope-surface: var(--scope-hermes-surface);
46
+ --scope-line: var(--scope-hermes-line);
47
+ --scope-ink: var(--scope-hermes-ink);
48
+ }
49
+
50
+ .column-title {
51
+ display: flex;
52
+ min-height: 84px;
53
+ align-items: center;
54
+ gap: 10px;
55
+ border-bottom: 1px solid var(--hairline);
56
+ padding: 14px 16px;
57
+ }
58
+
59
+ .enabled-toolbar {
60
+ display: flex;
61
+ align-items: flex-end;
62
+ gap: 12px;
63
+ border-bottom: 1px solid var(--hairline);
64
+ background: var(--canvas);
65
+ padding: 12px 20px;
66
+ }
67
+
68
+ .enabled-search {
69
+ display: grid;
70
+ flex: 1 1 auto;
71
+ gap: 4px;
72
+ min-width: 0;
73
+ }
74
+
75
+ .enabled-search > span {
76
+ display: block;
77
+ color: var(--muted);
78
+ font: 500 12px/1.4 var(--sans);
79
+ }
80
+
81
+ .enabled-search input {
82
+ min-height: 36px;
83
+ }
84
+
85
+ .enabled-search-summary {
86
+ flex: 0 0 auto;
87
+ margin: 0 0 9px;
88
+ color: var(--muted-soft);
89
+ font: 12px/1.4 var(--mono);
90
+ white-space: nowrap;
91
+ }
92
+
93
+ /* 专属符号是通道的快速识别物;固定线宽与单色字体避免落入彩色 emoji 风格。 */
94
+ .scope-icon {
95
+ display: grid;
96
+ width: 32px;
97
+ height: 32px;
98
+ flex: 0 0 32px;
99
+ place-items: center;
100
+ border: 1px solid currentColor;
101
+ border-radius: 50%;
102
+ color: var(--scope-ink);
103
+ font: 18px/1 "Apple Symbols", "Segoe UI Symbol", sans-serif;
104
+ }
105
+
106
+ .scope-title-copy {
107
+ min-width: 0;
108
+ }
109
+
110
+ .scope-title-copy h3 {
111
+ color: var(--scope-ink);
112
+ font-size: 15px;
113
+ font-weight: 600;
114
+ }
115
+
116
+ .scope-summary {
117
+ margin: 4px 0 0;
118
+ color: var(--muted);
119
+ font: 12px/1.45 var(--sans);
120
+ }
121
+
122
+ .scope-summary .count {
123
+ color: var(--scope-ink);
124
+ }
125
+
126
+ .scope-result {
127
+ color: var(--scope-ink);
128
+ font: 500 11px/1.4 var(--mono);
129
+ white-space: nowrap;
130
+ }
131
+
132
+ .column-title-actions {
133
+ display: inline-flex;
134
+ flex: 0 0 auto;
135
+ flex-wrap: nowrap;
136
+ align-items: center;
137
+ gap: 8px;
138
+ }
139
+
140
+ /* 批量动作固定在卡片底部,与通道身份、计数和说明分离。 */
141
+ .column-title-actions button,
142
+ .scope-card-footer > button.column-title-action {
143
+ height: 36px;
144
+ min-height: 36px;
145
+ padding: 0 12px;
146
+ font-size: 13px;
147
+ line-height: 1;
148
+ white-space: nowrap;
149
+ }
150
+
151
+ .scope-card-footer {
152
+ display: flex;
153
+ min-height: 58px;
154
+ align-items: center;
155
+ justify-content: space-between;
156
+ gap: 10px;
157
+ margin-top: auto;
158
+ border-top: 1px solid var(--hairline);
159
+ padding: 10px 12px;
160
+ }
161
+
162
+ .scope-card-footer code {
163
+ min-width: 0;
164
+ overflow: hidden;
165
+ color: var(--muted-soft);
166
+ font: 11px/1.4 var(--mono);
167
+ text-overflow: ellipsis;
168
+ white-space: nowrap;
169
+ }
170
+
171
+ .count {
172
+ display: inline-block;
173
+ margin-left: 0;
174
+ border-radius: 999px;
175
+ background: var(--surface-card);
176
+ color: var(--ink);
177
+ padding: 2px 8px;
178
+ font: 500 12px/1.4 var(--sans);
179
+ font-variant-numeric: tabular-nums;
180
+ vertical-align: middle;
181
+ }
182
+
183
+ /* 列表行:四个段(.agents/skills、Claude Code、全局、Hermes)共用同一个 .enabled 组件。
184
+ 之前 Claude Code 单独走 .claude-skill,导致没有「来源徽标」与项目列不齐。 */
185
+ .list {
186
+ display: grid;
187
+ gap: 1px;
188
+ background: var(--hairline);
189
+ min-width: 0;
190
+ }
191
+
192
+ .list.compact {
193
+ max-height: 280px;
194
+ overflow-y: auto;
195
+ margin: 0;
196
+ scrollbar-color: var(--scroll-thumb) transparent;
197
+ }
198
+
199
+ .enabled {
200
+ display: flex;
201
+ align-items: center;
202
+ justify-content: space-between;
203
+ gap: 12px;
204
+ background: var(--canvas);
205
+ padding: 14px 16px;
206
+ min-width: 0;
207
+ }
208
+
209
+ .enabled-head {
210
+ display: flex;
211
+ align-items: center;
212
+ gap: 8px;
213
+ min-width: 0;
214
+ flex: 1 1 auto;
215
+ }
216
+
217
+ .enabled-head .name {
218
+ min-width: 0;
219
+ overflow-wrap: anywhere;
220
+ color: var(--body-strong);
221
+ font-weight: 500;
222
+ }
223
+
224
+ .enabled-source {
225
+ flex: 0 0 auto;
226
+ border-radius: 999px;
227
+ background: var(--surface-card);
228
+ color: var(--ink);
229
+ padding: 2px 8px;
230
+ font: 500 12px/1.4 var(--sans);
231
+ font-variant-numeric: tabular-nums;
232
+ white-space: nowrap;
233
+ }
234
+
235
+ .enabled p {
236
+ display: none;
237
+ margin: 4px 0 0;
238
+ color: var(--muted);
239
+ font: 12px/1.45 var(--mono);
240
+ overflow-wrap: anywhere;
241
+ }
242
+
243
+ /* 路径默认隐藏(行高优先);悬停/键盘聚焦展开——同名 skill 靠它区分来源。 */
244
+ .enabled:hover p,
245
+ .enabled:focus-within p {
246
+ display: block;
247
+ }
248
+
249
+ @media (max-width: 760px) {
250
+ .enabled-toolbar {
251
+ align-items: stretch;
252
+ flex-direction: column;
253
+ gap: 6px;
254
+ }
255
+
256
+ .enabled-search input {
257
+ min-height: 44px;
258
+ }
259
+
260
+ .enabled-search-summary {
261
+ margin: 0;
262
+ }
263
+
264
+ .enabled-grid {
265
+ grid-template-columns: 1fr;
266
+ }
267
+
268
+ .scope-global,
269
+ .scope-hermes {
270
+ grid-column: auto;
271
+ }
272
+
273
+ .column-title-actions button,
274
+ .scope-card-footer > button.column-title-action {
275
+ min-height: 44px;
276
+ }
277
+
278
+ /* 触屏没有 hover:路径(同名 skill 的区分手段)常显。 */
279
+ .enabled p {
280
+ display: block;
281
+ }
282
+ }
283
+
284
+ @media (max-width: 520px) {
285
+ .scope-card-footer {
286
+ align-items: stretch;
287
+ flex-direction: column;
288
+ }
289
+
290
+ .scope-card-footer code {
291
+ white-space: normal;
292
+ }
293
+
294
+ .column-title-actions {
295
+ display: grid;
296
+ grid-template-columns: 1fr 1fr;
297
+ }
298
+
299
+ .column-title-actions button,
300
+ .scope-card-footer > button.column-title-action {
301
+ width: 100%;
302
+ }
303
+ }
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" rx="7" fill="#141413"/>
3
+ <rect x="15" y="7" width="2" height="18" rx="1" fill="#5db8a6"/>
4
+ <rect x="7" y="15" width="18" height="2" rx="1" fill="#5db8a6"/>
5
+ </svg>
@@ -0,0 +1,199 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>SkillCaddy</title>
7
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
8
+ <link rel="stylesheet" href="/styles.css">
9
+ <link rel="stylesheet" href="/enabled.css">
10
+ <link rel="stylesheet" href="/claude.css">
11
+ </head>
12
+ <body>
13
+ <main class="shell">
14
+ <header class="topbar">
15
+ <div class="brand">
16
+ <span class="brand-mark" aria-hidden="true"></span>
17
+ <span>SkillCaddy</span>
18
+ </div>
19
+ <div class="topbar-actions">
20
+ <label class="topbar-toggle" title="在已启用面板和库操作里显示或隐藏 Hermes 段">
21
+ <input id="hermesToggle" type="checkbox" data-focus-key="hermes-toggle" aria-describedby="hermesToggleLabel">
22
+ <span class="topbar-toggle-track" aria-hidden="true"></span>
23
+ <span id="hermesToggleLabel" class="topbar-toggle-label">Hermes</span>
24
+ </label>
25
+ <span id="versionTag" class="version" aria-label="版本号"></span>
26
+ <a id="githubLink" class="topbar-link" href="https://github.com/chenweil/skillcaddy" target="_blank" rel="noreferrer noopener" data-focus-key="github-link" title="在 GitHub 查看 Skillcaddy" aria-label="在 GitHub 查看 Skillcaddy">
27
+ <svg class="github-icon" viewBox="0 0 16 16" aria-hidden="true">
28
+ <path fill="currentColor" d="M8 1.25a6.75 6.75 0 0 0-2.134 13.154c.337.062.46-.146.46-.324v-1.264c-1.872.407-2.267-.793-2.267-.793-.307-.78-.75-.988-.75-.988-.612-.418.046-.41.046-.41.677.048 1.034.695 1.034.695.602 1.031 1.578.733 1.963.56.061-.436.236-.733.429-.902-1.494-.17-3.065-.747-3.065-3.328 0-.735.263-1.336.694-1.806-.07-.17-.301-.855.066-1.782 0 0 .566-.181 1.856.69A6.45 6.45 0 0 1 8 4.53a6.47 6.47 0 0 1 1.69.228c1.29-.871 1.855-.69 1.855-.69.368.927.137 1.612.067 1.782.432.47.694 1.071.694 1.806 0 2.587-1.574 3.156-3.073 3.322.242.209.457.62.457 1.25v1.848c0 .18.122.389.464.323A6.75 6.75 0 0 0 8 1.25Z"/>
29
+ </svg>
30
+ <span class="topbar-link-label">GitHub</span>
31
+ </a>
32
+ <button id="refreshButton" class="icon-button" data-focus-key="refresh" title="刷新" aria-label="刷新">↻</button>
33
+ </div>
34
+ </header>
35
+
36
+ <section class="hero">
37
+ <div class="hero-copy">
38
+ <h1>把本地 skills 像产品资产一样管理。</h1>
39
+ <p class="lede">集中整理 official、github、personal 与 archived 来源,再把需要的 skill 安全启用到当前项目和 Claude Code。</p>
40
+ </div>
41
+ <div class="product-surface" aria-label="当前库状态">
42
+ <div class="surface-header">
43
+ <span></span>
44
+ <span></span>
45
+ <span></span>
46
+ <strong>skillcaddy/state</strong>
47
+ </div>
48
+ <div class="surface-grid">
49
+ <div>
50
+ <span>Skill 原件</span>
51
+ <strong id="heroTotalSkills">0</strong>
52
+ </div>
53
+ <div>
54
+ <span>Agents 启用</span>
55
+ <strong id="heroAgentsCount">0</strong>
56
+ </div>
57
+ <div>
58
+ <span>Claude Code 启用</span>
59
+ <strong id="heroClaudeCount">0</strong>
60
+ </div>
61
+ </div>
62
+ <p id="activeProject" class="surface-path">等待读取项目路径</p>
63
+ </div>
64
+ </section>
65
+
66
+ <form class="controls" id="controlsForm">
67
+ <label>
68
+ <span>目标项目路径</span>
69
+ <small class="field-hint">输入项目路径后点「添加项目」保存到下方历史,回车直接读取</small>
70
+ <input id="projectPath" type="text" data-focus-key="project-path" spellcheck="false" placeholder="/Users/your-name/projects/my-app">
71
+ </label>
72
+ <button id="addProject" class="secondary" type="button" data-focus-key="add-project" title="仅把路径存入下方历史,不读取">添加项目</button>
73
+ <button id="loadProject" type="submit" data-focus-key="load-project" title="读取该项目的启用状态(回车同效)">读取项目</button>
74
+ <div id="projectHistory" class="project-history" aria-label="历史项目"></div>
75
+ </form>
76
+
77
+ <section class="workspace">
78
+ <aside class="panel">
79
+ <div class="panel-title">
80
+ <div>
81
+ <h2>已启用</h2>
82
+ </div>
83
+ <span>Agent 通道</span>
84
+ </div>
85
+ <div class="enabled-toolbar">
86
+ <label class="enabled-search" for="enabledSearch">
87
+ <span>查找已启用</span>
88
+ <input id="enabledSearch" type="search" data-focus-key="enabled-search" placeholder="搜索名称或来源…" aria-label="搜索已启用的 skill" aria-controls="enabledList claudeSkillList globalList hermesList" autocomplete="off">
89
+ </label>
90
+ <p id="enabledSearchSummary" class="enabled-search-summary" role="status" aria-live="polite" hidden></p>
91
+ </div>
92
+ <div class="enabled-grid">
93
+ <section class="enabled-column scope-card scope-agents">
94
+ <div class="column-title">
95
+ <span class="scope-icon" aria-hidden="true">⚙</span>
96
+ <div class="scope-title-copy">
97
+ <h3>Agents</h3>
98
+ <p class="scope-summary"><strong id="agentsCount" class="count">0</strong> 已启用 · 当前项目直接使用 <span id="agentsResultCount" class="scope-result" hidden></span></p>
99
+ </div>
100
+ </div>
101
+ <div id="enabledList" class="list compact"></div>
102
+ <div class="scope-card-footer">
103
+ <code>.agents/skills</code>
104
+ <button id="disableAgents" class="secondary danger column-title-action" data-focus-key="agents-clear" data-focus-fallback-key="skill-search" title="一键清空 .agents/skills(数量见上方计数)" aria-label="一键清空 .agents/skills">批量清空</button>
105
+ </div>
106
+ </section>
107
+ <section class="enabled-column scope-card scope-claude">
108
+ <div class="column-title">
109
+ <span class="scope-icon" aria-hidden="true">⌬</span>
110
+ <div class="scope-title-copy">
111
+ <h3>Claude Code</h3>
112
+ <p class="scope-summary"><strong id="claudeCount" class="count">0</strong> 已启用 · 同步自 Agents <span id="claudeResultCount" class="scope-result" hidden></span></p>
113
+ </div>
114
+ </div>
115
+ <div id="claudeSkillList" class="list compact"></div>
116
+ <div class="scope-card-footer">
117
+ <code>.claude/skills</code>
118
+ <div class="column-title-actions">
119
+ <button id="syncClaude" data-focus-key="claude-sync" title="把项目已启用的 skill 同步到 Claude Code">同步</button>
120
+ <button id="unlinkClaude" class="secondary danger column-title-action" data-focus-key="claude-clear" data-focus-fallback-key="claude-sync" title="一键清空 Claude Code(数量见上方计数)" aria-label="一键清空 Claude Code">批量清空</button>
121
+ </div>
122
+ </div>
123
+ </section>
124
+ <section class="enabled-column scope-card scope-global">
125
+ <div class="column-title">
126
+ <span class="scope-icon" aria-hidden="true">⌂</span>
127
+ <div class="scope-title-copy">
128
+ <h3>全局</h3>
129
+ <p class="scope-summary"><strong id="globalCount" class="count">0</strong> 已启用 · 所有项目都能使用 <span id="globalResultCount" class="scope-result" hidden></span></p>
130
+ </div>
131
+ </div>
132
+ <div id="globalList" class="list compact"></div>
133
+ <div class="scope-card-footer">
134
+ <code>~/.agents/skills</code>
135
+ <button id="disableGlobal" class="secondary danger column-title-action" data-focus-key="global-clear" data-focus-fallback-key="skill-search" title="一键清空全局 .agents/skills(数量见上方计数)" aria-label="一键清空全局 .agents/skills">批量清空</button>
136
+ </div>
137
+ </section>
138
+ <section class="enabled-column scope-card scope-hermes" data-hermes-section>
139
+ <div class="column-title">
140
+ <span class="scope-icon" aria-hidden="true">⏣</span>
141
+ <div class="scope-title-copy">
142
+ <h3>Hermes</h3>
143
+ <p class="scope-summary"><strong id="hermesCount" class="count">0</strong> 已启用 · 仅适用于 Hermes <span id="hermesResultCount" class="scope-result" hidden></span></p>
144
+ </div>
145
+ </div>
146
+ <div id="hermesList" class="list compact"></div>
147
+ <div class="scope-card-footer">
148
+ <code>~/.hermes/skills</code>
149
+ <button id="disableHermes" class="secondary danger column-title-action" data-focus-key="hermes-clear" data-focus-fallback-key="skill-search" title="一键清空 Hermes ~/.hermes/skills(数量见上方计数)" aria-label="一键清空 Hermes ~/.hermes/skills">批量清空</button>
150
+ </div>
151
+ </section>
152
+ </div>
153
+ </aside>
154
+
155
+ <section class="panel library-panel">
156
+ <div class="panel-title">
157
+ <div>
158
+ <h2>Skill 原件库 <strong id="totalSkills" class="count">0</strong></h2>
159
+ </div>
160
+ <div class="library-title-actions">
161
+ <div class="advice-popover-anchor">
162
+ <button id="adviceToggle" class="advice-toggle" type="button" data-focus-key="advice-toggle" hidden aria-expanded="false" aria-controls="adviceList" title="查看提示">
163
+ <svg class="advice-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
164
+ <circle cx="8" cy="8" r="6.5" />
165
+ <line x1="8" y1="7" x2="8" y2="11.5" />
166
+ <circle cx="8" cy="4.5" r="0.6" fill="currentColor" stroke="none" />
167
+ </svg>
168
+ <span id="adviceCount" class="count">0</span>
169
+ </button>
170
+ <section id="adviceList" class="advice-list advice-popover" hidden role="dialog" aria-label="提示列表"></section>
171
+ </div>
172
+ <select id="sourceFilter" data-focus-key="source-filter" title="来源过滤">
173
+ <option value="">全部来源</option>
174
+ <option value="local">local</option>
175
+ <option value="official">official</option>
176
+ <option value="github">github</option>
177
+ <option value="personal">personal</option>
178
+ <option value="archived">archived</option>
179
+ </select>
180
+ </div>
181
+ </div>
182
+ <div class="library-search">
183
+ <input id="skillSearch" type="text" data-focus-key="skill-search" placeholder="搜索名称、描述或标签…(/ 聚焦,Esc 清除)" aria-label="搜索 skills" aria-keyshortcuts="/">
184
+ </div>
185
+ <!-- advice 已搬到右上角 popover;主工作流(搜索 → 标签 → 库)保持线性。 -->
186
+ <nav id="tagTabs" class="tag-tabs" aria-label="Skill 标签过滤"></nav>
187
+ <div id="skillList" class="list"></div>
188
+ </section>
189
+ </section>
190
+
191
+ <div class="message-region">
192
+ <p id="message" class="message" role="status"></p>
193
+ <p id="errorMessage" class="message is-error" role="alert"></p>
194
+ </div>
195
+ </main>
196
+
197
+ <script src="/app.js" type="module"></script>
198
+ </body>
199
+ </html>
Binary file
Binary file