pocketrb 0.1.0 → 0.3.0

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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -0
  3. data/README.md +98 -17
  4. data/lib/pocketrb/agent/compaction.rb +235 -22
  5. data/lib/pocketrb/agent/context.rb +26 -3
  6. data/lib/pocketrb/agent/loop.rb +53 -5
  7. data/lib/pocketrb/agent/spawn_tool.rb +14 -0
  8. data/lib/pocketrb/agent/subagent_manager.rb +24 -2
  9. data/lib/pocketrb/bus/events.rb +42 -1
  10. data/lib/pocketrb/bus/message_bus.rb +37 -0
  11. data/lib/pocketrb/channels/base.rb +13 -0
  12. data/lib/pocketrb/channels/cli.rb +16 -1
  13. data/lib/pocketrb/channels/telegram.rb +70 -4
  14. data/lib/pocketrb/channels/whatsapp.rb +49 -7
  15. data/lib/pocketrb/cli/chat.rb +7 -0
  16. data/lib/pocketrb/cli/config.rb +4 -0
  17. data/lib/pocketrb/cli/cron.rb +6 -0
  18. data/lib/pocketrb/cli.rb +0 -1
  19. data/lib/pocketrb/config.rb +55 -6
  20. data/lib/pocketrb/cron/job.rb +41 -0
  21. data/lib/pocketrb/cron/service.rb +44 -15
  22. data/lib/pocketrb/heartbeat/service.rb +21 -0
  23. data/lib/pocketrb/mcp/client.rb +41 -3
  24. data/lib/pocketrb/mcp/memory_tool.rb +33 -0
  25. data/lib/pocketrb/media/processor.rb +32 -1
  26. data/lib/pocketrb/memory.rb +12 -3
  27. data/lib/pocketrb/planning/manager.rb +29 -1
  28. data/lib/pocketrb/planning/plan.rb +52 -0
  29. data/lib/pocketrb/planning/tool.rb +45 -0
  30. data/lib/pocketrb/providers/anthropic.rb +113 -9
  31. data/lib/pocketrb/providers/base.rb +39 -4
  32. data/lib/pocketrb/providers/claude_cli.rb +79 -7
  33. data/lib/pocketrb/providers/claude_max_proxy.rb +72 -6
  34. data/lib/pocketrb/providers/openai_compatible.rb +79 -0
  35. data/lib/pocketrb/providers/openrouter.rb +72 -12
  36. data/lib/pocketrb/providers/registry.rb +24 -0
  37. data/lib/pocketrb/providers/ruby_llm_provider.rb +168 -54
  38. data/lib/pocketrb/providers/types.rb +51 -1
  39. data/lib/pocketrb/session/manager.rb +34 -2
  40. data/lib/pocketrb/session/session.rb +53 -11
  41. data/lib/pocketrb/skills/builtin/tmux/SKILL.md +9 -9
  42. data/lib/pocketrb/skills/create_tool.rb +28 -0
  43. data/lib/pocketrb/skills/loader.rb +22 -1
  44. data/lib/pocketrb/skills/modify_tool.rb +22 -0
  45. data/lib/pocketrb/skills/skill.rb +19 -3
  46. data/lib/pocketrb/tools/background_job_manager.rb +29 -0
  47. data/lib/pocketrb/tools/base.rb +16 -1
  48. data/lib/pocketrb/tools/browser.rb +40 -0
  49. data/lib/pocketrb/tools/browser_advanced.rb +94 -4
  50. data/lib/pocketrb/tools/browser_session.rb +18 -0
  51. data/lib/pocketrb/tools/cron.rb +46 -0
  52. data/lib/pocketrb/tools/edit_file.rb +16 -0
  53. data/lib/pocketrb/tools/exec.rb +46 -0
  54. data/lib/pocketrb/tools/jobs.rb +26 -2
  55. data/lib/pocketrb/tools/list_dir.rb +19 -0
  56. data/lib/pocketrb/tools/memory.rb +34 -0
  57. data/lib/pocketrb/tools/message.rb +15 -0
  58. data/lib/pocketrb/tools/para_memory.rb +47 -0
  59. data/lib/pocketrb/tools/read_file.rb +11 -0
  60. data/lib/pocketrb/tools/registry.rb +12 -4
  61. data/lib/pocketrb/tools/send_file.rb +29 -0
  62. data/lib/pocketrb/tools/think.rb +9 -0
  63. data/lib/pocketrb/tools/web_fetch.rb +58 -3
  64. data/lib/pocketrb/tools/web_search.rb +25 -0
  65. data/lib/pocketrb/tools/write_file.rb +10 -0
  66. data/lib/pocketrb/version.rb +2 -1
  67. data/lib/pocketrb.rb +4 -1
  68. data/pocketrb.gemspec +6 -8
  69. metadata +24 -44
