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.
Files changed (30) hide show
  1. checksums.yaml +7 -0
  2. data/AGENTS.md +77 -0
  3. data/CHANGELOG.md +16 -0
  4. data/LICENSE +127 -0
  5. data/README.md +135 -0
  6. data/bin/fosm-coding-agent +14 -0
  7. data/lib/fosm_rails_coding_agent/acp_agent.rb +132 -0
  8. data/lib/fosm_rails_coding_agent/configuration.rb +27 -0
  9. data/lib/fosm_rails_coding_agent/exceptions_middleware.rb +67 -0
  10. data/lib/fosm_rails_coding_agent/middleware.rb +115 -0
  11. data/lib/fosm_rails_coding_agent/quiet_middleware.rb +18 -0
  12. data/lib/fosm_rails_coding_agent/railtie.rb +51 -0
  13. data/lib/fosm_rails_coding_agent/tools/base.rb +10 -0
  14. data/lib/fosm_rails_coding_agent/tools/execute_sql.rb +44 -0
  15. data/lib/fosm_rails_coding_agent/tools/fosm/available_events.rb +46 -0
  16. data/lib/fosm_rails_coding_agent/tools/fosm/fire_event.rb +46 -0
  17. data/lib/fosm_rails_coding_agent/tools/fosm/inspect_lifecycle.rb +44 -0
  18. data/lib/fosm_rails_coding_agent/tools/fosm/list_lifecycles.rb +45 -0
  19. data/lib/fosm_rails_coding_agent/tools/fosm/model_resolver.rb +25 -0
  20. data/lib/fosm_rails_coding_agent/tools/fosm/transition_history.rb +56 -0
  21. data/lib/fosm_rails_coding_agent/tools/fosm/why_blocked.rb +36 -0
  22. data/lib/fosm_rails_coding_agent/tools/get_docs.rb +58 -0
  23. data/lib/fosm_rails_coding_agent/tools/get_logs.rb +66 -0
  24. data/lib/fosm_rails_coding_agent/tools/get_models.rb +35 -0
  25. data/lib/fosm_rails_coding_agent/tools/get_source_location.rb +69 -0
  26. data/lib/fosm_rails_coding_agent/tools/project_eval.rb +76 -0
  27. data/lib/fosm_rails_coding_agent/transport.rb +84 -0
  28. data/lib/fosm_rails_coding_agent/version.rb +5 -0
  29. data/lib/fosm_rails_coding_agent.rb +26 -0
  30. metadata +139 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a86e08a22e13276222b43f30f800f409ce3bfe8452c2ef9c51a7d3c1d8379b60
