Skip to content

Commit c3d84bc

Browse files
committed
Simplify summary provider CLI execution
1 parent 23beb24 commit c3d84bc

File tree

8 files changed

+100
-228
lines changed

8 files changed

+100
-228
lines changed
Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package claudecode
22

33
import (
4-
"bytes"
54
"context"
65
"encoding/json"
7-
"errors"
86
"fmt"
9-
"os"
107
"os/exec"
11-
"strings"
8+
9+
"github.com/entireio/cli/cmd/entire/cli/agent"
1210
)
1311

1412
// GenerateText sends a prompt to the Claude CLI and returns the raw text response.
@@ -21,52 +19,22 @@ func (c *ClaudeCodeAgent) GenerateText(ctx context.Context, prompt string, model
2119
model = "haiku"
2220
}
2321

24-
cmd := exec.CommandContext(ctx, claudePath,
22+
args := []string{
2523
"--print", "--output-format", "json",
26-
"--model", model, "--setting-sources", "")
27-
28-
// Isolate from the user's git repo to prevent recursive hook triggers
29-
// and index pollution (same approach as summarize/claude.go).
30-
cmd.Dir = os.TempDir()
31-
cmd.Env = stripGitEnv(os.Environ())
32-
cmd.Stdin = strings.NewReader(prompt)
33-
34-
var stdout, stderr bytes.Buffer
35-
cmd.Stdout = &stdout
36-
cmd.Stderr = &stderr
37-
38-
if err := cmd.Run(); err != nil {
39-
var execErr *exec.Error
40-
if errors.As(err, &execErr) {
41-
return "", fmt.Errorf("claude CLI not found: %w", err)
42-
}
43-
var exitErr *exec.ExitError
44-
if errors.As(err, &exitErr) {
45-
return "", fmt.Errorf("claude CLI failed (exit %d): %s", exitErr.ExitCode(), stderr.String())
46-
}
47-
return "", fmt.Errorf("failed to run claude CLI: %w", err)
24+
"--model", model, "--setting-sources", "",
25+
}
26+
stdoutText, err := agent.RunIsolatedTextGeneratorCLI(ctx, exec.CommandContext, claudePath, "claude", args, prompt)
27+
if err != nil {
28+
return "", fmt.Errorf("claude text generation failed: %w", err)
4829
}
4930

5031
// Parse the {"result": "..."} envelope
5132
var response struct {
5233
Result string `json:"result"`
5334
}
54-
if err := json.Unmarshal(stdout.Bytes(), &response); err != nil {
35+
if err := json.Unmarshal([]byte(stdoutText), &response); err != nil {
5536
return "", fmt.Errorf("failed to parse claude CLI response: %w", err)
5637
}
5738

5839
return response.Result, nil
5940
}
60-
61-
// stripGitEnv returns a copy of env with all GIT_* variables removed.
62-
// This prevents a subprocess from discovering or modifying the parent's git repo.
63-
// Duplicated from summarize/claude.go — simple filter not worth extracting to shared package.
64-
func stripGitEnv(env []string) []string {
65-
filtered := make([]string, 0, len(env))
66-
for _, e := range env {
67-
if !strings.HasPrefix(e, "GIT_") {
68-
filtered = append(filtered, e)
69-
}
70-
}
71-
return filtered
72-
}
Lines changed: 7 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,26 @@
11
package codex
22

33
import (
4-
"bytes"
54
"context"
6-
"errors"
75
"fmt"
8-
"os"
96
"os/exec"
10-
"strings"
7+
8+
"github.com/entireio/cli/cmd/entire/cli/agent"
119
)
1210

1311
var codexCommandRunner = exec.CommandContext
1412

1513
// GenerateText sends a prompt to the Codex CLI and returns the raw text response.
1614
func (c *CodexAgent) GenerateText(ctx context.Context, prompt string, model string) (string, error) {
17-
args := []string{"exec"}
15+
args := []string{"exec", "--skip-git-repo-check"}
1816
if model != "" {
1917
args = append(args, "--model", model)
2018
}
2119
args = append(args, "-")
2220

23-
cmd := codexCommandRunner(ctx, "codex", args...)
24-
cmd.Dir = os.TempDir()
25-
cmd.Env = stripGitEnv(os.Environ())
26-
cmd.Stdin = strings.NewReader(prompt)
27-
28-
var stdout, stderr bytes.Buffer
29-
cmd.Stdout = &stdout
30-
cmd.Stderr = &stderr
31-
32-
if err := cmd.Run(); err != nil {
33-
var execErr *exec.Error
34-
if errors.As(err, &execErr) {
35-
return "", fmt.Errorf("codex CLI not found: %w", err)
36-
}
37-
var exitErr *exec.ExitError
38-
if errors.As(err, &exitErr) {
39-
return "", fmt.Errorf("codex CLI failed (exit %d): %s", exitErr.ExitCode(), stderr.String())
40-
}
41-
return "", fmt.Errorf("failed to run codex CLI: %w", err)
42-
}
43-
44-
return strings.TrimSpace(stdout.String()), nil
45-
}
46-
47-
func stripGitEnv(env []string) []string {
48-
filtered := make([]string, 0, len(env))
49-
for _, e := range env {
50-
if !strings.HasPrefix(e, "GIT_") {
51-
filtered = append(filtered, e)
52-
}
21+
result, err := agent.RunIsolatedTextGeneratorCLI(ctx, codexCommandRunner, "codex", "codex", args, prompt)
22+
if err != nil {
23+
return "", fmt.Errorf("codex text generation failed: %w", err)
5324
}
54-
return filtered
25+
return result, nil
5526
}
Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
package copilotcli
22

33
import (
4-
"bytes"
54
"context"
6-
"errors"
75
"fmt"
8-
"os"
96
"os/exec"
10-
"strings"
7+
8+
"github.com/entireio/cli/cmd/entire/cli/agent"
119
)
1210

1311
var copilotCommandRunner = exec.CommandContext
@@ -19,35 +17,9 @@ func (c *CopilotCLIAgent) GenerateText(ctx context.Context, prompt string, model
1917
args = append(args, "--model", model)
2018
}
2119

22-
cmd := copilotCommandRunner(ctx, "copilot", args...)
23-
cmd.Dir = os.TempDir()
24-
cmd.Env = stripGitEnv(os.Environ())
25-
26-
var stdout, stderr bytes.Buffer
27-
cmd.Stdout = &stdout
28-
cmd.Stderr = &stderr
29-
30-
if err := cmd.Run(); err != nil {
31-
var execErr *exec.Error
32-
if errors.As(err, &execErr) {
33-
return "", fmt.Errorf("copilot CLI not found: %w", err)
34-
}
35-
var exitErr *exec.ExitError
36-
if errors.As(err, &exitErr) {
37-
return "", fmt.Errorf("copilot CLI failed (exit %d): %s", exitErr.ExitCode(), stderr.String())
38-
}
39-
return "", fmt.Errorf("failed to run copilot CLI: %w", err)
40-
}
41-
42-
return strings.TrimSpace(stdout.String()), nil
43-
}
44-
45-
func stripGitEnv(env []string) []string {
46-
filtered := make([]string, 0, len(env))
47-
for _, e := range env {
48-
if !strings.HasPrefix(e, "GIT_") {
49-
filtered = append(filtered, e)
50-
}
20+
result, err := agent.RunIsolatedTextGeneratorCLI(ctx, copilotCommandRunner, "copilot", "copilot", args, "")
21+
if err != nil {
22+
return "", fmt.Errorf("copilot text generation failed: %w", err)
5123
}
52-
return filtered
24+
return result, nil
5325
}
Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package cursor
22

33
import (
4-
"bytes"
54
"context"
6-
"errors"
75
"fmt"
86
"os"
97
"os/exec"
10-
"strings"
8+
9+
"github.com/entireio/cli/cmd/entire/cli/agent"
1110
)
1211

1312
var cursorCommandRunner = exec.CommandContext
@@ -19,35 +18,9 @@ func (c *CursorAgent) GenerateText(ctx context.Context, prompt string, model str
1918
args = append(args, "--model", model)
2019
}
2120

22-
cmd := cursorCommandRunner(ctx, "agent", args...)
23-
cmd.Dir = os.TempDir()
24-
cmd.Env = stripGitEnv(os.Environ())
25-
26-
var stdout, stderr bytes.Buffer
27-
cmd.Stdout = &stdout
28-
cmd.Stderr = &stderr
29-
30-
if err := cmd.Run(); err != nil {
31-
var execErr *exec.Error
32-
if errors.As(err, &execErr) {
33-
return "", fmt.Errorf("cursor CLI not found: %w", err)
34-
}
35-
var exitErr *exec.ExitError
36-
if errors.As(err, &exitErr) {
37-
return "", fmt.Errorf("cursor CLI failed (exit %d): %s", exitErr.ExitCode(), stderr.String())
38-
}
39-
return "", fmt.Errorf("failed to run cursor CLI: %w", err)
40-
}
41-
42-
return strings.TrimSpace(stdout.String()), nil
43-
}
44-
45-
func stripGitEnv(env []string) []string {
46-
filtered := make([]string, 0, len(env))
47-
for _, e := range env {
48-
if !strings.HasPrefix(e, "GIT_") {
49-
filtered = append(filtered, e)
50-
}
21+
result, err := agent.RunIsolatedTextGeneratorCLI(ctx, cursorCommandRunner, "agent", "cursor", args, "")
22+
if err != nil {
23+
return "", fmt.Errorf("cursor text generation failed: %w", err)
5124
}
52-
return filtered
25+
return result, nil
5326
}
Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
package geminicli
22

33
import (
4-
"bytes"
54
"context"
6-
"errors"
75
"fmt"
8-
"os"
96
"os/exec"
10-
"strings"
7+
8+
"github.com/entireio/cli/cmd/entire/cli/agent"
119
)
1210

1311
var geminiCommandRunner = exec.CommandContext
@@ -19,36 +17,9 @@ func (g *GeminiCLIAgent) GenerateText(ctx context.Context, prompt string, model
1917
args = append(args, "--model", model)
2018
}
2119

22-
cmd := geminiCommandRunner(ctx, "gemini", args...)
23-
cmd.Dir = os.TempDir()
24-
cmd.Env = stripGitEnv(os.Environ())
25-
cmd.Stdin = strings.NewReader(prompt)
26-
27-
var stdout, stderr bytes.Buffer
28-
cmd.Stdout = &stdout
29-
cmd.Stderr = &stderr
30-
31-
if err := cmd.Run(); err != nil {
32-
var execErr *exec.Error
33-
if errors.As(err, &execErr) {
34-
return "", fmt.Errorf("gemini CLI not found: %w", err)
35-
}
36-
var exitErr *exec.ExitError
37-
if errors.As(err, &exitErr) {
38-
return "", fmt.Errorf("gemini CLI failed (exit %d): %s", exitErr.ExitCode(), stderr.String())
39-
}
40-
return "", fmt.Errorf("failed to run gemini CLI: %w", err)
41-
}
42-
43-
return strings.TrimSpace(stdout.String()), nil
44-
}
45-
46-
func stripGitEnv(env []string) []string {
47-
filtered := make([]string, 0, len(env))
48-
for _, e := range env {
49-
if !strings.HasPrefix(e, "GIT_") {
50-
filtered = append(filtered, e)
51-
}
20+
result, err := agent.RunIsolatedTextGeneratorCLI(ctx, geminiCommandRunner, "gemini", "gemini", args, prompt)
21+
if err != nil {
22+
return "", fmt.Errorf("gemini text generation failed: %w", err)
5223
}
53-
return filtered
24+
return result, nil
5425
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package agent
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"errors"
7+
"fmt"
8+
"os"
9+
"os/exec"
10+
"strings"
11+
)
12+
13+
// TextCommandRunner matches exec.CommandContext and allows tests to inject a runner.
14+
type TextCommandRunner func(ctx context.Context, name string, args ...string) *exec.Cmd
15+
16+
// RunIsolatedTextGeneratorCLI executes a text-generation CLI in an isolated temp
17+
// directory with all GIT_* environment variables removed. This avoids recursive
18+
// hook triggers and repo side effects while preserving provider-specific flags.
19+
func RunIsolatedTextGeneratorCLI(ctx context.Context, runner TextCommandRunner, binary, displayName string, args []string, stdin string) (string, error) {
20+
if runner == nil {
21+
runner = exec.CommandContext
22+
}
23+
24+
cmd := runner(ctx, binary, args...)
25+
cmd.Dir = os.TempDir()
26+
cmd.Env = StripGitEnv(os.Environ())
27+
if stdin != "" {
28+
cmd.Stdin = strings.NewReader(stdin)
29+
}
30+
31+
var stdout, stderr bytes.Buffer
32+
cmd.Stdout = &stdout
33+
cmd.Stderr = &stderr
34+
35+
if err := cmd.Run(); err != nil {
36+
var execErr *exec.Error
37+
if errors.As(err, &execErr) {
38+
return "", fmt.Errorf("%s CLI not found: %w", displayName, err)
39+
}
40+
var exitErr *exec.ExitError
41+
if errors.As(err, &exitErr) {
42+
return "", fmt.Errorf("%s CLI failed (exit %d): %s", displayName, exitErr.ExitCode(), stderr.String())
43+
}
44+
return "", fmt.Errorf("failed to run %s CLI: %w", displayName, err)
45+
}
46+
47+
return strings.TrimSpace(stdout.String()), nil
48+
}
49+
50+
func StripGitEnv(env []string) []string {
51+
filtered := make([]string, 0, len(env))
52+
for _, e := range env {
53+
if !strings.HasPrefix(e, "GIT_") {
54+
filtered = append(filtered, e)
55+
}
56+
}
57+
return filtered
58+
}

0 commit comments

Comments
 (0)