@@ -6,14 +6,20 @@ module Pocketrb
6
6
  module Tools
7
7
  # Browser automation tool using Playwright
8
8
  class Browser < Base
9
+ # Tool name
10
+ # @return [String]
9
11
  def name
10
12
  "browser"
11
13
  end
12
14
 
15
+ # Tool description
16
+ # @return [String]
13
17
  def description
14
18
  "Automate browser interactions using headless Chromium. Can navigate to URLs, extract text content, take screenshots, interact with elements, and execute JavaScript. Use this for web scraping, research, testing, or any task requiring web browsing."
15
19
  end
16
20
 
21
+ # Parameter schema
22
+ # @return [Hash]
17
23
  def parameters
18
24
  {
19
25
  type: "object",
@@ -52,6 +58,16 @@ module Pocketrb
52
58
  }
53
59
  end
54
60
 
61
+ # Execute browser automation action
62
+ # @param action [String] Action to perform (navigate, extract_text, screenshot, click, type, execute_js)
63
+ # @param url [String, nil] URL to navigate to
64
+ # @param selector [String, nil] CSS selector for element targeting
65
+ # @param text [String, nil] Text to type into an element
66
+ # @param javascript [String, nil] JavaScript code to execute in browser context
67
+ # @param screenshot_path [String, nil] File path for saving screenshot
68
+ # @param wait_time [Integer] Milliseconds to wait after navigation
69
+ # @option _kwargs [Object] * Additional action-specific options
70
+ # @return [String] Action result
55
71
  def execute(action:, url: nil, selector: nil, text: nil, javascript: nil, screenshot_path: nil, wait_time: 1000,
56
72
  **_kwargs)
57
73
  case action
@@ -86,6 +102,9 @@ module Pocketrb
86
102
 
87
103
  private
88
104
 
105
+ # Launch headless browser and yield the page
106
+ # @yield [Playwright::Page] browser page instance
107
+ # @return [Object] result of the block
89
108
  def with_browser
90
109
  Playwright.create(playwright_cli_executable_path: "npx playwright") do |playwright|
91
110
  playwright.chromium.launch(headless: true) do |browser|
@@ -98,6 +117,10 @@ module Pocketrb
98
117
  @current_page = nil
99
118
  end
100
119
 
120
+ # Navigate to URL and extract page content
121
+ # @param url [String] URL to navigate to
122
+ # @param wait_time [Integer] milliseconds to wait after navigation
123
+ # @return [String] page title and text content
101
124
  def navigate_and_extract(url, wait_time)
102
125
  with_browser do |page|
103
126
  page.goto(url)
@@ -110,6 +133,8 @@ module Pocketrb
110
133
  end
111
134
  end
112
135
 
136
+ # Extract text content from the currently loaded page
137
+ # @return [String] page text content or error if no page is loaded
113
138
  def extract_text_from_page
114
139
  return error("No page is currently loaded. Use navigate action first.") unless @current_page
115
140
 
