openclacky 0.7.6 → 0.7.8

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: 001070cf8c2d2587620da0669c4cc014bff55930033beafd038ae86ae7374276
4
- data.tar.gz: 6c08cf048ab754c8e3be4841496b19b14cf081bdc62cadb313070226cb4e8679
3
+ metadata.gz: 9b2a72212b8b9e8072acb09a055df1afdd8225ee81de41f11dce460a68ff1781
4
+ data.tar.gz: e8048bb38dc4a8ae8c85937165e10a855f8642d6759c8a2a94aa336b084e4188
5
5
  SHA512:
6
- metadata.gz: 1bd44e7efcb16f9d9a15bb49c8d253183697107dcbb25f9ad8edc624d1a357d67c7f54f974cc06ae2140f66c0dec4bd2c2d8b50fae93ad3a8cf2453a0ee5f808
7
- data.tar.gz: 4328e4ff99db8731e5ed0a1c298c281261da097d214b600e8d6d1b544073055cc35145d76f53853ea30f6f4827f831cd3444129559ff66e10b4d5a23b30e71e0
6
+ metadata.gz: '095710e060b7c29882eb128be1a4008281435c13af07e7da01ec9ad1af27c30e252eb19e77f557882628409058d504f29024ea0dbb396ed20d326b26cfe1925c'
7
+ data.tar.gz: 2875ac5911322341be5fe396ad44a98e4cbcea51c95ed7d832e3978d5790542ca94ae0f065f6170484d9900be2f1ca190f6db0d25c14a33366f924f9d13a147d
@@ -1,7 +1,7 @@
1
1
  ---
2
+ ---
2
3
  name: commit
3
4
  description: Smart Git commit helper that analyzes changes and creates semantic commits
4
- disable-model-invocation: false
5
5
  user-invocable: true
6
6
  ---
7
7
 
@@ -472,4 +472,4 @@ This skill works best:
472
472
 
473
473
  - Created: 2025-02-01
474
474
  - Purpose: Improve commit quality and development workflow
475
- - Compatible with: Any git repository
475
+ - Compatible with: Any git repository
data/.clackyrules CHANGED
@@ -49,7 +49,11 @@ It provides chat functionality and autonomous AI agent capabilities with tool us
49
49
  - **IMPORTANT**: When testing clacky commands or debugging, always use `bundle exec ruby bin/clacky` instead of the global `clacky` command. The global command loads the system-installed gem version (e.g., `openclacky-0.7.0`), not your local development code
50
50
 
51
51
  ### Tool Development
52
- When adding new tools:
52
+ **IMPORTANT**: Do NOT add new Ruby Tool classes without careful consideration. Tools are low-level primitives (file I/O, shell, web search, etc.). Before creating a new Tool, always ask: can this capability be achieved with an existing Tool + a Skill (SKILL.md) instead? If yes, use a Skill.
53
+
54
+ Only add a new Tool when it requires capabilities that no existing tool can provide (e.g., a new API integration, a new system-level operation).
55
+
56
+ When a new Tool is truly necessary:
53
57
  1. Create class in `lib/clacky/tools/`
54
58
  2. Inherit from `Clacky::Tools::Base`
55
59
  3. Define required class attributes
@@ -57,6 +61,14 @@ When adding new tools:
57
61
  5. Add optional `format_call` and `format_result` methods
58
62
  6. Require in `lib/clacky.rb`
59
63
 
64
+ ### Skill Development
65
+ Skills are the preferred way to add new high-level capabilities. A skill is a Markdown instruction file (SKILL.md) that guides the Agent to accomplish a goal using existing Tools.
66
+
67
+ - Built-in default skills live in `lib/clacky/default_skills/<skill-name>/SKILL.md` (shipped with the gem)
68
+ - Project-level skills live in `.clacky/skills/<skill-name>/SKILL.md`
69
+ - User-level skills live in `~/.clacky/skills/<skill-name>/SKILL.md`
70
+ - **Prefer Skills over new Tools** whenever the task can be composed from existing primitives (write, shell, read, etc.)
71
+
60
72
  ### Agent Behavior
61
73
  - TODO manager is for planning only - must execute tasks after planning
62
74
  - Always use tools to create/modify files, don't just return code
