claude-on-rails 0.1.3 → 0.1.4

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.
@@ -1,159 +1,147 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ClaudeOnRails
2
4
  class ProjectAnalyzer
3
5
  attr_reader :root_path
4
-
6
+
5
7
  def initialize(root_path)
6
8
  @root_path = root_path
7
9
  end
8
-
10
+
9
11
  def analyze
10
12
  {
11
13
  api_only: api_only?,
12
14
  test_framework: detect_test_framework,
13
- has_graphql: has_graphql?,
14
- has_turbo: has_turbo?,
15
- has_devise: has_devise?,
16
- has_sidekiq: has_sidekiq?,
15
+ has_graphql: graphql?,
16
+ has_turbo: turbo?,
17
+ has_devise: devise?,
18
+ has_sidekiq: sidekiq?,
17
19
  javascript_framework: detect_javascript_framework,
18
20
  database: detect_database,
19
21
  deployment: detect_deployment_method,
20
22
  custom_patterns: detect_custom_patterns
21
23
  }
22
24
  end
23
-
25
+
24
26
  private
25
-
27
+
26
28
  def api_only?
27
29
  application_rb = File.join(root_path, 'config', 'application.rb')
28
30
  return false unless File.exist?(application_rb)
29
-
31
+
30
32
  File.read(application_rb).include?('config.api_only = true')
31
33
  end
32
-
34
+
33
35
  def detect_test_framework
34
36
  if File.exist?(File.join(root_path, 'spec'))
35
37
  'RSpec'
36
38
  elsif File.exist?(File.join(root_path, 'test'))
37
39
  'Minitest'
38
- else
39
- nil
40
40
  end
41
41
  end
42
-
43
- def has_graphql?
42
+
43
+ def graphql?
44
44
  gemfile_path = File.join(root_path, 'Gemfile')
45
45
  return false unless File.exist?(gemfile_path)
46
-
46
+
47
47
  gemfile_content = File.read(gemfile_path)
48
- gemfile_content.include?('graphql') ||
48
+ gemfile_content.include?('graphql') ||
49
49
  File.exist?(File.join(root_path, 'app', 'graphql'))
50
50
  end
51
-
52
- def has_turbo?
51
+
52
+ def turbo?
53
53
  gemfile_path = File.join(root_path, 'Gemfile')
54
54
  return false unless File.exist?(gemfile_path)
55
-
55
+
56
56
  gemfile_content = File.read(gemfile_path)
57
- gemfile_content.include?('turbo-rails') ||
57
+ gemfile_content.include?('turbo-rails') ||
58
58
  gemfile_content.include?('stimulus-rails') ||
59
59
  File.exist?(File.join(root_path, 'app', 'javascript', 'controllers'))
60
60
  end
61
-
62
- def has_devise?
61
+
62
+ def devise?
63
63
  gemfile_path = File.join(root_path, 'Gemfile')
64
64
  return false unless File.exist?(gemfile_path)
65
-
65
+
66
66
  File.read(gemfile_path).include?('devise')
67
67
  end
68
-
69
- def has_sidekiq?
68
+
69
+ def sidekiq?
70
70
  gemfile_path = File.join(root_path, 'Gemfile')
71
71
  return false unless File.exist?(gemfile_path)
72
-
72
+
73
73
  File.read(gemfile_path).include?('sidekiq')
74
74
  end
75
-
75
+
76
76
  def detect_javascript_framework
77
77
  package_json = File.join(root_path, 'package.json')
78
78
  return 'importmap' unless File.exist?(package_json)
79
-
79
+
80
80
  content = File.read(package_json)
81
- case
82
- when content.include?('webpack')
81
+ if content.include?('webpack')
83
82
  'webpack'
84
- when content.include?('esbuild')
83
+ elsif content.include?('esbuild')
85
84
  'esbuild'
86
- when content.include?('vite')
85
+ elsif content.include?('vite')
87
86
  'vite'