@@ -117,6 +142,9 @@ module Pocketrb
117
142
  success(content[0..5000] + (content.length > 5000 ? "...(truncated)" : ""))
118
143
  end
119
144
 
145
+ # Take a screenshot and save to the given path
146
+ # @param path [String] file path to save the screenshot
147
+ # @return [String] confirmation message
120
148
  def take_screenshot(path)
121
149
  with_browser do |page|
122
150
  screenshot_path = resolve_path(path)
@@ -125,6 +153,10 @@ module Pocketrb
125
153
  end
126
154
  end
127
155
 
156
+ # Click an element matching the selector
157
+ # @param selector [String] CSS selector for the element
158
+ # @param wait_time [Integer] milliseconds to wait after clicking
159
+ # @return [String] confirmation message
128
160
  def click_element(selector, wait_time)
129
161
  with_browser do |page|
130
162
  page.click(selector)
@@ -133,6 +165,11 @@ module Pocketrb
133
165
  end
134
166
  end
135
167
 
168
+ # Type text into an element matching the selector
169
+ # @param selector [String] CSS selector for the input element
170
+ # @param text [String] content to fill into the input element
171
+ # @param wait_time [Integer] milliseconds to wait after typing
172
+ # @return [String] confirmation message
136
173
  def type_into_element(selector, text, wait_time)
137
174
  with_browser do |page|
138
175
  page.fill(selector, text)
@@ -141,6 +178,9 @@ module Pocketrb
141
178
  end
142
179
  end
143
180
 
181
+ # Execute JavaScript code in the browser page context
182
+ # @param javascript [String] JavaScript code to evaluate
183
+ # @return [String] execution result
144
184
  def execute_javascript(javascript)
145
185
  with_browser do |page|
146
186
  result = page.evaluate(javascript)
@@ -6,6 +6,7 @@ module Pocketrb
6
6
  module Tools
7
7
  # Enhanced browser tool with sessions and tabs
8
8
  class BrowserAdvanced < Base
9
+ # Available browser actions
9
10
  ACTIONS = %w[
10
11
  new_tab close_tab focus_tab list_tabs
11
12
  navigate back forward refresh
@@ -17,10 +18,14 @@ module Pocketrb
17
18
  press_key
18
19
  ].freeze
19
20
 
21
+ # Tool name
22
+ # @return [String] Tool identifier
20
23
  def name
21
24
  "browser"
22
25
  end
23
26
 
27
+ # Tool description
28
+ # @return [String] Human-readable description
24
29
  def description
25
30
  <<~DESC.strip
26
31
  Browse the web with a persistent browser session. Supports multiple tabs,
@@ -29,6 +34,8 @@ module Pocketrb
29
34
  DESC
30
35
  end
31
36
 
37
+ # Tool parameters schema
38
+ # @return [Hash] JSON schema for tool parameters
32
39
  def parameters
33
40
  {
34
41
  type: "object",
@@ -88,11 +95,23 @@ module Pocketrb
88
95
  }
89
96
  end
90
97
 
98
+ # Check if Playwright is available
99
+ # @return [Boolean] True if npx or playwright command is found
91
100
  def available?
92
101
  # Check if playwright is available
93
102
  system("which npx > /dev/null 2>&1") || system("which playwright > /dev/null 2>&1")
94
103
  end
95
104
 
105
+ # Execute browser operation
106
+ # @param action [String] Action to perform (new_tab, close_tab, focus_tab, list_tabs, navigate, etc.)
107
+ # @option args [String] :url URL for navigation or new tab
108
+ # @option args [String] :tab_id Tab identifier
109
+ # @option args [String] :selector CSS/XPath selector
110
+ # @option args [String] :text Text to type
111
+ # @option args [String] :key Key to press
112
+ # @option args [String] :javascript JavaScript code to execute
113
+ # @option args [Hash] :options Additional action-specific options
114
+ # @return [String] JSON result of operation
96
115
  def execute(action:, **args)
97
116
  case action
