openclacky 0.7.4 → 0.7.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4cbc9f7d1de1e64b78bd09ac59e9d911d101ab4e5e4bc6a2e869810311fffbc
4
- data.tar.gz: 26de964a5a19c4eacf6a2fecff08dd6fe0dcdcc5baacb435fd53ed30095860cb
3
+ metadata.gz: f623e852b5705d9339509514773528e2937fe90fe1fb8feca214a44633aa4d8d
4
+ data.tar.gz: bfff8a16fc705012a2d30a4ea8ac9a90ac6506ddbd45d9f30debe6019d04c52c
5
5
  SHA512:
6
- metadata.gz: fdefc255c000713dc69b8062ce8a1e249fbbfe219716dcae1bc16c2ad32a5672080216172f63b4648f30680f9b4295e8cd3a717d0f7d05fc17f93922cc35227e
7
- data.tar.gz: e650a5ef793d1c435ab23436f407bfff1a76def00a6047c0d6e75fbc13e41dcc5bc3b5ec538b6eef39a928d4dc5339c1c5b3f69da242b12b2fe8d4c59e51e381
6
+ metadata.gz: 06cf4fe9c03e899ad4f9ccf6a325c10c1c952179240e37d5f18bd9c40b3d0088a4928c2536c578ea4735a606e55d3b5d58ffe9c19d21830f5589e002c99b887a
7
+ data.tar.gz: aa8bf7a73ca171fc1a234bfd3fac2ed51db67c18610fa525179d656186853d00023b0835d8bc7590c772f57563957299968f540239c1d736057d43dcd9f4488c
@@ -88,9 +88,28 @@ To use this skill, simply say:
88
88
  git push origin main --tags
89
89
  ```
90
90
 
91
- 4. **Verify Publication**
91
+ 4. **Create GitHub Release**
92
+
93
+ Extract the release notes for this version from CHANGELOG.md, then create a GitHub Release:
94
+ ```bash
95
+ gh release create v{version} \
96
+ --title "v{version}" \
97
+ --notes-file /tmp/release_notes.md \
98
+ --latest
99
+ ```
100
+
101
+ Steps:
102
+ - Parse the CHANGELOG.md section for `[{version}]`
103
+ - Write it to a temp file (e.g., `/tmp/release_notes_{version}.md`) to avoid shell escaping issues
104
+ - Run `gh release create` with `--notes-file`
105
+ - Verify the release appears at: `https://github.com/clacky-ai/open-clacky/releases`
106
+
107
+ > **Prerequisite**: `gh` CLI must be installed (`brew install gh`) and authenticated (`gh auth login`)
108
+
109
+ 5. **Verify Publication**
92
110
  - Check gem appears on RubyGems.org
93
111
  - Verify version information is correct
112
+ - Confirm GitHub Release is visible at the releases page
94
113
 
95
114
  ### 6. Documentation - CHANGELOG Writing Process
96
115
 
@@ -201,6 +220,15 @@ git commit -m "chore: bump version to X.Y.Z"
201
220
  git tag vX.Y.Z
202
221
  git push origin main
203
222
  git push origin --tags
