Class: Silas::Generators::InstallGenerator
- Inherits:
-
Rails::Generators::Base
- Object
- Rails::Generators::Base
- Silas::Generators::InstallGenerator
- Defined in:
- lib/generators/silas/install/install_generator.rb
Constant Summary collapse
- RESCUER_ENTRY =
<<~YAML.freeze # Silas: retries jobs failed by dead-worker reaping/pruning, sweeps # expired approvals, and fails turns stranded by dead loop jobs. Part # of the durability contract — do not remove. silas_dead_job_rescuer: class: Silas::DeadJobRescuerJob queue: default schedule: every 30 seconds YAML
Instance Method Summary collapse
-
#add_rescuer_recurring_task ⇒ Object
Idempotent and environment-aware: the entry is injected directly under EVERY deployable top-level env key (production, staging, …) — never a blind append into whatever block happens to end the file, and never a duplicate key on a re-run.
- #create_agent_directory ⇒ Object
-
#create_channels ⇒ Object
Channels are opt-in: scaffold the two reference bindings + mount the engine that serves their webhooks.
-
#create_eval_gate ⇒ Object
Evals as a deploy gate: an example scenario + a bin/ci wrapper.
- #create_initializer ⇒ Object
- #install_migrations ⇒ Object
- #mount_engine ⇒ Object
- #show_next_steps ⇒ Object
Instance Method Details
#add_rescuer_recurring_task ⇒ Object
Idempotent and environment-aware: the entry is injected directly under EVERY deployable top-level env key (production, staging, …) — never a blind append into whatever block happens to end the file, and never a duplicate key on a re-run. A staging worker needs the rescuer exactly as much as production does.
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/generators/silas/install/install_generator.rb', line 71 def add_rescuer_recurring_task recurring = "config/recurring.yml" path = File.(recurring, destination_root) unless File.exist?(path) create_file recurring, "production:\n#{RESCUER_ENTRY.indent(2)}" return end if File.read(path).include?("silas_dead_job_rescuer") say_status :skip, "#{recurring} already contains silas_dead_job_rescuer", :yellow return end injected = false gsub_file recurring, /^(?!development:|test:)([a-zA-Z_]+:)[ \t]*$/ do |header| injected = true "#{header}\n#{RESCUER_ENTRY.indent(2)}" end append_to_file recurring, "\nproduction:\n#{RESCUER_ENTRY.indent(2)}" unless injected end |
#create_agent_directory ⇒ Object
22 23 24 25 26 27 28 |
# File 'lib/generators/silas/install/install_generator.rb', line 22 def create_agent_directory template "instructions.md", "app/agent/instructions.md" template "agent.yml", "app/agent/agent.yml" template "example_tool.rb", "app/agent/tools/example_tool.rb" template "example_skill.md", "app/agent/skills/example.md" template "example_schedule.md", "app/agent/schedules/example.md" end |
#create_channels ⇒ Object
Channels are opt-in: scaffold the two reference bindings + mount the engine that serves their webhooks. Delete a channel file to disable it.
32 33 34 35 |
# File 'lib/generators/silas/install/install_generator.rb', line 32 def create_channels template "channel_slack.rb", "app/agent/channels/slack.rb" template "channel_email.rb", "app/agent/channels/email.rb" end |
#create_eval_gate ⇒ Object
Evals as a deploy gate: an example scenario + a bin/ci wrapper. Rails 8.1 apps ship their own bin/ci (rubocop, brakeman, tests) — never clobber it; tell the user to add the eval gate to theirs instead.
44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/generators/silas/install/install_generator.rb', line 44 def create_eval_gate template "example_eval.rb", "test/agent_evals/example_eval.rb" if File.exist?(File.("bin/ci", destination_root)) say "bin/ci already exists — left untouched. Add `bin/rails silas:eval` " \ "to it to gate deploys on agent evals.", :yellow else copy_file "bin_ci", "bin/ci" chmod "bin/ci", 0o755 end end |
#create_initializer ⇒ Object
8 9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/generators/silas/install/install_generator.rb', line 8 def create_initializer template "initializer.rb", "config/initializers/silas.rb" # ruby_llm reads no provider keys from ENV on its own; without this the # first model call raises ConfigurationError. Brownfield apps often # already configure ruby_llm themselves — never touch an existing one # (not even a conflict prompt: an accidental Y clobbers production # provider config). if File.exist?(File.join(destination_root, "config/initializers/ruby_llm.rb")) say_status :skip, "config/initializers/ruby_llm.rb exists — left untouched", :yellow else template "ruby_llm.rb", "config/initializers/ruby_llm.rb" end end |
#install_migrations ⇒ Object
93 94 95 96 97 |
# File 'lib/generators/silas/install/install_generator.rb', line 93 def install_migrations rake "silas:install:migrations" rescue StandardError say "Run `bin/rails silas:install:migrations db:migrate` to install Silas's tables.", :yellow end |
#mount_engine ⇒ Object
37 38 39 |
# File 'lib/generators/silas/install/install_generator.rb', line 37 def mount_engine route 'mount Silas::Engine => "/silas"' end |
#show_next_steps ⇒ Object
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/generators/silas/install/install_generator.rb', line 99 def show_next_steps say <<~MSG, :green Silas installed. Next: 1. bin/rails db:migrate 2. export ANTHROPIC_API_KEY=sk-ant-... (config/initializers/ruby_llm.rb reads it) then `bin/rails silas:doctor` to verify the whole setup 3. Edit app/agent/instructions.md (your agent's persona) 4. Write tools in app/agent/tools/ (keyword signature = schema) 5. Talk to it: bin/rails silas:chat (or Silas.agent.start(input: "hello")) 6. Schedules: edit app/agent/schedules/*, then `bin/rails silas:schedules` 7. Channels (optional): set credentials.silas.slack.{signing_secret,bot_token} for Slack; route inbound mail to Silas::AgentMailbox for email. Delete app/agent/channels/{slack,email}.rb to disable. Any other transport: `bin/rails g silas:channel whatsapp`. 8. Inbox + web chat: /silas/inbox, deny-by-default — uncomment config.inbox_auth in config/initializers/silas.rb to make it visible. 9. Restart your server if it was running (app/agent/ registers at boot). MSG end |