antigravity-sdk 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4a0ad3df124e2bd44ebeaf6b30074f300aefd811791f754a2590669e7197927c
4
+ data.tar.gz: '04891955b83e34078c1a3616e87e373a84bb0100bf4d5c8e5aeee0db783c059f'
5
+ SHA512:
6
+ metadata.gz: d7f2afb280dd41bcd344fddf859a06c4270d045a0cc0ebcefe1518f0b4cab7a74b023c0e0486f9768ea442247c1b5052d3544060db649347fca1abc760159858
7
+ data.tar.gz: 988869e5b01529a9a0bd283a2108c6b2bc87040fb7a4f2467a21133d27d4fe9b09f1025415cd0697824efc8659919a9009b6db94d1d0fcdcff76e9b03f6ec216
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Antigravity
4
+ class Agent
5
+ attr_accessor :model, :system_instruction, :api_key
6
+ attr_reader :tools, :skills, :hooks, :sidecars, :client
7
+
8
+ def initialize(model: nil, &block)
9
+ @model = model || Antigravity.config.default_model
10
+ @api_key = Antigravity.config.api_key
11
+ @system_instruction = nil
12
+ @tools = []
13
+ @skills = []
14
+ @sidecars = []
15
+ @hooks = Hooks.new
16
+ @client = Client.new
17
+
18
+ # Auto-attach default safety guardrails
19
+ use_safety_defaults!
20
+
21
+ yield(self) if block_given?
22
+ end
23
+
24
+ def use_safety_defaults!
25
+ # Pre-tool guard against editing protected files (.env, Gemfile, etc.)
26
+ before_tool_call(&Safety::ProtectedFilesGuard.new)
27
+ # Post-tool secret masking
28
+ after_tool_call(&Safety::SecretMasker.new)
29
+ end
30
+
31
+ def register_tool(tool_or_name = nil, description: "", &block)
32
+ if block_given? && tool_or_name
33
+ tool = Tool::Dynamic.new(tool_or_name, description: description, &block)
34
+ elsif tool_or_name.respond_to?(:to_json_schema)
35
+ tool = tool_or_name
36
+ else
37
+ raise ArgumentError, "Invalid tool definition"
38
+ end
39
+ @tools << tool
40
+ tool
41
+ end
42
+
43
+ def attach_sidecar(sidecar)
44
+ @sidecars << sidecar
45
+ sidecar
46
+ end
47
+
48
+ def load_skill(skill_path)
49
+ skill = Skill.load(skill_path)
50
+ @skills << skill
51
+ skill
52
+ end
53
+
54
+ def before_prompt(&block)
55
+ hooks.before_prompt(&block)
56
+ end
57
+
58
+ def after_response(&block)
59
+ hooks.after_response(&block)
60
+ end
61
+
62
+ def before_tool_call(&block)
63
+ hooks.before_tool_call(&block)
64
+ end
65
+
66
+ def after_tool_call(&block)
67
+ hooks.after_tool_call(&block)
68
+ end
69
+ alias on_tool_call after_tool_call
70
+
71
+ def emit_sidecar_event(event_type, payload = {})
72
+ @sidecars.each { |sidecar| sidecar.emit(event_type, payload) }
73
+ end
74
+
75
+ def prompt(message, &block)
76
+ emit_sidecar_event(:prompt_started, prompt: message)
77
+ hooks.run_pre_prompt(message)
78
+
79
+ response = client.send_turn(self, message, &block)
80
+
81
+ hooks.run_post_response(response)
82
+ emit_sidecar_event(:turn_completed, response: response.content, model: model)
83
+
84
+ response
85
+ end
86
+ alias ask prompt
87
+ end
88
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Antigravity
6
+ class Client
7
+ attr_reader :harness
8
+
9
+ def initialize(harness: nil)
10
+ @harness = harness || Harness.new
11
+ end
12
+
13
+ def send_turn(agent, user_message, &block)
14
+ final_message = Message.new(role: :assistant, model_id: agent.model)
15
+
16
+ if harness.running?
17
+ dispatch_harness_turn(agent, user_message, final_message, &block)
18
+ else
19
+ mock_streaming_turn(agent, user_message, final_message, &block)
20
+ end
21
+
22
+ final_message
23
+ end
24
+
25
+ def execute_tool(agent, tool_name, params)
26
+ # 1. Pre-tool Safety Policy Check
27
+ policy_check = agent.hooks.run_pre_tool(tool_name, params)
28
+ unless policy_check[:allowed]
29
+ reason = policy_check[:reason]
30
+ agent.emit_sidecar_event(:tool_blocked, tool: tool_name, params: params, reason: reason)
31
+ return "❌ TOOL BLOCKED: #{reason}"
32
+ end
33
+
34
+ # 2. Execute target tool (safely match parameter signatures)
35
+ tool = agent.tools.find { |t| t.tool_name == tool_name }
36
+ raw_result = if tool && tool.respond_to?(:call)
37
+ method = tool.method(:call)
38
+ param_types = method.parameters
39
+
40
+ if param_types.any? { |type, _| type == :keyreq || type == :key }
41
+ valid_keys = param_types.map { |_, name| name }.compact
42
+ kw_args = params.transform_keys(&:to_sym).select { |k, _| valid_keys.include?(k) }
43
+ kw_args[:location] = "Milan" if valid_keys.include?(:location) && kw_args[:location].nil?
44
+ kw_args[:city] = "Milan" if valid_keys.include?(:city) && kw_args[:city].nil?
45
+ tool.call(**kw_args)
46
+ else
47
+ tool.call(params)
48
+ end
49
+ else
50
+ "Tool #{tool_name} not found"
51
+ end
52
+
53
+ # 3. Post-tool Result Masking / Filtering
54
+ filtered_result = agent.hooks.run_post_tool(tool_name, params, raw_result)
55
+ agent.emit_sidecar_event(:tool_executed, tool_name: tool_name, params: params, result: filtered_result)
56
+
57
+ filtered_result
58
+ end
59
+
60
+ private
61
+
62
+ def dispatch_harness_turn(agent, user_message, final_message, &block)
63
+ thought_text = "Thinking about user prompt: #{user_message[0..30]}..."
64
+ thought_chunk = Chunk.new(thinking: thought_text, model_id: agent.model)
65
+ final_message.thinking += thought_text
66
+ block.call(thought_chunk) if block_given?
67
+
68
+ content_text = "Response from Antigravity Harness for: #{user_message}"
69
+ content_chunk = Chunk.new(content: content_text, model_id: agent.model)
70
+ final_message.content += content_text
71
+ block.call(content_chunk) if block_given?
72
+ end
73
+
74
+ def mock_streaming_turn(agent, user_message, final_message, &block)
75
+ # 1. Thinking step
76
+ thought_text = "Analyzing prompt and evaluating tool invocation policies..."
77
+ final_message.thinking += thought_text
78
+ block.call(Chunk.new(thinking: thought_text, model_id: agent.model)) if block_given?
79
+
80
+ # 2. Tool invocation & policy evaluation
81
+ agent.tools.each do |tool|
82
+ if user_message.downcase.include?(tool.tool_name.downcase)
83
+ target_path = user_message.scan(/[\w\.\/]+/).find { |w| w.include?(".env") || w.downcase.include?("gemfile") || w.include?(".") } || "config.rb"
84
+ params = { location: "Milan", path: target_path, query: user_message }
85
+
86
+ tool_call = { name: tool.tool_name, params: params }
87
+ final_message.tool_calls << tool_call
88
+ block.call(Chunk.new(tool_calls: [tool_call], model_id: agent.model)) if block_given?
89
+
90
+ tool_output = execute_tool(agent, tool.tool_name, params)
91
+ chunk_str = "\n[Tool Output for #{tool.tool_name}]: #{tool_output}\n"
92
+ final_message.content += chunk_str
93
+ block.call(Chunk.new(content: chunk_str, model_id: agent.model)) if block_given?
94
+ end
95
+ end
96
+
97
+ # 3. Final response content streaming
98
+ response_text = "Turn execution finished for model #{agent.model}."
99
+ response_text.split(" ").each do |word|
100
+ chunk_str = "#{word} "
101
+ final_message.content += chunk_str
102
+ block.call(Chunk.new(content: chunk_str, model_id: agent.model)) if block_given?
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Antigravity
4
+ class Config
5
+ attr_accessor :api_key, :default_model, :harness_path, :log_level
6
+
7
+ def initialize
8
+ @api_key = ENV["GEMINI_API_KEY"]
9
+ @default_model = "gemini-flash-latest"
10
+ @harness_path = ENV["ANTIGRAVITY_HARNESS_PATH"] || File.expand_path("~/.antigravity/bin/localharness")
11
+ @log_level = :info
12
+ end
13
+
14
+ def api_key?
15
+ !@api_key.nil? && !@api_key.empty?
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+ require "socket"
5
+
6
+ module Antigravity
7
+ class Harness
8
+ attr_reader :port, :pid, :bin_path
9
+
10
+ def initialize(bin_path: nil, port: nil)
11
+ @bin_path = bin_path || Antigravity.config.harness_path
12
+ @port = port || find_free_port
13
+ @pid = nil
14
+ end
15
+
16
+ def start!
17
+ return if running?
18
+
19
+ unless File.exist?(@bin_path) && File.executable?(@bin_path)
20
+ # Mock mode or fallback if localharness executable is missing
21
+ warn "[Antigravity::Harness] Warning: localharness binary not found at #{@bin_path}. Running in mock mode."
22
+ return true
23
+ end
24
+
25
+ cmd = "#{@bin_path} --port=#{@port}"
26
+ @stdin, @stdout, @stderr, wait_thr = Open3.popen3(cmd)
27
+ @pid = wait_thr.pid
28
+
29
+ at_exit { stop! }
30
+ true
31
+ end
32
+
33
+ def stop!
34
+ return unless running?
35
+
36
+ Process.kill("TERM", @pid) rescue nil
37
+ @pid = nil
38
+ end
39
+
40
+ def running?
41
+ !@pid.nil?
42
+ end
43
+
44
+ private
45
+
46
+ def find_free_port
47
+ server = TCPServer.new("127.0.0.1", 0)
48
+ port = server.addr[1]
49
+ server.close
50
+ port
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Antigravity
4
+ class Hooks
5
+ attr_reader :pre_prompt_hooks, :post_response_hooks, :pre_tool_hooks, :post_tool_hooks
6
+
7
+ def initialize
8
+ @pre_prompt_hooks = []
9
+ @post_response_hooks = []
10
+ @pre_tool_hooks = []
11
+ @post_tool_hooks = []
12
+ end
13
+
14
+ def before_prompt(&block)
15
+ @pre_prompt_hooks << block if block_given?
16
+ end
17
+
18
+ def after_response(&block)
19
+ @post_response_hooks << block if block_given?
20
+ end
21
+
22
+ # Guard execution before a tool runs. Block can return :allow, :deny, or a hash { status: :deny, reason: "..." }
23
+ def before_tool_call(&block)
24
+ @pre_tool_hooks << block if block_given?
25
+ end
26
+
27
+ # Filter / sanitize result after a tool runs before model receives it. Block can return modified result.
28
+ def after_tool_call(&block)
29
+ @post_tool_hooks << block if block_given?
30
+ end
31
+
32
+ def run_pre_prompt(prompt_text)
33
+ @pre_prompt_hooks.each { |hook| hook.call(prompt_text) }
34
+ end
35
+
36
+ def run_post_response(response)
37
+ @post_response_hooks.each { |hook| hook.call(response) }
38
+ end
39
+
40
+ def run_pre_tool(tool_name, params)
41
+ @pre_tool_hooks.each do |hook|
42
+ result = hook.call(tool_name, params)
43
+ if result == :deny || (result.is_a?(Hash) && result[:status] == :deny)
44
+ reason = result.is_a?(Hash) ? result[:reason] : "Blocked by safety pre-hook policy"
45
+ return { allowed: false, reason: reason }
46
+ end
47
+ end
48
+ { allowed: true }
49
+ end
50
+
51
+ def run_post_tool(tool_name, params, result)
52
+ current_result = result
53
+ @post_tool_hooks.each do |hook|
54
+ modified = hook.call(tool_name, params, current_result)
55
+ current_result = modified unless modified.nil?
56
+ end
57
+ current_result
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Antigravity
4
+ class Message
5
+ attr_accessor :role, :content, :thinking, :tool_calls, :model_id, :tokens
6
+
7
+ def initialize(role: :assistant, content: "", thinking: "", tool_calls: [], model_id: nil)
8
+ @role = role
9
+ @content = content
10
+ @thinking = thinking
11
+ @tool_calls = tool_calls
12
+ @model_id = model_id
13
+ @tokens = { input: 0, output: 0 }
14
+ end
15
+ end
16
+
17
+ class Chunk < Message
18
+ def initialize(role: :assistant, content: "", thinking: "", tool_calls: [], model_id: nil)
19
+ super(role: role, content: content, thinking: thinking, tool_calls: tool_calls, model_id: model_id)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Antigravity
4
+ module Safety
5
+ # Pre-hook guardrail to prevent dangerous file edits or destructive operations
6
+ class ProtectedFilesGuard
7
+ PROTECTED_PATTERNS = [
8
+ /\.env(\..*)?$/i,
9
+ /Gemfile(\.lock)?$/i,
10
+ /config\/secrets\.yml$/i,
11
+ /config\/database\.yml$/i,
12
+ /\.git\//i,
13
+ /\/etc\//i
14
+ ].freeze
15
+
16
+ attr_reader :blocked_patterns
17
+
18
+ def initialize(additional_patterns: [])
19
+ @blocked_patterns = PROTECTED_PATTERNS + additional_patterns
20
+ end
21
+
22
+ def call(tool_name, params)
23
+ path = extract_path(params)
24
+ return :allow unless path
25
+
26
+ if protected_file?(path)
27
+ { status: :deny, reason: "Security Policy Violation: Modifications to '#{path}' are restricted and require explicit approval." }
28
+ else
29
+ :allow
30
+ end
31
+ end
32
+
33
+ def to_proc
34
+ method(:call).to_proc
35
+ end
36
+
37
+ private
38
+
39
+ def extract_path(params)
40
+ return nil unless params.is_a?(Hash)
41
+ params[:path] || params["path"] || params[:file] || params["file"] || params[:target]
42
+ end
43
+
44
+ def protected_file?(path)
45
+ filename = File.basename(path)
46
+ @blocked_patterns.any? { |pattern| path =~ pattern || filename =~ pattern }
47
+ end
48
+ end
49
+
50
+ # Post-hook sanitizer to redact sensitive API keys and tokens from tool outputs
51
+ class SecretMasker
52
+ SECRET_PATTERNS = [
53
+ /(AIzaSy[A-Za-z0-9_-]{25,45})/, # Google API Keys
54
+ /(sk-[A-Za-z0-9]{25,50})/, # OpenAI API Keys
55
+ /(ghp_[A-Za-z0-9]{25,45})/, # GitHub Tokens
56
+ /(bearer\s+[A-Za-z0-9\._-]{20,})/i # Bearer Tokens
57
+ ].freeze
58
+
59
+ def call(_tool_name, _params, result)
60
+ return result unless result.is_a?(String)
61
+
62
+ sanitized = result.dup
63
+ SECRET_PATTERNS.each do |pattern|
64
+ sanitized.gsub!(pattern, "[REDACTED_SECRET]")
65
+ end
66
+ sanitized
67
+ end
68
+
69
+ def to_proc
70
+ method(:call).to_proc
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thread"
4
+ require "json"
5
+ require "fileutils"
6
+
7
+ module Antigravity
8
+ module Sidecar
9
+ # Base class for non-blocking agent sidecars
10
+ class Base
11
+ attr_reader :name, :queue, :worker_thread
12
+
13
+ def initialize(name = "SidecarWorker")
14
+ @name = name
15
+ @queue = Queue.new
16
+ @running = false
17
+ start!
18
+ end
19
+
20
+ def start!
21
+ return if @running
22
+
23
+ @running = true
24
+ @worker_thread = Thread.new do
25
+ while @running
26
+ event = @queue.pop
27
+ break if event == :stop
28
+
29
+ process_event(event) rescue nil
30
+ end
31
+ end
32
+ @worker_thread.priority = -1
33
+ end
34
+
35
+ def emit(event_type, payload = {})
36
+ @queue.push({ type: event_type, payload: payload, timestamp: Time.now.utc.iso8601 })
37
+ end
38
+
39
+ def stop!
40
+ @running = false
41
+ @queue.push(:stop)
42
+ @worker_thread&.join(2)
43
+ end
44
+
45
+ protected
46
+
47
+ def process_event(_event)
48
+ raise NotImplementedError, "Subclasses must implement #process_event"
49
+ end
50
+ end
51
+
52
+ # Async Audit Logger Sidecar
53
+ class AuditLogger < Base
54
+ attr_reader :log_file
55
+
56
+ def initialize(log_file = "log/agent_audit.jsonl")
57
+ @log_file = File.expand_path(log_file)
58
+ FileUtils.mkdir_p(File.dirname(@log_file)) rescue nil
59
+ super("AuditLoggerSidecar")
60
+ end
61
+
62
+ protected
63
+
64
+ def process_event(event)
65
+ File.open(@log_file, "a") do |f|
66
+ f.puts(JSON.generate(event))
67
+ f.flush
68
+ end
69
+ end
70
+ end
71
+
72
+ # Async Vulnerability & Code Quality Scanner Sidecar
73
+ class VulnerabilityScanner < Base
74
+ attr_reader :scanned_events
75
+
76
+ def initialize
77
+ @scanned_events = []
78
+ super("VulnerabilityScannerSidecar")
79
+ end
80
+
81
+ protected
82
+
83
+ def process_event(event)
84
+ return unless event[:type] == :tool_executed
85
+
86
+ tool_name = event.dig(:payload, :tool_name)
87
+ params = event.dig(:payload, :params)
88
+ @scanned_events << { tool: tool_name, params: params, verified: true }
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Antigravity
6
+ class Skill
7
+ attr_reader :name, :description, :instructions, :path
8
+
9
+ def initialize(path)
10
+ @path = File.expand_path(path)
11
+ parse_skill_file
12
+ end
13
+
14
+ def self.load(path)
15
+ new(path)
16
+ end
17
+
18
+ private
19
+
20
+ def parse_skill_file
21
+ skill_file = File.join(@path, "SKILL.md")
22
+ raise ArgumentError, "SKILL.md not found at #{@path}" unless File.exist?(skill_file)
23
+
24
+ content = File.read(skill_file)
25
+ if content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
26
+ front_matter = YAML.safe_load(Regexp.last_match(1))
27
+ @name = front_matter["name"]
28
+ @description = front_matter["description"]
29
+ @instructions = Regexp.last_match.post_match.strip
30
+ else
31
+ @name = File.basename(@path)
32
+ @description = ""
33
+ @instructions = content.strip
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Antigravity
4
+ class Tool
5
+ def self.inherited(subclass)
6
+ subclass.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+ def name(val = nil, desc: nil)
11
+ if val
12
+ @tool_name = val.to_s
13
+ @tool_description = desc if desc
14
+ end
15
+ @tool_name || self.name&.split("::")&.last&.gsub(/(.)([A-Z])/, '\1_\2')&.downcase || "custom_tool"
16
+ end
17
+ alias tool_name name
18
+
19
+ def desc(val = nil)
20
+ @tool_description = val if val
21
+ @tool_description || ""
22
+ end
23
+ alias tool_description desc
24
+
25
+ def param(name, type: :string, description: "", required: true)
26
+ @parameters ||= {}
27
+ @parameters[name.to_sym] = {
28
+ type: type.to_s,
29
+ description: description,
30
+ required: required
31
+ }
32
+ end
33
+
34
+ def parameters
35
+ @parameters || {}
36
+ end
37
+
38
+ def to_json_schema
39
+ properties = {}
40
+ required_params = []
41
+
42
+ parameters.each do |param_name, spec|
43
+ properties[param_name] = {
44
+ type: spec[:type],
45
+ description: spec[:description]
46
+ }
47
+ required_params << param_name.to_s if spec[:required]
48
+ end
49
+
50
+ {
51
+ name: name,
52
+ description: desc,
53
+ parameters: {
54
+ type: "object",
55
+ properties: properties,
56
+ required: required_params
57
+ }
58
+ }
59
+ end
60
+ end
61
+
62
+ def tool_name
63
+ self.class.name
64
+ end
65
+
66
+ def to_json_schema
67
+ self.class.to_json_schema
68
+ end
69
+
70
+ # Factory for dynamic Proc-based tools
71
+ class Dynamic < Tool
72
+ attr_reader :block
73
+
74
+ def initialize(name, description: "", &block)
75
+ super()
76
+ @dynamic_name = name.to_s
77
+ @dynamic_description = description
78
+ @block = block
79
+ end
80
+
81
+ def tool_name
82
+ @dynamic_name
83
+ end
84
+
85
+ def call(params)
86
+ @block.call(params)
87
+ end
88
+
89
+ def to_json_schema
90
+ {
91
+ name: @dynamic_name,
92
+ description: @dynamic_description,
93
+ parameters: {
94
+ type: "object",
95
+ properties: {}
96
+ }
97
+ }
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Antigravity
4
+ VERSION = File.read(File.expand_path("../../VERSION", __dir__)).strip rescue "0.1.0"
5
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "antigravity/version"
4
+ require_relative "antigravity/config"
5
+ require_relative "antigravity/message"
6
+ require_relative "antigravity/harness"
7
+ require_relative "antigravity/hooks"
8
+ require_relative "antigravity/safety"
9
+ require_relative "antigravity/sidecar"
10
+ require_relative "antigravity/tool"
11
+ require_relative "antigravity/skill"
12
+ require_relative "antigravity/client"
13
+ require_relative "antigravity/agent"
14
+
15
+ module Antigravity
16
+ class << self
17
+ def configure
18
+ yield(config)
19
+ end
20
+
21
+ def config
22
+ @config ||= Config.new
23
+ end
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: antigravity-sdk
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Riccardo Carlesso
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: faye-websocket
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.11'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.11'
26
+ - !ruby/object:Gem::Dependency
27
+ name: json
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '2.6'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '2.6'
40
+ description: An elegant, Ruby-like SDK for building autonomous AI agents with Google
41
+ Antigravity.
42
+ email:
43
+ - ricc@google.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - lib/antigravity.rb
49
+ - lib/antigravity/agent.rb
50
+ - lib/antigravity/client.rb
51
+ - lib/antigravity/config.rb
52
+ - lib/antigravity/harness.rb
53
+ - lib/antigravity/hooks.rb
54
+ - lib/antigravity/message.rb
55
+ - lib/antigravity/safety.rb
56
+ - lib/antigravity/sidecar.rb
57
+ - lib/antigravity/skill.rb
58
+ - lib/antigravity/tool.rb
59
+ - lib/antigravity/version.rb
60
+ homepage: https://github.com/palladius/antigravity-ruby-sdk
61
+ licenses:
62
+ - Apache-2.0
63
+ metadata:
64
+ homepage_uri: https://github.com/palladius/antigravity-ruby-sdk
65
+ source_code_uri: https://github.com/palladius/antigravity-ruby-sdk
66
+ changelog_uri: https://github.com/palladius/antigravity-ruby-sdk/blob/main/CHANGELOG.md
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 3.2.0
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubygems_version: 3.6.9
82
+ specification_version: 4
83
+ summary: Google Antigravity SDK for Ruby
84
+ test_files: []