openclacky 0.7.6 → 0.7.7

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: 5c3815310a11bea9ccad7ab6891b4ad85e248635af6919606e8963096699a1d7
4
+ data.tar.gz: 17f8059179701394a6951966ec0284ee6321750e2651c4de0ecbf37a4542c7fc
5
5
  SHA512:
6
- metadata.gz: 1bd44e7efcb16f9d9a15bb49c8d253183697107dcbb25f9ad8edc624d1a357d67c7f54f974cc06ae2140f66c0dec4bd2c2d8b50fae93ad3a8cf2453a0ee5f808
7
- data.tar.gz: 4328e4ff99db8731e5ed0a1c298c281261da097d214b600e8d6d1b544073055cc35145d76f53853ea30f6f4827f831cd3444129559ff66e10b4d5a23b30e71e0
6
+ metadata.gz: b7b32ce62e12f9aa12db59b1f6640e4a6234c34c485d9b679df80654dc8722a369687e1fc0b5165a78a12f6210fcd02959133d6d3753c5c22873d071251b07fb
7
+ data.tar.gz: 20f4498c976579333b7e261490d287a33cec89b888c5ae1a640205c25f48c70c3863a551e1bef6173804b7670970ead88ed9a67702987da97963c0a981cd1e2a
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,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.7.7] - 2026-03-04
11
+
12
+ ### Added
13
+ - Web UI server with WebSocket support for real-time agent interaction in the browser (`clacky serve`)
14
+ - Task scheduler with cron-based automation, REST API, and scheduled task execution
15
+ - Settings panel in web UI for viewing and editing AI model configurations (API keys, base URL, provider presets)
16
+ - Image upload support in web UI with attach button for multimodal prompts
17
+ - Create Task button in the task list panel for quick task creation from the web UI
18
+ - `create-task` default skill for guided automated task creation
19
+
20
+ ### Improved
21
+ - Web UI frontend split into modular files (`ws.js`, `sessions.js`, `tasks.js`, `settings.js`) for maintainability
22
+ - Web session agents now run in `auto_approve` mode for unattended execution
23
+ - Session management moved to client-side for faster, round-trip-free navigation
24
+ - User message rendering moved to the UI layer for cleaner architecture
25
+ - No-cache headers for static file serving to ensure fresh asset delivery
26
+
27
+ ### Fixed
28
+ - `DELETE`/`PUT`/`PATCH` HTTP methods now supported via custom WEBrick servlet
29
+ - Task run broadcasts correctly after WebSocket subscription; table button visibility fixed
30
+ - Mutex deadlock in scheduler `stop` method when called from a signal trap context
31
+ - `split` used instead of `shellsplit` for skill arguments to avoid parsing errors
32
+
33
+ ### More
34
+ - Add HTTP server spec and scheduler spec with full test coverage
35
+ - Minor web UI style improvements and reduced mouse dependency
36
+
10
37
  ## [0.7.6] - 2026-03-02
11
38
 
12
39
  ### Added
data/lib/clacky/agent.rb CHANGED
@@ -774,7 +774,7 @@ module Clacky
774
774
 
775
775
  # Format user content with optional images
776
776
  # @param text [String] User's text input
777
- # @param images [Array<String>] Array of image file paths
777
+ # @param images [Array<String>] Array of image file paths or data: URLs
778
778
  # @return [String|Array] String if no images, Array with text and image_url objects if images present
779
779
  private def format_user_content(text, images)
780
780
  return text if images.nil? || images.empty?
@@ -782,8 +782,13 @@ module Clacky
782
782
  content = []
783
783
  content << { type: "text", text: text } unless text.nil? || text.empty?
784
784
 
785
- images.each do |image_path|
786
- image_url = Utils::FileProcessor.image_path_to_data_url(image_path)
785
+ images.each do |image|
786
+ # Accept both file paths and pre-encoded data: URLs (e.g. from Web UI)
787
+ image_url = if image.start_with?("data:")
788
+ image
789
+ else
790
+ Utils::FileProcessor.image_path_to_data_url(image)
791
+ end
787
792
  content << { type: "image_url", image_url: { url: image_url } }
788
793
  end
789
794
 
data/lib/clacky/cli.rb CHANGED
@@ -690,5 +690,41 @@ module Clacky
690
690
 
691
691
 
692
692
  end
693
+
694
+ # ── server command ─────────────────────────────────────────────────────────
695
+ desc "server", "Start the Clacky web UI server"
696
+ long_desc <<-LONGDESC
697
+ Start a long-running HTTP + WebSocket server that serves the Clacky web UI.
698
+
699
+ Open http://localhost:7070 in your browser to access the multi-session interface.
700
+ Multiple sessions (e.g. "coding", "copywriting") can run simultaneously.
701
+
702
+ Examples:
703
+ $ clacky server
704
+ $ clacky server --port 8080
705
+ LONGDESC
706
+ option :host, type: :string, default: "127.0.0.1", desc: "Bind host (default: 127.0.0.1)"
707
+ option :port, type: :numeric, default: 7070, desc: "Listen port (default: 7070)"
708
+ def server
709
+ require_relative "server/http_server"
710
+
711
+ agent_config = Clacky::AgentConfig.load
712
+
713
+ # Factory so each new session gets a fresh Client instance
714
+ client_factory = lambda do
715
+ Clacky::Client.new(
716
+ agent_config.api_key,
717
+ base_url: agent_config.base_url,
718
+ anthropic_format: agent_config.anthropic_format?
719
+ )
720
+ end
721
+
722
+ Clacky::Server::HttpServer.new(
723
+ host: options[:host],
724
+ port: options[:port],
725
+ agent_config: agent_config,
726
+ client_factory: client_factory
727
+ ).start
728
+ end
693
729
  end
694
730
  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) ===