data/CHANGELOG.md CHANGED
@@ -7,6 +7,54 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.7.8] - 2026-03-06
11
+
12
+ ### Added
13
+ - Skills panel in web UI: list all skills, enable/disable with toggle, view skill details
14
+ - Hash-based routing (`#session/:id`, `#tasks`, `#skills`, `#settings`) with deep-link and refresh support
15
+ - REST API endpoints for skills management (`GET /api/skills`, `PATCH /api/skills/:name/toggle`)
16
+ - `disabled?` helper on `Skill` model for quick enabled/disabled state checks
17
+
18
+ ### Improved
19
+ - Centralized `Router` object in web UI — single source of truth for all panel switching and sidebar highlight state
20
+ - Web UI frontend split further: `skills.js` extracted as standalone module
21
+ - Ctrl-C in web server now exits immediately via `StartCallback` trap override
22
+ - Skill enable/disable now writes `disable-model-invocation: false` (retains field) instead of deleting it
23
+
24
+ ### Fixed
25
+ - Sidebar highlight for Tasks and Skills stuck active after navigating away
26
+ - Router correctly restores last view on page refresh via hash URL
27
+
28
+ ### Changed
29
+ - Removed `plan_only` permission mode from agent, CLI, and web UI
30
+
31
+ ## [0.7.7] - 2026-03-04
32
+
33
+ ### Added
34
+ - Web UI server with WebSocket support for real-time agent interaction in the browser (`clacky serve`)
35
+ - Task scheduler with cron-based automation, REST API, and scheduled task execution
36
+ - Settings panel in web UI for viewing and editing AI model configurations (API keys, base URL, provider presets)
37
+ - Image upload support in web UI with attach button for multimodal prompts
38
+ - Create Task button in the task list panel for quick task creation from the web UI
39
+ - `create-task` default skill for guided automated task creation
40
+
41
+ ### Improved
42
+ - Web UI frontend split into modular files (`ws.js`, `sessions.js`, `tasks.js`, `settings.js`) for maintainability
43
+ - Web session agents now run in `auto_approve` mode for unattended execution
44
+ - Session management moved to client-side for faster, round-trip-free navigation
45
+ - User message rendering moved to the UI layer for cleaner architecture
46
+ - No-cache headers for static file serving to ensure fresh asset delivery
47
+
48
+ ### Fixed
49
+ - `DELETE`/`PUT`/`PATCH` HTTP methods now supported via custom WEBrick servlet
50
+ - Task run broadcasts correctly after WebSocket subscription; table button visibility fixed
51
+ - Mutex deadlock in scheduler `stop` method when called from a signal trap context
52
+ - `split` used instead of `shellsplit` for skill arguments to avoid parsing errors
53
+
54
+ ### More
55
+ - Add HTTP server spec and scheduler spec with full test coverage
56
+ - Minor web UI style improvements and reduced mouse dependency
57
+
10
58
  ## [0.7.6] - 2026-03-02
11
59
 
12
60
  ### Added
@@ -16,8 +16,6 @@ module Clacky
16
16
  when :confirm_safes
17
17
  # Use SafeShell integration for safety check
18
18
  is_safe_operation?(tool_name, tool_params)
19
- when :plan_only
20
- false
21
19
  else
22
20
  false
23
21
  end
@@ -217,16 +215,6 @@ module Clacky
217
215
  }
218
216
  end
219
217
 
220
- # Build planned result for plan-only mode
221
- # @param call [Hash] Tool call
222
- # @return [Hash] Formatted planned result
223
- def build_planned_result(call)
224
- {
225
- id: call[:id],
226
- content: JSON.generate({ planned: true, message: "Tool execution skipped (plan mode)" })
227
- }
228
- end
229
-
230
218
  # Check if a tool is potentially slow and should show progress
231
219
  # @param tool_name [String] Name of the tool
232
220
  # @param args [Hash] Tool arguments
data/lib/clacky/agent.rb CHANGED
@@ -389,12 +389,6 @@ module Clacky
389
389
  end
390
390
  else
391
391
  # Permission check (if not in auto-approve mode)
392
- if @config.is_plan_only?
393
- @ui&.show_info("Planned: #{call[:name]}")
394
- results << build_planned_result(call)
395
- next
396
- end
397
-
398
392
  confirmation = confirm_tool_use?(call)
399
393
  unless confirmation[:approved]
400
394
  # Show denial warning only for user-initiated denials (not system-injected preview errors)
@@ -774,7 +768,7 @@ module Clacky
774
768
 
775
769
  # Format user content with optional images
776
770
  # @param text [String] User's text input
777
- # @param images [Array<String>] Array of image file paths
771
+ # @param images [Array<String>] Array of image file paths or data: URLs
778
772
  # @return [String|Array] String if no images, Array with text and image_url objects if images present
779
773
  private def format_user_content(text, images)
780
774
  return text if images.nil? || images.empty?
@@ -782,8 +776,13 @@ module Clacky
782
776
  content = []
783
777
  content << { type: "text", text: text } unless text.nil? || text.empty?
784
778
 