223
+
224
+ # Create GitHub Release (requires gh CLI)
225
+ # 1. Extract release notes from CHANGELOG.md for this version
226
+ # 2. Write to temp file to avoid shell escaping issues
227
+ # 3. Create the release
228
+ gh release create vX.Y.Z \
229
+ --title "vX.Y.Z" \
230
+ --notes-file /tmp/release_notes_X.Y.Z.md \
231
+ --latest
204
232
  ```
205
233
 
206
234
  ## File Locations
@@ -218,6 +246,7 @@ git push origin --tags
218
246
  - New version successfully published to RubyGems
219
247
  - Git repository updated with version tag
220
248
  - CHANGELOG.md updated with release notes
249
+ - GitHub Release created and visible at https://github.com/clacky-ai/open-clacky/releases
221
250
  - No build or deployment errors
222
251
 
223
252
  ## Error Handling
@@ -227,6 +256,8 @@ git push origin --tags
227
256
  - If gem build fails, check gemspec configuration
228
257
  - If git push fails, verify repository permissions
229
258
  - If gem push fails, check RubyGems credentials
259
+ - If `gh release create` fails, ensure `gh` CLI is installed (`brew install gh`) and authenticated (`gh auth login`)
260
+ - If GitHub Release notes look wrong, check CHANGELOG.md formatting for the version section
230
261
 
231
262
  ## Notes
232
263
 
@@ -241,6 +272,7 @@ git push origin --tags
241
272
  - Git repository access
242
273
  - RubyGems account with push permissions
243
274
  - Bundle and RSpec for testing
275
+ - `gh` CLI installed and authenticated (`brew install gh && gh auth login`)
244
276
 
245
277
  ## Version History
246
278
 
data/CHANGELOG.md CHANGED
@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.7.5] - 2026-02-28
11
+
12
+ ### Fixed
13
+ - Tool errors now display in low-key style (same as tool result) to avoid alarming users for non-critical errors the agent can retry
14
+ - Session list now shows last message instead of first message for better context
15
+ - Shell tool uses login shell (`-l`) instead of interactive shell (`-i`) for proper environment variable loading
16
+
17
+ ### Improved
18
+ - Shell tool now reliably loads user environment (PATH, rbenv, nvm, etc.) on every execution
19
+ - Session list shows resume tip (`clacky -a <session_id>`) to help users continue previous sessions
20
+
21
+ ### More
22
+ - Add GitHub Release creation step to gem-release skill
23
+ - Remove debug logging from API client
24
+
25
+ ## [0.7.4] - 2026-02-27
26
+
27
+ ### Added
28
+ - Real-time command output viewing with Ctrl+O hotkey
29
+ - GitHub skill installation support in skill-add
30
+ - Rails project creation scripts in new skill
31
+ - Auto-create ~/clacky_workspace when starting from home directory
32
+
33
+ ### Improved
34
+ - System prompt with glob tool usage guidance
35
+ - Commit skill with holistic grouping strategy and purpose-driven commits
36
+ - Theme color support for light backgrounds (bright mode refinements)
37
+ - Shell output handling and preview functionality
38
+ - Message compressor optimization (reduced to 200)
39
+
40
+ ### Fixed
41
+ - UI2 output re-rendering on modal close and height changes
42
+ - Double render issue in inline input cleanup
43
+ - Small terminal width handling for logo display
44
+ - Extra newline in question display
45
+
46
+ ### More
47
+ - Commented out idle timer debug logs for cleaner output
48
+
10
49
  ## [0.7.3] - 2026-02-26
11
50
 
12
51
  ### Fixed
data/lib/clacky/cli.rb CHANGED
@@ -273,8 +273,9 @@ module Clacky
273
273
  is_current_dir = session[:working_dir] == working_dir
274
274
 
275
275
  dir_marker = is_current_dir ? "📍" : " "
276
- say "#{dir_marker} #{index + 1}. [#{session_id}] #{created_at} (#{tasks} tasks, $#{cost.round(4)}) - #{first_msg}", :cyan
276
+ say "#{dir_marker} #{index + 1}. [#{session_id}] #{created_at} (#{tasks} tasks, $#{cost.round(4)}) - #{last_msg}", :cyan
277
277
  end
278
+ say "\n\n💡 Use `clacky -a <session_id>` to resume a session.", :yellow
278
279
  say ""
279
280
  end
280
281
 
data/lib/clacky/client.rb CHANGED
@@ -220,12 +220,6 @@ module Clacky
220
220
  end
221
221
  end
222
222
 
223
- # Debug: Save request body to see what we're actually sending
224
- if ENV['CLACKY_DEBUG_REQUEST']
225
- debug_file = "/tmp/clacky_request_#{Time.now.to_i}.json"
226
- File.write(debug_file, JSON.pretty_generate(body))
227
- end
228
-
229
223
  response = openai_connection.post("chat/completions") do |req|
230
224
  req.body = body.to_json
231
225
  end
@@ -238,26 +232,10 @@ module Clacky
238
232
  # Convert OpenAI message format to Anthropic format
239
233
  body = build_anthropic_body(messages, model, tools, max_tokens, caching_enabled)
240
234
 
241
- # DEBUG: Always save request body
242
- debug_file = "/tmp/clacky_request_body.json"
243
- File.write(debug_file, JSON.pretty_generate(body))
244
-
245
235
  response = anthropic_connection.post("v1/messages") do |req|
246
236
  req.body = body.to_json
247
237
  end
248
238
 
249
- # Debug response
250
- if ENV['CLACKY_DEBUG_REQUEST']
251
- timestamp = Time.now.to_i
252
- File.write("/tmp/clacky_debug_#{timestamp}.txt", "URL = #{@base_url}/v1/messages\n")
253
- File.write("/tmp/clacky_debug_#{timestamp}.txt", "API Key = #{@api_key[0..10]}...\n")
254
- File.write("/tmp/clacky_debug_#{timestamp}.txt", "Headers = #{anthropic_connection.headers.inspect}\n")
255
- File.open("/tmp/clacky_debug_#{timestamp}.txt", "a") do |f|
256
- f.puts "Response status = #{response.status}"
257
- f.puts "Response body = #{response.body}"
258
- end
259
- end
260
-
261
239
  handle_anthropic_response(response)
262
240
  end
263
241
 
@@ -294,18 +272,6 @@ module Clacky
294
272
  anthropic_tools.last[:cache_control] = { type: "ephemeral" }
295
273
  end
296
274
 
297
- # DEBUG: Log tools transformation
298
- debug_file = "/tmp/clacky_tools_debug.txt"
299
- File.write(debug_file, "Tools Debug:\n")
300
- File.write(debug_file, "Input tools count: #{tools&.length || 0}\n", mode: "a")
301
- File.write(debug_file, "Anthropic tools count: #{anthropic_tools&.length || 0}\n", mode: "a")
302
- if tools&.any?
303
- File.write(debug_file, "First input tool: #{JSON.pretty_generate(tools.first)}\n", mode: "a")
304
- end
305
- if anthropic_tools&.any?
306
- File.write(debug_file, "First anthropic tool: #{JSON.pretty_generate(anthropic_tools.first)}\n", mode: "a")
307
- end
308
-
309
275
  body = {
310
276
  model: model,
311
277
  max_tokens: max_tokens,
@@ -317,10 +283,6 @@ module Clacky
317
283
 
318
284
  body[:tools] = anthropic_tools if anthropic_tools&.any?
319
285
 
320
- # DEBUG: Log final body
321
- File.write(debug_file, "Body has tools: #{body.key?(:tools)}\n", mode: "a")
322
- File.write(debug_file, "Body tools count: #{body[:tools]&.length || 0}\n", mode: "a")
323
-
324
286
  body
325
287
  end
326
288
 
@@ -453,16 +415,6 @@ module Clacky
453
415
  }
454
416
  end
455
417
 
456
- # DEBUG: Log what we got
457
- debug_file = "/tmp/clacky_anthropic_response.txt"
458
- File.write(debug_file, "Response Analysis:\n")
459
- File.write(debug_file, "content_blocks count: #{content_blocks.length}\n", mode: "a")
460
- File.write(debug_file, "content_block types: #{content_blocks.map { |b| b["type"] }.inspect}\n", mode: "a")
461
- File.write(debug_file, "tool_use blocks found: #{content_blocks.select { |b| b["type"] == "tool_use" }.length}\n", mode: "a")
462
- File.write(debug_file, "extracted tool_calls: #{tool_calls.length}\n", mode: "a")
463
- File.write(debug_file, "finish_reason (raw): #{data["stop_reason"]}\n", mode: "a")
464
- File.write(debug_file, "Full content_blocks: #{JSON.pretty_generate(content_blocks)}\n", mode: "a")
465
-
466
418
  # Parse finish reason
467
419
  finish_reason = case data["stop_reason"]
468
420
  when "end_turn" then "stop"
@@ -59,9 +59,18 @@ After the script completes, use the run_project tool to start the server:
59
59
  run_project(action: "start")
60
60
  ```
