fosm-rails-coding-agent 0.0.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 +7 -0
- data/AGENTS.md +77 -0
- data/CHANGELOG.md +16 -0
- data/LICENSE +127 -0
- data/README.md +135 -0
- data/bin/fosm-coding-agent +14 -0
- data/lib/fosm_rails_coding_agent/acp_agent.rb +132 -0
- data/lib/fosm_rails_coding_agent/configuration.rb +27 -0
- data/lib/fosm_rails_coding_agent/exceptions_middleware.rb +67 -0
- data/lib/fosm_rails_coding_agent/middleware.rb +115 -0
- data/lib/fosm_rails_coding_agent/quiet_middleware.rb +18 -0
- data/lib/fosm_rails_coding_agent/railtie.rb +51 -0
- data/lib/fosm_rails_coding_agent/tools/base.rb +10 -0
- data/lib/fosm_rails_coding_agent/tools/execute_sql.rb +44 -0
- data/lib/fosm_rails_coding_agent/tools/fosm/available_events.rb +46 -0
- data/lib/fosm_rails_coding_agent/tools/fosm/fire_event.rb +46 -0
- data/lib/fosm_rails_coding_agent/tools/fosm/inspect_lifecycle.rb +44 -0
- data/lib/fosm_rails_coding_agent/tools/fosm/list_lifecycles.rb +45 -0
- data/lib/fosm_rails_coding_agent/tools/fosm/model_resolver.rb +25 -0
- data/lib/fosm_rails_coding_agent/tools/fosm/transition_history.rb +56 -0
- data/lib/fosm_rails_coding_agent/tools/fosm/why_blocked.rb +36 -0
- data/lib/fosm_rails_coding_agent/tools/get_docs.rb +58 -0
- data/lib/fosm_rails_coding_agent/tools/get_logs.rb +66 -0
- data/lib/fosm_rails_coding_agent/tools/get_models.rb +35 -0
- data/lib/fosm_rails_coding_agent/tools/get_source_location.rb +69 -0
- data/lib/fosm_rails_coding_agent/tools/project_eval.rb +76 -0
- data/lib/fosm_rails_coding_agent/transport.rb +84 -0
- data/lib/fosm_rails_coding_agent/version.rb +5 -0
- data/lib/fosm_rails_coding_agent.rb +26 -0
- metadata +139 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FosmRailsCodingAgent
|
|
4
|
+
module Tools
|
|
5
|
+
# Lists all ActiveRecord models in the application with their source locations.
|
|
6
|
+
class GetModels < Base
|
|
7
|
+
tool_name "get_models"
|
|
8
|
+
description <<~DESC
|
|
9
|
+
Returns a list of all ActiveRecord model classes in the application
|
|
10
|
+
with their source file locations.
|
|
11
|
+
DESC
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
Rails.application.eager_load!
|
|
15
|
+
|
|
16
|
+
ActiveRecord::Base.descendants.reject(&:abstract_class?).sort_by(&:name).map do |model|
|
|
17
|
+
location = source_location_for(model.name)
|
|
18
|
+
location ? "* #{model.name} at #{location}" : "* #{model.name}"
|
|
19
|
+
end.join("\n")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def source_location_for(class_name)
|
|
25
|
+
file_path, line_number = Object.const_source_location(class_name)
|
|
26
|
+
return nil unless file_path
|
|
27
|
+
|
|
28
|
+
relative = Pathname.new(file_path).relative_path_from(Rails.root)
|
|
29
|
+
"#{relative}:#{line_number}"
|
|
30
|
+
rescue ArgumentError
|
|
31
|
+
"#{file_path}:#{line_number}"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FosmRailsCodingAgent
|
|
4
|
+
module Tools
|
|
5
|
+
# Finds the source file and line number for a Ruby constant or method.
|
|
6
|
+
# Supports constants (String), instance methods (String#gsub),
|
|
7
|
+
# class methods (File.executable?), and gem roots (dep:rails).
|
|
8
|
+
class GetSourceLocation < Base
|
|
9
|
+
tool_name "get_source_location"
|
|
10
|
+
description <<~DESC
|
|
11
|
+
Returns the source location for a Ruby constant or method reference.
|
|
12
|
+
|
|
13
|
+
Examples: `String`, `String#gsub`, `File.executable?`, `dep:rails`
|
|
14
|
+
|
|
15
|
+
Works across the current project and all loaded gems.
|
|
16
|
+
Prefer this over grepping when you know the exact reference.
|
|
17
|
+
DESC
|
|
18
|
+
|
|
19
|
+
arguments do
|
|
20
|
+
required(:reference).filled(:string).description("Constant, method, or dep:PACKAGE to look up")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def call(reference:)
|
|
24
|
+
if reference.start_with?("dep:")
|
|
25
|
+
return package_location(reference.delete_prefix("dep:"))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
file_path, line_number = self.class.resolve(reference)
|
|
29
|
+
raise NameError, "could not find source location for #{reference}" unless file_path
|
|
30
|
+
|
|
31
|
+
relative_path(file_path, line_number)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Resolve a reference to [file_path, line_number].
|
|
35
|
+
# Used by GetDocs to share resolution logic.
|
|
36
|
+
def self.resolve(reference)
|
|
37
|
+
constant_path, selector, method_name = reference.rpartition(/\.|#/)
|
|
38
|
+
return Object.const_source_location(method_name) if selector.empty?
|
|
39
|
+
|
|
40
|
+
mod = Object.const_get(constant_path)
|
|
41
|
+
raise "#{constant_path} is not a class/module" unless mod.is_a?(Module)
|
|
42
|
+
|
|
43
|
+
if selector == "#"
|
|
44
|
+
mod.instance_method(method_name).source_location
|
|
45
|
+
else
|
|
46
|
+
mod.method(method_name).source_location
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def package_location(package)
|
|
53
|
+
raise "dep: prefix requires Bundler" unless defined?(Bundler)
|
|
54
|
+
|
|
55
|
+
spec = Bundler.load.specs.find { |s| s.name == package }
|
|
56
|
+
raise "Package #{package} not found in Gemfile" unless spec
|
|
57
|
+
|
|
58
|
+
spec.full_gem_path
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def relative_path(file_path, line_number)
|
|
62
|
+
relative = Pathname.new(file_path).relative_path_from(Rails.root)
|
|
63
|
+
"#{relative}:#{line_number}"
|
|
64
|
+
rescue ArgumentError
|
|
65
|
+
"#{file_path}:#{line_number}"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "timeout"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
module FosmRailsCodingAgent
|
|
7
|
+
module Tools
|
|
8
|
+
# Evaluates Ruby code in the running Rails application context.
|
|
9
|
+
# Captures stdout/stderr and enforces a configurable timeout.
|
|
10
|
+
class ProjectEval < Base
|
|
11
|
+
tool_name "project_eval"
|
|
12
|
+
description <<~DESC
|
|
13
|
+
Evaluates Ruby code in the context of the running Rails application.
|
|
14
|
+
Current Ruby version: #{RUBY_VERSION}
|
|
15
|
+
|
|
16
|
+
Use this to test behavior, inspect objects, or debug issues.
|
|
17
|
+
Returns the result plus any stdout/stderr output.
|
|
18
|
+
DESC
|
|
19
|
+
|
|
20
|
+
arguments do
|
|
21
|
+
required(:code).filled(:string).description("The Ruby code to evaluate")
|
|
22
|
+
optional(:arguments).value(:array).description("Arguments available as `arguments` in the evaluated code")
|
|
23
|
+
optional(:timeout).filled(:integer).description("Timeout in milliseconds (default: 30000)")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def @input_schema.json_schema
|
|
27
|
+
schema = super
|
|
28
|
+
schema[:properties][:arguments][:items] = {}
|
|
29
|
+
schema
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def call(code:, arguments: [], timeout: nil)
|
|
33
|
+
timeout ||= FosmRailsCodingAgent.config.eval_timeout_ms
|
|
34
|
+
original_stdout = $stdout
|
|
35
|
+
original_stderr = $stderr
|
|
36
|
+
|
|
37
|
+
stdout_capture = StringIO.new
|
|
38
|
+
stderr_capture = StringIO.new
|
|
39
|
+
$stdout = stdout_capture
|
|
40
|
+
$stderr = stderr_capture
|
|
41
|
+
|
|
42
|
+
begin
|
|
43
|
+
timeout_seconds = timeout / 1000.0
|
|
44
|
+
|
|
45
|
+
success, result = begin
|
|
46
|
+
Timeout.timeout(timeout_seconds) do
|
|
47
|
+
[true, eval(code, eval_binding(arguments))] # rubocop:disable Security/Eval
|
|
48
|
+
end
|
|
49
|
+
rescue Timeout::Error
|
|
50
|
+
[false, "Timeout::Error: Evaluation timed out after #{timeout}ms"]
|
|
51
|
+
rescue => e
|
|
52
|
+
[false, e.full_message]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
stdout = stdout_capture.string
|
|
56
|
+
stderr = stderr_capture.string
|
|
57
|
+
|
|
58
|
+
if stdout.empty? && stderr.empty?
|
|
59
|
+
result.to_s
|
|
60
|
+
else
|
|
61
|
+
"STDOUT:\n#{stdout}\n\nSTDERR:\n#{stderr}\n\nResult:\n#{result}"
|
|
62
|
+
end
|
|
63
|
+
ensure
|
|
64
|
+
$stdout = original_stdout
|
|
65
|
+
$stderr = original_stderr
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def eval_binding(arguments)
|
|
72
|
+
binding
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "rack"
|
|
5
|
+
require "fast_mcp"
|
|
6
|
+
|
|
7
|
+
module FosmRailsCodingAgent
|
|
8
|
+
# Streamable HTTP transport for MCP — POST-only, no SSE.
|
|
9
|
+
# Implements the MCP Streamable HTTP protocol for synchronous
|
|
10
|
+
# JSON-RPC message exchange between coding agents and FosmRailsCodingAgent.
|
|
11
|
+
class Transport < FastMcp::Transports::BaseTransport
|
|
12
|
+
attr_reader :app, :path
|
|
13
|
+
|
|
14
|
+
def initialize(app, server, options = {})
|
|
15
|
+
super(server, logger: options[:logger])
|
|
16
|
+
@app = app
|
|
17
|
+
@path = options[:path_prefix] || "/mcp"
|
|
18
|
+
@running = false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def start
|
|
22
|
+
@logger.debug("[FosmRailsCodingAgent] MCP transport started at #{@path}")
|
|
23
|
+
@running = true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def stop
|
|
27
|
+
@logger.debug("[FosmRailsCodingAgent] MCP transport stopped")
|
|
28
|
+
@running = false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def send_message(message)
|
|
32
|
+
@captured_response = message
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def call(env)
|
|
36
|
+
request = Rack::Request.new(env)
|
|
37
|
+
|
|
38
|
+
if request.path == @path
|
|
39
|
+
@server.transport = self
|
|
40
|
+
handle_request(request)
|
|
41
|
+
else
|
|
42
|
+
@app.call(env)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def handle_request(request)
|
|
49
|
+
return method_not_allowed unless request.post?
|
|
50
|
+
|
|
51
|
+
body = request.body.read
|
|
52
|
+
message = JSON.parse(body)
|
|
53
|
+
|
|
54
|
+
return json_error(400, -32600, "Invalid Request") unless valid_jsonrpc?(message)
|
|
55
|
+
|
|
56
|
+
@captured_response = nil
|
|
57
|
+
@server.handle_json_request(message)
|
|
58
|
+
|
|
59
|
+
if @captured_response
|
|
60
|
+
[200, { "Content-Type" => "application/json" }, [JSON.generate(@captured_response)]]
|
|
61
|
+
else
|
|
62
|
+
[202, {}, []]
|
|
63
|
+
end
|
|
64
|
+
rescue JSON::ParserError
|
|
65
|
+
json_error(400, -32700, "Parse error")
|
|
66
|
+
rescue => e
|
|
67
|
+
@logger.error("[FosmRailsCodingAgent] #{e.message}")
|
|
68
|
+
json_error(500, -32603, "Internal error")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def valid_jsonrpc?(msg)
|
|
72
|
+
msg.is_a?(Hash) && msg["jsonrpc"] == "2.0" && (msg.key?("method") || msg.key?("result") || msg.key?("error"))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def method_not_allowed
|
|
76
|
+
json_error(405, -32601, "Only POST requests are supported")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def json_error(http_status, code, message)
|
|
80
|
+
[http_status, { "Content-Type" => "application/json" },
|
|
81
|
+
[JSON.generate({ jsonrpc: "2.0", error: { code: code, message: message }, id: nil })]]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fosm_rails_coding_agent/version"
|
|
4
|
+
require "fosm_rails_coding_agent/configuration"
|
|
5
|
+
|
|
6
|
+
module FosmRailsCodingAgent
|
|
7
|
+
class << self
|
|
8
|
+
# Returns the global FosmRailsCodingAgent configuration.
|
|
9
|
+
def config
|
|
10
|
+
@config ||= Configuration.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Yields the configuration for block-style setup.
|
|
14
|
+
def configure
|
|
15
|
+
yield config
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Returns true when fosm-rails is loaded and the Fosm::Lifecycle constant
|
|
19
|
+
# is defined. FOSM tools are only registered when this returns true.
|
|
20
|
+
def fosm_available?
|
|
21
|
+
defined?(::Fosm::Lifecycle) && defined?(::Fosm::Registry)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "fosm_rails_coding_agent/railtie" if defined?(Rails::Railtie)
|
metadata
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: fosm-rails-coding-agent
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Abhishek Parolkar
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-03-29 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rails
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '8.1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '8.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: fast-mcp
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.6'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.6'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rack
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '2.0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '2.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: acp_ruby
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0.1'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0.1'
|
|
69
|
+
description: |
|
|
70
|
+
Embeds a FOSM-aware MCP server and ACP agent into your Rails development
|
|
71
|
+
environment, giving coding agents (Claude Code, Codex, Copilot) runtime intelligence:
|
|
72
|
+
database queries, logs, code evaluation, and deep introspection of FOSM lifecycle
|
|
73
|
+
definitions, state machines, transitions, guards, and audit trails.
|
|
74
|
+
|
|
75
|
+
Built on the FOSM (Finite Object State Machine) paradigm — declarative lifecycles
|
|
76
|
+
for business objects where AI agents operate within bounded, auditable state machines.
|
|
77
|
+
email:
|
|
78
|
+
- abhishek@parolkar.com
|
|
79
|
+
executables:
|
|
80
|
+
- fosm-coding-agent
|
|
81
|
+
extensions: []
|
|
82
|
+
extra_rdoc_files: []
|
|
83
|
+
files:
|
|
84
|
+
- AGENTS.md
|
|
85
|
+
- CHANGELOG.md
|
|
86
|
+
- LICENSE
|
|
87
|
+
- README.md
|
|
88
|
+
- bin/fosm-coding-agent
|
|
89
|
+
- lib/fosm_rails_coding_agent.rb
|
|
90
|
+
- lib/fosm_rails_coding_agent/acp_agent.rb
|
|
91
|
+
- lib/fosm_rails_coding_agent/configuration.rb
|
|
92
|
+
- lib/fosm_rails_coding_agent/exceptions_middleware.rb
|
|
93
|
+
- lib/fosm_rails_coding_agent/middleware.rb
|
|
94
|
+
- lib/fosm_rails_coding_agent/quiet_middleware.rb
|
|
95
|
+
- lib/fosm_rails_coding_agent/railtie.rb
|
|
96
|
+
- lib/fosm_rails_coding_agent/tools/base.rb
|
|
97
|
+
- lib/fosm_rails_coding_agent/tools/execute_sql.rb
|
|
98
|
+
- lib/fosm_rails_coding_agent/tools/fosm/available_events.rb
|
|
99
|
+
- lib/fosm_rails_coding_agent/tools/fosm/fire_event.rb
|
|
100
|
+
- lib/fosm_rails_coding_agent/tools/fosm/inspect_lifecycle.rb
|
|
101
|
+
- lib/fosm_rails_coding_agent/tools/fosm/list_lifecycles.rb
|
|
102
|
+
- lib/fosm_rails_coding_agent/tools/fosm/model_resolver.rb
|
|
103
|
+
- lib/fosm_rails_coding_agent/tools/fosm/transition_history.rb
|
|
104
|
+
- lib/fosm_rails_coding_agent/tools/fosm/why_blocked.rb
|
|
105
|
+
- lib/fosm_rails_coding_agent/tools/get_docs.rb
|
|
106
|
+
- lib/fosm_rails_coding_agent/tools/get_logs.rb
|
|
107
|
+
- lib/fosm_rails_coding_agent/tools/get_models.rb
|
|
108
|
+
- lib/fosm_rails_coding_agent/tools/get_source_location.rb
|
|
109
|
+
- lib/fosm_rails_coding_agent/tools/project_eval.rb
|
|
110
|
+
- lib/fosm_rails_coding_agent/transport.rb
|
|
111
|
+
- lib/fosm_rails_coding_agent/version.rb
|
|
112
|
+
homepage: https://github.com/inloopstudio/fosm-rails-coding-agent
|
|
113
|
+
licenses:
|
|
114
|
+
- FSL-1.1-Apache-2.0
|
|
115
|
+
metadata:
|
|
116
|
+
homepage_uri: https://github.com/inloopstudio/fosm-rails-coding-agent
|
|
117
|
+
source_code_uri: https://github.com/inloopstudio/fosm-rails-coding-agent
|
|
118
|
+
changelog_uri: https://github.com/inloopstudio/fosm-rails-coding-agent/blob/main/CHANGELOG.md
|
|
119
|
+
post_install_message:
|
|
120
|
+
rdoc_options: []
|
|
121
|
+
require_paths:
|
|
122
|
+
- lib
|
|
123
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
124
|
+
requirements:
|
|
125
|
+
- - ">="
|
|
126
|
+
- !ruby/object:Gem::Version
|
|
127
|
+
version: '3.1'
|
|
128
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
|
+
requirements:
|
|
130
|
+
- - ">="
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '0'
|
|
133
|
+
requirements: []
|
|
134
|
+
rubygems_version: 3.5.22
|
|
135
|
+
signing_key:
|
|
136
|
+
specification_version: 4
|
|
137
|
+
summary: FOSM-aware runtime intelligence for Rails — MCP server + ACP agent for coding
|
|
138
|
+
agents
|
|
139
|
+
test_files: []
|