rubycode 0.1.6 → 0.1.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 +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +70 -3
- data/config/exploration_prompt.md +22 -1
- data/exe/rubycode_client +6 -6
- data/lib/rubycode/version.rb +1 -1
- data/lib/rubycode/views/cli/plan_mode_enter.rb +2 -2
- data/rubycode_cli.rb +6 -6
- metadata +1 -2
- data/EXPLORE_TOOL_DESIGN.md +0 -375
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e4bc11cde6a1cf792d500338c15529b2d9599a9c79d917aba62b548ec67a234e
|
|
4
|
+
data.tar.gz: e221bd379bec42cac844889163a58d7f9bc2d3592453617884ea44cd0ab46211
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f8f83579fbbff76245c88310f9cfe35977ab2dbd762c151aaaac0ffafbb4679dd44f7518e5c7e8f62b8f4b32bfa17bb5eefc2de1cfb32eec4122aed16b17d75f
|
|
7
|
+
data.tar.gz: bd9316933c2f74cbf8da7c345b8cea7c90224cc490bc67a9297346c128c5396bbf6ca0a410231625cbf2c95b6c66c7ec060e1a4b36d08e14342894d41acbc06b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.1.7] - 2026-03-08
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **Plan Mode Workflow**: Removed redundant "Describe what you want to implement" prompt
|
|
7
|
+
- Now uses original user input for both exploration and implementation
|
|
8
|
+
- User only describes task once, not twice
|
|
9
|
+
- Clearer approval prompt: "Do you accept this exploration and want to proceed?"
|
|
10
|
+
- **Exploration Behavior**: Enhanced to prevent hitting max iterations
|
|
11
|
+
- Exploration prompt now emphasizes MUST use `done` tool
|
|
12
|
+
- Added "When to Call done" section with clear guidance (8-10 iterations)
|
|
13
|
+
- Added example `done` call format
|
|
14
|
+
- Updated critical rules to prevent iteration loops
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- **CLI Messages**: Improved clarity throughout plan mode workflow
|
|
18
|
+
- Plan mode entry shows 4-step process
|
|
19
|
+
- Status message during exploration: "🔍 Exploring codebase for: {task}"
|
|
20
|
+
- Implementation message: "✓ Plan accepted. Proceeding with implementation (auto-approve enabled)..."
|
|
21
|
+
|
|
3
22
|
## [0.1.6] - 2026-03-08
|
|
4
23
|
|
|
5
24
|
### Added
|
data/README.md
CHANGED
|
@@ -18,12 +18,18 @@ A Ruby-native AI coding assistant with pluggable LLM adapters. RubyCode provides
|
|
|
18
18
|
- **AI Agent Loop**: Autonomous task execution with tool calling
|
|
19
19
|
- **Multiple Cloud LLM Adapters**: Support for Ollama Cloud, DeepSeek, Gemini, OpenAI, and OpenRouter
|
|
20
20
|
- **Interactive Setup Wizard**: First-time configuration with provider selection and API key management
|
|
21
|
+
- **Plan Mode**: Interactive planning workflow with autonomous codebase exploration
|
|
22
|
+
- Enter with `plan mode` command
|
|
23
|
+
- AI explores codebase and presents findings
|
|
24
|
+
- User approves plan before implementation
|
|
25
|
+
- Auto-approve enabled during implementation
|
|
21
26
|
- **Built-in Tools**:
|
|
22
27
|
- `bash`: Execute safe bash commands for filesystem exploration
|
|
23
28
|
- `search`: Search file contents using grep with regex support
|
|
24
29
|
- `read`: Read files and directories with line numbers
|
|
25
30
|
- `write`: Create new files with user approval
|
|
26
31
|
- `update`: Edit existing files with user approval
|
|
32
|
+
- `explore`: Autonomous codebase exploration agent (read-only)
|
|
27
33
|
- `web_search`: Search the internet with automatic provider fallback (DuckDuckGo/Brave/Exa)
|
|
28
34
|
- `fetch`: Fetch content from URLs
|
|
29
35
|
- `done`: Signal task completion with final answer
|
|
@@ -76,6 +82,49 @@ The first time you run it, an interactive setup wizard will guide you through:
|
|
|
76
82
|
|
|
77
83
|
Your configuration is automatically saved and reloaded on subsequent runs.
|
|
78
84
|
|
|
85
|
+
#### CLI Commands
|
|
86
|
+
|
|
87
|
+
Once running, you have access to these special commands:
|
|
88
|
+
|
|
89
|
+
- `plan mode` or `plan` - Enter plan mode for guided exploration and implementation
|
|
90
|
+
- `auto-approve on` - Enable auto-approval for write operations
|
|
91
|
+
- `auto-approve off` - Disable auto-approval
|
|
92
|
+
- `auto-approve status` - Check auto-approval status
|
|
93
|
+
- `config` - View/reconfigure settings
|
|
94
|
+
- `clear` - Clear conversation history
|
|
95
|
+
- `exit` or `quit` - Exit the CLI
|
|
96
|
+
|
|
97
|
+
#### Using Plan Mode
|
|
98
|
+
|
|
99
|
+
Plan mode provides a structured workflow for complex tasks:
|
|
100
|
+
|
|
101
|
+
1. Type `plan mode` to enter
|
|
102
|
+
2. Describe what you want to explore/implement (e.g., "add user authentication")
|
|
103
|
+
3. AI autonomously explores the codebase using the explore tool
|
|
104
|
+
4. Review the exploration findings and plan
|
|
105
|
+
5. Accept or reject the plan
|
|
106
|
+
6. If accepted, describe the implementation and AI proceeds with auto-approve enabled
|
|
107
|
+
|
|
108
|
+
Example session:
|
|
109
|
+
```
|
|
110
|
+
You: plan mode
|
|
111
|
+
📋 Entering Plan Mode
|
|
112
|
+
Next: Describe what you want to explore and implement.
|
|
113
|
+
|
|
114
|
+
You: add JWT authentication to the API
|
|
115
|
+
|
|
116
|
+
🔍 Exploring codebase...
|
|
117
|
+
[AI explores and presents findings]
|
|
118
|
+
|
|
119
|
+
Do you accept this plan? (Y/n) y
|
|
120
|
+
|
|
121
|
+
✓ Plan accepted. Auto-approve enabled for implementation.
|
|
122
|
+
|
|
123
|
+
Describe what you want to implement: add JWT token generation to User model
|
|
124
|
+
|
|
125
|
+
[AI implements changes without requiring approval for each file]
|
|
126
|
+
```
|
|
127
|
+
|
|
79
128
|
### Programmatic Usage
|
|
80
129
|
|
|
81
130
|
You can also use RubyCode in your Ruby projects:
|
|
@@ -161,12 +210,17 @@ The agent has access to several built-in tools:
|
|
|
161
210
|
3. **read**: Read files with line numbers or list directory contents
|
|
162
211
|
4. **write**: Create new files (requires user approval)
|
|
163
212
|
5. **update**: Edit existing files with exact string replacement (requires user approval)
|
|
164
|
-
6. **
|
|
213
|
+
6. **explore**: Autonomous codebase exploration agent (read-only):
|
|
214
|
+
- Spawns sub-agent with constrained tools (bash, read, search, web_search, fetch, done)
|
|
215
|
+
- Configurable max iterations (default: 10, max: 15)
|
|
216
|
+
- Returns structured findings with summary, key files, and code flow
|
|
217
|
+
- Used automatically in plan mode
|
|
218
|
+
7. **web_search**: Search the internet with automatic provider fallback (requires user approval):
|
|
165
219
|
- Primary: Exa.ai (AI-native search, optional with API key)
|
|
166
220
|
- Fallback 1: DuckDuckGo Instant Answer API (free, no API key)
|
|
167
221
|
- Fallback 2: Brave Search API (optional, for better results)
|
|
168
|
-
|
|
169
|
-
|
|
222
|
+
8. **fetch**: Fetch and extract text content from URLs (requires user approval)
|
|
223
|
+
9. **done**: Signal completion and provide the final answer
|
|
170
224
|
|
|
171
225
|
**Note**: Tool schemas are externalized in `config/tools/*.json` for easy customization.
|
|
172
226
|
|
|
@@ -188,6 +242,19 @@ export EXA_API_KEY=your_api_key_here
|
|
|
188
242
|
export BRAVE_API_KEY=your_api_key_here
|
|
189
243
|
```
|
|
190
244
|
|
|
245
|
+
### New in 0.1.6
|
|
246
|
+
|
|
247
|
+
- **Plan Mode**: Interactive planning workflow with autonomous codebase exploration
|
|
248
|
+
- Enter with `plan mode` or `plan` command
|
|
249
|
+
- AI explores and presents findings for user approval
|
|
250
|
+
- Auto-approve enabled for implementation after plan acceptance
|
|
251
|
+
- **Auto-Approve Commands**: Manual control over write operation approvals
|
|
252
|
+
- `auto-approve on/off/status` for toggling and checking approval mode
|
|
253
|
+
- **Explore Tool**: Autonomous read-only codebase exploration agent
|
|
254
|
+
- Constrained sub-agent with dedicated exploration prompt
|
|
255
|
+
- Structured output with summary, key files, and code flow
|
|
256
|
+
- Configurable iteration limits (10 default, 15 max)
|
|
257
|
+
|
|
191
258
|
### New in 0.1.5
|
|
192
259
|
|
|
193
260
|
- **CLI Executable**: `rubycode_client` command for interactive chat after gem installation
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
You are an expert codebase explorer. Your goal is to thoroughly explore the codebase to answer the user's question.
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**CRITICAL RULES:**
|
|
6
|
+
1. This is READ-ONLY exploration. You cannot modify any files.
|
|
7
|
+
2. You MUST call the `done` tool when you have findings to present.
|
|
8
|
+
3. Do NOT exceed 10-15 iterations. Explore efficiently and present findings promptly.
|
|
6
9
|
|
|
7
10
|
## Available Tools
|
|
8
11
|
|
|
@@ -49,8 +52,26 @@ When you finish exploring, use the 'done' tool with this structured format:
|
|
|
49
52
|
## Important Reminders
|
|
50
53
|
|
|
51
54
|
- **READ-ONLY MODE**: You cannot write, update, or modify any files
|
|
55
|
+
- **MUST USE `done` TOOL**: Always call `done` with your findings (typically after 5-10 iterations)
|
|
52
56
|
- Be thorough but efficient with your iterations
|
|
53
57
|
- Actually read the files you identify as important
|
|
54
58
|
- If stuck, try web search for documentation
|
|
55
59
|
- Focus on answering the user's specific question, not general exploration
|
|
56
60
|
- Your findings will help the user plan their implementation
|
|
61
|
+
|
|
62
|
+
## When to Call `done`
|
|
63
|
+
|
|
64
|
+
Call `done` after you have:
|
|
65
|
+
- Identified the key files relevant to the query
|
|
66
|
+
- Understood the current implementation or architecture
|
|
67
|
+
- Found enough information to answer the user's question
|
|
68
|
+
- Reached 8-10 iterations (don't wait for max iterations!)
|
|
69
|
+
|
|
70
|
+
**Example `done` call:**
|
|
71
|
+
```
|
|
72
|
+
Use the done tool with a structured markdown summary of your findings including:
|
|
73
|
+
- Summary of what you found
|
|
74
|
+
- Key files and their purposes
|
|
75
|
+
- Code flow or architecture notes
|
|
76
|
+
- Recommendations for implementation
|
|
77
|
+
```
|
data/exe/rubycode_client
CHANGED
|
@@ -285,22 +285,22 @@ end
|
|
|
285
285
|
|
|
286
286
|
def process_user_message(client, user_input, context)
|
|
287
287
|
if context.plan_mode
|
|
288
|
-
# In plan mode -
|
|
288
|
+
# In plan mode - explore codebase first
|
|
289
|
+
puts "\n🔍 Exploring codebase for: #{user_input}\n"
|
|
289
290
|
response = client.ask(prompt: "Use the explore tool with this query: #{user_input}")
|
|
290
291
|
puts RubyCode::Views::Cli::ResponseBox.build(response: response)
|
|
291
292
|
|
|
292
293
|
# Ask user if they accept the plan
|
|
293
|
-
accept_plan = context.prompt.yes?("\
|
|
294
|
+
accept_plan = context.prompt.yes?("\n📋 Do you accept this exploration and want to proceed with implementation?",
|
|
294
295
|
default: true)
|
|
295
296
|
|
|
296
297
|
if accept_plan
|
|
297
|
-
puts "\n✓ Plan accepted.
|
|
298
|
+
puts "\n✓ Plan accepted. Proceeding with implementation (auto-approve enabled)...\n"
|
|
298
299
|
# Enable auto-approve for implementation
|
|
299
300
|
client.approval_handler.enable_auto_approve_write
|
|
300
301
|
|
|
301
|
-
#
|
|
302
|
-
|
|
303
|
-
response = client.ask(prompt: impl_prompt)
|
|
302
|
+
# Use the ORIGINAL user input as the implementation task
|
|
303
|
+
response = client.ask(prompt: user_input)
|
|
304
304
|
puts RubyCode::Views::Cli::ResponseBox.build(response: response)
|
|
305
305
|
|
|
306
306
|
# Disable auto-approve after implementation
|
data/lib/rubycode/version.rb
CHANGED
|
@@ -10,8 +10,8 @@ module RubyCode
|
|
|
10
10
|
def self.build
|
|
11
11
|
pastel = Pastel.new
|
|
12
12
|
"\n#{pastel.cyan("📋")} Entering Plan Mode\n" \
|
|
13
|
-
"Next: Describe
|
|
14
|
-
"
|
|
13
|
+
"Next: Describe your task (e.g., 'add user authentication').\n" \
|
|
14
|
+
"AI will: 1) Explore codebase 2) Show findings 3) Ask approval 4) Implement if approved\n\n"
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
end
|
data/rubycode_cli.rb
CHANGED
|
@@ -285,22 +285,22 @@ end
|
|
|
285
285
|
|
|
286
286
|
def process_user_message(client, user_input, context)
|
|
287
287
|
if context.plan_mode
|
|
288
|
-
# In plan mode -
|
|
288
|
+
# In plan mode - explore codebase first
|
|
289
|
+
puts "\n🔍 Exploring codebase for: #{user_input}\n"
|
|
289
290
|
response = client.ask(prompt: "Use the explore tool with this query: #{user_input}")
|
|
290
291
|
puts RubyCode::Views::Cli::ResponseBox.build(response: response)
|
|
291
292
|
|
|
292
293
|
# Ask user if they accept the plan
|
|
293
|
-
accept_plan = context.prompt.yes?("\
|
|
294
|
+
accept_plan = context.prompt.yes?("\n📋 Do you accept this exploration and want to proceed with implementation?",
|
|
294
295
|
default: true)
|
|
295
296
|
|
|
296
297
|
if accept_plan
|
|
297
|
-
puts "\n✓ Plan accepted.
|
|
298
|
+
puts "\n✓ Plan accepted. Proceeding with implementation (auto-approve enabled)...\n"
|
|
298
299
|
# Enable auto-approve for implementation
|
|
299
300
|
client.approval_handler.enable_auto_approve_write
|
|
300
301
|
|
|
301
|
-
#
|
|
302
|
-
|
|
303
|
-
response = client.ask(prompt: impl_prompt)
|
|
302
|
+
# Use the ORIGINAL user input as the implementation task
|
|
303
|
+
response = client.ask(prompt: user_input)
|
|
304
304
|
puts RubyCode::Views::Cli::ResponseBox.build(response: response)
|
|
305
305
|
|
|
306
306
|
# Disable auto-approve after implementation
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubycode
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jonas Medeiros
|
|
@@ -232,7 +232,6 @@ files:
|
|
|
232
232
|
- ".env.example"
|
|
233
233
|
- ".rubocop.yml"
|
|
234
234
|
- CHANGELOG.md
|
|
235
|
-
- EXPLORE_TOOL_DESIGN.md
|
|
236
235
|
- LICENSE.txt
|
|
237
236
|
- README.md
|
|
238
237
|
- Rakefile
|
data/EXPLORE_TOOL_DESIGN.md
DELETED
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
# Explore Tool Design for RubyCode
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
An intelligent codebase exploration tool that combines file discovery, content search, and automatic summarization.
|
|
5
|
-
|
|
6
|
-
## How Claude Code's Explore Works
|
|
7
|
-
|
|
8
|
-
Claude Code's Explore agent:
|
|
9
|
-
1. Takes a natural language query (e.g., "how does authentication work?")
|
|
10
|
-
2. Automatically uses multiple tools (Glob, Grep, Read) in sequence
|
|
11
|
-
3. Follows breadcrumbs and references
|
|
12
|
-
4. Builds a mental map of the codebase
|
|
13
|
-
5. Returns a comprehensive answer
|
|
14
|
-
|
|
15
|
-
## Implementation Approach for RubyCode
|
|
16
|
-
|
|
17
|
-
### Option 1: Single Explore Tool (Recommended)
|
|
18
|
-
Create a new `explore` tool that acts as an intelligent wrapper.
|
|
19
|
-
|
|
20
|
-
**Tool Definition** (`config/tools/explore.json`):
|
|
21
|
-
```json
|
|
22
|
-
{
|
|
23
|
-
"type": "function",
|
|
24
|
-
"function": {
|
|
25
|
-
"name": "explore",
|
|
26
|
-
"description": "Intelligently explore the codebase to answer questions. Automatically finds relevant files, searches content, and builds context. Use this for questions like 'how does X work?', 'where is Y implemented?', 'what files handle Z?'",
|
|
27
|
-
"parameters": {
|
|
28
|
-
"type": "object",
|
|
29
|
-
"properties": {
|
|
30
|
-
"query": {
|
|
31
|
-
"type": "string",
|
|
32
|
-
"description": "Natural language question or exploration goal (e.g., 'how does user authentication work?')"
|
|
33
|
-
},
|
|
34
|
-
"thoroughness": {
|
|
35
|
-
"type": "string",
|
|
36
|
-
"enum": ["quick", "medium", "thorough"],
|
|
37
|
-
"description": "How deep to explore. 'quick' = 1-2 passes, 'medium' = 3-5 passes, 'thorough' = 5-10 passes. Default: 'medium'"
|
|
38
|
-
},
|
|
39
|
-
"focus_paths": {
|
|
40
|
-
"type": "array",
|
|
41
|
-
"items": {"type": "string"},
|
|
42
|
-
"description": "Optional array of directory paths to focus on (e.g., ['app/models', 'lib/auth'])"
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
"required": ["query"]
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
**Implementation** (`lib/rubycode/tools/explore.rb`):
|
|
52
|
-
```ruby
|
|
53
|
-
module RubyCode
|
|
54
|
-
module Tools
|
|
55
|
-
class Explore < Base
|
|
56
|
-
def execute(params)
|
|
57
|
-
query = params["query"]
|
|
58
|
-
thoroughness = params["thoroughness"] || "medium"
|
|
59
|
-
focus_paths = params["focus_paths"] || ["."]
|
|
60
|
-
|
|
61
|
-
# Create a mini-agent that explores
|
|
62
|
-
explorer = Explorer.new(
|
|
63
|
-
root_path: context[:root_path],
|
|
64
|
-
query: query,
|
|
65
|
-
thoroughness: thoroughness,
|
|
66
|
-
focus_paths: focus_paths
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
result = explorer.run
|
|
70
|
-
|
|
71
|
-
CommandResult.new(
|
|
72
|
-
success: true,
|
|
73
|
-
output: result.summary,
|
|
74
|
-
metadata: {
|
|
75
|
-
files_examined: result.files_examined,
|
|
76
|
-
patterns_found: result.patterns_found
|
|
77
|
-
}
|
|
78
|
-
)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
**Explorer Engine** (`lib/rubycode/explorer.rb`):
|
|
86
|
-
```ruby
|
|
87
|
-
module RubyCode
|
|
88
|
-
class Explorer
|
|
89
|
-
MAX_ITERATIONS = {
|
|
90
|
-
"quick" => 3,
|
|
91
|
-
"medium" => 7,
|
|
92
|
-
"thorough" => 15
|
|
93
|
-
}.freeze
|
|
94
|
-
|
|
95
|
-
def initialize(root_path:, query:, thoroughness:, focus_paths:)
|
|
96
|
-
@root_path = root_path
|
|
97
|
-
@query = query
|
|
98
|
-
@max_iterations = MAX_ITERATIONS[thoroughness]
|
|
99
|
-
@focus_paths = focus_paths
|
|
100
|
-
@examined_files = []
|
|
101
|
-
@findings = []
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def run
|
|
105
|
-
# Phase 1: Keyword extraction
|
|
106
|
-
keywords = extract_keywords(@query)
|
|
107
|
-
|
|
108
|
-
# Phase 2: File discovery
|
|
109
|
-
candidate_files = discover_files(keywords)
|
|
110
|
-
|
|
111
|
-
# Phase 3: Content analysis
|
|
112
|
-
analyze_content(candidate_files, keywords)
|
|
113
|
-
|
|
114
|
-
# Phase 4: Follow references
|
|
115
|
-
follow_references(keywords)
|
|
116
|
-
|
|
117
|
-
# Phase 5: Summarize findings
|
|
118
|
-
summarize
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
private
|
|
122
|
-
|
|
123
|
-
def extract_keywords(query)
|
|
124
|
-
# Extract relevant keywords from query
|
|
125
|
-
# Remove stop words, extract technical terms
|
|
126
|
-
words = query.downcase.split(/\s+/)
|
|
127
|
-
stop_words = %w[how does what where is the a an in on at to for of with]
|
|
128
|
-
words - stop_words
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def discover_files(keywords)
|
|
132
|
-
files = []
|
|
133
|
-
|
|
134
|
-
# Try file name patterns
|
|
135
|
-
keywords.each do |keyword|
|
|
136
|
-
# Try exact match
|
|
137
|
-
files += glob_search("**/*#{keyword}*")
|
|
138
|
-
|
|
139
|
-
# Try snake_case variant
|
|
140
|
-
snake = keyword.gsub(/([A-Z])/, '_\1').downcase.sub(/^_/, '')
|
|
141
|
-
files += glob_search("**/*#{snake}*")
|
|
142
|
-
|
|
143
|
-
# Try camel_case variant
|
|
144
|
-
camel = keyword.split('_').map(&:capitalize).join
|
|
145
|
-
files += glob_search("**/*#{camel}*")
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
files.uniq
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def analyze_content(files, keywords)
|
|
152
|
-
files.first(20).each do |file|
|
|
153
|
-
next if @examined_files.include?(file)
|
|
154
|
-
|
|
155
|
-
content = read_file(file)
|
|
156
|
-
next unless content
|
|
157
|
-
|
|
158
|
-
# Check if file is relevant
|
|
159
|
-
relevance_score = calculate_relevance(content, keywords)
|
|
160
|
-
|
|
161
|
-
if relevance_score > 0.3
|
|
162
|
-
@findings << {
|
|
163
|
-
file: file,
|
|
164
|
-
score: relevance_score,
|
|
165
|
-
snippets: extract_relevant_snippets(content, keywords)
|
|
166
|
-
}
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
@examined_files << file
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def follow_references(keywords)
|
|
174
|
-
# Look for requires, includes, imports
|
|
175
|
-
@findings.each do |finding|
|
|
176
|
-
content = read_file(finding[:file])
|
|
177
|
-
|
|
178
|
-
# Extract references (require, include, etc.)
|
|
179
|
-
references = extract_references(content)
|
|
180
|
-
|
|
181
|
-
# Recursively explore referenced files
|
|
182
|
-
references.each do |ref|
|
|
183
|
-
ref_file = resolve_reference(ref)
|
|
184
|
-
next unless ref_file
|
|
185
|
-
|
|
186
|
-
analyze_content([ref_file], keywords)
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def summarize
|
|
192
|
-
# Build a structured summary
|
|
193
|
-
summary = "# Exploration Results for: #{@query}\n\n"
|
|
194
|
-
summary += "Examined #{@examined_files.length} files\n\n"
|
|
195
|
-
|
|
196
|
-
# Group findings by relevance
|
|
197
|
-
top_findings = @findings.sort_by { |f| -f[:score] }.first(10)
|
|
198
|
-
|
|
199
|
-
summary += "## Most Relevant Files:\n\n"
|
|
200
|
-
top_findings.each do |finding|
|
|
201
|
-
summary += "**#{finding[:file]}** (relevance: #{(finding[:score] * 100).round}%)\n"
|
|
202
|
-
finding[:snippets].each do |snippet|
|
|
203
|
-
summary += " - Line #{snippet[:line]}: #{snippet[:text]}\n"
|
|
204
|
-
end
|
|
205
|
-
summary += "\n"
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
ExplorerResult.new(
|
|
209
|
-
summary: summary,
|
|
210
|
-
files_examined: @examined_files.length,
|
|
211
|
-
patterns_found: @findings.length
|
|
212
|
-
)
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
# Helper methods
|
|
216
|
-
def glob_search(pattern)
|
|
217
|
-
Dir.glob(File.join(@root_path, pattern)).select { |f| File.file?(f) }
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
def read_file(path)
|
|
221
|
-
File.read(path) rescue nil
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
def calculate_relevance(content, keywords)
|
|
225
|
-
matches = keywords.sum { |kw| content.scan(/#{Regexp.escape(kw)}/i).length }
|
|
226
|
-
matches.to_f / (content.length / 100.0)
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
def extract_relevant_snippets(content, keywords)
|
|
230
|
-
snippets = []
|
|
231
|
-
content.lines.each_with_index do |line, idx|
|
|
232
|
-
if keywords.any? { |kw| line.match?(/#{Regexp.escape(kw)}/i) }
|
|
233
|
-
snippets << { line: idx + 1, text: line.strip }
|
|
234
|
-
end
|
|
235
|
-
end
|
|
236
|
-
snippets.first(5)
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
def extract_references(content)
|
|
240
|
-
references = []
|
|
241
|
-
|
|
242
|
-
# Ruby requires
|
|
243
|
-
content.scan(/require\s+['"](.+?)['"]/) { references << $1 }
|
|
244
|
-
content.scan(/require_relative\s+['"](.+?)['"]/) { references << $1 }
|
|
245
|
-
|
|
246
|
-
references
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
def resolve_reference(ref)
|
|
250
|
-
# Try to find the actual file
|
|
251
|
-
possible_paths = [
|
|
252
|
-
"#{ref}.rb",
|
|
253
|
-
"lib/#{ref}.rb",
|
|
254
|
-
"app/#{ref}.rb"
|
|
255
|
-
]
|
|
256
|
-
|
|
257
|
-
possible_paths.find { |p| File.exist?(File.join(@root_path, p)) }
|
|
258
|
-
end
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
ExplorerResult = Struct.new(:summary, :files_examined, :patterns_found, keyword_init: true)
|
|
262
|
-
end
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
### Option 2: Sub-Agent Approach (More Advanced)
|
|
266
|
-
|
|
267
|
-
Create a separate mini-agent that has access to a limited set of tools and runs autonomously:
|
|
268
|
-
|
|
269
|
-
```ruby
|
|
270
|
-
class ExploreAgent
|
|
271
|
-
def initialize(query:, root_path:, thoroughness:)
|
|
272
|
-
@query = query
|
|
273
|
-
@root_path = root_path
|
|
274
|
-
@memory = Memory.new
|
|
275
|
-
@adapter = RubyCode.configuration.adapter
|
|
276
|
-
@max_iterations = thoroughness_to_iterations(thoroughness)
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
def run
|
|
280
|
-
# Give the sub-agent a focused prompt
|
|
281
|
-
prompt = build_exploration_prompt(@query)
|
|
282
|
-
@memory.add_message(role: "user", content: prompt)
|
|
283
|
-
|
|
284
|
-
# Run mini agent loop with limited tools
|
|
285
|
-
agent_loop = AgentLoop.new(
|
|
286
|
-
adapter: @adapter,
|
|
287
|
-
memory: @memory,
|
|
288
|
-
config: exploration_config,
|
|
289
|
-
system_prompt: exploration_system_prompt,
|
|
290
|
-
max_iterations: @max_iterations
|
|
291
|
-
)
|
|
292
|
-
|
|
293
|
-
agent_loop.run
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
private
|
|
297
|
-
|
|
298
|
-
def exploration_config
|
|
299
|
-
# Limited config for sub-agent
|
|
300
|
-
config = RubyCode::Configuration.new
|
|
301
|
-
config.root_path = @root_path
|
|
302
|
-
config.memory_window = 5 # Smaller window
|
|
303
|
-
config
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
def exploration_system_prompt
|
|
307
|
-
<<~PROMPT
|
|
308
|
-
You are an expert codebase explorer. Your goal is to thoroughly explore
|
|
309
|
-
the codebase to answer the user's question. You have access to these tools:
|
|
310
|
-
|
|
311
|
-
- bash: Run ls, find, tree commands to discover files
|
|
312
|
-
- search: Search file contents for patterns
|
|
313
|
-
- read: Read specific files
|
|
314
|
-
- done: Finish with your findings
|
|
315
|
-
|
|
316
|
-
Be thorough but efficient. Follow these steps:
|
|
317
|
-
1. Identify key terms from the question
|
|
318
|
-
2. Search for relevant files and code
|
|
319
|
-
3. Read the most promising files
|
|
320
|
-
4. Follow references and imports
|
|
321
|
-
5. Summarize your findings
|
|
322
|
-
|
|
323
|
-
Use 'done' when you have a comprehensive answer.
|
|
324
|
-
PROMPT
|
|
325
|
-
end
|
|
326
|
-
|
|
327
|
-
def build_exploration_prompt(query)
|
|
328
|
-
"Explore the codebase to answer this question: #{query}\n\n" \
|
|
329
|
-
"Provide a comprehensive answer with file references and code snippets."
|
|
330
|
-
end
|
|
331
|
-
end
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
## Comparison
|
|
335
|
-
|
|
336
|
-
| Approach | Pros | Cons |
|
|
337
|
-
|----------|------|------|
|
|
338
|
-
| **Single Tool** | Simple, fast, deterministic | Less flexible, hardcoded logic |
|
|
339
|
-
| **Sub-Agent** | Intelligent, adaptable, can reason | Higher token cost, slower |
|
|
340
|
-
|
|
341
|
-
## Recommendation
|
|
342
|
-
|
|
343
|
-
**Start with Option 1 (Single Tool)**, then evolve to Option 2 if needed:
|
|
344
|
-
|
|
345
|
-
1. **Phase 1**: Implement basic explore tool with keyword extraction
|
|
346
|
-
2. **Phase 2**: Add pattern matching and file scoring
|
|
347
|
-
3. **Phase 3**: Add reference following
|
|
348
|
-
4. **Phase 4**: Consider sub-agent for complex queries
|
|
349
|
-
|
|
350
|
-
## Token Efficiency
|
|
351
|
-
|
|
352
|
-
The sub-agent approach uses more tokens but provides better results:
|
|
353
|
-
|
|
354
|
-
- **Single tool**: ~500-1000 tokens per exploration
|
|
355
|
-
- **Sub-agent**: ~5000-15000 tokens per exploration
|
|
356
|
-
|
|
357
|
-
You could make it configurable:
|
|
358
|
-
|
|
359
|
-
```ruby
|
|
360
|
-
# Quick exploration (single tool)
|
|
361
|
-
explore(query: "find authentication", mode: "fast")
|
|
362
|
-
|
|
363
|
-
# Deep exploration (sub-agent)
|
|
364
|
-
explore(query: "how does authentication work?", mode: "deep")
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
## Next Steps
|
|
368
|
-
|
|
369
|
-
1. Create `lib/rubycode/tools/explore.rb`
|
|
370
|
-
2. Create `lib/rubycode/explorer.rb`
|
|
371
|
-
3. Create `config/tools/explore.json`
|
|
372
|
-
4. Add tests in `test/test_explore_tool.rb`
|
|
373
|
-
5. Update README with explore tool documentation
|
|
374
|
-
|
|
375
|
-
Would you like me to implement the basic version (Option 1)?
|