4
+ data.tar.gz: 8a0272687e4df3142c3cf229709ee10af544ac7d325822a5d9cc87bad936bc7a
5
+ SHA512:
6
+ metadata.gz: 470c6fdb093d724f40f96aaabdd021842ba448c40b4a2b269544237cdc58fedac0355cedab53199b76b4b1c689bb89c64c308dc6b84903ad1742912c04bfd33b
7
+ data.tar.gz: fcd04b3eb16daf0116d5c7ef806daa701d0fd82422ca6fe8014f1ddb091f8cc27e0439f8734cac52fe40264810fa2d191779d42cd166667933689b685ebb7bb0
data/AGENTS.md ADDED
@@ -0,0 +1,77 @@
1
+ # Agents Guide — fosm-rails-coding-agent
2
+
3
+ Instructions for AI coding agents contributing to this codebase.
4
+
5
+ ## Project Structure
6
+
7
+ ```
8
+ lib/fosm_rails_coding_agent.rb # Main entrypoint, FosmRailsCodingAgent.fosm_available?
9
+ lib/fosm_rails_coding_agent/
10
+ version.rb # Gem version
11
+ configuration.rb # Configuration class (MCP + ACP settings)
12
+ transport.rb # MCP Streamable HTTP transport
13
+ middleware.rb # Rack middleware, tool registration
14
+ quiet_middleware.rb # Silence request log noise
15
+ exceptions_middleware.rb # Inject exception context for agents
16
+ railtie.rb # Rails integration
17
+ acp_agent.rb # ACP agent (AgentClientProtocol)
18
+ tools/
19
+ base.rb # Base class < FastMcp::Tool
20
+ execute_sql.rb # SQL query tool
21
+ get_logs.rb # Log tail tool
22
+ project_eval.rb # Ruby eval tool
23
+ get_models.rb # ActiveRecord model lister
24
+ get_source_location.rb # Source location finder
25
+ get_docs.rb # Documentation extractor
26
+ fosm/ # FOSM-aware tools (conditional)
27
+ model_resolver.rb # Shared model name resolution
28
+ list_lifecycles.rb # List all FOSM lifecycles
29
+ inspect_lifecycle.rb # Deep lifecycle introspection
30
+ fire_event.rb # Fire lifecycle events
31
+ transition_history.rb # Audit trail
32
+ available_events.rb # Current available events
33
+ why_blocked.rb # Diagnostic: why can't event fire
34
+ bin/
35
+ fosm-coding-agent # ACP agent executable (stdio)
36
+ ```
37
+
38
+ ## Code Conventions
39
+
40
+ - `frozen_string_literal: true` on every `.rb` file
41
+ - One class per file, clear single responsibility
42
+ - Ruby 3.1+ features where they improve clarity
43
+ - Comment block on every class explaining what it does
44
+ - No trailing whitespace
45
+ - Tools follow the FastMcp::Tool DSL: `tool_name`, `description`, `arguments do`, `def call`
46
+
47
+ ## Key Design Decisions
48
+
49
+ 1. **FOSM tools are conditional** — only registered when `FosmRailsCodingAgent.fosm_available?` is true (checks `defined?(Fosm::Lifecycle)`)
50
+ 2. **All tools inherit from `FosmRailsCodingAgent::Tools::Base`** which inherits from `FastMcp::Tool`
51
+ 3. **FOSM tools live under `FosmRailsCodingAgent::Tools::Fosm` namespace** — the middleware filters by module ancestry
52
+ 4. **ACP agent pushes context proactively** — the `new_session` hook sends FOSM lifecycle summaries
53
+ 5. **Development only** — the Railtie raises if `config.enable_reloading` is false
54
+ 6. **ModelResolver mixin** — shared across all FOSM tools to resolve model names (direct, Fosm:: prefix, or Registry lookup)
55
+
56
+ ## Adding a New Tool
57
+
58
+ 1. Create `lib/fosm_rails_coding_agent/tools/your_tool.rb`
59
+ 2. Inherit from `FosmRailsCodingAgent::Tools::Base`
60
+ 3. Define `tool_name`, `description`, `arguments`, and `def call`
61
+ 4. The tool auto-registers via `Base.descendants` in the middleware
62
+
63
+ For FOSM-specific tools, place under `lib/fosm_rails_coding_agent/tools/fosm/` and namespace under `FosmRailsCodingAgent::Tools::Fosm`.
64
+
65
+ ## Testing
66
+
67
+ ```
68
+ bundle exec rake spec
69
+ ```
70
+
71
+ ## Dependencies
72
+
73
+ - `rails >= 7.1` — framework integration
74
+ - `fast-mcp ~> 1.6` — MCP server protocol
75
+ - `rack >= 2.0` — middleware
76
+ - `acp_ruby ~> 0.1` — Agent Client Protocol
77
+ - `fosm-rails` — optional, for FOSM lifecycle introspection
data/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ All notable changes to fosm-rails-coding-agent will be documented in this file.
4
+
5
+ ## [0.0.1] - 2026-03-29
6
+
7
+ ### Added
8
+
9
+ - Initial release
10
+ - MCP server with core tools: execute_sql, get_logs, project_eval, get_models, get_source_location, get_docs
11
+ - Conditional FOSM tools: fosm_list_lifecycles, fosm_inspect_lifecycle, fosm_fire_event, fosm_transition_history, fosm_available_events, fosm_why_blocked
12
+ - ACP agent with proactive FOSM context push on session start
13
+ - Rack middleware with localhost-only access control
14
+ - Rails Railtie for automatic development environment setup
15
+ - Exception interception middleware for agent-readable error context
16
+ - Quiet middleware to suppress request logging
data/LICENSE ADDED
@@ -0,0 +1,127 @@
1
+ # Functional Source License, Version 1.1, Apache 2.0 Future License
2
+
3
+ ## Abbreviation
4
+
5
+ FSL-1.1-Apache-2.0
6
+
7
+ ## Notice
8
+
9
+ Copyright 2026 Abhishek Parolkar and INLOOP.STUDIO PTE LTD
10
+
11
+ ## Change Date
12
+
13
+ Three years from the release date of each version of the Software and Content.
14
+
15
+ ## Terms and Conditions
16
+
17
+ ### Licensor ("We")
18
+
19
+ Abhishek Parolkar, INLOOP.STUDIO PTE LTD, and their affiliated entities, the party offering the Software and Content under these Terms and Conditions.
20
+
21
+ ### Succession of Rights
22
+
23
+ All rights, title and interest of Abhishek Parolkar ("Original Author") under
24
+ this License — including but not limited to copyright ownership, the right to
25
+ grant licenses, the right to enforce these Terms and Conditions, and the right
26
+ to receive any royalties or consideration arising from the Software and
27
+ Content — shall, upon the Original Author's death or permanent incapacity,
28
+ pass to and vest in the Original Author's next-of-kin as determined by
29
+ applicable law of succession. The next-of-kin shall hold and may exercise all
30
+ such rights to the same extent as the Original Author, including the right to
31
+ modify the terms of future releases.
32
+
33
+ ### The Software and Content
34
+
35
+ The "Software and Content" refers to each version of the software, documentation, approaches, strategies, methodologies, and all other materials that we make available under these Terms and Conditions, as indicated by our inclusion of these Terms and Conditions with the Software and Content.
36
+
37
+ ### License Grant
38
+
39
+ Subject to your compliance with this License Grant and the Patents,
40
+ Redistribution and Trademark clauses below, we hereby grant you the right to
41
+ use, copy, modify, create derivative works, publicly perform, publicly display
42
+ and redistribute the Software and Content for any Permitted Purpose identified below.
43
+
44
+ ### Permitted Purpose
45
+
46
+ A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
47
+ means making the Software and Content available to others in a commercial product or
48
+ service that:
49
+
50
+ 1. substitutes for the Software and Content;
51
+
52
+ 2. substitutes for any other product or service we offer using the Software and Content
53
+ that exists as of the date we make the Software and Content available; or
54
+
55
+ 3. offers the same or substantially similar functionality as the Software and Content,
56
+ including making the Software and Content available as a hosted or managed service
57
+ (e.g., software-as-a-service, platform-as-a-service, or any other on-demand service)
58
+ where third parties access the functionality of the Software and Content.
59
+
60
+ Permitted Purposes specifically include using the Software and Content:
61
+
62
+ 1. for your internal use and access;
63
+
64
+ 2. for non-commercial education;
65
+
66
+ 3. for non-commercial research; and
67
+
68
+ 4. in connection with professional services that you provide to a licensee
69
+ using the Software and Content in accordance with these Terms and Conditions.
70
+
71
+ ### Patents
72
+
73
+ To the extent your use for a Permitted Purpose would necessarily infringe our
74
+ patents, the license grant above includes a license under our patents. If you
75
+ make a claim against any party that the Software and Content infringes or contributes to
76
+ the infringement of any patent, then your patent license to the Software and Content ends
77
+ immediately.
78
+
79
+ ### Redistribution
80
+
81
+ The Terms and Conditions apply to all copies, modifications and derivatives of
82
+ the Software and Content.
83
+
84
+ If you redistribute any copies, modifications or derivatives of the Software and Content,
85
+ you must include a copy of or a link to these Terms and Conditions and not
86
+ remove any copyright notices provided in or with the Software and Content.
87
+
88
+ ### Disclaimer
89
+
90
+ THE SOFTWARE AND CONTENT IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
91
+ IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
92
+ PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
93
+
94
+ IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
95
+ SOFTWARE AND CONTENT, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
96
+ EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
97
+
98
+ ### Trademarks and Domain Names
99
+
100
+ Except for displaying the License Details and identifying us as the origin of
101
+ the Software and Content, you have no right under these Terms and Conditions to use our
102
+ trademarks, trade names, service marks or product names.
103
+ The name "FOSM-RAILS" is exclusive
104
+ property of Abhishek Parolkar with unlimited license to INLOOP.STUDIO PTE LTD. No right or license is granted
105
+ to use these names or domains in any manner whatsoever without the express written
106
+ permission of Abhishek Parolkar. Any unauthorized use of these names or domains is
107
+ strictly prohibited.
108
+
109
+ ## Grant of Future License
110
+
111
+ We hereby irrevocably grant you an additional license to use the Software and Content under
112
+ the Apache License, Version 2.0 that is effective on the Change Date (as defined above —
113
+ three years from the release date of each version of the Software and Content).
114
+ On or after the Change Date, you may use the Software and Content under the Apache
115
+ License, Version 2.0, in which case the following will apply:
116
+
117
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
118
+ this file except in compliance with the License.
119
+
120
+ You may obtain a copy of the License at
121
+
122
+ [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
123
+
124
+ Unless required by applicable law or agreed to in writing, software distributed
125
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
126
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
127
+ specific language governing permissions and limitations under the License.
data/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # fosm-rails-coding-agent
2
+
3
+ **FOSM-aware runtime intelligence for Rails — MCP server + ACP agent for coding agents.**
4
+
5
+ Gives coding agents (Claude Code, Codex, Copilot, OpenCode) runtime access to your Rails application: database queries, logs, code evaluation, and — when [fosm-rails](https://github.com/inloopstudio/fosm-rails) is present — deep introspection of FOSM lifecycle state machines, transitions, guards, and audit trails.
6
+
7
+ ## Architecture: MCP + ACP
8
+
9
+ This gem exposes two complementary protocols:
10
+
11
+ ```
12
+ ┌─────────────────────────────────────────────────┐
13
+ │ Rails Application │
14
+ │ │
15
+ │ ┌──────────┐ ┌───────────────────────────┐ │
16
+ │ │ fosm-rails│ │ fosm-rails-coding-agent │ │
17
+ │ │ lifecycles│◄──►│ │ │
18
+ │ └──────────┘ │ ┌─────────┐ ┌──────────┐ │ │
19
+ │ │ │MCP Tools│ │ACP Agent │ │ │
20
+ │ │ │(pull) │ │(push) │ │ │
21
+ │ │ └────┬────┘ └─────┬─────┘ │ │
22
+ │ └───────┼────────────┼───────┘ │
23
+ └──────────────────────────┼────────────┼──────────┘
24
+ │ │
25
+ ┌──────┴────────────┴───────┐
26
+ │ Claude Code / Codex / │
27
+ │ Copilot / OpenCode │
28
+ └───────────────────────────┘
29
+ ```
30
+
31
+ ### MCP Server (pull-based)
32
+
33
+ An MCP server embedded as Rack middleware at `/fosm-agent/mcp`. Coding agents query it on demand via standard MCP tool calls. Built on [fast-mcp](https://github.com/yjacquin/fast-mcp).
34
+
35
+ ### ACP Agent (push-based)
36
+
37
+ An ACP agent (`bin/fosm-coding-agent`) that implements the [Agent Client Protocol](https://agentclientprotocol.com/). When a session starts, it proactively pushes FOSM lifecycle context — state definitions, events, guards, recent transitions — so the coding agent starts with full situational awareness.
38
+
39
+ The most powerful developer tooling doesn't wait for agents to discover context through trial and error — it **pushes** the right context at the right time. When your coding agent opens a PR that touches an `Invoice` model, it should already know that `Invoice` has a lifecycle with states `draft → sent → paid`, a guard requiring line items before sending, and that the last three transitions were all `pay` events. MCP lets the agent pull when it needs to; ACP lets the application push when it knows the agent should care.
40
+
41
+ Inspired by the thinking in [The future of coding agents is vertical integration](https://tidewave.ai/blog/the-future-of-coding-agents-is-vertical-integration).
42
+
43
+ ## Installation
44
+
45
+ Add to your Gemfile:
46
+
47
+ ```ruby
48
+ gem "fosm-rails-coding-agent"
49
+
50
+ # Optional: for FOSM lifecycle introspection
51
+ gem "fosm-rails"
52
+ ```
53
+
54
+ Run:
55
+
56
+ ```
57
+ bundle install
58
+ ```
59
+
60
+ Auto-activates in development via its Railtie. No configuration needed for basic use.
61
+
62
+ ## Configuration
63
+
64
+ In `config/environments/development.rb`:
65
+
66
+ ```ruby
67
+ config.fosm_coding_agent.allow_remote_access = false # default: localhost only
68
+ config.fosm_coding_agent.sql_row_limit = 50 # max rows from execute_sql
69
+ config.fosm_coding_agent.eval_timeout_ms = 30_000 # project_eval timeout
70
+ config.fosm_coding_agent.log_tail_default = 100 # default log lines
71
+ config.fosm_coding_agent.acp_enabled = true # enable ACP agent
72
+ config.fosm_coding_agent.acp_push_fosm_context = true # push FOSM context on session start
73
+ ```
74
+
75
+ ## MCP Tools
76
+
77
+ ### Core Tools (always available)
78
+
79
+ | Tool | Description |
80
+ |------|-------------|
81
+ | `execute_sql` | Run SQL queries against the app database (50 row limit) |
82
+ | `get_logs` | Tail the Rails log with optional regex filter |
83
+ | `project_eval` | Evaluate Ruby code in the running app context |
84
+ | `get_models` | List all ActiveRecord models with source locations |
85
+ | `get_source_location` | Find source location of any Ruby constant/method |
86
+ | `get_docs` | Extract documentation comments from source |
87
+
88
+ ### FOSM Tools (when fosm-rails is detected)
89
+
90
+ | Tool | Description |
91
+ |------|-------------|
92
+ | `fosm_list_lifecycles` | List all FOSM lifecycle models with states/events |
93
+ | `fosm_inspect_lifecycle` | Deep introspection of one lifecycle |
94
+ | `fosm_fire_event` | Fire a lifecycle event (actor: :agent) |
95
+ | `fosm_transition_history` | Audit trail for a FOSM record |
96
+ | `fosm_available_events` | Events that can fire from current state |
97
+ | `fosm_why_blocked` | Explain why an event can't fire |
98
+
99
+ ## ACP Agent
100
+
101
+ The ACP agent runs as a subprocess over stdio:
102
+
103
+ ```
104
+ fosm-coding-agent
105
+ ```
106
+
107
+ Or point your ACP client at the binary:
108
+
109
+ ```json
110
+ {
111
+ "agent": {
112
+ "command": "bundle",
113
+ "args": ["exec", "fosm-coding-agent"]
114
+ }
115
+ }
116
+ ```
117
+
118
+ ## Philosophy
119
+
120
+ Coding agents need **runtime intelligence**, not just static code analysis. A state machine definition in source code tells you the *structure*; runtime introspection tells you the *situation*.
121
+
122
+ FOSM (Finite Object State Machine) is a paradigm where business entities have explicit, enforced lifecycles — with guards that explain why they block, transitions that log who did what, and access controls that make autonomous agent actions safe and auditable. This gem is the bridge between FOSM's runtime observability and the coding agents that need it.
123
+
124
+ Read the FOSM paper: [Implementing Human+AI Collaboration Using Finite Object State Machine](https://www.parolkar.com/fosm)
125
+
126
+ ## Development
127
+
128
+ ```
129
+ bundle install
130
+ bundle exec rake spec
131
+ ```
132
+
133
+ ## License
134
+
135
+ FSL-1.1-Apache-2.0 (Functional Source License, Version 1.1, Apache 2.0 Future License). See [LICENSE](LICENSE).
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # FOSM Rails Coding Agent — ACP agent over stdio.
5
+ # Spawned by ACP clients (Claude Code, Codex, etc.) to provide
6
+ # FOSM-aware runtime intelligence for Rails applications.
7
+ #
8
+ # Usage: fosm-coding-agent
9
+
10
+ require "bundler/setup" if File.exist?(File.expand_path("../../Gemfile", __FILE__))
11
+ require "fosm_rails_coding_agent"
12
+ require "fosm_rails_coding_agent/acp_agent"
13
+
14
+ AgentClientProtocol.run_agent(FosmRailsCodingAgent::AcpAgent.new)
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "agent_client_protocol"
4
+ require "json"
5
+ require "securerandom"
6
+
7
+ module FosmRailsCodingAgent
8
+ # ACP agent that implements the Agent Client Protocol.
9
+ # Provides push-based FOSM lifecycle context to coding agents.
10
+ #
11
+ # When a session starts, the agent proactively pushes:
12
+ # - All FOSM lifecycle definitions in the project
13
+ # - Available events for key records
14
+ # - Recent transition activity
15
+ #
16
+ # This is the José Valim insight: don't wait for the agent to ask,
17
+ # PUSH context so the coding agent starts with full situational awareness.
18
+ class AcpAgent
19
+ include AgentClientProtocol::AgentInterface
20
+ include AgentClientProtocol::Helpers
21
+
22
+ def on_connect(conn)
23
+ @conn = conn
24
+ end
25
+
26
+ def initialize_agent(protocol_version:, **)
27
+ AgentClientProtocol::Schema::InitializeResponse.new(
28
+ protocol_version: AgentClientProtocol::PROTOCOL_VERSION,
29
+ agent_info: AgentClientProtocol::Schema::Implementation.new(
30
+ name: "fosm-rails-coding-agent",
31
+ version: FosmRailsCodingAgent::VERSION
32
+ ),
33
+ capabilities: AgentClientProtocol::Schema::AgentCapabilities.new
34
+ )
35
+ end
36
+
37
+ def new_session(cwd:, **)
38
+ session_id = "fosm-agent-#{SecureRandom.uuid}"
39
+
40
+ # Proactively push FOSM context if available
41
+ push_fosm_context(session_id) if FosmRailsCodingAgent.fosm_available?
42
+
43
+ AgentClientProtocol::Schema::NewSessionResponse.new(
44
+ session_id: session_id
45
+ )
46
+ end
47
+
48
+ def prompt(prompt:, session_id:, **)
49
+ prompt.each do |block|
50
+ text = case block
51
+ when AgentClientProtocol::Schema::TextContent then block.text
52
+ when Hash then block["text"] || block.inspect
53
+ else block.inspect
54
+ end
55
+
56
+ response = handle_prompt_text(text, session_id)
57
+
58
+ @conn.session_update(
59
+ session_id: session_id,
60
+ update: update_agent_message_text(response)
61
+ )
62
+ end
63
+
64
+ AgentClientProtocol::Schema::PromptResponse.new(
65
+ stop_reason: AgentClientProtocol::Schema::StopReason::END_TURN
66
+ )
67
+ end
68
+
69
+ def authenticate(method_id:, **)
70
+ AgentClientProtocol::Schema::AuthenticateResponse.new
71
+ end
72
+
73
+ private
74
+
75
+ # Push FOSM lifecycle context at session start so the coding agent
76
+ # has full awareness of the state machines in the project.
77
+ def push_fosm_context(session_id)
78
+ context = build_fosm_context
79
+ return if context.empty?
80
+
81
+ @conn.session_update(
82
+ session_id: session_id,
83
+ update: update_agent_message_text(
84
+ "## FOSM Lifecycle Context\n\n" \
85
+ "This Rails application uses FOSM (Finite Observable State Machine) " \
86
+ "lifecycles. Here are the registered state machines:\n\n#{context}"
87
+ )
88
+ )
89
+ end
90
+
91
+ def build_fosm_context
92
+ return "" unless defined?(::Fosm::Registry)
93
+
94
+ models = ::Fosm::Registry.model_classes
95
+ return "" if models.empty?
96
+
97
+ models.filter_map do |klass|
98
+ lifecycle = klass.fosm_lifecycle
99
+ next unless lifecycle
100
+
101
+ states = lifecycle.states.map do |s|
102
+ label = s.name.to_s
103
+ label += " (initial)" if s.initial?
104
+ label += " (terminal)" if s.terminal?
105
+ label
106
+ end
107
+
108
+ events = lifecycle.events.map do |e|
109
+ " #{e.name}: #{e.from_states.join(", ")} → #{e.to_state}"
110
+ end
111
+
112
+ "### #{klass.name}\n" \
113
+ "States: #{states.join(", ")}\n" \
114
+ "Events:\n#{events.join("\n")}\n"
115
+ end.join("\n")
116
+ end
117
+
118
+ def handle_prompt_text(text, session_id)
119
+ case text.downcase.strip
120
+ when /lifecycles?/
121
+ build_fosm_context.presence || "No FOSM lifecycles found in this project."
122
+ when /help/
123
+ "FosmRailsCodingAgent ACP agent provides FOSM-aware runtime intelligence.\n" \
124
+ "FOSM tools are available via the MCP server at /fosm-agent/mcp.\n" \
125
+ "Ask about lifecycles, states, events, or transitions."
126
+ else
127
+ "FosmRailsCodingAgent is listening. Use the MCP tools at /fosm-agent/mcp for " \
128
+ "SQL queries, logs, code evaluation, and FOSM introspection."
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FosmRailsCodingAgent
4
+ # Central configuration for FosmRailsCodingAgent's MCP server and ACP agent.
5
+ # Set via config.fosm_coding_agent in your Rails application.
6
+ class Configuration
7
+ attr_accessor :logger,
8
+ :allow_remote_access,
9
+ :sql_row_limit,
10
+ :eval_timeout_ms,
11
+ :log_tail_default,
12
+ :acp_enabled,
13
+ :acp_agent_name,
14
+ :acp_push_fosm_context
15
+
16
+ def initialize
17
+ @logger = nil
18
+ @allow_remote_access = false
19
+ @sql_row_limit = 50
20
+ @eval_timeout_ms = 30_000
21
+ @log_tail_default = 100
22
+ @acp_enabled = true
23
+ @acp_agent_name = "fosm-rails-coding-agent"
24
+ @acp_push_fosm_context = true
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FosmRailsCodingAgent
4
+ # Intercepts Rails exception pages and injects structured error context
5
+ # as a hidden textarea — coding agents can parse this to understand
6
+ # stacktraces, controller context, and request metadata.
7
+ class ExceptionsMiddleware
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ request = ActionDispatch::Request.new(env)
14
+ status, headers, body = @app.call(env)
15
+
16
+ if (exception = request.get_header("fosm_agent.exception"))
17
+ formatted = format_exception(exception, request)
18
+ body, headers = inject_into_body(body, headers, formatted)
19
+ end
20
+
21
+ [status, headers, body]
22
+ rescue => error
23
+ Rails.logger.error("[FosmRailsCodingAgent] ExceptionsMiddleware failure: #{error.message}")
24
+ raise
25
+ end
26
+
27
+ private
28
+
29
+ def format_exception(exception, request)
30
+ backtrace = Rails.backtrace_cleaner.clean(exception.backtrace)
31
+ params = safe_params(request)
32
+ controller = params["controller"]&.camelize
33
+ action = params["action"]
34
+
35
+ text = exception.class.name.dup
36
+ text << " in #{controller}Controller" if controller
37
+ text << "##{action}" if action
38
+ text << "\n\n## Message\n\n#{exception.message}"
39
+ text << "\n\n## Backtrace\n\n#{backtrace.join("\n")}" if backtrace.any?
40
+ text << "\n\n## Request\n\n * URI: #{request.base_url}#{request.path}"
41
+ text << "\n * Query: #{request.query_string}"
42
+
43
+ %(<textarea style="display:none;" data-fosm-agent-exception>#{ERB::Util.html_escape(text)}</textarea>)
44
+ end
45
+
46
+ def safe_params(request)
47
+ request.parameters
48
+ rescue ActionController::BadRequest
49
+ {}
50
+ end
51
+
52
+ def inject_into_body(body, headers, content)
53
+ html = "".dup
54
+ body.each { |part| html << part }
55
+ body.close if body.respond_to?(:close)
56
+
57
+ if (pos = html.rindex("</body>"))
58
+ html.insert(pos, content)
59
+ else
60
+ html << content
61
+ end
62
+
63
+ headers[Rack::CONTENT_LENGTH] = html.bytesize.to_s if headers[Rack::CONTENT_LENGTH]
64
+ [[html], headers]
65
+ end
66
+ end
67
+ end