Files
Agent-System/internal/agent/cursor.go
T
WeiCHEN 7d51e40b1c refactor(boundaries): clear data/dev path leaks; start output.md via DeliverableKey
Remove coordinator/bizdev/storage data/dev fallbacks and register
agent→workspace so default agent targets use workspace.DeliverableKey.
Remaining U5 path and G3 task_runs clearance stay open.
2026-09-22 21:44:57 +08:00

116 lines
2.5 KiB
Go

package agent
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"chat-agent-system/internal/config"
"chat-agent-system/internal/workspace"
)
type CursorCLIBackend struct {
cfg *config.Config
}
func (c *CursorCLIBackend) Name() string { return "cursor-cli" }
func (c *CursorCLIBackend) Execute(ctx context.Context, req Request) (*Result, error) {
if len(req.Targets) == 0 {
req.Targets = []string{workspace.DeliverableKey}
}
workDir := req.WorkDir
if !filepath.IsAbs(workDir) {
wd, _ := os.Getwd()
workDir = filepath.Join(wd, workDir)
}
payload, _ := json.Marshal(map[string]any{
"workspace": workDir,
"api_key": c.cfg.CursorAPIKey,
"model": c.cfg.CursorModel,
"prompt": req.Prompt,
"instructions": req.Instructions,
"targets": req.Targets,
})
bridgeBin := c.cfg.CursorBridgeBin
if !filepath.IsAbs(bridgeBin) {
wd, _ := os.Getwd()
bridgeBin = filepath.Join(wd, bridgeBin)
}
timeout := req.Timeout
if timeout <= 0 {
timeout = c.cfg.AgentTimeout
}
runCtx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
cmd := exec.CommandContext(runCtx, bridgeBin)
cmd.Dir = workDir
cmd.Stdin = bytes.NewReader(payload)
cmd.Env = append(os.Environ(),
"CURSOR_API_KEY="+c.cfg.CursorAPIKey,
"CURSOR_MODEL="+c.cfg.CursorModel,
)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
return &Result{
Success: false,
ErrorReason: fmt.Sprintf("cursor execution error: %v, stderr: %s", err, stderr.String()),
}, nil
}
type bridgeResponse struct {
OK bool `json:"ok"`
FallbackReason string `json:"fallback_reason"`
Stderr string `json:"stderr"`
}
var bResp bridgeResponse
lines := strings.Split(strings.TrimSpace(stdout.String()), "\n")
if len(lines) > 0 {
_ = json.Unmarshal([]byte(lines[len(lines)-1]), &bResp)
}
if !bResp.OK {
errMsg := bResp.FallbackReason
if bResp.Stderr != "" {
errMsg += " (" + bResp.Stderr + ")"
}
if errMsg == "" {
errMsg = "cursor bridge reported task failed"
}
return &Result{
Success: false,
ErrorReason: errMsg,
Logs: stdout.String(),
}, nil
}
patches := make(map[string][]byte)
for _, t := range req.Targets {
p := filepath.Join(workDir, t)
if b, err := os.ReadFile(p); err == nil {
patches[t] = b
}
}
return &Result{
Success: true,
Patches: patches,
Logs: stdout.String(),
}, nil
}