61
61
 
62
- This will start the Rails development server in the background. Inform user:
63
- - Application: http://localhost:3000
64
- - Admin: http://localhost:3000/admin (admin/admin)
62
+ **Important**: If run_project executes without errors, the server has started successfully.
63
+
64
+ Then inform the user and ask what to develop next:
65
+ ```
66
+ ✨ Rails project created successfully!
67
+
68
+ The development server is now running at: http://localhost:3000
69
+
70
+ You can open your browser and visit the URL to see the application.
71
+
72
+ What would you like to develop next?
73
+ ```
65
74
 
66
75
  ## Error Handling
67
76
  - Directory not empty → Ask user confirmation, abort if declined
@@ -78,7 +78,7 @@ module Clacky
78
78
  @stderr_buffer = stderr_buffer
79
79
 
80
80
  begin
81
- Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
81
+ Open3.popen3(wrap_with_shell(command)) do |stdin, stdout, stderr, wait_thr|
82
82
  process_pid = wait_thr.pid
83
83
  start_time = Time.now
84
84
 
@@ -193,6 +193,15 @@ module Clacky
193
193
  end
194
194
  end
195
195
 
196
+ # Wrap command in an interactive shell so it loads user's rc files
197
+ # (e.g. ~/.bashrc, ~/.zshrc) and picks up PATH changes from tools
198
+ # like nvm, rbenv, brew, etc. Falls back to bash if $SHELL is unset.
199
+ def wrap_with_shell(command)
200
+ shell = ENV['SHELL'].to_s
201
+ shell = '/bin/bash' if shell.empty?
202
+ "#{shell} -l -c #{Shellwords.escape(command)}"
203
+ end
204
+
196
205
  def determine_timeouts(command, soft_timeout, hard_timeout)