785
- images.each do |image_path|
786
- image_url = Utils::FileProcessor.image_path_to_data_url(image_path)
779
+ images.each do |image|
780
+ # Accept both file paths and pre-encoded data: URLs (e.g. from Web UI)
781
+ image_url = if image.start_with?("data:")
782
+ image
783
+ else
784
+ Utils::FileProcessor.image_path_to_data_url(image)
785
+ end
787
786
  content << { type: "image_url", image_url: { url: image_url } }
788
787
  end
789
788
 
@@ -148,7 +148,7 @@ module Clacky
148
148
  # Default model for ClaudeCode environment
149
149
  CLAUDE_DEFAULT_MODEL = "claude-sonnet-4-5"
150
150
 
151
- PERMISSION_MODES = [:auto_approve, :confirm_safes, :plan_only].freeze
151
+ PERMISSION_MODES = [:auto_approve, :confirm_safes].freeze
152
152
 
153
153
  attr_accessor :permission_mode, :max_tokens, :verbose,
154
154
  :enable_compression, :enable_prompt_caching,
@@ -397,10 +397,6 @@ module Clacky
397
397
  true
398
398
  end
399
399
 
400
- def is_plan_only?
401
- @permission_mode == :plan_only
402
- end
403
-
404
400
  private def validate_permission_mode(mode)
405
401
  mode ||= :confirm_safes
406
402
  mode = mode.to_sym
data/lib/clacky/cli.rb CHANGED
@@ -27,7 +27,6 @@ module Clacky
27
27
  Permission modes:
28
28
  auto_approve - Automatically execute all tools (use with caution)
29
29
  confirm_safes - Auto-approve safe operations, confirm risky ones (default)
30
- plan_only - Generate plan without executing
31
30
 
32
31
  UI themes:
33
32
  hacker - Matrix/hacker-style with bracket symbols (default)
@@ -42,7 +41,7 @@ module Clacky
42
41
  $ clacky agent --mode=auto_approve --path /path/to/project
43
42
  LONGDESC
44
43
  option :mode, type: :string, default: "confirm_safes",
45
- desc: "Permission mode: auto_approve, confirm_safes, plan_only"
44
+ desc: "Permission mode: auto_approve, confirm_safes"
46
45
  option :theme, type: :string, default: "hacker",
47
46
  desc: "UI theme: hacker, minimal (default: hacker)"
48
47
  option :verbose, type: :boolean, aliases: "-v", default: false, desc: "Show detailed output"
@@ -690,5 +689,41 @@ module Clacky
690
689
 
691
690
 
692
691
  end
692
+
693
+ # ── server command ─────────────────────────────────────────────────────────
694
+ desc "server", "Start the Clacky web UI server"
695
+ long_desc <<-LONGDESC
696
+ Start a long-running HTTP + WebSocket server that serves the Clacky web UI.
697
+
698
+ Open http://localhost:7070 in your browser to access the multi-session interface.
699
+ Multiple sessions (e.g. "coding", "copywriting") can run simultaneously.
700
+
701
+ Examples:
702
+ $ clacky server
703
+ $ clacky server --port 8080
704
+ LONGDESC
705
+ option :host, type: :string, default: "127.0.0.1", desc: "Bind host (default: 127.0.0.1)"
706
+ option :port, type: :numeric, default: 7070, desc: "Listen port (default: 7070)"
707
+ def server
708
+ require_relative "server/http_server"
709
+
710
+ agent_config = Clacky::AgentConfig.load
711
+
712
+ # Factory so each new session gets a fresh Client instance
713
+ client_factory = lambda do
714
+ Clacky::Client.new(
715
+ agent_config.api_key,
716
+ base_url: agent_config.base_url,
717
+ anthropic_format: agent_config.anthropic_format?
718
+ )
719
+ end
720
+
721
+ Clacky::Server::HttpServer.new(
722
+ host: options[:host],
723
+ port: options[:port],
724
+ agent_config: agent_config,
725
+ client_factory: client_factory
726
+ ).start
727
+ end
693
728
  end
694
729
  end
