mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-05-22 15:45:06 +08:00
add memory_get tool and docker-based make test target
This commit is contained in:
6
Makefile
6
Makefile
@@ -161,6 +161,12 @@ deps:
|
|||||||
run: build
|
run: build
|
||||||
@$(BUILD_DIR)/$(BINARY_NAME) $(ARGS)
|
@$(BUILD_DIR)/$(BINARY_NAME) $(ARGS)
|
||||||
|
|
||||||
|
## test: Build and compile-check in Docker (Dockerfile.test)
|
||||||
|
test:
|
||||||
|
@echo "Running Docker compile test..."
|
||||||
|
docker build -f Dockerfile.test -t clawgo:test .
|
||||||
|
@echo "Docker compile test passed"
|
||||||
|
|
||||||
## help: Show this help message
|
## help: Show this help message
|
||||||
help:
|
help:
|
||||||
@echo "clawgo Makefile"
|
@echo "clawgo Makefile"
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers
|
|||||||
// Register memory tools
|
// Register memory tools
|
||||||
memorySearchTool := tools.NewMemorySearchTool(workspace)
|
memorySearchTool := tools.NewMemorySearchTool(workspace)
|
||||||
toolsRegistry.Register(memorySearchTool)
|
toolsRegistry.Register(memorySearchTool)
|
||||||
|
toolsRegistry.Register(tools.NewMemoryGetTool(workspace))
|
||||||
toolsRegistry.Register(tools.NewMemoryWriteTool(workspace))
|
toolsRegistry.Register(tools.NewMemoryWriteTool(workspace))
|
||||||
|
|
||||||
// Register parallel execution tool (leveraging Go's concurrency)
|
// Register parallel execution tool (leveraging Go's concurrency)
|
||||||
|
|||||||
123
pkg/tools/memory_get.go
Normal file
123
pkg/tools/memory_get.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package tools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MemoryGetTool struct {
|
||||||
|
workspace string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMemoryGetTool(workspace string) *MemoryGetTool {
|
||||||
|
return &MemoryGetTool{workspace: workspace}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *MemoryGetTool) Name() string {
|
||||||
|
return "memory_get"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *MemoryGetTool) Description() string {
|
||||||
|
return "Read safe snippets from MEMORY.md or memory/*.md using optional line range."
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *MemoryGetTool) Parameters() map[string]interface{} {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"type": "object",
|
||||||
|
"properties": map[string]interface{}{
|
||||||
|
"path": map[string]interface{}{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Relative path to MEMORY.md or memory/*.md",
|
||||||
|
},
|
||||||
|
"from": map[string]interface{}{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Start line (1-indexed)",
|
||||||
|
"default": 1,
|
||||||
|
},
|
||||||
|
"lines": map[string]interface{}{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Number of lines to read",
|
||||||
|
"default": 80,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": []string{"path"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *MemoryGetTool) Execute(ctx context.Context, args map[string]interface{}) (string, error) {
|
||||||
|
rawPath, _ := args["path"].(string)
|
||||||
|
rawPath = strings.TrimSpace(rawPath)
|
||||||
|
if rawPath == "" {
|
||||||
|
return "", fmt.Errorf("path is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
from := 1
|
||||||
|
if v, ok := args["from"].(float64); ok && int(v) > 0 {
|
||||||
|
from = int(v)
|
||||||
|
}
|
||||||
|
lines := 80
|
||||||
|
if v, ok := args["lines"].(float64); ok && int(v) > 0 {
|
||||||
|
lines = int(v)
|
||||||
|
}
|
||||||
|
if lines > 500 {
|
||||||
|
lines = 500
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath := filepath.Clean(filepath.Join(t.workspace, rawPath))
|
||||||
|
if !t.isAllowedMemoryPath(fullPath) {
|
||||||
|
return "", fmt.Errorf("path not allowed: %s", rawPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
lineNo := 0
|
||||||
|
end := from + lines - 1
|
||||||
|
var out strings.Builder
|
||||||
|
for scanner.Scan() {
|
||||||
|
lineNo++
|
||||||
|
if lineNo < from {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if lineNo > end {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
out.WriteString(fmt.Sprintf("%d: %s\n", lineNo, scanner.Text()))
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
content := strings.TrimSpace(out.String())
|
||||||
|
if content == "" {
|
||||||
|
return fmt.Sprintf("No content in range for %s (from=%d, lines=%d)", rawPath, from, lines), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rel, _ := filepath.Rel(t.workspace, fullPath)
|
||||||
|
return fmt.Sprintf("Source: %s#L%d-L%d\n%s", rel, from, end, content), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *MemoryGetTool) isAllowedMemoryPath(fullPath string) bool {
|
||||||
|
workspaceMemory := filepath.Join(t.workspace, "MEMORY.md")
|
||||||
|
if fullPath == workspaceMemory {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryDir := filepath.Join(t.workspace, "memory")
|
||||||
|
rel, err := filepath.Rel(memoryDir, fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(rel, "..") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return strings.HasSuffix(strings.ToLower(fullPath), ".md")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user