claude-skill-antivirus 2.0.0 → 2.1.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/README.md +37 -0
- package/README.zh-TW.md +34 -0
- package/package.json +2 -2
- package/src/index.js +1 -1
- package/src/scanner/permissions.js +132 -49
- package/src/utils/downloader.js +16 -0
package/README.md
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
# Claude Skill Antivirus
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/claude-skill-antivirus)
|
|
4
|
+
[](https://github.com/claude-world/claude-skill-antivirus/actions/workflows/ci.yml)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
[](https://docs.anthropic.com/en/docs/claude-code)
|
|
8
|
+
|
|
9
|
+
<!-- TODO: Replace with actual GIF -->
|
|
10
|
+
<img src="assets/demo.gif" alt="Claude Skill Antivirus scanning a CLAUDE.md file for prompt injection" title="Demo shows: trigger scan → detect patterns → show results" />
|
|
11
|
+
|
|
3
12
|
A security scanner and safe installer for Claude Code Skills. Detects malicious patterns, data exfiltration attempts, and dangerous operations before installing third-party skills.
|
|
4
13
|
|
|
14
|
+
Compatible with Claude Code using Opus 4.6, Sonnet 4.6, and Haiku 4.5 models.
|
|
15
|
+
|
|
5
16
|
[繁體中文說明](./README.zh-TW.md) | [SkillsMP Scan Report](./SCAN-REPORT.md)
|
|
6
17
|
|
|
7
18
|
## SkillsMP Platform Scan Results
|
|
@@ -327,6 +338,31 @@ claude-skill-antivirus/
|
|
|
327
338
|
└── README.md
|
|
328
339
|
```
|
|
329
340
|
|
|
341
|
+
## Latest Updates
|
|
342
|
+
|
|
343
|
+
### v2.1.0 (2026-03-13)
|
|
344
|
+
- Verified compatibility with Claude Code Opus 4.6
|
|
345
|
+
- Updated documentation and metadata
|
|
346
|
+
|
|
347
|
+
### v2.0.1
|
|
348
|
+
- Separated capability warnings from actual threats in permission scanner
|
|
349
|
+
- Fixed array format handling in `allowed-tools`
|
|
350
|
+
|
|
351
|
+
### v2.0.0
|
|
352
|
+
- Added 4 new scanning engines: MCP Security, SSRF, Dependency, and Sub-agent scanners (total: 9 engines)
|
|
353
|
+
- Added i18n support (English + Traditional Chinese)
|
|
354
|
+
- Added batch scanner for SkillsMP platform
|
|
355
|
+
- Scanned all 71,577 skills on SkillsMP
|
|
356
|
+
|
|
357
|
+
### v1.0.0
|
|
358
|
+
- Initial release with 5 core scanning engines
|
|
359
|
+
- CLI installer with interactive prompts
|
|
360
|
+
|
|
361
|
+
## Related Projects
|
|
362
|
+
|
|
363
|
+
- [cf-browser](https://github.com/claude-world/cf-browser) - Open-source Cloudflare Browser Rendering proxy with 9 MCP tools for Claude Code
|
|
364
|
+
- [claude-world.com](https://claude-world.com) - Claude Code advanced usage community
|
|
365
|
+
|
|
330
366
|
## Contributing
|
|
331
367
|
|
|
332
368
|
Contributions are welcome! Please feel free to submit issues and pull requests.
|
|
@@ -350,4 +386,5 @@ Lucas Wang <support@claude-world.com>
|
|
|
350
386
|
## Links
|
|
351
387
|
|
|
352
388
|
- [GitHub Repository](https://github.com/claude-world/claude-skill-antivirus)
|
|
389
|
+
- [npm Package](https://www.npmjs.com/package/claude-skill-antivirus)
|
|
353
390
|
- [Report Issues](https://github.com/claude-world/claude-skill-antivirus/issues)
|
package/README.zh-TW.md
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
# Claude Skill Antivirus
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/claude-skill-antivirus)
|
|
4
|
+
[](https://github.com/claude-world/claude-skill-antivirus/actions/workflows/ci.yml)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
[](https://docs.anthropic.com/en/docs/claude-code)
|
|
8
|
+
|
|
3
9
|
一個安全的 Claude Skills 安裝器,內建完整的惡意行為偵測引擎。
|
|
4
10
|
|
|
5
11
|
**Skills Installer + Antivirus for Claude**
|
|
6
12
|
|
|
13
|
+
支援 Claude Code Opus 4.6、Sonnet 4.6、Haiku 4.5 模型。
|
|
14
|
+
|
|
7
15
|
[English](./README.md) | [SkillsMP 掃描報告](./SCAN-REPORT.md)
|
|
8
16
|
|
|
9
17
|
## SkillsMP 平台掃描結果
|
|
@@ -320,6 +328,31 @@ claude-skill-antivirus/
|
|
|
320
328
|
└── README.md
|
|
321
329
|
```
|
|
322
330
|
|
|
331
|
+
## 更新紀錄
|
|
332
|
+
|
|
333
|
+
### v2.1.0 (2026-03-13)
|
|
334
|
+
- 確認相容 Claude Code Opus 4.6
|
|
335
|
+
- 更新文件與中繼資料
|
|
336
|
+
|
|
337
|
+
### v2.0.1
|
|
338
|
+
- 權限掃描器分離能力警告與實際威脅
|
|
339
|
+
- 修復 `allowed-tools` 的陣列格式處理
|
|
340
|
+
|
|
341
|
+
### v2.0.0
|
|
342
|
+
- 新增 4 個掃描引擎:MCP 安全、SSRF、依賴、Sub-agent 偵測(共 9 個引擎)
|
|
343
|
+
- 新增多語言支援(英文 + 繁體中文)
|
|
344
|
+
- 新增 SkillsMP 批次掃描功能
|
|
345
|
+
- 掃描 SkillsMP 上所有 71,577 個技能
|
|
346
|
+
|
|
347
|
+
### v1.0.0
|
|
348
|
+
- 首次發布,包含 5 個核心掃描引擎
|
|
349
|
+
- CLI 安裝器,含互動式提示
|
|
350
|
+
|
|
351
|
+
## 相關專案
|
|
352
|
+
|
|
353
|
+
- [cf-browser](https://github.com/claude-world/cf-browser) - 開源 Cloudflare 瀏覽器渲染代理,提供 9 個 MCP 工具給 Claude Code 使用
|
|
354
|
+
- [claude-world.com](https://claude-world.com) - Claude Code 進階使用社群
|
|
355
|
+
|
|
323
356
|
## 貢獻
|
|
324
357
|
|
|
325
358
|
歡迎貢獻!請隨時提交 issues 和 pull requests。
|
|
@@ -343,4 +376,5 @@ Lucas Wang <support@claude-world.com>
|
|
|
343
376
|
## 連結
|
|
344
377
|
|
|
345
378
|
- [GitHub Repository](https://github.com/claude-world/claude-skill-antivirus)
|
|
379
|
+
- [npm 套件](https://www.npmjs.com/package/claude-skill-antivirus)
|
|
346
380
|
- [回報問題](https://github.com/claude-world/claude-skill-antivirus/issues)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-skill-antivirus",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "A secure Claude Skills installer with comprehensive malicious operation detection - Skills 安裝器 + 防毒軟體",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
],
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"chalk": "^5.3.0",
|
|
42
|
-
"commander": "^
|
|
42
|
+
"commander": "^14.0.3",
|
|
43
43
|
"ora": "^8.0.1",
|
|
44
44
|
"inquirer": "^9.2.12",
|
|
45
45
|
"node-fetch": "^3.3.2",
|
package/src/index.js
CHANGED
|
@@ -14,7 +14,7 @@ const program = new Command();
|
|
|
14
14
|
program
|
|
15
15
|
.name('skill-install')
|
|
16
16
|
.description('A secure Claude Skills installer with malicious operation detection')
|
|
17
|
-
.version('2.
|
|
17
|
+
.version('2.1.1');
|
|
18
18
|
|
|
19
19
|
program
|
|
20
20
|
.argument('<source>', 'Skill URL (SkillsMP link) or local path')
|
|
@@ -3,47 +3,86 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export class PermissionScanner {
|
|
5
5
|
constructor() {
|
|
6
|
-
//
|
|
6
|
+
// Bash-specific risk patterns (checked in order, first match wins)
|
|
7
|
+
// Note: These are CAPABILITY warnings only. Actual malicious commands
|
|
8
|
+
// are detected by DangerousCommandScanner with higher severity.
|
|
9
|
+
this.bashRiskPatterns = [
|
|
10
|
+
// HIGH - Unrestricted (capability warning)
|
|
11
|
+
{ pattern: /^Bash\(\*\)$/i, risk: 'high', desc: 'Unrestricted shell access (capability)' },
|
|
12
|
+
|
|
13
|
+
// MEDIUM - Potentially dangerous operations (capability warning)
|
|
14
|
+
{ pattern: /^Bash\(rm[\s:]/i, risk: 'medium', desc: 'Delete operations (capability)' },
|
|
15
|
+
{ pattern: /^Bash\(sudo[\s:]/i, risk: 'medium', desc: 'Sudo operations (capability)' },
|
|
16
|
+
{ pattern: /^Bash\(chmod[\s:]/i, risk: 'medium', desc: 'Permission changes (capability)' },
|
|
17
|
+
{ pattern: /^Bash\(chown[\s:]/i, risk: 'medium', desc: 'Ownership changes (capability)' },
|
|
18
|
+
{ pattern: /^Bash\(curl[\s:]/i, risk: 'medium', desc: 'Network requests (capability)' },
|
|
19
|
+
{ pattern: /^Bash\(wget[\s:]/i, risk: 'medium', desc: 'Network requests (capability)' },
|
|
20
|
+
{ pattern: /^Bash\(nc[\s:]/i, risk: 'medium', desc: 'Netcat operations (capability)' },
|
|
21
|
+
{ pattern: /^Bash\(ssh[\s:]/i, risk: 'medium', desc: 'SSH operations (capability)' },
|
|
22
|
+
{ pattern: /^Bash\(scp[\s:]/i, risk: 'medium', desc: 'SCP operations (capability)' },
|
|
23
|
+
|
|
24
|
+
// LOW - Common dev tools with wildcards
|
|
25
|
+
{ pattern: /^Bash\(git:\*\)$/i, risk: 'low', desc: 'Git operations (all)' },
|
|
26
|
+
{ pattern: /^Bash\(npm:\*\)$/i, risk: 'low', desc: 'NPM operations (all)' },
|
|
27
|
+
{ pattern: /^Bash\(pnpm:\*\)$/i, risk: 'low', desc: 'PNPM operations (all)' },
|
|
28
|
+
{ pattern: /^Bash\(yarn:\*\)$/i, risk: 'low', desc: 'Yarn operations (all)' },
|
|
29
|
+
{ pattern: /^Bash\(pip:\*\)$/i, risk: 'low', desc: 'PIP operations (all)' },
|
|
30
|
+
{ pattern: /^Bash\(gh:\*\)$/i, risk: 'low', desc: 'GitHub CLI (all)' },
|
|
31
|
+
{ pattern: /^Bash\(docker:\*\)$/i, risk: 'low', desc: 'Docker operations (all)' },
|
|
32
|
+
{ pattern: /^Bash\(make:\*\)$/i, risk: 'low', desc: 'Make operations (all)' },
|
|
33
|
+
|
|
34
|
+
// INFO - Specific safe commands (just informational)
|
|
35
|
+
{ pattern: /^Bash\(git\s+status\)/i, risk: 'info', desc: 'Git status (read-only)' },
|
|
36
|
+
{ pattern: /^Bash\(git\s+log\)/i, risk: 'info', desc: 'Git log (read-only)' },
|
|
37
|
+
{ pattern: /^Bash\(git\s+diff\)/i, risk: 'info', desc: 'Git diff (read-only)' },
|
|
38
|
+
{ pattern: /^Bash\(git\s+branch\)/i, risk: 'info', desc: 'Git branch (read-only)' },
|
|
39
|
+
{ pattern: /^Bash\(npm\s+test\)/i, risk: 'info', desc: 'NPM test' },
|
|
40
|
+
{ pattern: /^Bash\(npm\s+run\)/i, risk: 'info', desc: 'NPM run script' },
|
|
41
|
+
{ pattern: /^Bash\(ls[\s\)]/i, risk: 'info', desc: 'List directory' },
|
|
42
|
+
{ pattern: /^Bash\(pwd\)/i, risk: 'info', desc: 'Print working directory' },
|
|
43
|
+
{ pattern: /^Bash\(echo[\s\)]/i, risk: 'info', desc: 'Echo command' },
|
|
44
|
+
{ pattern: /^Bash\(cat[\s\)]/i, risk: 'info', desc: 'Cat file (read)' },
|
|
45
|
+
|
|
46
|
+
// LOW - Other Bash with specific scope (has parentheses but not matched above)
|
|
47
|
+
{ pattern: /^Bash\([^)]+\)$/i, risk: 'low', desc: 'Bash with specific scope' },
|
|
48
|
+
|
|
49
|
+
// MEDIUM - Bare "Bash" without scope (unspecified)
|
|
50
|
+
{ pattern: /^Bash$/i, risk: 'medium', desc: 'Unscoped Bash access (capability)' },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// Define tool risk levels (non-Bash tools)
|
|
54
|
+
// Note: These are CAPABILITY warnings only - lower severity than actual malicious content
|
|
7
55
|
this.toolRiskLevels = {
|
|
8
|
-
// Critical
|
|
9
|
-
critical: [
|
|
10
|
-
'Bash(*)', // Unrestricted bash access
|
|
11
|
-
'Bash', // General bash (depends on context)
|
|
12
|
-
'Execute',
|
|
13
|
-
'Shell',
|
|
14
|
-
'Terminal'
|
|
15
|
-
],
|
|
56
|
+
// Critical - reserved for actual malicious content (not capabilities)
|
|
57
|
+
critical: [],
|
|
16
58
|
|
|
17
|
-
// High risk
|
|
59
|
+
// High risk capabilities - unrestricted execution
|
|
18
60
|
high: [
|
|
61
|
+
'Execute', // Arbitrary code execution
|
|
62
|
+
'Shell', // Shell access
|
|
63
|
+
'Terminal' // Terminal access
|
|
64
|
+
],
|
|
65
|
+
|
|
66
|
+
// Medium risk capabilities - can make changes
|
|
67
|
+
medium: [
|
|
19
68
|
'Write', // Can write to any file
|
|
20
69
|
'Edit', // Can modify any file
|
|
21
|
-
'Delete',
|
|
22
|
-
'Bash(rm:*)', // Delete operations
|
|
23
|
-
'Bash(chmod:*)', // Permission changes
|
|
24
|
-
'Bash(chown:*)', // Ownership changes
|
|
25
|
-
'Bash(sudo:*)', // Sudo operations
|
|
26
|
-
'Bash(curl:*)', // Network requests
|
|
27
|
-
'Bash(wget:*)', // Download operations
|
|
70
|
+
'Delete', // Can delete files
|
|
28
71
|
'WebFetch', // External web requests
|
|
29
72
|
'mcp_*' // MCP tools (depends on implementation)
|
|
30
73
|
],
|
|
31
74
|
|
|
32
|
-
//
|
|
33
|
-
|
|
75
|
+
// Low risk capabilities - read/discover
|
|
76
|
+
low: [
|
|
34
77
|
'Read', // Can read files
|
|
35
78
|
'Glob', // Can discover files
|
|
36
79
|
'Grep', // Can search file contents
|
|
37
|
-
'Bash(git:*)', // Git operations
|
|
38
|
-
'Bash(npm:*)', // NPM operations
|
|
39
|
-
'Bash(pip:*)', // PIP operations
|
|
40
|
-
'Bash(gh:*)', // GitHub CLI
|
|
41
80
|
'Task', // Can spawn sub-agents
|
|
42
81
|
'TodoWrite' // Task management
|
|
43
82
|
],
|
|
44
83
|
|
|
45
|
-
//
|
|
46
|
-
|
|
84
|
+
// Info - safe tools
|
|
85
|
+
info: [
|
|
47
86
|
'Read(*)', // Read with specific patterns
|
|
48
87
|
'Glob(*)', // Limited glob patterns
|
|
49
88
|
'AskUser',
|
|
@@ -51,49 +90,49 @@ export class PermissionScanner {
|
|
|
51
90
|
]
|
|
52
91
|
};
|
|
53
92
|
|
|
54
|
-
// Dangerous tool combinations
|
|
93
|
+
// Dangerous tool combinations (capability warnings - not actual threats)
|
|
55
94
|
this.dangerousCombinations = [
|
|
56
95
|
{
|
|
57
96
|
tools: ['Bash', 'Write'],
|
|
58
|
-
risk: '
|
|
59
|
-
reason: 'Can execute commands and persist
|
|
97
|
+
risk: 'medium',
|
|
98
|
+
reason: 'Can execute commands and persist files (capability)'
|
|
60
99
|
},
|
|
61
100
|
{
|
|
62
101
|
tools: ['Read', 'WebFetch'],
|
|
63
|
-
risk: '
|
|
64
|
-
reason: 'Can read
|
|
102
|
+
risk: 'medium',
|
|
103
|
+
reason: 'Can read data and send via network (capability)'
|
|
65
104
|
},
|
|
66
105
|
{
|
|
67
106
|
tools: ['Bash(curl:*)', 'Bash'],
|
|
68
|
-
risk: '
|
|
69
|
-
reason: 'Can download and execute remote code'
|
|
107
|
+
risk: 'high',
|
|
108
|
+
reason: 'Can download and execute remote code (capability)'
|
|
70
109
|
},
|
|
71
110
|
{
|
|
72
111
|
tools: ['Glob', 'Read', 'Bash(curl:*)'],
|
|
73
|
-
risk: '
|
|
74
|
-
reason: 'Can discover, read, and
|
|
112
|
+
risk: 'medium',
|
|
113
|
+
reason: 'Can discover, read, and send files (capability)'
|
|
75
114
|
}
|
|
76
115
|
];
|
|
77
116
|
|
|
78
|
-
// Overly permissive patterns
|
|
117
|
+
// Overly permissive patterns (capability warnings)
|
|
79
118
|
this.overlyPermissivePatterns = [
|
|
80
119
|
{
|
|
81
120
|
pattern: /Bash\(\*\)/i,
|
|
82
|
-
risk: '
|
|
83
|
-
title: 'Unrestricted Bash access',
|
|
84
|
-
description: 'Skill
|
|
121
|
+
risk: 'high',
|
|
122
|
+
title: 'Unrestricted Bash access (capability)',
|
|
123
|
+
description: 'Skill declares unrestricted shell access'
|
|
85
124
|
},
|
|
86
125
|
{
|
|
87
126
|
pattern: /Bash\([^)]*\*[^)]*\)/i,
|
|
88
|
-
risk: '
|
|
89
|
-
title: 'Wildcard Bash permissions',
|
|
90
|
-
description: 'Bash permissions use wildcards
|
|
127
|
+
risk: 'medium',
|
|
128
|
+
title: 'Wildcard Bash permissions (capability)',
|
|
129
|
+
description: 'Bash permissions use wildcards'
|
|
91
130
|
},
|
|
92
131
|
{
|
|
93
132
|
pattern: /\*/g,
|
|
94
|
-
risk: '
|
|
133
|
+
risk: 'low',
|
|
95
134
|
title: 'Wildcard in tool permissions',
|
|
96
|
-
description: 'Wildcards in permissions may grant broader access
|
|
135
|
+
description: 'Wildcards in permissions may grant broader access'
|
|
97
136
|
}
|
|
98
137
|
];
|
|
99
138
|
}
|
|
@@ -173,16 +212,58 @@ export class PermissionScanner {
|
|
|
173
212
|
}
|
|
174
213
|
|
|
175
214
|
parseToolString(toolStr) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
215
|
+
// Handle undefined/null
|
|
216
|
+
if (!toolStr) {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Handle array format (YAML list)
|
|
221
|
+
if (Array.isArray(toolStr)) {
|
|
222
|
+
return toolStr
|
|
223
|
+
.flatMap(t => typeof t === 'string' ? t.split(/[,;]/) : [])
|
|
224
|
+
.map(t => t.trim())
|
|
225
|
+
.filter(t => t.length > 0);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Handle string format
|
|
229
|
+
if (typeof toolStr === 'string') {
|
|
230
|
+
return toolStr
|
|
231
|
+
.split(/[,;]/)
|
|
232
|
+
.map(t => t.trim())
|
|
233
|
+
.filter(t => t.length > 0);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Unknown format, return empty
|
|
237
|
+
return [];
|
|
180
238
|
}
|
|
181
239
|
|
|
182
240
|
analyzeToolRisk(tool, findings) {
|
|
183
241
|
const normalizedTool = tool.trim();
|
|
184
242
|
|
|
185
|
-
//
|
|
243
|
+
// Special handling for Bash tools - use pattern matching
|
|
244
|
+
if (normalizedTool.toLowerCase().startsWith('bash')) {
|
|
245
|
+
for (const { pattern, risk, desc } of this.bashRiskPatterns) {
|
|
246
|
+
if (pattern.test(normalizedTool)) {
|
|
247
|
+
findings[risk].push({
|
|
248
|
+
title: `${risk.toUpperCase()} risk tool: ${normalizedTool}`,
|
|
249
|
+
description: desc,
|
|
250
|
+
location: 'allowed-tools',
|
|
251
|
+
scanner: 'PermissionScanner'
|
|
252
|
+
});
|
|
253
|
+
return; // First match wins
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// No pattern matched - treat as medium (unknown Bash variant)
|
|
257
|
+
findings.medium.push({
|
|
258
|
+
title: `MEDIUM risk tool: ${normalizedTool}`,
|
|
259
|
+
description: 'Bash tool with unknown scope',
|
|
260
|
+
location: 'allowed-tools',
|
|
261
|
+
scanner: 'PermissionScanner'
|
|
262
|
+
});
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Non-Bash tools - check each risk level
|
|
186
267
|
for (const [riskLevel, tools] of Object.entries(this.toolRiskLevels)) {
|
|
187
268
|
for (const riskTool of tools) {
|
|
188
269
|
if (this.toolMatches(normalizedTool, riskTool)) {
|
|
@@ -200,8 +281,10 @@ export class PermissionScanner {
|
|
|
200
281
|
findings.high.push(finding);
|
|
201
282
|
} else if (riskLevel === 'medium') {
|
|
202
283
|
findings.medium.push(finding);
|
|
203
|
-
} else {
|
|
284
|
+
} else if (riskLevel === 'low') {
|
|
204
285
|
findings.low.push(finding);
|
|
286
|
+
} else {
|
|
287
|
+
findings.info.push(finding);
|
|
205
288
|
}
|
|
206
289
|
return; // Found the risk level, stop checking
|
|
207
290
|
}
|
package/src/utils/downloader.js
CHANGED
|
@@ -171,6 +171,22 @@ ${description}
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
async fetchFromGitHub(url) {
|
|
174
|
+
// If URL points to a repo root (no file path), try SKILL.md on default branch
|
|
175
|
+
const repoRootMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/?$/);
|
|
176
|
+
if (repoRootMatch) {
|
|
177
|
+
const [, owner, repo] = repoRootMatch;
|
|
178
|
+
// Try common default branches
|
|
179
|
+
for (const branch of ['main', 'master']) {
|
|
180
|
+
const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/SKILL.md`;
|
|
181
|
+
const response = await fetch(rawUrl);
|
|
182
|
+
if (response.ok) {
|
|
183
|
+
const content = await response.text();
|
|
184
|
+
return this.parseSkillMd(content, url);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
throw new Error(`No SKILL.md found at ${url} (tried main and master branches)`);
|
|
188
|
+
}
|
|
189
|
+
|
|
174
190
|
// Convert GitHub URL to raw content URL
|
|
175
191
|
const rawUrl = this.convertToGitHubRaw(url);
|
|
176
192
|
|