88
87
  else
89
88
  'importmap'
90
89
  end
91
90
  end
92
-
91
+
93
92
  def detect_database
94
93
  database_yml = File.join(root_path, 'config', 'database.yml')
95
94
  return 'sqlite3' unless File.exist?(database_yml)
96
-
95
+
97
96
  content = File.read(database_yml)
98
- case
99
- when content.include?('postgresql')
97
+ if content.include?('postgresql')
100
98
  'postgresql'
101
- when content.include?('mysql2')
99
+ elsif content.include?('mysql2')
102
100
  'mysql'
103
- when content.include?('sqlite3')
101
+ elsif content.include?('sqlite3')
104
102
  'sqlite3'
105
103
  else
106
104
  'unknown'
107
105
  end
108
106
  end
109
-
107
+
110
108
  def detect_deployment_method
111
109
  # Check for various deployment configurations
112
110
  return 'kubernetes' if File.exist?(File.join(root_path, 'k8s')) ||
113
- File.exist?(File.join(root_path, 'kubernetes'))
111
+ File.exist?(File.join(root_path, 'kubernetes'))
114
112
  return 'docker' if File.exist?(File.join(root_path, 'Dockerfile'))
115
113
  return 'capistrano' if File.exist?(File.join(root_path, 'Capfile'))
116
114
  return 'heroku' if File.exist?(File.join(root_path, 'Procfile'))
117
115
  return 'kamal' if File.exist?(File.join(root_path, 'config', 'deploy.yml'))
118
-
116
+
119
117
  'unknown'
120
118
  end
121
-
119
+
122
120
  def detect_custom_patterns
123
121
  patterns = {}
124
-
122
+
125
123
  # Check for service objects
126
- if File.directory?(File.join(root_path, 'app', 'services'))
127
- patterns[:has_services] = true
128
- end
129
-
124
+ patterns[:has_services] = true if File.directory?(File.join(root_path, 'app', 'services'))
125
+
130
126
  # Check for form objects
131
- if File.directory?(File.join(root_path, 'app', 'forms'))
132
- patterns[:has_forms] = true
133
- end
134
-
127
+ patterns[:has_forms] = true if File.directory?(File.join(root_path, 'app', 'forms'))
128
+
135
129
  # Check for presenters/decorators
136
130
  if File.directory?(File.join(root_path, 'app', 'presenters')) ||
137
131
  File.directory?(File.join(root_path, 'app', 'decorators'))
138
132
  patterns[:has_presenters] = true
139
133
  end
140
-
134
+
141
135
  # Check for query objects
142
- if File.directory?(File.join(root_path, 'app', 'queries'))
143
- patterns[:has_queries] = true
144
- end
145
-
136
+ patterns[:has_queries] = true if File.directory?(File.join(root_path, 'app', 'queries'))
137
+
146
138
  # Check for policies (authorization)
147
- if File.directory?(File.join(root_path, 'app', 'policies'))
148
- patterns[:has_policies] = true
149
- end
150
-
139
+ patterns[:has_policies] = true if File.directory?(File.join(root_path, 'app', 'policies'))
140
+
151
141
  # Check for serializers
152
- if File.directory?(File.join(root_path, 'app', 'serializers'))
153
- patterns[:has_serializers] = true
154
- end
155
-
142
+ patterns[:has_serializers] = true if File.directory?(File.join(root_path, 'app', 'serializers'))
143
+
156
144
  patterns
157
145
  end
158
146
  end
159
- end
147
+ end
@@ -1,192 +1,184 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ClaudeOnRails
2
4
  class SwarmBuilder
3
5
  attr_reader :project_analysis
4
-
6
+
5
7
  def initialize(project_analysis)
6
8
  @project_analysis = project_analysis
7
9
  end
8
-
10
+
9
11
  def build