197
206
  # 检查是否是慢命令
198
207
  is_slow = SLOW_COMMANDS.any? { |slow_cmd| command.include?(slow_cmd) }
@@ -59,14 +59,16 @@ module Clacky
59
59
  end
60
60
 
61
61
  # Render tool error
62
+ # Use a low-key style (same as tool_result) since most tool errors
63
+ # (e.g. tool not found, invalid args) are non-critical and the agent can retry.
62
64
  # @param data [Hash] Tool error data
63
65
  # @return [String] Rendered tool error
64
66
  def render_tool_error(data)
65
- symbol = format_symbol(:tool_error)
67
+ symbol = format_symbol(:tool_result)
66
68
  error_msg = data[:error] || "Unknown error"
67
- text = format_text("Error: #{error_msg}", :tool_error)
69
+ text = format_text(truncate(error_msg, 200), :tool_result)
68
70
 
69
- "\n#{symbol} #{text}"
71
+ "#{symbol} #{text}"
70
72
  end
71
73
 
72
74
  # Render tool denied
@@ -96,30 +96,30 @@ module Clacky
96
96
  @render_mutex.synchronize do
97
97
  # Clear entire screen
98
98
  screen.clear_screen
99
-
99
+
100
100
  # Re-render output from buffer
101
101
  render_output_from_buffer
102
-
102
+
103
103
  # Re-render fixed areas at new positions
104
104
  render_fixed_areas
105
105
  screen.flush
106
106
  end
107
107
  end
108
-
108
+
109
109
  # Render output area from buffer (clears and re-renders last N lines)
