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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 83bacc2e8f9264fa7203aa6b9c9d1ded8a18974c63c72fbd3ef4f0e0ae6f7a76
4
- data.tar.gz: '094836c43eab0251dc04fc8738b3b885b57cda69edb7197704c123a40d0450c1'
3
+ metadata.gz: 221fba49d1c1620e0e5423517dc5b0c63f1d6d71898377b3df90671ae5732a2a
4
+ data.tar.gz: 499df0b58c473f06738f1e725e5c9b023a7b1b481c3192fbdc6494e2c24e5626
5
5
  SHA512:
6
- metadata.gz: 2f39c86e23698bd6883c23521050f745ffbea60547c235a5d0bcb7083f575b8ef77361faac640169354403fdfbdcb77071352ce5330773b892faccc355420d68
7
- data.tar.gz: b392ac3a82514828f91f3eb59cc77699b8c3842cbdefca33969c9763ac528180e3396037420c390d50915936af9fa6f46c59fcdf9b47a0d7105854206b838111
6
+ metadata.gz: 62e89d04b3c8cbb5ed97a4a4b20bf16b3044cddf4eb37863304fcd7222a7973b3987465fa69a18ae84f40b7e205cb2d69ad41dfc25022657629b12edd2eea243
7
+ data.tar.gz: bea46903bc722717c6ca60506a13d2124c58edd277e16f0e390fce4f282c6783904557b8b9cb20d427426a91a9e54aa2838382958677b3e3c86b38c30e162f64
@@ -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 = { "agents" => agents.map { |a| a[:name] }, "installed_skills" => [] }
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
@@ -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
- client = ApiClient.new
11
- response = client.get("/workspaces/#{workspace_slug}/skills")
9
+ workspace_skills = []
10
+ if workspace_slug
11
+ client = ApiClient.new
12
+ response = client.get("/workspaces/#{workspace_slug}/skills")
12
13
 
13
- unless response.code == 200
14
- puts "Failed to fetch skills (#{response.code}). Check your token with `baid whoami`."
15
- return
14
+ if response.code == 200
15
+ data = JSON.parse(response.body)
16
+ workspace_skills = data["skills"] || []
17
+ end
16
18
  end
17
19
 
18
- data = JSON.parse(response.body)
19
- skills = data["skills"] || []
20
+ skills_sh = SkillsShScraper.new.fetch
21
+
22
+ all_skills = workspace_skills + skills_sh
20
23
 
21
- if skills.empty?
22
- puts "No skills in workspace."
24
+ if all_skills.empty?
25
+ puts "No skills found."
23
26
  return
24
27
  end
25
28
 
26
- puts format("%-25s %-15s %s", "NAME", "SOURCE", "VERSION")
27
- puts "-" * 50
28
- skills.each do |skill|
29
- puts format("%-25s %-15s %s", skill["name"], skill["source"], skill["latest_version"])
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
- BAID_MCP_ENTRY = {
6
- "command" => "npx",
7
- "args" => ["-y", "@baid/mcp-server"],
8
- "env" => {}
9
- }.freeze
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"] = BAID_MCP_ENTRY.dup
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.1.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