98
117
  # Tab management
@@ -160,10 +179,15 @@ module Pocketrb
160
179
 
161
180
  private
162
181
 
182
+ # Access the browser session singleton
183
+ # @return [BrowserSession]
163
184
  def session
164
185
  BrowserSession.instance
165
186
  end
166
187
 
188
+ # Get the Playwright page for a tab
189
+ # @param tab_id [String, nil] tab identifier, uses active tab if nil
190
+ # @return [Playwright::Page, nil]
167
191
  def page(tab_id = nil)
168
192
  if tab_id
169
193
  session.tabs[tab_id]&.dig(:page)
@@ -172,6 +196,8 @@ module Pocketrb
172
196
  end
173
197
  end
174
198
 
199
+ # Get active page, creating a new tab if none exists
200
+ # @return [Playwright::Page]
175
201
  def ensure_page!
176
202
  p = page
177
203
  return p if p
@@ -181,8 +207,9 @@ module Pocketrb
181
207
  session.active_page
182
208
  end
183
209
 
184
- # === Tab Management ===
185
-
210
+ # Open a new browser tab, optionally navigating to a URL
211
+ # @param url [String, nil] URL to load in the new tab
212
+ # @return [String] confirmation with tab ID
186
213
  def new_tab(url)
187
214
  tab_id = session.new_tab(url: url)
188
215
  info = session.tab_info(tab_id)
@@ -194,6 +221,9 @@ module Pocketrb
194
221
  end
195
222
  end
196
223
 
224
+ # Close a browser tab
225
+ # @param tab_id [String, nil] tab identifier, uses active tab if nil
226
+ # @return [String] confirmation or error message
197
227
  def close_tab(tab_id)
198
228
  tab_id ||= session.active_tab_id
199
229
  return error("No tab to close") unless tab_id
@@ -206,6 +236,9 @@ module Pocketrb
206
236
  end
207
237
  end
208
238
 
239
+ # Switch focus to a specific tab
240
+ # @param tab_id [String] tab identifier to focus
241
+ # @return [String] confirmation or error message
209
242
  def focus_tab(tab_id)
210
243
  return error("Tab ID required") unless tab_id
211
244
 
@@ -217,6 +250,8 @@ module Pocketrb
217
250
  end
218
251
  end
219
252
 
253
+ # List all open browser tabs
254
+ # @return [String] formatted list of open tabs
220
255
  def list_tabs
221
256
  tabs = session.list_tabs
222
257
  return "No tabs open. Use action 'new_tab' to open one." if tabs.empty?
@@ -232,6 +267,10 @@ module Pocketrb
232
267
 
233
268
  # === Navigation ===
234
269
 
270
+ # Navigate to URL
271
+ # @param url [String] URL to navigate to
272
+ # @option options [String] :wait_until Wait condition (load, domcontentloaded, networkidle)
273
+ # @return [String] Success message
235
274
  def navigate(url, options)
236
275
  return error("URL required") unless url
237
276
 
@@ -245,6 +284,8 @@ module Pocketrb
245
284
  success("Navigated to: #{title}\nURL: #{p.url}")
246
285
  end
247
286
 
287
+ # Navigate back in browser history
288
+ # @return [String] confirmation message
248
289
  def go_back
249
290
  p = ensure_page!
250
291
  p.go_back
@@ -252,6 +293,8 @@ module Pocketrb
252
293
  success("Navigated back to: #{p.title}")
253
294
  end
254
295
 
296
+ # Navigate forward in browser history
297
+ # @return [String] confirmation message
255
298
  def go_forward
256
299
  p = ensure_page!
257
300
  p.go_forward
@@ -259,6 +302,8 @@ module Pocketrb
259
302
  success("Navigated forward to: #{p.title}")
260
303
  end
261
304
 
305
+ # Reload the current page
306
+ # @return [String] confirmation message
262
307
  def refresh_page
263
308
  p = ensure_page!