10
12
  {
11
13
  version: 1,
12
14
  swarm: {
13
- name: "Rails Development Team",
14
- main: "architect",
15
+ name: 'Rails Development Team',
16
+ main: 'architect',
15
17
  instances: build_instances
16
18
  }
17
19
  }
18
20
  end
19
-
21
+
20
22
  private
21
-
23
+
22
24
  def build_instances
23
25
  instances = {}
24
-
26
+
25
27
  # Always include architect
26
28
  instances[:architect] = build_architect
27
-
29
+
28
30
  # Core agents
29
31
  instances[:models] = build_models_agent
30
32
  instances[:controllers] = build_controllers_agent
31
-
33
+
32
34
  # Conditional agents
33
- unless project_analysis[:api_only]
34
- instances[:views] = build_views_agent
35
- end
36
-
37
- if project_analysis[:api_only]
38
- instances[:api] = build_api_agent
39
- end
40
-
41
- if project_analysis[:has_graphql]
42
- instances[:graphql] = build_graphql_agent
43
- end
44
-
45
- if project_analysis[:has_turbo] && !project_analysis[:api_only]
46
- instances[:stimulus] = build_stimulus_agent
47
- end
48
-
35
+ instances[:views] = build_views_agent unless project_analysis[:api_only]
36
+
37
+ instances[:api] = build_api_agent if project_analysis[:api_only]
38
+
39
+ instances[:graphql] = build_graphql_agent if project_analysis[:has_graphql]
40
+
41
+ instances[:stimulus] = build_stimulus_agent if project_analysis[:has_turbo] && !project_analysis[:api_only]
42
+
49
43
  # Supporting agents
50
44
  instances[:services] = build_services_agent
51
45
  instances[:jobs] = build_jobs_agent
52
-
53
- if project_analysis[:test_framework]
54
- instances[:tests] = build_tests_agent
55
- end
56
-
46
+
47
+ instances[:tests] = build_tests_agent if project_analysis[:test_framework]
48
+
57
49
  instances[:devops] = build_devops_agent
58
-
50
+
59
51
  instances
60
52
  end
61
-
53
+
62
54
  def build_architect
63
- connections = ["models", "controllers"]
64
- connections << "views" unless project_analysis[:api_only]
65
- connections << "api" if project_analysis[:api_only]
66
- connections << "graphql" if project_analysis[:has_graphql]
67
- connections << "services"
68
- connections << "jobs"
69
- connections << "tests" if project_analysis[:test_framework]
70
- connections << "devops"
71
-
55
+ connections = %w[models controllers]
56
+ connections << 'views' unless project_analysis[:api_only]
57
+ connections << 'api' if project_analysis[:api_only]
58
+ connections << 'graphql' if project_analysis[:has_graphql]
59
+ connections << 'services'
60
+ connections << 'jobs'
61
+ connections << 'tests' if project_analysis[:test_framework]
62
+ connections << 'devops'
63
+
72
64
  {
73
65
  description: "Rails architect coordinating #{project_analysis[:api_only] ? 'API' : 'full-stack'} development",
74
- directory: ".",
66
+ directory: '.',
75
67
  model: ClaudeOnRails.configuration.default_model,
76
68
  connections: connections,
77
- prompt_file: ".claude-on-rails/prompts/architect.md",
69
+ prompt_file: '.claude-on-rails/prompts/architect.md',
78
70
  vibe: ClaudeOnRails.configuration.vibe_mode
79
71
  }
80
72
  end
81
-
73
+
82
74
  def build_models_agent
83
75
  {
84
- description: "ActiveRecord models, migrations, and database optimization specialist",
85
- directory: "./app/models",
76
+ description: 'ActiveRecord models, migrations, and database optimization specialist',
77
+ directory: './app/models',
86
78
  model: ClaudeOnRails.configuration.default_model,
87
79
  allowed_tools: %w[Read Edit Write Bash Grep Glob LS],
88
- prompt_file: ".claude-on-rails/prompts/models.md"
80
+ prompt_file: '.claude-on-rails/prompts/models.md'
89
81
  }
