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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -0
- data/README.md +98 -17
- data/lib/pocketrb/agent/compaction.rb +235 -22
- data/lib/pocketrb/agent/context.rb +26 -3
- data/lib/pocketrb/agent/loop.rb +53 -5
- data/lib/pocketrb/agent/spawn_tool.rb +14 -0
- data/lib/pocketrb/agent/subagent_manager.rb +24 -2
- data/lib/pocketrb/bus/events.rb +42 -1
- data/lib/pocketrb/bus/message_bus.rb +37 -0
- data/lib/pocketrb/channels/base.rb +13 -0
- data/lib/pocketrb/channels/cli.rb +16 -1
- data/lib/pocketrb/channels/telegram.rb +70 -4
- data/lib/pocketrb/channels/whatsapp.rb +49 -7
- data/lib/pocketrb/cli/chat.rb +7 -0
- data/lib/pocketrb/cli/config.rb +4 -0
- data/lib/pocketrb/cli/cron.rb +6 -0
- data/lib/pocketrb/cli.rb +0 -1
- data/lib/pocketrb/config.rb +55 -6
- data/lib/pocketrb/cron/job.rb +41 -0
- data/lib/pocketrb/cron/service.rb +44 -15
- data/lib/pocketrb/heartbeat/service.rb +21 -0
- data/lib/pocketrb/mcp/client.rb +41 -3
- data/lib/pocketrb/mcp/memory_tool.rb +33 -0
- data/lib/pocketrb/media/processor.rb +32 -1
- data/lib/pocketrb/memory.rb +12 -3
- data/lib/pocketrb/planning/manager.rb +29 -1
- data/lib/pocketrb/planning/plan.rb +52 -0
- data/lib/pocketrb/planning/tool.rb +45 -0
- data/lib/pocketrb/providers/anthropic.rb +113 -9
- data/lib/pocketrb/providers/base.rb +39 -4
- data/lib/pocketrb/providers/claude_cli.rb +79 -7
- data/lib/pocketrb/providers/claude_max_proxy.rb +72 -6
- data/lib/pocketrb/providers/openai_compatible.rb +79 -0
- data/lib/pocketrb/providers/openrouter.rb +72 -12
- data/lib/pocketrb/providers/registry.rb +24 -0
- data/lib/pocketrb/providers/ruby_llm_provider.rb +168 -54
- data/lib/pocketrb/providers/types.rb +51 -1
- data/lib/pocketrb/session/manager.rb +34 -2
- data/lib/pocketrb/session/session.rb +53 -11
- data/lib/pocketrb/skills/builtin/tmux/SKILL.md +9 -9
- data/lib/pocketrb/skills/create_tool.rb +28 -0
- data/lib/pocketrb/skills/loader.rb +22 -1
- data/lib/pocketrb/skills/modify_tool.rb +22 -0
- data/lib/pocketrb/skills/skill.rb +19 -3
- data/lib/pocketrb/tools/background_job_manager.rb +29 -0
- data/lib/pocketrb/tools/base.rb +16 -1
- data/lib/pocketrb/tools/browser.rb +40 -0
- data/lib/pocketrb/tools/browser_advanced.rb +94 -4
- data/lib/pocketrb/tools/browser_session.rb +18 -0
- data/lib/pocketrb/tools/cron.rb +46 -0
- data/lib/pocketrb/tools/edit_file.rb +16 -0
- data/lib/pocketrb/tools/exec.rb +46 -0
- data/lib/pocketrb/tools/jobs.rb +26 -2
- data/lib/pocketrb/tools/list_dir.rb +19 -0
- data/lib/pocketrb/tools/memory.rb +34 -0
- data/lib/pocketrb/tools/message.rb +15 -0
- data/lib/pocketrb/tools/para_memory.rb +47 -0
- data/lib/pocketrb/tools/read_file.rb +11 -0
- data/lib/pocketrb/tools/registry.rb +12 -4
- data/lib/pocketrb/tools/send_file.rb +29 -0
- data/lib/pocketrb/tools/think.rb +9 -0
- data/lib/pocketrb/tools/web_fetch.rb +58 -3
- data/lib/pocketrb/tools/web_search.rb +25 -0
- data/lib/pocketrb/tools/write_file.rb +10 -0
- data/lib/pocketrb/version.rb +2 -1
- data/lib/pocketrb.rb +4 -1
- data/pocketrb.gemspec +6 -8
- metadata +24 -44
data/lib/pocketrb/tools/exec.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "open3"
|
|
|
4
4
|
require "timeout"
|
|
5
5
|
|
|
6
6
|
module Pocketrb
|
|
7
|
+
# Tool implementations for agent capabilities
|
|
7
8
|
module Tools
|
|
8
9
|
# Execute shell commands with smart timeout and background job support
|
|
9
10
|
class Exec < Base
|
|
@@ -14,7 +15,9 @@ module Pocketrb
|
|
|
14
15
|
complex: 300 # Builds, installs
|
|
15
16
|
}.freeze
|
|
16
17
|
|
|
18
|
+
# Default timeout for standard commands in seconds
|
|
17
19
|
DEFAULT_TIMEOUT = TIMEOUTS[:standard]
|
|
20
|
+
# Maximum output size to capture in bytes
|
|
18
21
|
MAX_OUTPUT_SIZE = 100_000
|
|
19
22
|
|
|
20
23
|
# Quick commands that should finish fast
|
|
@@ -29,14 +32,20 @@ module Pocketrb
|
|
|
29
32
|
/^\[/ # test bracket syntax
|
|
30
33
|
].freeze
|
|
31
34
|
|
|
35
|
+
# Tool name
|
|
36
|
+
# @return [String]
|
|
32
37
|
def name
|
|
33
38
|
"exec"
|
|
34
39
|
end
|
|
35
40
|
|
|
41
|
+
# Tool description
|
|
42
|
+
# @return [String]
|
|
36
43
|
def description
|
|
37
44
|
"Execute a shell command. Long-running commands (apt install, npm install, etc.) auto-run in background. Use for git, npm, make, and other development tools."
|
|
38
45
|
end
|
|
39
46
|
|
|
47
|
+
# Parameter schema
|
|
48
|
+
# @return [Hash]
|
|
40
49
|
def parameters
|
|
41
50
|
{
|
|
42
51
|
type: "object",
|
|
@@ -62,6 +71,12 @@ module Pocketrb
|
|
|
62
71
|
}
|
|
63
72
|
end
|
|
64
73
|
|
|
74
|
+
# Execute shell command
|
|
75
|
+
# @param command [String] Shell command to execute
|
|
76
|
+
# @param timeout [Integer, nil] Execution timeout in seconds
|
|
77
|
+
# @param working_dir [String, nil] Working directory for command
|
|
78
|
+
# @param background [Boolean, nil] Force background execution
|
|
79
|
+
# @return [String] Command output or job information
|
|
65
80
|
def execute(command:, timeout: nil, working_dir: nil, background: nil)
|
|
66
81
|
work_dir = resolve_working_dir(working_dir)
|
|
67
82
|
return work_dir if work_dir.is_a?(String) && work_dir.start_with?("Error:")
|
|
@@ -80,10 +95,15 @@ module Pocketrb
|
|
|
80
95
|
|
|
81
96
|
private
|
|
82
97
|
|
|
98
|
+
# Access or initialize the background job manager
|
|
99
|
+
# @return [BackgroundJobManager]
|
|
83
100
|
def job_manager
|
|
84
101
|
@job_manager ||= BackgroundJobManager.new(workspace: workspace)
|
|
85
102
|
end
|
|
86
103
|
|
|
104
|
+
# Resolve and validate the working directory
|
|
105
|
+
# @param working_dir [String, nil] explicit directory path or nil for workspace
|
|
106
|
+
# @return [String] resolved directory path or error string
|
|
87
107
|
def resolve_working_dir(working_dir)
|
|
88
108
|
if working_dir
|
|
89
109
|
resolved = resolve_path(working_dir)
|
|
@@ -95,6 +115,10 @@ module Pocketrb
|
|
|
95
115
|
end
|
|
96
116
|
end
|
|
97
117
|
|
|
118
|
+
# Run a command as a background job
|
|
119
|
+
# @param command [String] shell command to run
|
|
120
|
+
# @param work_dir [String] working directory
|
|
121
|
+
# @return [String] job metadata including ID and PID
|
|
98
122
|
def execute_background(command, work_dir)
|
|
99
123
|
Pocketrb.logger.debug("Running in background: #{command}")
|
|
100
124
|
|
|
@@ -114,6 +138,11 @@ module Pocketrb
|
|
|
114
138
|
OUTPUT
|
|
115
139
|
end
|
|
116
140
|
|
|
141
|
+
# Run a command in the foreground with timeout
|
|
142
|
+
# @param command [String] shell command to run
|
|
143
|
+
# @param work_dir [String] working directory
|
|
144
|
+
# @param explicit_timeout [Integer, nil] timeout in seconds, auto-detected if nil
|
|
145
|
+
# @return [String] command output and exit status
|
|
117
146
|
def execute_foreground(command, work_dir, explicit_timeout)
|
|
118
147
|
timeout = explicit_timeout || smart_timeout(command)
|
|
119
148
|
timeout = [timeout, 600].min if timeout
|
|
@@ -137,6 +166,9 @@ module Pocketrb
|
|
|
137
166
|
format_result(stdout, stderr, status)
|
|
138
167
|
end
|
|
139
168
|
|
|
169
|
+
# Determine timeout based on command type
|
|
170
|
+
# @param command [String] shell command to analyze
|
|
171
|
+
# @return [Integer, nil] timeout in seconds or nil for no timeout
|
|
140
172
|
def smart_timeout(command)
|
|
141
173
|
return TIMEOUTS[:simple] if quick_command?(command)
|
|
142
174
|
return nil if job_manager.long_running?(command)
|
|
@@ -144,12 +176,18 @@ module Pocketrb
|
|
|
144
176
|
TIMEOUTS[:standard]
|
|
145
177
|
end
|
|
146
178
|
|
|
179
|
+
# Check if command matches fast-executing patterns
|
|
180
|
+
# @param command [String] shell command to check
|
|
181
|
+
# @return [Boolean]
|
|
147
182
|
def quick_command?(command)
|
|
148
183
|
return false if command.nil? || command.empty?
|
|
149
184
|
|
|
150
185
|
QUICK_PATTERNS.any? { |p| command.match?(p) }
|
|
151
186
|
end
|
|
152
187
|
|
|
188
|
+
# Check if command matches known dangerous patterns
|
|
189
|
+
# @param command [String] shell command to check
|
|
190
|
+
# @return [Boolean]
|
|
153
191
|
def dangerous_command?(command)
|
|
154
192
|
dangerous_patterns = [
|
|
155
193
|
%r{\brm\s+-rf\s+[/~]},
|
|
@@ -165,6 +203,11 @@ module Pocketrb
|
|
|
165
203
|
dangerous_patterns.any? { |pattern| command.match?(pattern) }
|
|
166
204
|
end
|
|
167
205
|
|
|
206
|
+
# Format command execution result for display
|
|
207
|
+
# @param stdout [String] standard output
|
|
208
|
+
# @param stderr [String] standard error
|
|
209
|
+
# @param status [Process::Status] exit status
|
|
210
|
+
# @return [String] formatted result
|
|
168
211
|
def format_result(stdout, stderr, status)
|
|
169
212
|
output_parts = []
|
|
170
213
|
|
|
@@ -183,6 +226,9 @@ module Pocketrb
|
|
|
183
226
|
output_parts.join("\n\n")
|
|
184
227
|
end
|
|
185
228
|
|
|
229
|
+
# Truncate output that exceeds the maximum size
|
|
230
|
+
# @param output [String] output text to truncate
|
|
231
|
+
# @return [String] truncated or original output
|
|
186
232
|
def truncate_output(output)
|
|
187
233
|
return output if output.length <= MAX_OUTPUT_SIZE
|
|
188
234
|
|
data/lib/pocketrb/tools/jobs.rb
CHANGED
|
@@ -4,14 +4,20 @@ module Pocketrb
|
|
|
4
4
|
module Tools
|
|
5
5
|
# Manage background jobs
|
|
6
6
|
class Jobs < Base
|
|
7
|
+
# Tool name
|
|
8
|
+
# @return [String] Tool identifier
|
|
7
9
|
def name
|
|
8
10
|
"jobs"
|
|
9
11
|
end
|
|
10
12
|
|
|
13
|
+
# Tool description
|
|
14
|
+
# @return [String] Human-readable description
|
|
11
15
|
def description
|
|
12
16
|
"Manage background jobs. List running/completed jobs, get output, or kill jobs."
|
|
13
17
|
end
|
|
14
18
|
|
|
19
|
+
# Tool parameters schema
|
|
20
|
+
# @return [Hash] JSON schema for tool parameters
|
|
15
21
|
def parameters
|
|
16
22
|
{
|
|
17
23
|
type: "object",
|
|
@@ -34,6 +40,11 @@ module Pocketrb
|
|
|
34
40
|
}
|
|
35
41
|
end
|
|
36
42
|
|
|
43
|
+
# Execute background job operation
|
|
44
|
+
# @param action [String] Action to perform (list, status, output, kill)
|
|
45
|
+
# @param job_id [String, nil] Job identifier (for status/output/kill actions)
|
|
46
|
+
# @param lines [Integer] Number of output lines to show (defaults to 50)
|
|
47
|
+
# @return [String] JSON result of operation
|
|
37
48
|
def execute(action:, job_id: nil, lines: 50)
|
|
38
49
|
case action
|
|
39
50
|
when "list"
|
|
@@ -51,12 +62,16 @@ module Pocketrb
|
|
|
51
62
|
|
|
52
63
|
private
|
|
53
64
|
|
|
65
|
+
# Access or initialize the background job manager
|
|
66
|
+
# @return [BackgroundJobManager]
|
|
54
67
|
def job_manager
|
|
55
68
|
# Use memory_dir for job storage (falls back to workspace if not set)
|
|
56
69
|
storage_dir = @context[:memory_dir] || workspace
|
|
57
70
|
@job_manager ||= BackgroundJobManager.new(workspace: storage_dir)
|
|
58
71
|
end
|
|
59
72
|
|
|
73
|
+
# List all background jobs grouped by running/completed status
|
|
74
|
+
# @return [String] formatted job listing
|
|
60
75
|
def list_jobs
|
|
61
76
|
jobs = job_manager.list
|
|
62
77
|
|
|
@@ -64,8 +79,7 @@ module Pocketrb
|
|
|
64
79
|
|
|
65
80
|
output = ["Background Jobs:\n"]
|
|
66
81
|
|
|
67
|
-
running = jobs.
|
|
68
|
-
completed = jobs.reject { |j| j[:running] }
|
|
82
|
+
running, completed = jobs.partition { |j| j[:running] }
|
|
69
83
|
|
|
70
84
|
if running.any?
|
|
71
85
|
output << "RUNNING:"
|
|
@@ -86,6 +100,9 @@ module Pocketrb
|
|
|
86
100
|
output.join("\n")
|
|
87
101
|
end
|
|
88
102
|
|
|
103
|
+
# Get detailed status of a job
|
|
104
|
+
# @param job_id [String] job identifier
|
|
105
|
+
# @return [String] job status details or error message
|
|
89
106
|
def get_status(job_id)
|
|
90
107
|
return error("Job ID required") unless job_id
|
|
91
108
|
|
|
@@ -104,6 +121,10 @@ module Pocketrb
|
|
|
104
121
|
STATUS
|
|
105
122
|
end
|
|
106
123
|
|
|
124
|
+
# Get recent output from a job
|
|
125
|
+
# @param job_id [String] job identifier
|
|
126
|
+
# @param lines [Integer] number of lines to retrieve
|
|
127
|
+
# @return [String] job output or error message
|
|
107
128
|
def get_output(job_id, lines)
|
|
108
129
|
return error("Job ID required") unless job_id
|
|
109
130
|
|
|
@@ -113,6 +134,9 @@ module Pocketrb
|
|
|
113
134
|
"Output (last #{lines} lines):\n#{output}"
|
|
114
135
|
end
|
|
115
136
|
|
|
137
|
+
# Kill a running job
|
|
138
|
+
# @param job_id [String] job identifier
|
|
139
|
+
# @return [String] confirmation or error message
|
|
116
140
|
def kill_job(job_id)
|
|
117
141
|
return error("Job ID required") unless job_id
|
|
118
142
|
|
|
@@ -4,14 +4,20 @@ module Pocketrb
|
|
|
4
4
|
module Tools
|
|
5
5
|
# List directory contents
|
|
6
6
|
class ListDir < Base
|
|
7
|
+
# Tool name
|
|
8
|
+
# @return [String]
|
|
7
9
|
def name
|
|
8
10
|
"list_dir"
|
|
9
11
|
end
|
|
10
12
|
|
|
13
|
+
# Tool description
|
|
14
|
+
# @return [String]
|
|
11
15
|
def description
|
|
12
16
|
"List the contents of a directory. Shows files and subdirectories with basic metadata."
|
|
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 directory listing
|
|
47
|
+
# @param path [String, nil] Directory path to list (defaults to workspace root)
|
|
48
|
+
# @param pattern [String, nil] Glob pattern to filter results
|
|
49
|
+
# @param recursive [Boolean] Whether to list subdirectories recursively
|
|
50
|
+
# @param include_hidden [Boolean] Whether to include hidden files
|
|
51
|
+
# @return [String] Formatted directory listing
|
|
40
52
|
def execute(path: nil, pattern: nil, recursive: false, include_hidden: false)
|
|
41
53
|
resolved = path ? validate_path!(path) : workspace
|
|
42
54
|
|
|
@@ -73,6 +85,10 @@ module Pocketrb
|
|
|
73
85
|
|
|
74
86
|
private
|
|
75
87
|
|
|
88
|
+
# Format a directory entry for display
|
|
89
|
+
# @param path [Pathname] absolute path to the entry
|
|
90
|
+
# @param relative [Pathname] path relative to workspace
|
|
91
|
+
# @return [String] formatted entry with metadata
|
|
76
92
|
def format_entry(path, relative)
|
|
77
93
|
if path.directory?
|
|
78
94
|
"#{relative}/"
|
|
@@ -85,6 +101,9 @@ module Pocketrb
|
|
|
85
101
|
relative.to_s
|
|
86
102
|
end
|
|
87
103
|
|
|
104
|
+
# Format byte size into human-readable units
|
|
105
|
+
# @param bytes [Integer] size in bytes
|
|
106
|
+
# @return [String] formatted size string (e.g. "1.5KB")
|
|
88
107
|
def format_size(bytes)
|
|
89
108
|
units = %w[B KB MB GB]
|
|
90
109
|
unit_index = 0
|
|
@@ -4,13 +4,19 @@ module Pocketrb
|
|
|
4
4
|
module Tools
|
|
5
5
|
# Simple memory tool - store and recall facts
|
|
6
6
|
class Memory < Base
|
|
7
|
+
# Available memory actions
|
|
7
8
|
ACTIONS = %w[store recall search recent stats].freeze
|
|
9
|
+
# Memory categories for organization
|
|
8
10
|
CATEGORIES = %w[learned user preference context].freeze
|
|
9
11
|
|
|
12
|
+
# Tool name
|
|
13
|
+
# @return [String]
|
|
10
14
|
def name
|
|
11
15
|
"memory"
|
|
12
16
|
end
|
|
13
17
|
|
|
18
|
+
# Tool description
|
|
19
|
+
# @return [String]
|
|
14
20
|
def description
|
|
15
21
|
<<~DESC.strip
|
|
16
22
|
Store and recall memories. Use this to remember important facts, preferences, and learnings.
|
|
@@ -24,6 +30,8 @@ module Pocketrb
|
|
|
24
30
|
DESC
|
|
25
31
|
end
|
|
26
32
|
|
|
33
|
+
# Parameter schema
|
|
34
|
+
# @return [Hash]
|
|
27
35
|
def parameters
|
|
28
36
|
{
|
|
29
37
|
type: "object",
|
|
@@ -55,10 +63,19 @@ module Pocketrb
|
|
|
55
63
|
}
|
|
56
64
|
end
|
|
57
65
|
|
|
66
|
+
# Check if tool is available
|
|
67
|
+
# @return [Boolean] true if memory system is initialized
|
|
58
68
|
def available?
|
|
59
69
|
!memory_instance.nil?
|
|
60
70
|
end
|
|
61
71
|
|
|
72
|
+
# Execute memory action
|
|
73
|
+
# @param action [String] Action to perform (store, recall, search, recent, stats)
|
|
74
|
+
# @param category [String, nil] Category for storing memories
|
|
75
|
+
# @param key [String, nil] Memory key or topic
|
|
76
|
+
# @param value [String, nil] Memory content to store
|
|
77
|
+
# @param query [String, nil] Search query for recall/search
|
|
78
|
+
# @return [String] Action result or error message
|
|
62
79
|
def execute(action:, category: nil, key: nil, value: nil, query: nil, **)
|
|
63
80
|
return error("Memory not initialized") unless memory_instance
|
|
64
81
|
|
|
@@ -82,10 +99,17 @@ module Pocketrb
|
|
|
82
99
|
|
|
83
100
|
private
|
|
84
101
|
|
|
102
|
+
# Access the memory system from context
|
|
103
|
+
# @return [Object, nil]
|
|
85
104
|
def memory_instance
|
|
86
105
|
@context[:memory]
|
|
87
106
|
end
|
|
88
107
|
|
|
108
|
+
# Store a fact in the specified category
|
|
109
|
+
# @param category [String] memory category (learned, user, preference, context)
|
|
110
|
+
# @param key [String] memory key or topic
|
|
111
|
+
# @param value [String] content to store
|
|
112
|
+
# @return [String] confirmation or error message
|
|
89
113
|
def store_memory(category, key, value)
|
|
90
114
|
return error("Category, key, and value required") unless category && key && value
|
|
91
115
|
|
|
@@ -105,6 +129,9 @@ module Pocketrb
|
|
|
105
129
|
success(result)
|
|
106
130
|
end
|
|
107
131
|
|
|
132
|
+
# Recall relevant memories matching a query
|
|
133
|
+
# @param query [String] search query
|
|
134
|
+
# @return [String] matching memories or not-found message
|
|
108
135
|
def recall_memory(query)
|
|
109
136
|
return error("Query required") unless query
|
|
110
137
|
|
|
@@ -117,6 +144,9 @@ module Pocketrb
|
|
|
117
144
|
end
|
|
118
145
|
end
|
|
119
146
|
|
|
147
|
+
# Deep search across all memory categories
|
|
148
|
+
# @param query [String] search query
|
|
149
|
+
# @return [String] search results or not-found message
|
|
120
150
|
def search_memory(query)
|
|
121
151
|
return error("Query required") unless query
|
|
122
152
|
|
|
@@ -134,6 +164,8 @@ module Pocketrb
|
|
|
134
164
|
lines.join("\n")
|
|
135
165
|
end
|
|
136
166
|
|
|
167
|
+
# Show recent events from memory
|
|
168
|
+
# @return [String] formatted list of recent events
|
|
137
169
|
def show_recent
|
|
138
170
|
events = memory_instance.recent_events(10)
|
|
139
171
|
|
|
@@ -148,6 +180,8 @@ module Pocketrb
|
|
|
148
180
|
lines.join("\n")
|
|
149
181
|
end
|
|
150
182
|
|
|
183
|
+
# Show memory system statistics
|
|
184
|
+
# @return [String] formatted memory statistics
|
|
151
185
|
def show_stats
|
|
152
186
|
stats = memory_instance.stats
|
|
153
187
|
|
|
@@ -4,14 +4,20 @@ module Pocketrb
|
|
|
4
4
|
module Tools
|
|
5
5
|
# Tool for sending messages to chat channels programmatically
|
|
6
6
|
class Message < Base
|
|
7
|
+
# Tool name
|
|
8
|
+
# @return [String]
|
|
7
9
|
def name
|
|
8
10
|
"message"
|
|
9
11
|
end
|
|
10
12
|
|
|
13
|
+
# Tool description
|
|
14
|
+
# @return [String]
|
|
11
15
|
def description
|
|
12
16
|
"Send a message to a chat channel. Use this to proactively communicate with users, send notifications, or respond on specific channels."
|
|
13
17
|
end
|
|
14
18
|
|
|
19
|
+
# Parameter schema
|
|
20
|
+
# @return [Hash]
|
|
15
21
|
def parameters
|
|
16
22
|
{
|
|
17
23
|
type: "object",
|
|
@@ -33,6 +39,11 @@ module Pocketrb
|
|
|
33
39
|
}
|
|
34
40
|
end
|
|
35
41
|
|
|
42
|
+
# Execute message sending
|
|
43
|
+
# @param content [String] Message text to send
|
|
44
|
+
# @param channel [String, nil] Target channel name
|
|
45
|
+
# @param chat_id [String, nil] Target chat identifier
|
|
46
|
+
# @return [String] Success or error message
|
|
36
47
|
def execute(content:, channel: nil, chat_id: nil)
|
|
37
48
|
# Use defaults from context if not provided
|
|
38
49
|
channel = (channel || @context[:default_channel])&.to_sym
|
|
@@ -56,12 +67,16 @@ module Pocketrb
|
|
|
56
67
|
success("Message sent to #{channel}:#{chat_id}")
|
|
57
68
|
end
|
|
58
69
|
|
|
70
|
+
# Check if message bus is available
|
|
71
|
+
# @return [Boolean] True if bus is configured
|
|
59
72
|
def available?
|
|
60
73
|
!bus.nil?
|
|
61
74
|
end
|
|
62
75
|
|
|
63
76
|
private
|
|
64
77
|
|
|
78
|
+
# Access the message bus from context
|
|
79
|
+
# @return [Object, nil]
|
|
65
80
|
def bus
|
|
66
81
|
@context[:bus]
|
|
67
82
|
end
|
|
@@ -4,10 +4,14 @@ module Pocketrb
|
|
|
4
4
|
module Tools
|
|
5
5
|
# PARA-based memory tool for structured knowledge management
|
|
6
6
|
class ParaMemory < Base
|
|
7
|
+
# Tool name
|
|
8
|
+
# @return [String]
|
|
7
9
|
def name
|
|
8
10
|
"para_memory"
|
|
9
11
|
end
|
|
10
12
|
|
|
13
|
+
# Tool description
|
|
14
|
+
# @return [String]
|
|
11
15
|
def description
|
|
12
16
|
<<~DESC.strip
|
|
13
17
|
Structured memory using PARA method. Store and retrieve facts about people, projects,
|
|
@@ -16,6 +20,8 @@ module Pocketrb
|
|
|
16
20
|
DESC
|
|
17
21
|
end
|
|
18
22
|
|
|
23
|
+
# Parameter schema
|
|
24
|
+
# @return [Hash]
|
|
19
25
|
def parameters
|
|
20
26
|
{
|
|
21
27
|
type: "object",
|
|
@@ -74,10 +80,25 @@ module Pocketrb
|
|
|
74
80
|
}
|
|
75
81
|
end
|
|
76
82
|
|
|
83
|
+
# Check if tool is available
|
|
84
|
+
# @return [Boolean] true if PARA manager is configured
|
|
77
85
|
def available?
|
|
78
86
|
!para_manager.nil?
|
|
79
87
|
end
|
|
80
88
|
|
|
89
|
+
# Execute memory action
|
|
90
|
+
# @param action [String] Action to perform (store, search, entity_info, create_entity, list_entities, context,
|
|
91
|
+
# preferences, learn_preference)
|
|
92
|
+
# @option args [String] :content Fact content to store
|
|
93
|
+
# @option args [String] :category Fact category (relationship, milestone, status, preference, context)
|
|
94
|
+
# @option args [String] :entity_type PARA entity type (projects, areas, resources, archives)
|
|
95
|
+
# @option args [String] :entity_name Entity name
|
|
96
|
+
# @option args [String] :query Search query
|
|
97
|
+
# @option args [String] :entity_subtype Entity subtype for creation (project, person, company, topic)
|
|
98
|
+
# @option args [String] :preference_category Preference category
|
|
99
|
+
# @option args [String] :preference_key Preference key
|
|
100
|
+
# @option args [String] :preference_value Preference value
|
|
101
|
+
# @return [String] Action result or error message
|
|
81
102
|
def execute(action:, **args)
|
|
82
103
|
return error("PARA memory not available. Configure memory_dir in agent settings.") unless para_manager
|
|
83
104
|
|
|
@@ -105,10 +126,15 @@ module Pocketrb
|
|
|
105
126
|
|
|
106
127
|
private
|
|
107
128
|
|
|
129
|
+
# Access the PARA manager from context
|
|
130
|
+
# @return [Object, nil]
|
|
108
131
|
def para_manager
|
|
109
132
|
@context[:para_manager]
|
|
110
133
|
end
|
|
111
134
|
|
|
135
|
+
# Store a fact in the knowledge graph
|
|
136
|
+
# @param args [Hash] options including :content, :category, :entity_type, :entity_name
|
|
137
|
+
# @return [String] confirmation or error message
|
|
112
138
|
def store_fact(args)
|
|
113
139
|
content = args[:content]
|
|
114
140
|
return error("Content required") if content.nil? || content.empty?
|
|
@@ -131,6 +157,9 @@ module Pocketrb
|
|
|
131
157
|
end
|
|
132
158
|
end
|
|
133
159
|
|
|
160
|
+
# Search across knowledge graph, daily notes, and preferences
|
|
161
|
+
# @param query [String] search query
|
|
162
|
+
# @return [String] formatted search results
|
|
134
163
|
def search_memory(query)
|
|
135
164
|
return error("Query required") if query.nil? || query.empty?
|
|
136
165
|
|
|
@@ -166,6 +195,10 @@ module Pocketrb
|
|
|
166
195
|
lines.join("\n")
|
|
167
196
|
end
|
|
168
197
|
|
|
198
|
+
# Get summary information for an entity
|
|
199
|
+
# @param entity_type [String] PARA type (projects, areas, resources, archives)
|
|
200
|
+
# @param entity_name [String] entity name
|
|
201
|
+
# @return [String] entity summary or error message
|
|
169
202
|
def get_entity_info(entity_type, entity_name)
|
|
170
203
|
return error("Entity type and name required") unless entity_type && entity_name
|
|
171
204
|
|
|
@@ -175,6 +208,9 @@ module Pocketrb
|
|
|
175
208
|
summary
|
|
176
209
|
end
|
|
177
210
|
|
|
211
|
+
# Create a new entity in the knowledge graph
|
|
212
|
+
# @param args [Hash] options including :entity_subtype, :entity_name, :content
|
|
213
|
+
# @return [String] confirmation or error message
|
|
178
214
|
def create_entity(args)
|
|
179
215
|
subtype = args[:entity_subtype]
|
|
180
216
|
name = args[:entity_name]
|
|
@@ -204,6 +240,9 @@ module Pocketrb
|
|
|
204
240
|
end
|
|
205
241
|
end
|
|
206
242
|
|
|
243
|
+
# List entities, optionally filtered by type
|
|
244
|
+
# @param entity_type [String, nil] PARA type to filter by, or nil for all
|
|
245
|
+
# @return [String] formatted entity list
|
|
207
246
|
def list_entities(entity_type)
|
|
208
247
|
if entity_type
|
|
209
248
|
entities = para_manager.knowledge_graph.list_entities(entity_type.to_sym)
|
|
@@ -220,6 +259,9 @@ module Pocketrb
|
|
|
220
259
|
end
|
|
221
260
|
end
|
|
222
261
|
|
|
262
|
+
# Get relevant context for a query or full summary
|
|
263
|
+
# @param query [String, nil] context query, or nil for full summary
|
|
264
|
+
# @return [String] relevant context
|
|
223
265
|
def get_context(query)
|
|
224
266
|
if query && !query.empty?
|
|
225
267
|
para_manager.relevant_context(query)
|
|
@@ -228,6 +270,8 @@ module Pocketrb
|
|
|
228
270
|
end
|
|
229
271
|
end
|
|
230
272
|
|
|
273
|
+
# Get all learned user preferences
|
|
274
|
+
# @return [String] formatted preference list
|
|
231
275
|
def get_preferences
|
|
232
276
|
prefs = para_manager.get_preferences
|
|
233
277
|
return "No learned preferences yet." if prefs.empty?
|
|
@@ -243,6 +287,9 @@ module Pocketrb
|
|
|
243
287
|
lines.join("\n")
|
|
244
288
|
end
|
|
245
289
|
|
|
290
|
+
# Store a learned user preference
|
|
291
|
+
# @param args [Hash] options including :preference_category, :preference_key, :preference_value
|
|
292
|
+
# @return [String] confirmation or error message
|
|
246
293
|
def learn_preference(args)
|
|
247
294
|
category = args[:preference_category]
|
|
248
295
|
key = args[:preference_key]
|
|
@@ -4,14 +4,20 @@ module Pocketrb
|
|
|
4
4
|
module Tools
|
|
5
5
|
# Read file contents
|
|
6
6
|
class ReadFile < Base
|
|
7
|
+
# Tool name
|
|
8
|
+
# @return [String]
|
|
7
9
|
def name
|
|
8
10
|
"read_file"
|
|
9
11
|
end
|
|
10
12
|
|
|
13
|
+
# Tool description
|
|
14
|
+
# @return [String]
|
|
11
15
|
def description
|
|
12
16
|
"Read the contents of a file. Returns the file content as text."
|
|
13
17
|
end
|
|
14
18
|
|
|
19
|
+
# Parameter schema
|
|
20
|
+
# @return [Hash]
|
|
15
21
|
def parameters
|
|
16
22
|
{
|
|
17
23
|
type: "object",
|
|
@@ -33,6 +39,11 @@ module Pocketrb
|
|
|
33
39
|
}
|
|
34
40
|
end
|
|
35
41
|
|
|
42
|
+
# Execute file read operation
|
|
43
|
+
# @param path [String] File path (relative or absolute)
|
|
44
|
+
# @param offset [Integer, nil] Starting line number (1-indexed)
|
|
45
|
+
# @param limit [Integer, nil] Maximum lines to read
|
|
46
|
+
# @return [String] File content or error message
|
|
36
47
|
def execute(path:, offset: nil, limit: nil)
|
|
37
48
|
resolved = validate_path!(path)
|
|
38
49
|
|
|
@@ -6,6 +6,8 @@ module Pocketrb
|
|
|
6
6
|
class Registry
|
|
7
7
|
attr_reader :context
|
|
8
8
|
|
|
9
|
+
# Initialize tools registry
|
|
10
|
+
# @param context [Hash] Context to pass to tool instances (defaults to empty hash)
|
|
9
11
|
def initialize(context = {})
|
|
10
12
|
@tools = {}
|
|
11
13
|
@context = context
|
|
@@ -35,7 +37,7 @@ module Pocketrb
|
|
|
35
37
|
|
|
36
38
|
# Get a tool by name
|
|
37
39
|
# @param name [String] Tool name
|
|
38
|
-
# @return [Base
|
|
40
|
+
# @return [Base, nil]
|
|
39
41
|
def get(name)
|
|
40
42
|
@tools[name]
|
|
41
43
|
end
|
|
@@ -62,7 +64,8 @@ module Pocketrb
|
|
|
62
64
|
end
|
|
63
65
|
|
|
64
66
|
# Get Anthropic-format definitions
|
|
65
|
-
# @
|
|
67
|
+
# @param filter_unavailable [Boolean] Exclude unavailable tools (defaults to true)
|
|
68
|
+
# @return [Array<Hash>] Tool definitions in Anthropic format
|
|
66
69
|
def anthropic_definitions(filter_unavailable: true)
|
|
67
70
|
tools = filter_unavailable ? available_tools : @tools.values
|
|
68
71
|
tools.map(&:to_anthropic_definition)
|
|
@@ -113,6 +116,8 @@ module Pocketrb
|
|
|
113
116
|
end
|
|
114
117
|
|
|
115
118
|
# Update context for all tools
|
|
119
|
+
# @param new_context [Hash] New context values to merge
|
|
120
|
+
# @return [void]
|
|
116
121
|
def update_context(new_context)
|
|
117
122
|
@context = @context.merge(new_context)
|
|
118
123
|
@tools.each_value { |tool| tool.instance_variable_set(:@context, @context) }
|
|
@@ -140,8 +145,11 @@ module Pocketrb
|
|
|
140
145
|
|
|
141
146
|
private
|
|
142
147
|
|
|
143
|
-
# Filter arguments to only include those defined in tool's parameter schema
|
|
144
|
-
# This prevents LLM from passing unexpected arguments like 'description'
|
|
148
|
+
# Filter arguments to only include those defined in tool's parameter schema.
|
|
149
|
+
# This prevents LLM from passing unexpected arguments like 'description'.
|
|
150
|
+
# @param tool [Base] tool instance with parameter schema
|
|
151
|
+
# @param args [Hash] raw arguments from LLM
|
|
152
|
+
# @return [Hash] filtered arguments matching the schema
|
|
145
153
|
def filter_arguments(tool, args)
|
|
146
154
|
schema = tool.parameters
|
|
147
155
|
return args unless schema.is_a?(Hash) && schema[:properties]
|