264
309
  p.reload
@@ -267,6 +312,10 @@ module Pocketrb
267
312
 
268
313
  # === Interaction ===
269
314
 
315
+ # Click an element
316
+ # @param selector [String] CSS or XPath selector
317
+ # @option options [Integer] :timeout Timeout in milliseconds
318
+ # @return [String] Success message
270
319
  def click_element(selector, options)
271
320
  return error("Selector required") unless selector
272
321
 
@@ -277,6 +326,11 @@ module Pocketrb
277
326
  success("Clicked: #{selector}")
278
327
  end
279
328
 
329
+ # Type text into an element
330
+ # @param selector [String] CSS or XPath selector
331
+ # @param text [String] content to fill into the input element
332
+ # @param _options [Hash] additional options (unused)
333
+ # @return [String] confirmation or error message
280
334
  def type_text(selector, text, _options)
281
335
  return error("Selector and text required") unless selector && text
282
336
 
@@ -286,6 +340,9 @@ module Pocketrb
286
340
  success("Typed into #{selector}: #{text[0..50]}#{"..." if text.length > 50}")
287
341
  end
288
342
 
343
+ # Hover over an element
344
+ # @param selector [String] CSS or XPath selector
345
+ # @return [String] confirmation or error message
289
346
  def hover_element(selector)
290
347
  return error("Selector required") unless selector
291
348
 
@@ -294,6 +351,10 @@ module Pocketrb
294
351
  success("Hovering over: #{selector}")
295
352
  end
296
353
 
354
+ # Scroll the page to a position or element
355
+ # @param direction [String, nil] scroll target: top, bottom, or element
356
+ # @param selector [String, nil] CSS selector when scrolling to element
357
+ # @return [String] confirmation or error message
297
358
  def scroll_page(direction, selector)
298
359
  p = ensure_page!
299
360
 
@@ -315,6 +376,10 @@ module Pocketrb
315
376
  end
316
377
  end
317
378
 
379
+ # Press a keyboard key, optionally targeting an element
380
+ # @param key [String] key to press (e.g. Enter, Tab, Escape)
381
+ # @param selector [String, nil] CSS selector to target, uses keyboard if nil
382
+ # @return [String] confirmation or error message
318
383
  def press_key(key, selector)
319
384
  return error("Key required") unless key
320
385
 
@@ -329,6 +394,9 @@ module Pocketrb
329
394
  success("Pressed key: #{key}")
330
395
  end
331
396
 
397
+ # Fill multiple form fields at once
398
+ # @param form_data [Hash] mapping of CSS selectors to values
399
+ # @return [String] confirmation or error message
332
400
  def fill_form(form_data)
333
401
  return error("Form data required") unless form_data.is_a?(Hash)
334
402
 
@@ -343,6 +411,10 @@ module Pocketrb
343
411
  success("Filled #{filled.size} form fields: #{filled.join(", ")}")
344
412
  end
345
413
 
414
+ # Select an option from a dropdown element
415
+ # @param selector [String] CSS selector for the select element
416
+ # @param value [String] option value to select
417
+ # @return [String] confirmation or error message
346
418
  def select_option(selector, value)
347
419
  return error("Selector and value required") unless selector && value
348
420
 
@@ -351,8 +423,9 @@ module Pocketrb
351
423
  success("Selected '#{value}' in #{selector}")
352
424
  end
353
425
 
354
- # === Content ===
355
-
426
+ # Take a screenshot of the current page
427
+ # @param full_page [Boolean, nil] whether to capture the full scrollable page
428
+ # @return [String] path to saved screenshot
356
429
  def take_screenshot(full_page)
357
430
  p = ensure_page!
358
431
 
@@ -364,6 +437,8 @@ module Pocketrb
364
437
  success("Screenshot saved: #{path}")
365
438
  end
366
439
 
440
+ # Get a text snapshot of the page including content and interactive elements
441
+ # @return [String] formatted page snapshot
367
442
  def get_snapshot
