claw-tools 0.1.0 → 0.1.1
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.
- checksums.yaml +4 -4
- data/bin/claw-tools +77 -41
- data/lib/claw-tools/github.rb +82 -47
- data/lib/claw-tools/version.rb +1 -1
- data/lib/claw-tools.rb +0 -2
- metadata +1 -29
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 37932e5ef36e52a9aa4432e81a5f5e407cc9056cfc8923ab68b3f36e2184f7df
|
|
4
|
+
data.tar.gz: 6fc66eaea058dfe67a483c6d54c0544bd4f7c49d4d4bee6ebbcb75e0b2c85e1b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 66dfc293e39aa59715ac76078aa699365b6414c6fbf38fb8b7eb5da3d3b68f153bfa81b0159bf3a76e8baeee8002671f5fac26375742b5811e96936472802e79
|
|
7
|
+
data.tar.gz: 9f9893eefc9967df05ba825291f49402d2c512e5c1693d12b96ce47948246275da5329fbc9818a5e9f0fce89d1d9c2ef52fb5301f0f236e72c2bf1040c685261
|
data/bin/claw-tools
CHANGED
|
@@ -1,54 +1,90 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
-
require "thor"
|
|
3
2
|
require "claw-tools"
|
|
4
3
|
require "fileutils"
|
|
4
|
+
require "json"
|
|
5
5
|
|
|
6
6
|
module ClawTools
|
|
7
|
-
class CLI
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
7
|
+
class CLI
|
|
8
|
+
def self.run(args)
|
|
9
|
+
command = args[0]
|
|
10
|
+
|
|
11
|
+
case command
|
|
12
|
+
when "list", nil
|
|
13
|
+
github = GitHub.new
|
|
14
|
+
github.list_skills
|
|
15
|
+
|
|
16
|
+
when "install"
|
|
17
|
+
name = args[1]
|
|
18
|
+
if name.nil?
|
|
19
|
+
puts "Usage: claw-tools install <skill-name>"
|
|
20
|
+
exit 1
|
|
21
|
+
end
|
|
22
|
+
github = GitHub.new
|
|
23
|
+
github.install_skill(name)
|
|
24
|
+
|
|
25
|
+
when "search"
|
|
26
|
+
query = args[1]
|
|
27
|
+
if query.nil?
|
|
28
|
+
puts "Usage: claw-tools search <query>"
|
|
29
|
+
exit 1
|
|
30
|
+
end
|
|
31
|
+
github = GitHub.new
|
|
32
|
+
github.search_skills(query)
|
|
33
|
+
|
|
34
|
+
when "publish"
|
|
35
|
+
path = args[1]
|
|
36
|
+
if path.nil?
|
|
37
|
+
puts "Usage: claw-tools publish <path>"
|
|
38
|
+
exit 1
|
|
39
|
+
end
|
|
40
|
+
github = GitHub.new
|
|
41
|
+
github.publish(path)
|
|
42
|
+
|
|
43
|
+
when "update"
|
|
44
|
+
if Dir.exist?(ClawTools::SKILLS_DIR)
|
|
45
|
+
puts "🔄 Updating skills..."
|
|
46
|
+
Dir.glob(File.join(ClawTools::SKILLS_DIR, "*")).each do |skill|
|
|
47
|
+
if File.directory?(skill)
|
|
48
|
+
name = File.basename(skill)
|
|
49
|
+
puts " Updating #{name}..."
|
|
50
|
+
github = GitHub.new
|
|
51
|
+
github.install_skill(name)
|
|
52
|
+
end
|
|
39
53
|
end
|
|
54
|
+
puts "✅ All skills updated!"
|
|
55
|
+
else
|
|
56
|
+
puts "❌ No skills installed yet."
|
|
40
57
|
end
|
|
41
|
-
|
|
58
|
+
|
|
59
|
+
when "version", "-v", "--version"
|
|
60
|
+
puts "claw-tools v#{ClawTools::VERSION}"
|
|
61
|
+
|
|
62
|
+
when "help", "-h", "--help"
|
|
63
|
+
puts <<~HELP
|
|
64
|
+
claw-tools - OpenClaw Skills & MCP 管理工具
|
|
65
|
+
|
|
66
|
+
使用方法:
|
|
67
|
+
claw-tools list # 列出所有可用的 skills
|
|
68
|
+
claw-tools install <name> # 安装一个 skill
|
|
69
|
+
claw-tools search <query> # 搜索 skills
|
|
70
|
+
claw-tools publish <path> # 发布本地 skill
|
|
71
|
+
claw-tools update # 更新所有 skills
|
|
72
|
+
claw-tools version # 显示版本
|
|
73
|
+
claw-tools help # 显示帮助
|
|
74
|
+
|
|
75
|
+
示例:
|
|
76
|
+
claw-tools list
|
|
77
|
+
claw-tools install ai-slides
|
|
78
|
+
claw-tools publish ~/my-skill
|
|
79
|
+
HELP
|
|
80
|
+
|
|
42
81
|
else
|
|
43
|
-
puts "
|
|
82
|
+
puts "未知命令: #{command}"
|
|
83
|
+
puts "运行 'claw-tools help' 查看帮助"
|
|
84
|
+
exit 1
|
|
44
85
|
end
|
|
45
86
|
end
|
|
46
|
-
|
|
47
|
-
desc "version", "显示版本"
|
|
48
|
-
def version
|
|
49
|
-
puts "claw-tools v#{ClawTools::VERSION}"
|
|
50
|
-
end
|
|
51
87
|
end
|
|
52
88
|
end
|
|
53
89
|
|
|
54
|
-
ClawTools::CLI.
|
|
90
|
+
ClawTools::CLI.run(ARGV)
|
data/lib/claw-tools/github.rb
CHANGED
|
@@ -1,30 +1,39 @@
|
|
|
1
|
-
require "
|
|
1
|
+
require "json"
|
|
2
|
+
require "open-uri"
|
|
3
|
+
require "net/http"
|
|
2
4
|
|
|
3
5
|
module ClawTools
|
|
4
6
|
class GitHub
|
|
5
|
-
attr_reader :
|
|
7
|
+
attr_reader :token
|
|
6
8
|
|
|
7
9
|
def initialize(token: nil)
|
|
8
|
-
@
|
|
9
|
-
access_token: token || ENV["GITHUB_TOKEN"],
|
|
10
|
-
auto_paginate: true
|
|
11
|
-
)
|
|
10
|
+
@token = token || ENV["GITHUB_TOKEN"] || ""
|
|
12
11
|
end
|
|
13
12
|
|
|
14
13
|
# 列出仓库中的 skills
|
|
15
14
|
def list_skills(repo: ClawTools::DEFAULT_REPO)
|
|
16
15
|
begin
|
|
17
|
-
contents =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
contents = fetch_json("https://api.github.com/repos/#{repo}/contents")
|
|
17
|
+
if contents.is_a?(Array)
|
|
18
|
+
puts "📂 Available skills in #{repo}:"
|
|
19
|
+
contents.each do |item|
|
|
20
|
+
if item["type"] == "dir"
|
|
21
|
+
puts " - #{item["name"]}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
else
|
|
25
|
+
puts "❌ Error: #{contents['message']}"
|
|
26
|
+
end
|
|
27
|
+
rescue => e
|
|
28
|
+
puts "❌ Error: #{e.message}"
|
|
29
|
+
puts " Make sure GITHUB_TOKEN is set for private repos"
|
|
22
30
|
[]
|
|
23
31
|
end
|
|
24
32
|
end
|
|
25
33
|
|
|
26
34
|
# 安装 skill
|
|
27
|
-
def install_skill(name, repo: ClawTools::DEFAULT_REPO, target_dir:
|
|
35
|
+
def install_skill(name, repo: ClawTools::DEFAULT_REPO, target_dir: nil)
|
|
36
|
+
target_dir ||= ClawTools::SKILLS_DIR
|
|
28
37
|
puts "📥 Installing #{name}..."
|
|
29
38
|
|
|
30
39
|
# 创建目标目录
|
|
@@ -33,23 +42,24 @@ module ClawTools
|
|
|
33
42
|
|
|
34
43
|
begin
|
|
35
44
|
# 获取 skill 内容
|
|
36
|
-
contents =
|
|
45
|
+
contents = fetch_json("https://api.github.com/repos/#{repo}/contents/#{name}")
|
|
37
46
|
|
|
38
|
-
contents.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
download_directory(repo, "#{name}/#{file.name}", File.join(skill_dir, file.name))
|
|
47
|
+
if contents.is_a?(Array)
|
|
48
|
+
contents.each do |file|
|
|
49
|
+
if file["type"] == "file"
|
|
50
|
+
download_file(file["download_url"], File.join(skill_dir, file["name"]))
|
|
51
|
+
puts " ✅ #{file["name"]}"
|
|
52
|
+
elsif file["type"] == "dir"
|
|
53
|
+
download_directory(repo, "#{name}/#{file["name"]}", File.join(skill_dir, file["name"]))
|
|
54
|
+
end
|
|
47
55
|
end
|
|
56
|
+
puts "✅ Installed to: #{skill_dir}"
|
|
57
|
+
else
|
|
58
|
+
puts "❌ Skill not found: #{name}"
|
|
59
|
+
puts " Error: #{contents['message']}"
|
|
48
60
|
end
|
|
49
|
-
|
|
50
|
-
puts "
|
|
51
|
-
rescue Octokit::NotFound
|
|
52
|
-
puts "❌ Skill not found: #{name}"
|
|
61
|
+
rescue => e
|
|
62
|
+
puts "❌ Error: #{e.message}"
|
|
53
63
|
end
|
|
54
64
|
end
|
|
55
65
|
|
|
@@ -57,31 +67,39 @@ module ClawTools
|
|
|
57
67
|
def download_directory(repo, path, target_dir)
|
|
58
68
|
FileUtils.mkdir_p(target_dir)
|
|
59
69
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
begin
|
|
71
|
+
contents = fetch_json("https://api.github.com/repos/#{repo}/contents/#{path}")
|
|
72
|
+
|
|
73
|
+
if contents.is_a?(Array)
|
|
74
|
+
contents.each do |file|
|
|
75
|
+
if file["type"] == "file"
|
|
76
|
+
download_file(file["download_url"], File.join(target_dir, file["name"]))
|
|
77
|
+
puts " ✅ #{file["name"]}"
|
|
78
|
+
elsif file["type"] == "dir"
|
|
79
|
+
download_directory(repo, "#{path}/#{file["name"]}", File.join(target_dir, file["name"]))
|
|
80
|
+
end
|
|
81
|
+
end
|
|
69
82
|
end
|
|
83
|
+
rescue => e
|
|
84
|
+
puts " ⚠️ Could not read directory: #{path}"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# 下载文件
|
|
89
|
+
def download_file(url, path)
|
|
90
|
+
begin
|
|
91
|
+
content = URI.open(url).read
|
|
92
|
+
File.write(path, content)
|
|
93
|
+
rescue => e
|
|
94
|
+
puts " ⚠️ Failed to download: #{File.basename(path)}"
|
|
70
95
|
end
|
|
71
96
|
end
|
|
72
97
|
|
|
73
98
|
# 搜索 skills
|
|
74
99
|
def search_skills(query)
|
|
75
100
|
puts "🔍 Searching for: #{query}"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if results.items.empty?
|
|
79
|
-
puts "No results found."
|
|
80
|
-
else
|
|
81
|
-
results.items.first(10).each do |item|
|
|
82
|
-
puts " - #{item.path}"
|
|
83
|
-
end
|
|
84
|
-
end
|
|
101
|
+
puts " (Search is available via GitHub web interface)"
|
|
102
|
+
puts " https://github.com/cangming009/openclaw-skills/search?q=#{query}"
|
|
85
103
|
end
|
|
86
104
|
|
|
87
105
|
# 发布 skill(需要本地 git)
|
|
@@ -97,12 +115,29 @@ module ClawTools
|
|
|
97
115
|
# 检查是否是 git 仓库
|
|
98
116
|
unless File.exist?(File.join(local_path, ".git"))
|
|
99
117
|
puts "⚠️ Initializing git repository..."
|
|
100
|
-
system("cd #{local_path} && git init")
|
|
118
|
+
system("cd '#{local_path}' && git init")
|
|
101
119
|
end
|
|
102
120
|
|
|
103
121
|
puts "✅ Skill ready to publish!"
|
|
104
|
-
puts " Push to GitHub manually
|
|
105
|
-
puts "
|
|
122
|
+
puts " Push to GitHub manually:"
|
|
123
|
+
puts " cd #{local_path}"
|
|
124
|
+
puts " gh repo create #{repo || 'your-repo'} --private"
|
|
125
|
+
puts " git push origin main"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
def fetch_json(url)
|
|
131
|
+
uri = URI.parse(url)
|
|
132
|
+
req = Net::HTTP::Get.new(uri)
|
|
133
|
+
req["Accept"] = "application/vnd.github.v3+json"
|
|
134
|
+
req["Authorization"] = "token #{@token}" unless @token.empty?
|
|
135
|
+
|
|
136
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
137
|
+
http.use_ssl = true
|
|
138
|
+
response = http.request(req)
|
|
139
|
+
|
|
140
|
+
JSON.parse(response.body)
|
|
106
141
|
end
|
|
107
142
|
end
|
|
108
143
|
end
|
data/lib/claw-tools/version.rb
CHANGED
data/lib/claw-tools.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: claw-tools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- cangming
|
|
@@ -10,34 +10,6 @@ bindir: bin
|
|
|
10
10
|
cert_chain: []
|
|
11
11
|
date: 2026-03-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
-
- !ruby/object:Gem::Dependency
|
|
14
|
-
name: thor
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - "~>"
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '1.0'
|
|
20
|
-
type: :runtime
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - "~>"
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '1.0'
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: octokit
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - "~>"
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: '7.0'
|
|
34
|
-
type: :runtime
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - "~>"
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '7.0'
|
|
41
13
|
- !ruby/object:Gem::Dependency
|
|
42
14
|
name: bundler
|
|
43
15
|
requirement: !ruby/object:Gem::Requirement
|