90
82
  end
91
-
83
+
92
84
  def build_controllers_agent
93
- connections = ["services"]
94
- connections << "api" if project_analysis[:api_only]
95
-
85
+ connections = ['services']
86
+ connections << 'api' if project_analysis[:api_only]
87
+
96
88
  {
97
- description: "Rails controllers, routing, and request handling specialist",
98
- directory: "./app/controllers",
89
+ description: 'Rails controllers, routing, and request handling specialist',
90
+ directory: './app/controllers',
99
91
  model: ClaudeOnRails.configuration.default_model,
100
92
  connections: connections.empty? ? nil : connections,
101
93
  allowed_tools: %w[Read Edit Write Bash Grep Glob LS],
102
- prompt_file: ".claude-on-rails/prompts/controllers.md"
94
+ prompt_file: '.claude-on-rails/prompts/controllers.md'
103
95
  }.compact
104
96
  end
105
-
97
+
106
98
  def build_views_agent
107
99
  connections = []
108
- connections << "stimulus" if project_analysis[:has_turbo]
109
-
100
+ connections << 'stimulus' if project_analysis[:has_turbo]
101
+
110
102
  {
111
- description: "Rails views, layouts, partials, and asset pipeline specialist",
112
- directory: "./app/views",
103
+ description: 'Rails views, layouts, partials, and asset pipeline specialist',
104
+ directory: './app/views',
113
105
  model: ClaudeOnRails.configuration.default_model,
114
106
  connections: connections.empty? ? nil : connections,
115
107
  allowed_tools: %w[Read Edit Write Bash Grep Glob LS],
116
- prompt_file: ".claude-on-rails/prompts/views.md"
108
+ prompt_file: '.claude-on-rails/prompts/views.md'
117
109
  }.compact
118
110
  end
119
-
111
+
120
112
  def build_api_agent
121
113
  {
122
- description: "RESTful API design, serialization, and versioning specialist",
123
- directory: "./app/controllers/api",
114
+ description: 'RESTful API design, serialization, and versioning specialist',
115
+ directory: './app/controllers/api',
124
116
  model: ClaudeOnRails.configuration.default_model,
125
117
  allowed_tools: %w[Read Edit Write Bash Grep Glob LS],
126
- prompt_file: ".claude-on-rails/prompts/api.md"
118
+ prompt_file: '.claude-on-rails/prompts/api.md'
127
119
  }
128
120
  end
129
-
121
+
130
122
  def build_graphql_agent
131
123
  {
132
- description: "GraphQL schema, resolvers, and mutations specialist",
133
- directory: "./app/graphql",
124
+ description: 'GraphQL schema, resolvers, and mutations specialist',
125
+ directory: './app/graphql',
134
126
  model: ClaudeOnRails.configuration.default_model,
135
127
  allowed_tools: %w[Read Edit Write Bash Grep Glob LS],
136
- prompt_file: ".claude-on-rails/prompts/graphql.md"
128
+ prompt_file: '.claude-on-rails/prompts/graphql.md'
137
129
  }
138
130
  end
139
-
131
+
140
132
  def build_stimulus_agent
141
133
  {
142
- description: "Stimulus.js controllers and Turbo integration specialist",
143
- directory: "./app/javascript",
134
+ description: 'Stimulus.js controllers and Turbo integration specialist',
135
+ directory: './app/javascript',
144
136
  model: ClaudeOnRails.configuration.default_model,
145
137
  allowed_tools: %w[Read Edit Write Bash Grep Glob LS],
146
- prompt_file: ".claude-on-rails/prompts/stimulus.md"
138
+ prompt_file: '.claude-on-rails/prompts/stimulus.md'
147
139
  }
148
140
  end
149
-
141
+
150
142
  def build_services_agent