368
443
  p = ensure_page!
369
444
 
@@ -428,6 +503,9 @@ module Pocketrb
428
503
  SNAPSHOT
429
504
  end
430
505
 
506
+ # Get text content from an element or the entire page
507
+ # @param selector [String, nil] CSS selector, defaults to body if nil
508
+ # @return [String] element text content
431
509
  def get_text(selector)
432
510
  p = ensure_page!
433
511
 
@@ -437,6 +515,10 @@ module Pocketrb
437
515
  success(text)
438
516
  end
439
517
 
518
+ # Get an HTML attribute value from an element
519
+ # @param selector [String] CSS selector for the element
520
+ # @param attribute [String] attribute name to retrieve
521
+ # @return [String] attribute value or error message
440
522
  def get_attribute(selector, attribute)
441
523
  return error("Selector and attribute required") unless selector && attribute
442
524
 
@@ -445,6 +527,9 @@ module Pocketrb
445
527
  success("#{selector}[#{attribute}] = #{value}")
446
528
  end
447
529
 
530
+ # Execute JavaScript code in the browser context
531
+ # @param code [String] JavaScript code to evaluate
532
+ # @return [String] execution result or error message
448
533
  def execute_javascript(code)
449
534
  return error("JavaScript code required") unless code
450
535
 
@@ -455,6 +540,11 @@ module Pocketrb
455
540
 
456
541
  # === Wait ===
457
542
 
543
+ # Wait for selector to appear
544
+ # @param selector [String] CSS or XPath selector to wait for
545
+ # @option options [Integer] :timeout Timeout in milliseconds
546
+ # @option options [String] :state Element state to wait for (visible, hidden, attached, detached)
547
+ # @return [String] Success message
458
548
  def wait_for(selector, options)
459
549
  return error("Selector required") unless selector
460
550
 
@@ -21,6 +21,8 @@ module Pocketrb
21
21
  @started = false
22
22
  end
23
23
 
24
+ # Start the browser session
25
+ # @return [void]
24
26
  def start!
25
27
  return if @started
26
28
 
@@ -42,6 +44,8 @@ module Pocketrb
42
44
  end
43
45
  end
44
46
 
47
+ # Stop the browser session and cleanup
48
+ # @return [void]
45
49
  def stop!
46
50
  @mutex.synchronize do
47
51
  @tabs.each_value do |tab|
@@ -62,11 +66,15 @@ module Pocketrb
62
66
  end
63
67
  end
64
68
 
69
+ # Check if browser session is running
70
+ # @return [Boolean] True if browser is started
65
71
  def started?
66
72
  @started
67
73
  end
68
74
 
69
75
  # Create a new tab
76
+ # @param url [String, nil] Initial URL to navigate to
77
+ # @return [String] Tab identifier
70
78
  def new_tab(url: nil)
71
79
  start! unless started?
72
80
 
@@ -90,6 +98,8 @@ module Pocketrb
90
98
  end
91
99
 
92
100
  # Close a tab
101
+ # @param tab_id [String] Tab identifier to close
102
+ # @return [Boolean] True if tab was closed successfully
93
103
  def close_tab(tab_id)
94
104
  @mutex.synchronize do
95
105
  tab = @tabs.delete(tab_id)
@@ -102,6 +112,8 @@ module Pocketrb
102
112
  end
103
113
 
104
114
  # Focus a tab
115
+ # @param tab_id [String] Tab identifier to focus
116
+ # @return [Boolean] True if tab was focused successfully
105
117
  def focus_tab(tab_id)
106
118
  @mutex.synchronize do
107
119
  return false unless @tabs.key?(tab_id)
@@ -120,6 +132,8 @@ module Pocketrb
120
132
  end
121
133
 
122
134
  # Get tab info
135
+ # @param tab_id [String] Tab identifier
136
+ # @return [Hash, nil] Tab information hash or nil if not found
123
137
  def tab_info(tab_id)