110
110
  private def render_output_from_buffer
111
111
  max_output_row = fixed_area_start_row
112
-
112
+
113
113
  # Clear output area
114
114
  (0...max_output_row).each do |row|
115
115
  screen.move_cursor(row, 0)
116
116
  screen.clear_line
117
117
  end
118
-
118
+
119
119
  # Re-render from buffer (show last N lines that fit)
120
120
  @output_row = 0
121
121
  visible_lines = [@output_buffer.size, max_output_row].min
122
-
122
+
123
123
  @output_buffer.last(visible_lines).each do |line|
124
124
  screen.move_cursor(@output_row, 0)
125
125
  print line
@@ -217,7 +217,7 @@ module Clacky
217
217
 
218
218
  # Reset output position to beginning
219
219
  @output_row = 0
220
-
220
+
221
221
  # Clear the output buffer so re-renders don't restore old content
222
222
  @output_buffer.clear
223
223
 
@@ -349,27 +349,36 @@ module Clacky
349
349
 
350
350
  # Handle window resize
351
351
  private def handle_resize
352
+ # Record old dimensions before updating to detect shrink vs grow
353
+ old_height = screen.height
354
+ old_width = screen.width
355
+
352
356
  # Update terminal dimensions and recalculate layout
353
357
  screen.update_dimensions
354
358
  calculate_layout
355
359
 
356
- # Clear entire screen
357
- screen.clear_screen
358
-
360
+ # When shrinking: full reset (clears scrollback too), otherwise just clear current screen
361
+ shrinking = screen.height < old_height || screen.width < old_width
362
+ screen.clear_screen(mode: shrinking ? :reset : :current)
363
+
359
364
  # Re-render all output from buffer
360
365
  @output_row = 0
361
366
  max_output_row = fixed_area_start_row
362
-
367
+
363
368
  # Calculate how many lines we can show from the end of buffer
364
369
  visible_lines = [@output_buffer.size, max_output_row].min
365
-
370
+
366
371
  # Render the last N lines that fit in the output area
367
372
  @output_buffer.last(visible_lines).each do |line|
368
373
  screen.move_cursor(@output_row, 0)
369
374
  print line
370
375
  @output_row += 1
371
376
  end
372
-
377
+
378
+ # Sync @last_fixed_area_height so render_fixed_areas won't think the height
379
+ # changed and trigger a second render_output_from_buffer call
380
+ @last_fixed_area_height = fixed_area_height
381
+
373
382
  # Re-render fixed areas at new positions
374
383
  render_fixed_areas
375
384
  screen.flush
@@ -381,7 +390,7 @@ module Clacky
381
390
  def write_output_line(line)
382
391
  # Add to buffer for potential re-rendering
383
392
  @output_buffer << line
384
-
393
+
385
394
  # Calculate where fixed area starts (this is where output area ends)
386
395
  max_output_row = fixed_area_start_row
387
396
 
@@ -581,21 +590,21 @@ module Clacky
581
590
  # @param hint [String] Hint message at bottom
582
591
  def enter_fullscreen(lines, hint: "Press Ctrl+O to return")
583
592
  return if @fullscreen_mode
584
-
593
+
585
594
  @fullscreen_mode = true
586
-
595
+
587
596
  # Enter alternate screen buffer
588
597
  print "\e[?1049h"
589
598
  # Clear screen and move cursor to top
590
599
  print "\e[2J\e[H"
591
600
  $stdout.flush
592
-
601
+
593
602
  # Show all lines with proper line endings (CR+LF)
594
603
  lines.each do |line|
595
604
  # Strip trailing newline and print with CR+LF
596
605
  print line.chomp + "\r\n"
597
606
  end
598
-
607
+
599
608
  # Show hint at bottom
600
609
  print "\r\n"
601
610
  print "\e[36m#{hint}\e[0m\r\n"
