Class: Clacky::Tools::AskUser
- Inherits:
-
Base
- Object
- Base
- Clacky::Tools::AskUser
show all
- Defined in:
- lib/clacky/tools/ask_user.rb
Constant Summary
collapse
- TOOL_NAMES =
Both names route here: request_user_feedback is the retired predecessor,
still present in stored sessions and in skill instructions.
%w[ask_user request_user_feedback].freeze
Instance Attribute Summary
Attributes inherited from Base
#agent
Class Method Summary
collapse
Instance Method Summary
collapse
Methods inherited from Base
#category, #description, #name, #parameters, #to_function_definition
Class Method Details
80
81
82
|
# File 'lib/clacky/tools/ask_user.rb', line 80
def self.feedback_tool?(name)
TOOL_NAMES.include?(name.to_s)
end
|
.fetch_key(hash, key) ⇒ Object
133
134
135
136
137
|
# File 'lib/clacky/tools/ask_user.rb', line 133
def self.fetch_key(hash, key)
return nil unless hash.is_a?(Hash)
hash[key].nil? ? hash[key.to_s] : hash[key]
end
|
.normalize_question(item) ⇒ Object
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
# File 'lib/clacky/tools/ask_user.rb', line 108
def self.normalize_question(item)
return nil unless item.is_a?(Hash)
text = fetch_key(item, :question).to_s.strip
return nil if text.empty?
options = fetch_key(item, :options)
options = [] unless options.is_a?(Array)
options = options.map { |opt| opt.to_s.strip }.reject(&:empty?)
recommended = fetch_key(item, :recommended)
recommended = recommended.to_i if recommended.is_a?(Numeric) || recommended.to_s =~ /\A-?\d+\z/
recommended = nil unless recommended.is_a?(Integer) && recommended >= 0 && recommended < options.size
{
question: text,
description: fetch_key(item, :description).to_s.strip,
options: options,
multi: truthy?(fetch_key(item, :multi)),
allow_free_text: options.empty? || truthy?(fetch_key(item, :allow_free_text)),
recommended: recommended
}
end
|
.normalize_questions(args) ⇒ Object
Coerce whatever the model produced into a stable
[{ question:, description:, options: [String], multi:, allow_free_text:, recommended: }] shape.
Never raises: malformed input degrades to a plain free-form question so the
agent surfaces something answerable instead of stalling on a schema error.
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
# File 'lib/clacky/tools/ask_user.rb', line 88
def self.normalize_questions(args)
args = {} unless args.is_a?(Hash)
raw = fetch_key(args, :questions)
raw = [] unless raw.is_a?(Array)
list = raw.map { |item| normalize_question(item) }.compact
if list.empty?
question = fetch_key(args, :question).to_s.strip
return [] if question.empty?
list = [normalize_question(
question: question,
options: fetch_key(args, :options)
)].compact
end
list
end
|
.render_text(questions, context = nil) ⇒ Object
Render questions as markdown for UIs without a native card (terminal, IM channels).
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
# File 'lib/clacky/tools/ask_user.rb', line 147
def self.render_text(questions, context = nil)
parts = []
parts << "**Context:** #{context.strip}" if context && !context.to_s.strip.empty?
multiple = questions.size > 1
questions.each_with_index do |q, q_index|
parts << "" unless parts.empty?
heading = multiple ? "**Question #{q_index + 1}:** #{q[:question]}" : "**Question:** #{q[:question]}"
parts << heading
parts << q[:description] unless q[:description].empty?
next if q[:options].empty?
parts << ""
parts << (q[:multi] ? "**Options (choose any):**" : "**Options:**")
q[:options].each_with_index do |opt, index|
marker = q[:recommended] == index ? " _(recommended)_" : ""
parts << " #{index + 1}. #{opt}#{marker}"
end
parts << " #{q[:options].size + 1}. Other — type your own answer" if q[:allow_free_text]
end
parts.join("\n")
end
|
.truthy?(value) ⇒ Boolean
139
140
141
142
143
144
|
# File 'lib/clacky/tools/ask_user.rb', line 139
def self.truthy?(value)
return true if value == true
return false if value.nil? || value == false
%w[true yes 1].include?(value.to_s.strip.downcase)
end
|
Instance Method Details
#execute(questions: nil, question: nil, context: nil, options: nil, working_dir: nil) ⇒ Object
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
# File 'lib/clacky/tools/ask_user.rb', line 172
def execute(questions: nil, question: nil, context: nil, options: nil, working_dir: nil)
normalized = self.class.normalize_questions(
questions: questions, question: question, options: options
)
if normalized.empty?
return {
success: false,
error: "ask_user requires at least one question with non-empty text."
}
end
{
success: true,
message: self.class.render_text(normalized, context),
awaiting_feedback: true
}
end
|
191
192
193
194
195
196
197
198
199
200
201
202
|
# File 'lib/clacky/tools/ask_user.rb', line 191
def format_call(args)
questions = self.class.normalize_questions(args)
return "ask_user(...)" if questions.empty?
if questions.size > 1
"ask_user(#{questions.size} questions)"
else
text = questions.first[:question]
preview = text.length > 60 ? "#{text[0..60]}..." : text
"ask_user(\"#{preview}\")"
end
end
|
204
205
206
207
208
209
210
|
# File 'lib/clacky/tools/ask_user.rb', line 204
def format_result(result)
if result.is_a?(Hash) && result[:message]
result[:message]
else
"Waiting for user feedback..."
end
end
|