124
138
  tab = @tabs[tab_id]
125
139
  return nil unless tab
@@ -145,6 +159,8 @@ module Pocketrb
145
159
  end
146
160
 
147
161
  # Update tab metadata
162
+ # @param tab_id [String] Tab identifier
163
+ # @return [void]
148
164
  def update_tab_info(tab_id)
149
165
  tab = @tabs[tab_id]
150
166
  return unless tab
@@ -155,6 +171,8 @@ module Pocketrb
155
171
 
156
172
  private
157
173
 
174
+ # Locate the Playwright CLI executable
175
+ # @return [String] path or command to run Playwright
158
176
  def find_playwright_cli
159
177
  # Try common locations
160
178
  ["npx playwright", "playwright", "node_modules/.bin/playwright"].each do |cmd|
@@ -6,10 +6,14 @@ module Pocketrb
6
6
  module Tools
7
7
  # Manage scheduled cron jobs - allows agent to be proactive
8
8
  class Cron < Base
9
+ # Tool name
10
+ # @return [String] Tool identifier
9
11
  def name
10
12
  "cron"
11
13
  end
12
14
 
15
+ # Tool description
16
+ # @return [String] Human-readable description
13
17
  def description
14
18
  <<~DESC.strip
15
19
  Manage scheduled tasks. Create reminders, follow-ups, or recurring checks.
@@ -17,6 +21,8 @@ module Pocketrb
17
21
  DESC
18
22
  end
19
23
 
24
+ # Tool parameters schema
25
+ # @return [Hash] JSON schema for tool parameters
20
26
  def parameters
21
27
  {
22
28
  type: "object",
@@ -56,10 +62,21 @@ module Pocketrb
56
62
  }
57
63
  end
58
64
 
65
+ # Check if cron service is available
66
+ # @return [Boolean] True if cron service is configured
59
67
  def available?
60
68
  !cron_service.nil?
61
69
  end
62
70
 
71
+ # Execute cron operation
72
+ # @param action [String] Action to perform (add, list, remove, enable, disable)
73
+ # @param name [String, nil] Job name (for add action)
74
+ # @param message [String, nil] Job message/task (for add action)
75
+ # @param schedule_type [String, nil] Schedule type: "at", "every", or "cron" (for add action)
76
+ # @param schedule_value [String, Integer, nil] Schedule value based on type (for add action)
77
+ # @param job_id [String, nil] Job identifier (for remove/enable/disable actions)
78
+ # @param deliver [Boolean] Whether to deliver message via channel (defaults to true)
79
+ # @return [String] JSON result of operation
63
80
  def execute(action:, name: nil, message: nil, schedule_type: nil, schedule_value: nil, job_id: nil, deliver: true)
64
81
  return error("Cron service not available. Start with --enable-cron flag.") unless cron_service
65
82
 
@@ -81,18 +98,31 @@ module Pocketrb
81
98
 
82
99
  private
83
100
 
101
+ # Access cron service from context
102
+ # @return [Object, nil]
84
103
  def cron_service
85
104
  @context[:cron_service]
86
105
  end
87
106
 
107
+ # Get the default output channel
108
+ # @return [Symbol]
88
109
  def current_channel
89
110
  @context[:default_channel] || @context[:channel] || :telegram
90
111
  end
91
112
 
113
+ # Get the default chat identifier
114
+ # @return [String, nil]
92
115
  def current_chat_id
93
116
  @context[:default_chat_id] || @context[:chat_id]
94
117
  end
95
118
 
119
+ # Add a new scheduled job
120
+ # @param name [String] job name
121
+ # @param message [String] message to deliver when job runs
122
+ # @param schedule_type [String] schedule kind: at, every, or cron
123
+ # @param schedule_value [String] schedule value matching the type
124
+ # @param deliver [Boolean] whether to deliver message to channel
125
+ # @return [String] confirmation or error message
96
126
  def add_job(name, message, schedule_type, schedule_value, deliver)