@@ -605,9 +614,9 @@ module Clacky
605
614
  # Exit fullscreen mode and restore previous screen
606
615
  def exit_fullscreen
607
616
  return unless @fullscreen_mode
608
-
617
+
609
618
  @fullscreen_mode = false
610
-
619
+
611
620
  # Exit alternate screen buffer (automatically restores previous screen)
612
621
  print "\e[?1049l"
613
622
  $stdout.flush
@@ -628,7 +637,7 @@ module Clacky
628
637
  # Check and process pending resize (should be called from main thread periodically)
629
638
  def process_pending_resize
630
639
  return unless @resize_pending
631
-
640
+
632
641
  @resize_pending = false
633
642
  handle_resize_safely
634
643
  end
@@ -24,9 +24,21 @@ module Clacky
24
24
  print "\e[#{row + 1};#{col + 1}H"
25
25
  end
26
26
 
27
- # Clear entire screen
28
- def clear_screen
29
- print "\e[2J"
27
+ # Clear screen with different modes:
28
+ # :preserve - clear visible screen, scrollback history preserved (default)
29
+ # :current - cursor to top-left and erase to end, no new scrollback produced
30
+ # :reset - clear visible screen AND scrollback history (full reset)
31
+ # @param mode [Symbol] Clear mode (:preserve, :current, :reset)
32
+ def clear_screen(mode: :preserve)
33
+ case mode
34
+ when :reset
35
+ print "\e[3J" # erase scrollback buffer
36
+ print "\e[H\e[J" # cursor to top-left, erase to end of screen
37
+ when :current
38
+ print "\e[H\e[J" # cursor to top-left, erase to end of screen
39
+ else # :preserve
40
+ print "\e[2J\e[H" # erase visible screen, scrollback preserved
41
+ end
30
42
  move_cursor(0, 0)
31
43
  end
32
44
 
@@ -118,7 +130,7 @@ module Clacky
118
130
  if timeout
119
131
  return nil unless IO.select([$stdin], nil, nil, timeout)
120
132
  end
121
-
133
+
122
134
  $stdin.getc
123
135
  end
124
136
 
@@ -172,11 +184,11 @@ module Clacky
172
184
  # Keep reading available characters
173
185
  loop_count = 0
174
186
  empty_checks = 0
175
-
187
+
176
188
  loop do
177
189
  # Check if there's data available immediately
178
190
  has_data = IO.select([$stdin], nil, nil, 0)
179
-
191
+
180
192
  if has_data
181
193
  next_char = $stdin.getc
182
194
  break unless next_char
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Clacky
4
- VERSION = "0.7.4"
4
+ VERSION = "0.7.5"
5
5
  end
data/scripts/install.sh CHANGED
@@ -398,14 +398,6 @@ show_post_install_info() {
398
398
  echo "║ ║"
399
399
  echo "╚═══════════════════════════════════════════════════════════╝"
400
400
  echo ""
401
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
402
- echo ""
403
- echo " ⚠️ ${YELLOW}IMPORTANT: Please restart your terminal${NC}"
404
- echo ""
405
- echo " After restarting, type: ${GREEN}openclacky${NC}"
406
- echo ""
407
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
408
- echo ""
409
401
  print_step "Quick Start Guide:"
410
402
  echo ""
411
403
  print_info "1. Configure your API key:"
@@ -420,6 +412,14 @@ show_post_install_info() {
420
412
  echo ""
421
413
  print_success "Happy coding! 🚀"
422
414
  echo ""
415
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
416
+ echo ""
417
+ echo -e " ⚠️ ${YELLOW}IMPORTANT: Please restart your terminal${NC}"
418
+ echo ""
419
+ echo -e " After restarting, type: ${GREEN}openclacky${NC}"
420
+ echo ""
421
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
422
+ echo ""
423
423
  }
424
424
 
425
425
  # Run main installation
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openclacky
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.4
4
+ version: 0.7.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - windy