151
143
  {
152
- description: "Service objects, business logic, and design patterns specialist",
153
- directory: "./app/services",
144
+ description: 'Service objects, business logic, and design patterns specialist',
145
+ directory: './app/services',
154
146
  model: ClaudeOnRails.configuration.default_model,
155
147
  allowed_tools: %w[Read Edit Write Bash Grep Glob LS],
156
- prompt_file: ".claude-on-rails/prompts/services.md"
148
+ prompt_file: '.claude-on-rails/prompts/services.md'
157
149
  }
158
150
  end
159
-
151
+
160
152
  def build_jobs_agent
161
153
  {
162
- description: "Background jobs, ActiveJob, and async processing specialist",
163
- directory: "./app/jobs",
154
+ description: 'Background jobs, ActiveJob, and async processing specialist',
155
+ directory: './app/jobs',
164
156
  model: ClaudeOnRails.configuration.default_model,
165
157
  allowed_tools: %w[Read Edit Write Bash Grep Glob LS],
166
- prompt_file: ".claude-on-rails/prompts/jobs.md"
158
+ prompt_file: '.claude-on-rails/prompts/jobs.md'
167
159
  }
168
160
  end
169
-
161
+
170
162
  def build_tests_agent
171
163
  test_dir = project_analysis[:test_framework] == 'RSpec' ? './spec' : './test'
172
-
164
+
173
165
  {
174
166
  description: "#{project_analysis[:test_framework]} testing, factories, and test coverage specialist",
175
167
  directory: test_dir,
176
168
  model: ClaudeOnRails.configuration.default_model,
177
169
  allowed_tools: %w[Read Edit Write Bash Grep Glob LS],
178
- prompt_file: ".claude-on-rails/prompts/tests.md"
170
+ prompt_file: '.claude-on-rails/prompts/tests.md'
179
171
  }
180
172
  end
181
-
173
+
182
174
  def build_devops_agent
183
175
  {
184
- description: "Deployment, Docker, CI/CD, and production configuration specialist",
185
- directory: "./config",
176
+ description: 'Deployment, Docker, CI/CD, and production configuration specialist',
177
+ directory: './config',
186
178
  model: ClaudeOnRails.configuration.default_model,
187
179
  allowed_tools: %w[Read Edit Write Bash Grep Glob LS],
188
- prompt_file: ".claude-on-rails/prompts/devops.md"
180
+ prompt_file: '.claude-on-rails/prompts/devops.md'
189
181
  }
190
182
  end
191
183
  end
192
- end
184
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ClaudeOnRails
2
- VERSION = "0.1.3"
3
- end
4
+ VERSION = '0.1.4'
5
+ end
@@ -1,29 +1,28 @@
1
- require "claude_on_rails/version"
2
- require "claude_on_rails/configuration"
3
- require "claude_on_rails/project_analyzer"
4
- require "claude_on_rails/swarm_builder"
1
+ # frozen_string_literal: true
2
+
3
+ require 'claude_on_rails/version'
4
+ require 'claude_on_rails/configuration'
5
+ require 'claude_on_rails/project_analyzer'
6
+ require 'claude_on_rails/swarm_builder'
5
7
 
6
8
  module ClaudeOnRails
7
9
  class Error < StandardError; end
8
-
10
+
9
11
  class << self
10
- attr_accessor :configuration
11
-
12
12
  def configure
13
- self.configuration ||= Configuration.new
14
13
  yield(configuration) if block_given?
15
14
  end
16
-
15
+
17
16
  def configuration
18
17
  @configuration ||= Configuration.new
19
18
  end
20
-
19
+
21
20
  def analyze_project(root_path = Rails.root)
22
21
  ProjectAnalyzer.new(root_path).analyze
23
22
  end
24
-
23
+
25
24
  def build_swarm(project_analysis)
26
25
  SwarmBuilder.new(project_analysis).build
27
26
  end
28
27
  end
29
- end
28
+ end