@@ -0,0 +1,102 @@
1
+ ---
2
+ name: create-task
3
+ description: Create a scheduled automated task. User describes what they want to automate, agent creates the task prompt file and optional cron schedule.
4
+ disable-model-invocation: false
5
+ user-invocable: true
6
+ ---
7
+
8
+ # Create Task Skill
9
+
10
+ ## When to Use
11
+ Invoke this skill when a user wants to:
12
+ - Automate something on a schedule (e.g. "send a daily report", "remind me every Monday", "check prices every hour")
13
+ - Create a task that runs automatically ("set up a scheduled task for me")
14
+ - Use the command `/create-task`
15
+
16
+ ## Task & Schedule Storage Format
17
+
18
+ ### Task file: `~/.clacky/tasks/<name>.md`
19
+ Plain text prompt file. When the task fires, the Agent reads this file as its input prompt and executes it autonomously.
20
+
21
+ Example content of `~/.clacky/tasks/daily_report.md`:
22
+ ```
23
+ Check today's GitHub PR list, summarize the status of each PR, and save a daily report to ~/reports/daily_<date>.md
24
+ ```
25
+
26
+ ### Schedule file: `~/.clacky/schedules.yml`
27
+ YAML list. Each entry has:
28
+ ```yaml
29
+ - name: daily_report # unique name, matches task name
30
+ task: daily_report # references ~/.clacky/tasks/daily_report.md
31
+ cron: "0 9 * * 1-5" # 5-field cron: minute hour day month weekday
32
+ enabled: true
33
+ ```
34
+
35
+ Cron field order: `minute hour day-of-month month day-of-week`
36
+ - `0 9 * * 1-5` → 09:00 every weekday
37
+ - `0 9 * * *` → 09:00 every day
38
+ - `0 */2 * * *` → every 2 hours
39
+ - `*/30 * * * *` → every 30 minutes
40
+
41
+ ## Process
42
+
43
+ ### Step 1: Understand the user's intent
44
+ Ask clarifying questions if needed:
45
+ - What should the task DO? (what action, what goal)
46
+ - How often / when should it run? (if scheduling is needed)
47
+ - Any specific working directory or context?
48
+
49
+ ### Step 2: Generate a task name
50
+ - Lowercase, alphanumeric + underscores only
51
+ - Short and descriptive, e.g. `daily_standup`, `price_monitor`, `weekly_report`
52
+
53
+ ### Step 3: Write the task prompt file
54
+ Use the `write` tool to create `~/.clacky/tasks/<name>.md`.
55
+
56
+ The prompt content should be:
57
+ - Clear and self-contained (the agent running it has no prior context)
58
+ - Written as a direct instruction to an AI agent
59
+ - Include any relevant details the user provided (URLs, file paths, output format, etc.)
60
+
61
+ Example:
62
+ ```
63
+ write(
64
+ path: "~/.clacky/tasks/daily_standup.md",
65
+ content: "Check today's work progress:\n1. Review recent git commits\n2. List open TODOs\n3. Generate a standup summary and print it to the terminal"
66
+ )
67
+ ```
68
+
69
+ ### Step 4: Write the schedule (if user wants it automated)
70
+ Read the existing `~/.clacky/schedules.yml` first (if it exists), then append the new entry and write the whole file back.
71
+
72
+ Use the `write` tool. Example full file:
73
+ ```yaml
74
+ - name: daily_standup
75
+ task: daily_standup
76
+ cron: "0 9 * * 1-5"
77
+ enabled: true
78
+ ```
79
+
80
+ If `schedules.yml` already has entries, preserve them and append the new one.
81
+
82
+ ### Step 5: Confirm to the user
83
+ Reply with a clear summary:
84
+ ```
85
+ ✅ Task created successfully!
86
+
87
+ 📋 Name: daily_standup
88
+ 📄 File: ~/.clacky/tasks/daily_standup.md
89
+ ⏰ Schedule: Every weekday at 09:00 (cron: 0 9 * * 1-5)
90
+
91
+ Task prompt:
92
+ > Check today's work progress...
93
+
94
+ The task will run automatically at the next scheduled time.
95
+ You can also click ▶ in the sidebar to run it immediately.
96
+ ```
97
+
98
+ ## Notes
99
+ - If the user only wants a task without a schedule (run manually), skip Step 4
100
+ - Always expand `~` to the actual home path when writing files
101
+ - Task name must be sanitized: only `[a-z0-9_-]`, no spaces
102
+ - The cron scheduler checks every minute; `clacky server` must be running for auto-execution
@@ -28,10 +28,6 @@ module Clacky
28
28
 
29
29
  # === Output display ===
30
30
 
31
- def show_user_message(content, images: [])
32
- emit("user_message", content: content, images: images)
33
- end
34
-
35
31
  def show_assistant_message(content)
36
32
  return if content.nil? || content.strip.empty?
37
33
 
@@ -117,7 +113,7 @@ module Clacky
117
113
 
118
114
  # === Progress ===
119
115
 
120
- def show_progress(message = nil, prefix_newline: true)
116
+ def show_progress(message = nil, prefix_newline: true, output_buffer: nil)
121
117
  @progress_start_time = Time.now
122
118
  emit("progress", message: message, status: "start")
123
119
  end
@@ -107,7 +107,7 @@ module Clacky
107
107
 
108
108
  # === Progress (no-ops — no spinner in plain mode) ===
109
109
 
110
- def show_progress(message = nil, prefix_newline: true); end
110
+ def show_progress(message = nil, prefix_newline: true, output_buffer: nil); end
111
111
  def clear_progress; end
112
112
 
113
113
  # === State updates (no-ops) ===