agentcode 0.9.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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +59 -0
  3. data/lib/agentcode/blueprint/blueprint_parser.rb +198 -0
  4. data/lib/agentcode/blueprint/blueprint_validator.rb +209 -0
  5. data/lib/agentcode/blueprint/generators/factory_generator.rb +74 -0
  6. data/lib/agentcode/blueprint/generators/policy_generator.rb +154 -0
  7. data/lib/agentcode/blueprint/generators/seeder_generator.rb +160 -0
  8. data/lib/agentcode/blueprint/generators/test_generator.rb +291 -0
  9. data/lib/agentcode/blueprint/manifest_manager.rb +81 -0
  10. data/lib/agentcode/commands/base_command.rb +57 -0
  11. data/lib/agentcode/commands/blueprint_command.rb +549 -0
  12. data/lib/agentcode/commands/export_postman_command.rb +328 -0
  13. data/lib/agentcode/commands/generate_command.rb +563 -0
  14. data/lib/agentcode/commands/install_command.rb +441 -0
  15. data/lib/agentcode/commands/invitation_link_command.rb +107 -0
  16. data/lib/agentcode/concerns/belongs_to_organization.rb +49 -0
  17. data/lib/agentcode/concerns/has_agentcode.rb +93 -0
  18. data/lib/agentcode/concerns/has_audit_trail.rb +125 -0
  19. data/lib/agentcode/concerns/has_auto_scope.rb +91 -0
  20. data/lib/agentcode/concerns/has_permissions.rb +117 -0
  21. data/lib/agentcode/concerns/has_uuid.rb +26 -0
  22. data/lib/agentcode/concerns/has_validation.rb +250 -0
  23. data/lib/agentcode/concerns/hidable_columns.rb +180 -0
  24. data/lib/agentcode/configuration.rb +98 -0
  25. data/lib/agentcode/controllers/auth_controller.rb +242 -0
  26. data/lib/agentcode/controllers/invitations_controller.rb +231 -0
  27. data/lib/agentcode/controllers/resources_controller.rb +813 -0
  28. data/lib/agentcode/engine.rb +65 -0
  29. data/lib/agentcode/mailers/invitation_mailer.rb +22 -0
  30. data/lib/agentcode/middleware/resolve_organization_from_route.rb +72 -0
  31. data/lib/agentcode/models/agentcode_model.rb +387 -0
  32. data/lib/agentcode/models/audit_log.rb +17 -0
  33. data/lib/agentcode/models/organization_invitation.rb +57 -0
  34. data/lib/agentcode/policies/invitation_policy.rb +54 -0
  35. data/lib/agentcode/policies/resource_policy.rb +197 -0
  36. data/lib/agentcode/query_builder.rb +278 -0
  37. data/lib/agentcode/railtie.rb +11 -0
  38. data/lib/agentcode/resource_scope.rb +59 -0
  39. data/lib/agentcode/routes.rb +124 -0
  40. data/lib/agentcode/tasks/agentcode.rake +39 -0
  41. data/lib/agentcode/templates/agentcode.rb +71 -0
  42. data/lib/agentcode/templates/agentcode_model.rb +104 -0
  43. data/lib/agentcode/templates/audit_trail/create_audit_logs.rb.erb +26 -0
  44. data/lib/agentcode/templates/generate/factory.rb.erb +43 -0
  45. data/lib/agentcode/templates/generate/migration.rb.erb +26 -0
  46. data/lib/agentcode/templates/generate/model.rb.erb +55 -0
  47. data/lib/agentcode/templates/generate/policy.rb.erb +52 -0
  48. data/lib/agentcode/templates/generate/scope.rb.erb +31 -0
  49. data/lib/agentcode/templates/multi_tenant/factories/organizations.rb.erb +9 -0
  50. data/lib/agentcode/templates/multi_tenant/factories/roles.rb.erb +9 -0
  51. data/lib/agentcode/templates/multi_tenant/factories/user_roles.rb.erb +10 -0
  52. data/lib/agentcode/templates/multi_tenant/factories/users.rb.erb +9 -0
  53. data/lib/agentcode/templates/multi_tenant/migrations/create_organizations.rb.erb +15 -0
  54. data/lib/agentcode/templates/multi_tenant/migrations/create_roles.rb.erb +15 -0
  55. data/lib/agentcode/templates/multi_tenant/migrations/create_user_roles.rb.erb +16 -0
  56. data/lib/agentcode/templates/multi_tenant/migrations/create_users.rb.erb +15 -0
  57. data/lib/agentcode/templates/multi_tenant/models/organization.rb.erb +18 -0
  58. data/lib/agentcode/templates/multi_tenant/models/role.rb.erb +11 -0
  59. data/lib/agentcode/templates/multi_tenant/models/user.rb.erb +14 -0
  60. data/lib/agentcode/templates/multi_tenant/models/user_role.rb.erb +9 -0
  61. data/lib/agentcode/templates/multi_tenant/policies/organization_policy.rb.erb +6 -0
  62. data/lib/agentcode/templates/multi_tenant/policies/role_policy.rb.erb +6 -0
  63. data/lib/agentcode/templates/multi_tenant/seeders/organization_seeder.rb.erb +9 -0
  64. data/lib/agentcode/templates/multi_tenant/seeders/role_seeder.rb.erb +19 -0
  65. data/lib/agentcode/templates/routes.rb +13 -0
  66. data/lib/agentcode/version.rb +5 -0
  67. data/lib/agentcode/views/lumina/invitation_mailer/invite.html.erb +29 -0
  68. data/lib/agentcode-rails.rb +3 -0
  69. data/lib/agentcode.rb +26 -0
  70. metadata +281 -0
