Files
osmedeus/pkg/cli/agent.go
T
j3ssie 90ce5f4a16 refactor: consolidate process killing logic and ACP agent defaults
- Extract KillProcessAndChildren into core.types for reuse across CLI and server handlers
- Replace hardcoded 'claude-code' strings with core.DefaultACPAgent constant
- Precompute absolute allowed paths in ACP client to avoid repeated filepath.Abs calls
- Simplify runQuerySteps to delegate to runQueryTable, reducing code duplication
- Refactor agent_chat concurrency guard using sync.Mutex.TryLock for cleaner code
- Use request context for agent timeout instead of background context
2026-03-07 16:49:48 +08:00

110 lines
2.7 KiB
Go

package cli
import (
"context"
"fmt"
"io"
"os"
"sort"
"strings"
"github.com/j3ssie/osmedeus/v5/internal/core"
"github.com/j3ssie/osmedeus/v5/internal/executor"
"github.com/j3ssie/osmedeus/v5/internal/terminal"
"github.com/spf13/cobra"
)
var (
agentName string
agentCwd string
agentStdin bool
agentTimeout string
agentList bool
)
// agentCmd runs an ACP agent interactively from the terminal.
var agentCmd = &cobra.Command{
Use: "agent [message]",
Short: "Run an ACP agent interactively",
Long: UsageAgent(),
RunE: runAgent,
}
func init() {
agentCmd.Flags().StringVar(&agentName, "agent", core.DefaultACPAgent, "agent to use (see --list for available agents)")
agentCmd.Flags().StringVar(&agentCwd, "cwd", "", "working directory for the agent (default: current directory)")
agentCmd.Flags().BoolVar(&agentStdin, "stdin", false, "read message from stdin")
agentCmd.Flags().StringVar(&agentTimeout, "timeout", "30m", "timeout duration (e.g., 30m, 1h)")
agentCmd.Flags().BoolVar(&agentList, "list", false, "list available agents")
}
func runAgent(cmd *cobra.Command, args []string) error {
printer := terminal.NewPrinter()
// List agents
if agentList {
names := executor.ListAgentNames()
sort.Strings(names)
printer.Section("Available ACP Agents")
fmt.Println()
for _, name := range names {
fmt.Printf(" %s %s\n", terminal.SymbolBullet, terminal.Cyan(name))
}
fmt.Println()
return nil
}
// Resolve message
message, err := resolveAgentMessage(args)
if err != nil {
printer.Error("%s", err)
return err
}
// Parse timeout
timeout, err := parseRunDuration(agentTimeout)
if err != nil {
return fmt.Errorf("invalid timeout: %w", err)
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// Build config
cfg := &executor.RunAgentACPConfig{
Cwd: agentCwd,
StreamWriter: os.Stdout,
}
_, _, err = executor.RunAgentACP(ctx, message, agentName, cfg)
if err != nil {
printer.Error("Agent failed: %s", err)
return err
}
return nil
}
// resolveAgentMessage determines the message from positional args, --stdin, or piped stdin.
func resolveAgentMessage(args []string) (string, error) {
// Positional argument (not "-")
if len(args) > 0 && args[0] != "-" {
return strings.Join(args, " "), nil
}
// --stdin flag or "-" argument
if agentStdin || (len(args) > 0 && args[0] == "-") {
data, err := io.ReadAll(os.Stdin)
if err != nil {
return "", fmt.Errorf("failed to read from stdin: %w", err)
}
msg := strings.TrimSpace(string(data))
if msg == "" {
return "", fmt.Errorf("empty message from stdin")
}
return msg, nil
}
return "", fmt.Errorf("no message provided: use positional argument, --stdin, or pipe with -")
}