Class: RailsConsoleAi::Channel::Slack

Inherits:
Base
  • Object
show all
Defined in:
lib/rails_console_ai/channel/slack.rb

Constant Summary collapse

ANSI_REGEX =
/\e\[[0-9;]*m/

Instance Method Summary collapse

Methods inherited from Base

#edit_code

Constructor Details

#initialize(slack_bot:, channel_id:, thread_ts:, user_name: nil) ⇒ Slack

Returns a new instance of Slack.



8
9
10
11
12
13
14
15
16
17
# File 'lib/rails_console_ai/channel/slack.rb', line 8

def initialize(slack_bot:, channel_id:, thread_ts:, user_name: nil)
  @slack_bot = slack_bot
  @channel_id = channel_id
  @thread_ts = thread_ts
  @user_name = user_name
  @reply_queue = Queue.new
  @cancelled = false
  @log_prefix = "[#{@channel_id}/#{@thread_ts}] @#{@user_name}"
  @output_log = StringIO.new
end

Instance Method Details

#cancel!Object



19
20
21
# File 'lib/rails_console_ai/channel/slack.rb', line 19

def cancel!
  @cancelled = true
end

#cancelled?Boolean

Returns:

  • (Boolean)


23
24
25
# File 'lib/rails_console_ai/channel/slack.rb', line 23

def cancelled?
  @cancelled
end

#confirm(_text) ⇒ Object



81
82
83
# File 'lib/rails_console_ai/channel/slack.rb', line 81

def confirm(_text)
  'y'
end

#console_capture_stringObject



144
145
146
# File 'lib/rails_console_ai/channel/slack.rb', line 144

def console_capture_string
  @output_log.string
end

#display(text) ⇒ Object



27
28
29
# File 'lib/rails_console_ai/channel/slack.rb', line 27

def display(text)
  post(strip_ansi(text))
end

#display_code(_code) ⇒ Object



59
60
61
62
# File 'lib/rails_console_ai/channel/slack.rb', line 59

def display_code(_code)
  # Don't post raw code/plan steps to Slack — non-technical users don't need to see Ruby
  nil
end

#display_dim(text) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/rails_console_ai/channel/slack.rb', line 31

def display_dim(text)
  raw = strip_ansi(text)
  stripped = raw.strip

  if stripped =~ /\AThinking\.\.\.|\AAttempting to fix|\ACancelled|\A_session:/
    post(stripped)
  elsif stripped =~ /\ACalling LLM/
    # Technical LLM round status — suppress in Slack
    @output_log.write("#{stripped}\n")
    $stdout.puts "#{@log_prefix} (dim) #{stripped}"
  elsif raw =~ /\A {2,4}\S/ && stripped.length > 10
    # LLM thinking text (2-space indent from conversation engine) — show as status
    post(stripped)
  else
    # Tool result previews (5+ space indent) and other technical noise — log only
    @output_log.write("#{stripped}\n")
    $stdout.puts "#{@log_prefix} (dim) #{stripped}"
  end
end

#display_error(text) ⇒ Object



55
56
57
# File 'lib/rails_console_ai/channel/slack.rb', line 55

def display_error(text)
  post(":x: #{strip_ansi(text)}")
end

#display_result(_result) ⇒ Object



71
72
73
74
# File 'lib/rails_console_ai/channel/slack.rb', line 71

def display_result(_result)
  # Don't post raw return values to Slack — the LLM formats output via puts
  nil
end

#display_result_output(output) ⇒ Object



64
65
66
67
68
69
# File 'lib/rails_console_ai/channel/slack.rb', line 64

def display_result_output(output)
  text = strip_ansi(output).strip
  return if text.empty?
  text = text[0, 3000] + "\n... (truncated)" if text.length > 3000
  post("```#{text}```")
end

#display_warning(text) ⇒ Object



51
52
53
# File 'lib/rails_console_ai/channel/slack.rb', line 51

def display_warning(text)
  post(":warning: #{strip_ansi(text)}")
end

#log_input(text) ⇒ Object



134
135
136
# File 'lib/rails_console_ai/channel/slack.rb', line 134

def log_input(text)
  @output_log.write("@#{@user_name}: #{text}\n")
end

#modeObject



89
90
91
# File 'lib/rails_console_ai/channel/slack.rb', line 89

def mode
  'slack'
end

#prompt(text) ⇒ Object



76
77
78
79
# File 'lib/rails_console_ai/channel/slack.rb', line 76

def prompt(text)
  post(strip_ansi(text))
  @reply_queue.pop
end

#receive_reply(text) ⇒ Object

Called by SlackBot when a thread reply arrives



139
140
141
142
# File 'lib/rails_console_ai/channel/slack.rb', line 139

def receive_reply(text)
  @output_log.write("@#{@user_name}: #{text}\n")
  @reply_queue.push(text)
end

#supports_danger?Boolean

Returns:

  • (Boolean)


93
94
95
# File 'lib/rails_console_ai/channel/slack.rb', line 93

def supports_danger?
  false
end

#supports_editing?Boolean

Returns:

  • (Boolean)


97
98
99
# File 'lib/rails_console_ai/channel/slack.rb', line 97

def supports_editing?
  false
end

#system_instructionsObject



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/rails_console_ai/channel/slack.rb', line 105

def system_instructions
  <<~INSTRUCTIONS.strip
    ## Response Formatting (Slack Channel)

    You are responding to non-technical users in Slack. Follow these rules:

    - Slack does NOT support markdown tables. For tabular data, use `puts` to print
      a plain-text table inside a code block. Use fixed-width columns with padding so
      columns align. Example format:
      ```
      ID   Name              Email
      123  John Smith        john@example.com
      456  Jane Doe          jane@example.com
      ```
    - Use `puts` with formatted output instead of returning arrays or hashes
    - Summarize findings in plain, simple language
    - Do NOT show technical details like SQL queries, token counts, or class names
    - Keep explanations simple and jargon-free
    - Never return raw Ruby objects — always present data in a human-readable way
    - The output of `puts` in your code is automatically shown to the user. Do NOT
      repeat or re-display data that your code already printed via `puts`.
      Just add a brief summary after (e.g. "10 events found" or "Let me know if you need more detail").
    - Do not offer to make changes or take actions on behalf of the user. Only report findings.
    - This is a live production database — other processes, users, and background jobs are
      constantly changing data. Never assume results will be the same as a previous query.
      Always re-run queries when asked, even if you just ran the same one.
  INSTRUCTIONS
end

#user_identityObject



85
86
87
# File 'lib/rails_console_ai/channel/slack.rb', line 85

def user_identity
  @user_name
end

#wrap_llm_call(&block) ⇒ Object



101
102
103
# File 'lib/rails_console_ai/channel/slack.rb', line 101

def wrap_llm_call(&block)
  yield
end