baid 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/lib/baid/commands/init.rb +73 -1
- data/lib/baid/commands/skills.rb +17 -14
- data/lib/baid/mcp_configurator.rb +16 -6
- data/lib/baid/project_scanner.rb +63 -0
- data/lib/baid/skills_sh_scraper.rb +38 -0
- data/lib/baid.rb +3 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 221fba49d1c1620e0e5423517dc5b0c63f1d6d71898377b3df90671ae5732a2a
|
|
4
|
+
data.tar.gz: 499df0b58c473f06738f1e725e5c9b023a7b1b481c3192fbdc6494e2c24e5626
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 62e89d04b3c8cbb5ed97a4a4b20bf16b3044cddf4eb37863304fcd7222a7973b3987465fa69a18ae84f40b7e205cb2d69ad41dfc25022657629b12edd2eea243
|
|
7
|
+
data.tar.gz: bea46903bc722717c6ca60506a13d2124c58edd277e16f0e390fce4f282c6783904557b8b9cb20d427426a91a9e54aa2838382958677b3e3c86b38c30e162f64
|
data/lib/baid/commands/init.rb
CHANGED
|
@@ -20,13 +20,85 @@ module Baid
|
|
|
20
20
|
puts "Configured MCP for #{agent[:name]}"
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
init_result = smart_init(agents)
|
|
24
|
+
|
|
23
25
|
config_path = File.join(@project_dir, ".baid", "config.yml")
|
|
24
26
|
FileUtils.mkdir_p(File.dirname(config_path))
|
|
25
|
-
config = {
|
|
27
|
+
config = {
|
|
28
|
+
"agents" => agents.map { |a| a[:name] },
|
|
29
|
+
"installed_skills" => init_result[:skills]
|
|
30
|
+
}
|
|
31
|
+
config["tech_stack"] = init_result[:tech_stack] if init_result[:tech_stack]&.any?
|
|
26
32
|
File.write(config_path, config.to_yaml)
|
|
27
33
|
|
|
28
34
|
puts "Baid initialized with #{agents.length} agent(s)."
|
|
29
35
|
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def smart_init(agents)
|
|
40
|
+
token = Config.load_token
|
|
41
|
+
workspace = Config.load_active_workspace
|
|
42
|
+
return { skills: [], tech_stack: [] } unless token && workspace
|
|
43
|
+
|
|
44
|
+
puts "Analyzing project..."
|
|
45
|
+
scan = ProjectScanner.scan(@project_dir)
|
|
46
|
+
|
|
47
|
+
client = ApiClient.new(token: token)
|
|
48
|
+
response = client.post("/init/analyze", {
|
|
49
|
+
workspace_slug: workspace,
|
|
50
|
+
file_tree: scan[:file_tree],
|
|
51
|
+
package_files: scan[:package_files]
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
return { skills: [], tech_stack: [] } unless response.code == 200
|
|
55
|
+
|
|
56
|
+
data = JSON.parse(response.body)
|
|
57
|
+
tech_stack = data["tech_stack"] || []
|
|
58
|
+
answers = present_questions(data["questions"] || [])
|
|
59
|
+
|
|
60
|
+
setup_response = client.post("/init/setup", {
|
|
61
|
+
workspace_slug: workspace,
|
|
62
|
+
tech_stack: tech_stack,
|
|
63
|
+
answers: answers,
|
|
64
|
+
agents: agents.map { |a| a[:name] },
|
|
65
|
+
recommended_skills: data["recommended_skills"] || []
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
return { skills: [], tech_stack: tech_stack } unless setup_response.code == 200
|
|
69
|
+
|
|
70
|
+
setup_data = JSON.parse(setup_response.body)
|
|
71
|
+
skills = setup_data["skills"] || []
|
|
72
|
+
|
|
73
|
+
skills.each do |skill|
|
|
74
|
+
SkillWriter.write(skill, agents, @project_dir)
|
|
75
|
+
puts "Installed skill: #{skill["name"]}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
{ skills: skills.map { |s| s["name"] }, tech_stack: tech_stack }
|
|
79
|
+
rescue StandardError
|
|
80
|
+
{ skills: [], tech_stack: [] }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def present_questions(questions)
|
|
84
|
+
return {} if questions.empty?
|
|
85
|
+
|
|
86
|
+
prompt = TTY::Prompt.new
|
|
87
|
+
answers = {}
|
|
88
|
+
|
|
89
|
+
questions.each do |q|
|
|
90
|
+
case q["type"]
|
|
91
|
+
when "confirm"
|
|
92
|
+
answers[q["id"]] = prompt.yes?(q["text"], default: q["default"] != false)
|
|
93
|
+
when "select"
|
|
94
|
+
answers[q["id"]] = prompt.select(q["text"], q["options"] || [])
|
|
95
|
+
when "multi_select"
|
|
96
|
+
answers[q["id"]] = prompt.multi_select(q["text"], q["options"] || [], default: q["defaults"] || [])
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
answers
|
|
101
|
+
end
|
|
30
102
|
end
|
|
31
103
|
end
|
|
32
104
|
end
|
data/lib/baid/commands/skills.rb
CHANGED
|
@@ -5,28 +5,31 @@ module Baid
|
|
|
5
5
|
class Skills
|
|
6
6
|
def list(workspace_slug = nil)
|
|
7
7
|
workspace_slug = resolve_workspace(workspace_slug)
|
|
8
|
-
return unless workspace_slug
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
workspace_skills = []
|
|
10
|
+
if workspace_slug
|
|
11
|
+
client = ApiClient.new
|
|
12
|
+
response = client.get("/workspaces/#{workspace_slug}/skills")
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
if response.code == 200
|
|
15
|
+
data = JSON.parse(response.body)
|
|
16
|
+
workspace_skills = data["skills"] || []
|
|
17
|
+
end
|
|
16
18
|
end
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
skills_sh = SkillsShScraper.new.fetch
|
|
21
|
+
|
|
22
|
+
all_skills = workspace_skills + skills_sh
|
|
20
23
|
|
|
21
|
-
if
|
|
22
|
-
puts "No skills
|
|
24
|
+
if all_skills.empty?
|
|
25
|
+
puts "No skills found."
|
|
23
26
|
return
|
|
24
27
|
end
|
|
25
28
|
|
|
26
|
-
puts format("%-
|
|
27
|
-
puts "-" *
|
|
28
|
-
|
|
29
|
-
puts format("%-
|
|
29
|
+
puts format("%-35s %-15s %s", "NAME", "SOURCE", "VERSION/REPO")
|
|
30
|
+
puts "-" * 70
|
|
31
|
+
all_skills.each do |skill|
|
|
32
|
+
puts format("%-35s %-15s %s", skill["name"], skill["source"], skill["latest_version"])
|
|
30
33
|
end
|
|
31
34
|
end
|
|
32
35
|
|
|
@@ -2,11 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
module Baid
|
|
4
4
|
class McpConfigurator
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
|
|
5
|
+
def self.build_mcp_entry
|
|
6
|
+
token = Config.load_token
|
|
7
|
+
workspace = Config.load_active_workspace
|
|
8
|
+
api_url = ENV.fetch("BAID_API_URL", "https://baid.dev")
|
|
9
|
+
|
|
10
|
+
env = { "BAID_API_URL" => api_url }
|
|
11
|
+
env["BAID_TOKEN"] = token if token
|
|
12
|
+
env["BAID_WORKSPACE"] = workspace if workspace
|
|
13
|
+
|
|
14
|
+
{
|
|
15
|
+
"command" => "npx",
|
|
16
|
+
"args" => ["-y", "@baid/mcp-server"],
|
|
17
|
+
"env" => env
|
|
18
|
+
}
|
|
19
|
+
end
|
|
10
20
|
|
|
11
21
|
def self.configure(agent, project_dir = ".")
|
|
12
22
|
config_path = File.join(project_dir, agent[:config_path])
|
|
@@ -14,7 +24,7 @@ module Baid
|
|
|
14
24
|
|
|
15
25
|
config = load_config(config_path)
|
|
16
26
|
config["mcpServers"] ||= {}
|
|
17
|
-
config["mcpServers"]["baid"] =
|
|
27
|
+
config["mcpServers"]["baid"] = build_mcp_entry
|
|
18
28
|
|
|
19
29
|
File.write(config_path, JSON.pretty_generate(config))
|
|
20
30
|
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Baid
|
|
4
|
+
class ProjectScanner
|
|
5
|
+
PACKAGE_FILES = %w[
|
|
6
|
+
Gemfile
|
|
7
|
+
package.json
|
|
8
|
+
requirements.txt
|
|
9
|
+
pom.xml
|
|
10
|
+
go.mod
|
|
11
|
+
Cargo.toml
|
|
12
|
+
composer.json
|
|
13
|
+
pubspec.yaml
|
|
14
|
+
build.gradle
|
|
15
|
+
pyproject.toml
|
|
16
|
+
].freeze
|
|
17
|
+
|
|
18
|
+
IGNORED = %w[. .. node_modules vendor .git .svn .hg __pycache__ .DS_Store].freeze
|
|
19
|
+
|
|
20
|
+
MAX_PACKAGE_SIZE = 10_000
|
|
21
|
+
|
|
22
|
+
def self.scan(project_dir)
|
|
23
|
+
file_tree = collect_tree(project_dir, depth: 0)
|
|
24
|
+
package_files = collect_packages(project_dir)
|
|
25
|
+
|
|
26
|
+
{ file_tree: file_tree, package_files: package_files }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.collect_tree(dir, depth:)
|
|
30
|
+
entries = []
|
|
31
|
+
Dir.children(dir).sort.each do |name|
|
|
32
|
+
next if IGNORED.include?(name) || name.start_with?(".")
|
|
33
|
+
|
|
34
|
+
full = File.join(dir, name)
|
|
35
|
+
if File.directory?(full)
|
|
36
|
+
entries << "#{name}/"
|
|
37
|
+
if depth < 1
|
|
38
|
+
collect_tree(full, depth: depth + 1).each do |sub|
|
|
39
|
+
entries << "#{name}/#{sub}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
else
|
|
43
|
+
entries << name
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
entries
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.collect_packages(project_dir)
|
|
50
|
+
result = {}
|
|
51
|
+
PACKAGE_FILES.each do |filename|
|
|
52
|
+
path = File.join(project_dir, filename)
|
|
53
|
+
next unless File.exist?(path)
|
|
54
|
+
|
|
55
|
+
content = File.read(path)
|
|
56
|
+
result[filename] = content.length > MAX_PACKAGE_SIZE ? content[0...MAX_PACKAGE_SIZE] : content
|
|
57
|
+
end
|
|
58
|
+
result
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private_class_method :collect_tree, :collect_packages
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Baid
|
|
4
|
+
class SkillsShScraper
|
|
5
|
+
SKILLS_SH_URL = "https://skills.sh/"
|
|
6
|
+
MAX_SKILLS = 50
|
|
7
|
+
|
|
8
|
+
def fetch
|
|
9
|
+
response = HTTParty.get(SKILLS_SH_URL, timeout: 10)
|
|
10
|
+
return [] unless response.code == 200
|
|
11
|
+
|
|
12
|
+
parse_skills(response.body)
|
|
13
|
+
rescue StandardError
|
|
14
|
+
[]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def parse_skills(html)
|
|
20
|
+
# skills.sh embeds skill data as JSON objects in the Next.js RSC payload
|
|
21
|
+
# The payload uses backslash-escaped quotes, so unescape before matching
|
|
22
|
+
unescaped = html.gsub('\"', '"')
|
|
23
|
+
|
|
24
|
+
skills = []
|
|
25
|
+
|
|
26
|
+
unescaped.scan(/"source":"([^"]+)","skillId":"([^"]+)","name":"([^"]+)","installs":(\d+)/) do |source, skill_id, name, installs|
|
|
27
|
+
skills << {
|
|
28
|
+
"name" => name,
|
|
29
|
+
"source" => "skills.sh",
|
|
30
|
+
"latest_version" => source,
|
|
31
|
+
"installs" => installs.to_i
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
skills.first(MAX_SKILLS)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/baid.rb
CHANGED
|
@@ -5,6 +5,7 @@ require "httparty"
|
|
|
5
5
|
require "json"
|
|
6
6
|
require "yaml"
|
|
7
7
|
require "fileutils"
|
|
8
|
+
require "tty-prompt"
|
|
8
9
|
|
|
9
10
|
module Baid
|
|
10
11
|
VERSION = "0.1.0"
|
|
@@ -14,6 +15,7 @@ require_relative "baid/config"
|
|
|
14
15
|
require_relative "baid/api_client"
|
|
15
16
|
require_relative "baid/agent_detector"
|
|
16
17
|
require_relative "baid/mcp_configurator"
|
|
18
|
+
require_relative "baid/project_scanner"
|
|
17
19
|
require_relative "baid/commands/login"
|
|
18
20
|
require_relative "baid/commands/logout"
|
|
19
21
|
require_relative "baid/commands/whoami"
|
|
@@ -23,6 +25,7 @@ require_relative "baid/commands/reconfigure"
|
|
|
23
25
|
require_relative "baid/commands/install"
|
|
24
26
|
require_relative "baid/commands/search"
|
|
25
27
|
require_relative "baid/commands/update"
|
|
28
|
+
require_relative "baid/skills_sh_scraper"
|
|
26
29
|
require_relative "baid/commands/skills"
|
|
27
30
|
require_relative "baid/commands/workspace"
|
|
28
31
|
require_relative "baid/cli"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: baid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Baid Team
|
|
@@ -115,7 +115,9 @@ files:
|
|
|
115
115
|
- lib/baid/commands/workspace.rb
|
|
116
116
|
- lib/baid/config.rb
|
|
117
117
|
- lib/baid/mcp_configurator.rb
|
|
118
|
+
- lib/baid/project_scanner.rb
|
|
118
119
|
- lib/baid/skill_writer.rb
|
|
120
|
+
- lib/baid/skills_sh_scraper.rb
|
|
119
121
|
homepage: https://baid.dev
|
|
120
122
|
licenses:
|
|
121
123
|
- MIT
|