@@ -0,0 +1,563 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "agentcode/commands/base_command"
4
+
5
+ module AgentCode
6
+ module Commands
7
+ # Interactive scaffold generator — mirrors Laravel `php artisan agentcode:generate` exactly.
8
+ #
9
+ # Usage: rails agentcode:generate (or rails agentcode:g)
10
+ class GenerateCommand < BaseCommand
11
+ def perform
12
+ print_banner
13
+ print_styled_header
14
+
15
+ type = select("What type of resource would you like to generate?") do |menu|
16
+ menu.choice "Model (with migration and factory)", "model"
17
+ menu.choice "Policy (extends ResourcePolicy)", "policy"
18
+ menu.choice "Scope (for ScopedDB)", "scope"
19
+ end
20
+
21
+ name = ask("What is the resource name? (PascalCase singular, e.g., Post):")
22
+ name = name.strip.camelize
23
+
24
+ if name.blank? || name !~ /\A[A-Za-z][A-Za-z0-9]*\z/
25
+ say "Invalid name. Must start with a letter and contain only alphanumeric characters.", :red
26
+ return
27
+ end
28
+
29
+ case type
30
+ when "model"
31
+ generate_model(name)
32
+ when "policy"
33
+ generate_policy(name)
34
+ when "scope"
35
+ generate_scope(name)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ # ----------------------------------------------------------------
42
+ # Banner
43
+ # ----------------------------------------------------------------
44
+
45
+ def print_banner
46
+ say ""
47
+
48
+ lines = [
49
+ " █████╗ ██████╗ ███████╗███╗ ██╗████████╗ ██████╗ ██████╗ ██████╗ ███████╗",
50
+ " ██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝██╔════╝██╔═══██╗██╔══██╗██╔════╝",
51
+ " ███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ██║ ██║ ██║██║ ██║█████╗ ",
52
+ " ██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ██║ ██║ ██║██║ ██║██╔══╝ ",
53
+ " ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ╚██████╗╚██████╔╝██████╔╝███████╗",
54
+ " ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝"
55
+ ]
56
+
57
+ gradient = [
58
+ [0, 255, 255], [0, 230, 200], [100, 220, 100],
59
+ [255, 220, 50], [255, 170, 30], [255, 120, 0]
60
+ ]
61
+
62
+ lines.each_with_index do |text, i|
63
+ r, g, b = gradient[i]
64
+ $stdout.puts "\033[38;2;#{r};#{g};#{b}m#{text}\033[0m"
65
+ end
66
+ end
67
+
68
+ def print_styled_header
69
+ text = "+ AgentCode :: Generate :: Scaffold your resources +"
70
+ say ""
71
+ say " ┌#{"─" * (text.length + 8)}┐", :cyan
72
+ say " │ #{text} │", :cyan
73
+ say " └#{"─" * (text.length + 8)}┘", :cyan
74
+ say ""
75
+ end
76
+
77
+ # ----------------------------------------------------------------
78
+ # Model generation
79
+ # ----------------------------------------------------------------
80
+
81
+ def generate_model(name)
82
+ table_name = name.underscore.pluralize
83
+
84
+ # Multi-tenant check
85
+ belongs_to_org = false
86
+ owner_relation = nil
87
+ is_multi_tenant = multi_tenant_enabled?
88
+
89
+ if is_multi_tenant
90
+ belongs_to_org = yes?("Does this model belong to an organization?")
91
+
92
+ unless belongs_to_org
93
+ existing_models = get_existing_models
94
+ if existing_models.any?
95
+ has_parent = yes?("Does this model have a parent that belongs to an organization?")
96
+ if has_parent
97
+ owner_model = select("Which model is the parent owner?", existing_models)
98
+ owner_relation = owner_model.underscore.camelize(:lower)
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ # Collect columns
105
+ columns = []
106
+ if yes?("Would you like to define columns interactively?")
107
+ columns = collect_columns
108
+ end
109
+
110
+ # Auto-add organization_id FK
111
+ if belongs_to_org
112
+ columns.unshift({
113
+ name: "organization_id",
114
+ type: "references",
115
+ nullable: false,
116
+ unique: false,
117
+ index: true,
118
+ default: nil,
119
+ foreign_model: "Organization"
120
+ })
121
+ end
122
+
123
+ # Auto-add owner FK
124
+ if owner_relation
125
+ owner_fk = "#{owner_relation.underscore}_id"
126
+ unless columns.any? { |c| c[:name] == owner_fk }
127
+ columns.unshift({
128
+ name: owner_fk,
129
+ type: "references",
130
+ nullable: false,
131
+ unique: false,
132
+ index: true,
133
+ default: nil,
134
+ foreign_model: owner_relation.camelize
135
+ })
136
+ end
137
+ end
138
+
139
+ # Additional options
140
+ options = collect_additional_options
141
+
142
+ # Role access
143
+ role_access = {}
144
+ if options[:policy] && is_multi_tenant
145
+ role_access = collect_role_access(name)
146
+ end
147
+
148
+ # Generate model
149
+ task("Creating #{name} model") do
150
+ write_model_file(name, columns, belongs_to_org, owner_relation, options)
151
+ end
152
+
153
+ # Generate migration
154
+ unless columns.empty?
155
+ task("Creating migration for #{table_name}") do
156
+ write_migration_file(name, columns, options[:soft_deletes])
157
+ end
158
+ end
159
+
160
+ # Generate factory
161
+ task("Creating #{name} factory") do
162
+ write_factory_file(name, columns)
163
+ end
164
+
165
+ # Register in config
166
+ task("Registering #{name} in config/initializers/agentcode.rb") do
167
+ register_model_in_config(name)
168
+ end
169
+
170
+ # Generate policy
171
+ if options[:policy]
172
+ task("Generating #{name}Policy") do
173
+ write_policy_file(name)
174
+ end
175
+ end
176
+
177
+ # Generate scope
178
+ task("Generating #{name}Scope") do
179
+ write_scope_file(name)
180
+ end
181
+
182
+ say ""
183
+ say "#{name} model generated successfully!", :green
184
+ print_created_files(name, options)
185
+ print_model_next_steps(name, table_name)
186
+ end
187
+
188
+ # ----------------------------------------------------------------
189
+ # Policy generation
190
+ # ----------------------------------------------------------------
191
+
192
+ def generate_policy(name)
193
+ policy_name = name.end_with?("Policy") ? name : "#{name}Policy"
194
+ model_name = policy_name.sub(/Policy\z/, "")
195
+
196
+ task("Generating #{policy_name}") do
197
+ write_policy_file(model_name)
198
+ end
199
+
200
+ say ""
201
+ say "#{policy_name} generated successfully!", :green
202
+ say ""
203
+ say " Created: app/policies/#{policy_name.underscore}.rb"
204
+ say ""
205
+ say " Next steps:", :yellow
206
+ say " 1. Customize the authorization methods you need."
207
+ say ""
208
+ end
209
+
210
+ # ----------------------------------------------------------------
211
+ # Scope generation
212
+ # ----------------------------------------------------------------
213
+
214
+ def generate_scope(name)
215
+ scope_name = name.end_with?("Scope") ? name : "#{name}Scope"
216
+ model_name = scope_name.sub(/Scope\z/, "")
217
+
218
+ task("Generating #{scope_name}") do
219
+ write_scope_file(model_name)
220
+ end
221
+
222
+ say ""
223
+ say "#{scope_name} generated successfully!", :green
224
+ say ""
225
+ say " Created: app/models/scopes/#{scope_name.underscore}.rb"
226
+ say ""
227
+ end
228
+
229
+ # ----------------------------------------------------------------
230
+ # Column collection
231
+ # ----------------------------------------------------------------
232
+
233
+ def collect_columns
234
+ columns = []
235
+
236
+ loop do
237
+ col_name = ask("Column name (snake_case, e.g., title) — leave blank to finish:")
238
+ break if col_name.nil? || col_name.blank?
239
+
240
+ col_type = select("Column type for '#{col_name}'") do |menu|
241
+ menu.choice "string (VARCHAR 255)", "string"
242
+ menu.choice "text (TEXT)", "text"
243
+ menu.choice "integer", "integer"
244
+ menu.choice "bigInteger", "bigint"
245
+ menu.choice "boolean", "boolean"
246
+ menu.choice "date", "date"
247
+ menu.choice "datetime", "datetime"
248
+ menu.choice "decimal (8, 2)", "decimal"
249
+ menu.choice "float", "float"
250
+ menu.choice "json", "json"
251
+ menu.choice "uuid", "uuid"
252
+ menu.choice "references (foreign key)", "references"
253
+ end
254
+
255
+ column = {
256
+ name: col_name,
257
+ type: col_type,
258
+ nullable: false,
259
+ unique: false,
260
+ index: false,
261
+ default: nil,
262
+ foreign_model: nil
263
+ }
264
+
265
+ if col_type == "references"
266
+ existing = get_existing_models
267
+ if existing.any?
268
+ column[:foreign_model] = select("Which model does '#{col_name}' reference?", existing)
269
+ end
270
+ end
271
+
272
+ column[:nullable] = yes?("Is '#{col_name}' nullable?")
273
+ column[:unique] = yes?("Should '#{col_name}' be unique?")
274
+
275
+ columns << column
276
+
277
+ break unless yes?("Add another column?")
278
+ end
279
+
280
+ columns
281
+ end
282
+
283
+ def collect_additional_options
284
+ say "Additional options:", :yellow
285
+
286
+ options = multi_select("Select additional options:") do |menu|
287
+ menu.choice "Soft deletes", :soft_deletes
288
+ menu.choice "Generate policy", :policy
289
+ menu.choice "Audit trail", :audit_trail
290
+ end
291
+
292
+ {
293
+ soft_deletes: options.include?(:soft_deletes),
294
+ policy: options.include?(:policy),
295
+ audit_trail: options.include?(:audit_trail)
296
+ }
297
+ end
298
+
299
+ def collect_role_access(name)
300
+ roles = get_roles_from_config
301
+ return {} if roles.empty?
302
+
303
+ slug = name.underscore.pluralize
304
+ role_access = { "admin" => "editor" }
305
+
306
+ non_admin_roles = roles.reject { |r| r == "admin" }
307
+ return role_access if non_admin_roles.empty?
308
+
309
+ say ""
310
+ say "Define role access for #{slug}:", :cyan
311
+ say ""
312
+
313
+ non_admin_roles.each do |role|
314
+ access = select("Access level for '#{role}'") do |menu|
315
+ menu.choice "Editor — all actions on this model", "editor"
316
+ menu.choice "Viewer — read-only (index, show)", "viewer"
317
+ menu.choice "Writer — create & edit (index, show, store, update)", "writer"
318
+ menu.choice "No access", "none"
319
+ end
320
+ role_access[role] = access
321
+ end
322
+
323
+ role_access
324
+ end
325
+
326
+ # ----------------------------------------------------------------
327
+ # File writers
328
+ # ----------------------------------------------------------------
329
+
330
+ def write_model_file(name, columns, belongs_to_org, owner_relation, options)
331
+ template = File.expand_path("../../templates/generate/model.rb.erb", __FILE__)
332
+ dest = Rails.root.join("app/models/#{name.underscore}.rb")
333
+ FileUtils.mkdir_p(File.dirname(dest))
334
+
335
+ table_name = name.underscore.pluralize
336
+
337
+ # Build data for template
338
+ fillable = columns.map { |c| c[:name] }.reject { |n| n == "organization_id" && belongs_to_org }
339
+ filter_cols = columns.reject { |c| %w[text json].include?(c[:type]) }.map { |c| c[:name] }
340
+ sort_cols = (columns.reject { |c| %w[text json].include?(c[:type]) }.map { |c| c[:name] } + ["created_at"]).uniq
341
+ field_cols = (["id"] + columns.map { |c| c[:name] } + ["created_at"]).uniq
342
+ include_cols = columns.select { |c| c[:type] == "references" && c[:foreign_model] }
343
+ .map { |c| c[:name].sub(/_id\z/, "") }
344
+
345
+ validation_rules = columns.to_h { |c| [c[:name], column_to_rails_validations(c)] }
346
+
347
+ content = ERB.new(File.read(template), trim_mode: "-").result_with_hash(
348
+ name: name,
349
+ table_name: table_name,
350
+ fillable: fillable,
351
+ filter_cols: filter_cols,
352
+ sort_cols: sort_cols,
353
+ field_cols: field_cols,
354
+ include_cols: include_cols,
355
+ validation_rules: validation_rules,
356
+ columns: columns,
357
+ belongs_to_org: belongs_to_org,
358
+ owner_relation: owner_relation,
359
+ soft_deletes: options[:soft_deletes],
360
+ audit_trail: options[:audit_trail]
361
+ )
362
+
363
+ File.write(dest, content)
364
+ end
365
+
366
+ def write_migration_file(name, columns, soft_deletes)
367
+ template = File.expand_path("../../templates/generate/migration.rb.erb", __FILE__)
368
+ table_name = name.underscore.pluralize
369
+ timestamp = Time.current.strftime("%Y%m%d%H%M%S")
370
+ dest = Rails.root.join("db/migrate/#{timestamp}_create_#{table_name}.rb")
371
+ FileUtils.mkdir_p(File.dirname(dest))
372
+
373
+ content = ERB.new(File.read(template), trim_mode: "-").result_with_hash(
374
+ table_name: table_name,
375
+ class_name: "Create#{name.pluralize}",
376
+ columns: columns,
377
+ soft_deletes: soft_deletes
378
+ )
379
+
380
+ File.write(dest, content)
381
+ end
382
+
383
+ def write_factory_file(name, columns)
384
+ template = File.expand_path("../../templates/generate/factory.rb.erb", __FILE__)
385
+ dest = Rails.root.join("spec/factories/#{name.underscore.pluralize}.rb")
386
+ FileUtils.mkdir_p(File.dirname(dest))
387
+
388
+ content = ERB.new(File.read(template), trim_mode: "-").result_with_hash(
389
+ name: name,
390
+ columns: columns
391
+ )
392
+
393
+ File.write(dest, content)
394
+ end
395
+
396
+ def write_policy_file(name)
397
+ template = File.expand_path("../../templates/generate/policy.rb.erb", __FILE__)
398
+ dest = Rails.root.join("app/policies/#{name.underscore}_policy.rb")
399
+ FileUtils.mkdir_p(File.dirname(dest))
400
+
401
+ content = ERB.new(File.read(template), trim_mode: "-").result_with_hash(name: name)
402
+
403
+ File.write(dest, content)
404
+ end
405
+
406
+ def write_scope_file(name)
407
+ template = File.expand_path("../../templates/generate/scope.rb.erb", __FILE__)
408
+ dest = Rails.root.join("app/models/scopes/#{name.underscore}_scope.rb")
409
+ FileUtils.mkdir_p(File.dirname(dest))
410
+
411
+ table_name = name.underscore.pluralize
412
+ content = ERB.new(File.read(template), trim_mode: "-").result_with_hash(
413
+ name: name,
414
+ table_name: table_name
415
+ )
416
+
417
+ File.write(dest, content)
418
+ end
419
+
420
+ def register_model_in_config(name)
421
+ config_path = Rails.root.join("config/initializers/agentcode.rb")
422
+ return unless File.exist?(config_path)
423
+
424
+ content = File.read(config_path)
425
+ slug = name.underscore.pluralize
426
+
427
+ # Check if model is already registered (non-commented line)
428
+ return if content.match?(/^\s+\w+\.model\s+:#{slug}\b/)
429
+
430
+ # Detect the block variable name used in the config file
431
+ block_var = content.match(/AgentCode\.configure\s+do\s+\|(\w+)\|/)&.captures&.first || "config"
432
+
433
+ new_entry = " #{block_var}.model :#{slug}, '#{name}'"
434
+
435
+ if content.include?("# #{block_var}.model :posts, 'Post'")
436
+ content = content.gsub(
437
+ "# #{block_var}.model :posts, 'Post'",
438
+ "#{new_entry}\n # #{block_var}.model :posts, 'Post'"
439
+ )
440
+ elsif content.match?(/# \w+\.model :posts, 'Post'/)
441
+ content = content.sub(
442
+ /# (\w+)\.model :posts, 'Post'/,
443
+ "#{new_entry}\n # \\1.model :posts, 'Post'"
444
+ )
445
+ else
446
+ content = content.sub(
447
+ /(# Register your models here.*?\n)/,
448
+ "\\1#{new_entry}\n"
449
+ )
450
+ end
451
+
452
+ File.write(config_path, content)
453
+ end
454
+
455
+ # ----------------------------------------------------------------
456
+ # Helpers
457
+ # ----------------------------------------------------------------
458
+
459
+ def column_to_rails_validations(column)
460
+ validations = []
461
+
462
+ case column[:type]
463
+ when "string"
464
+ validations << "length: { maximum: 255 }"
465
+ when "integer", "bigint"
466
+ validations << "numericality: { only_integer: true }"
467
+ when "boolean"
468
+ validations << "inclusion: { in: [true, false] }"
469
+ when "references"
470
+ validations << "numericality: { only_integer: true }"
471
+ end
472
+
473
+ validations
474
+ end
475
+
476
+ def column_to_faker(column)
477
+ case column[:name]
478
+ when "name", "full_name" then "Faker::Name.name"
479
+ when "email" then "Faker::Internet.email"
480
+ when "title" then "Faker::Lorem.sentence(word_count: 3)"
481
+ when "description", "content", "body" then "Faker::Lorem.paragraph"
482
+ when "slug" then "Faker::Internet.slug"
483
+ when "phone", "phone_number" then "Faker::PhoneNumber.phone_number"
484
+ when "url", "website" then "Faker::Internet.url"
485
+ when /\Ais_/ then "[true, false].sample"
486
+ else
487
+ case column[:type]
488
+ when "string" then "Faker::Lorem.sentence(word_count: 3)"
489
+ when "text" then "Faker::Lorem.paragraph"
490
+ when "integer", "bigint" then "Faker::Number.between(from: 1, to: 100)"
491
+ when "boolean" then "[true, false].sample"
492
+ when "date" then "Faker::Date.between(from: 1.year.ago, to: Date.today)"
493
+ when "datetime" then "Faker::Time.between(from: 1.year.ago, to: Time.current)"
494
+ when "decimal", "float" then "Faker::Number.decimal(l_digits: 3, r_digits: 2)"
495
+ when "json" then "{}"
496
+ when "uuid" then "SecureRandom.uuid"
497
+ when "references"
498
+ if column[:foreign_model]
499
+ "association :#{column[:name].sub(/_id\z/, '')}"
500
+ else
501
+ "Faker::Number.between(from: 1, to: 10)"
502
+ end
503
+ else
504
+ "Faker::Lorem.word"
505
+ end
506
+ end
507
+ end
508
+
509
+ def multi_tenant_enabled?
510
+ config_path = Rails.root.join("config/initializers/agentcode.rb")
511
+ return false unless File.exist?(config_path)
512
+
513
+ content = File.read(config_path)
514
+ content.include?("route_group :tenant")
515
+ end
516
+
517
+ def get_existing_models
518
+ models_path = Rails.root.join("app/models")
519
+ return [] unless Dir.exist?(models_path)
520
+
521
+ Dir.glob(models_path.join("*.rb")).map do |f|
522
+ File.basename(f, ".rb").camelize
523
+ end.reject { |m| m == "ApplicationRecord" }
524
+ end
525
+
526
+ def get_roles_from_config
527
+ config_path = Rails.root.join("config/initializers/agentcode.rb")
528
+ return [] unless File.exist?(config_path)
529
+
530
+ content = File.read(config_path)
531
+ if content =~ /roles.*?\[(.*?)\]/m
532
+ $1.scan(/"([^"]+)"/).flatten
533
+ else
534
+ []
535
+ end
536
+ end
537
+
538
+ def print_created_files(name, options)
539
+ table_name = name.underscore.pluralize
540
+ say ""
541
+ say "Created files:", :yellow
542
+ say ""
543
+ say " Model app/models/#{name.underscore}.rb"
544
+ say " Migration db/migrate/..._create_#{table_name}.rb"
545
+ say " Factory spec/factories/#{table_name}.rb"
546
+ say " Config config/initializers/agentcode.rb (registered as '#{table_name}')"
547
+ say " Policy app/policies/#{name.underscore}_policy.rb" if options[:policy]
548
+ say " Scope app/models/scopes/#{name.underscore}_scope.rb"
549
+ end
550
+
551
+ def print_model_next_steps(name, table_name)
552
+ say ""
553
+ say "Next steps:", :yellow
554
+ say ""
555
+ say " 1. Run migrations: rails db:migrate"
556
+ say " 2. Review the generated model at: app/models/#{name.underscore}.rb"
557
+ say " 3. Run tests: rspec"
558
+ say " 4. Your API endpoints: GET/POST /api/#{table_name}, GET/PUT/DELETE /api/#{table_name}/{id}"
559
+ say ""
560
+ end
561
+ end
562
+ end
563
+ end