97
127
  return error("Name required") unless name && !name.empty?
98
128
  return error("Message required") unless message && !message.empty?
@@ -116,6 +146,10 @@ module Pocketrb
116
146
  success("Created job '#{name}' (ID: #{job.id}). Next run: #{next_run_time || "pending"}")
117
147
  end
118
148
 
149
+ # Build a Schedule object from type and value
150
+ # @param type [String] schedule kind: at, every, or cron
151
+ # @param value [String, nil] schedule value for the given type
152
+ # @return [Pocketrb::Cron::Schedule, String] schedule object or error string
119
153
  def build_schedule(type, value)
120
154
  case type
121
155
  when "at"
@@ -151,6 +185,8 @@ module Pocketrb
151
185
  end
152
186
  end
153
187
 
188
+ # List all scheduled jobs with status and next run time
189
+ # @return [String] formatted list of jobs
154
190
  def list_jobs
155
191
  jobs = cron_service.list_jobs(include_disabled: true)
156
192
 
@@ -177,6 +213,9 @@ module Pocketrb
177
213
  lines.join("\n")
178
214
  end
179
215
 
216
+ # Format a schedule object into a human-readable string
217
+ # @param schedule [Pocketrb::Cron::Schedule] schedule object containing kind and timing details
218
+ # @return [String] human-readable schedule description
180
219
  def format_schedule(schedule)
181
220
  case schedule.kind
182
221
  when :at
@@ -198,6 +237,9 @@ module Pocketrb
198
237
  end
199
238
  end
200
239
 
240
+ # Remove a scheduled job
241
+ # @param job_id [String] job identifier
242
+ # @return [String] confirmation or error message
201
243
  def remove_job(job_id)
202
244
  return error("Job ID required") unless job_id
203
245
 
@@ -208,6 +250,10 @@ module Pocketrb
208
250
  end
209
251
  end
210
252
 
253
+ # Enable or disable a scheduled job
254
+ # @param job_id [String] job identifier
255
+ # @param enabled [Boolean] true to enable, false to disable
256
+ # @return [String] confirmation or error message
211
257
  def toggle_job(job_id, enabled)
212
258
  return error("Job ID required") unless job_id
213
259
 
@@ -4,14 +4,20 @@ module Pocketrb
4
4
  module Tools
5
5
  # Edit a file with search/replace
6
6
  class EditFile < Base
7
+ # Tool name
8
+ # @return [String]
7
9
  def name
8
10
  "edit_file"
9
11
  end
10
12
 
13
+ # Tool description
14
+ # @return [String]
11
15
  def description
12
16
  "Edit a file by replacing specific text. The old_string must match exactly (including whitespace and indentation). Use for precise edits rather than rewriting entire files."
13
17
  end
14
18
 
19
+ # Parameter schema
20
+ # @return [Hash]
15
21
  def parameters
16
22
  {
17
23
  type: "object",
@@ -37,6 +43,12 @@ module Pocketrb
37
43
  }
38
44
  end
39
45
 
46
+ # Execute file edit operation
47
+ # @param path [String] File path to edit
48
+ # @param old_string [String] Exact text to find
49
+ # @param new_string [String] Replacement text
50
+ # @param replace_all [Boolean] Whether to replace all occurrences
51
+ # @return [String] Success or error message
40
52
  def execute(path:, old_string:, new_string:, replace_all: false)
41
53
  resolved = validate_path!(path)
42
54
 
@@ -75,6 +87,10 @@ module Pocketrb
75
87
 
76
88
  private
77
89
 
90
+ # Suggest similar content when old_string is not found
91
+ # @param content [String] full file content
92
+ # @param old_string [String] text that was not found
93
+ # @return [String] suggestion message with closest matches
78
94
  def suggest_match(content, old_string)
79
95
  # Try to find similar content
80
96
  lines = content.lines