diff --git a/pkg/agent/prompt_resolver.go b/pkg/agent/prompt_resolver.go index c4bfe88..4495a6a 100644 --- a/pkg/agent/prompt_resolver.go +++ b/pkg/agent/prompt_resolver.go @@ -46,11 +46,13 @@ func resolveExecPrompt(cmds []string, cwd string, env []string) (string, error) if cwd != "" { if info, err := os.Stat(cwd); err == nil && info.IsDir() { cmd.Dir = cwd + } else { + // CWD doesn't exist locally (remote session). Inject PWD so + // scripts that use ${PWD} via envsubst see the correct value. + env = append(env, "PWD="+cwd) } } - if len(env) > 0 { - cmd.Env = append(os.Environ(), env...) - } + cmd.Env = append(os.Environ(), env...) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr @@ -101,11 +103,17 @@ func resolveStringPrompt(prompt, cwd string, env []string) (string, error) { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() cmd := exec.CommandContext(ctx, "sh", "-c", cmdStr) + var cmdEnv []string if cwd != "" { if info, err := os.Stat(cwd); err == nil && info.IsDir() { cmd.Dir = cwd + } else { + cmdEnv = append(cmdEnv, "PWD="+cwd) } } + if len(cmdEnv) > 0 { + cmd.Env = append(os.Environ(), cmdEnv...) + } var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index 43eff71..58e19f9 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -31,6 +31,13 @@ import ( //go:embed bootstrap.sh var bootstrapTemplate string +// HostInfo holds environment details from the remote host. +type HostInfo struct { + Platform string `json:"platform"` + Arch string `json:"arch"` + IsGitRepo bool `json:"is_git_repo"` +} + // Server implements tools.Server by forwarding calls to a remote // ollie-remote process over SSH. type Server struct { @@ -41,6 +48,7 @@ type Server struct { enc *json.Encoder dec *json.Decoder nextID atomic.Int64 + Info HostInfo } // Config holds the parameters for connecting to a remote host. @@ -179,6 +187,15 @@ func Dial(ctx context.Context, cfg Config) (*Server, error) { return nil, fmt.Errorf("remote ping failed: %w", err) } + // Fetch remote host info for prompt resolution. + // FIXME: This duplicates information that prime scripts also try to detect + // locally (platform, is_git_repo). Eventually unify so prompt resolution + // always uses these env vars rather than running local checks against a + // non-existent remote CWD. + if info, err := s.fetchHostInfo(ctx); err == nil { + s.Info = info + } + return s, nil } @@ -300,6 +317,35 @@ func (s *Server) CallTool(ctx context.Context, tool string, args json.RawMessage return resp.Result, nil } +// fetchHostInfo retrieves environment details from the remote host. +func (s *Server) fetchHostInfo(ctx context.Context) (HostInfo, error) { + id := s.nextID.Add(1) + req := rpcRequest{ + JSONRPC: "2.0", + ID: id, + Method: "host_info", + } + + s.mu.Lock() + defer s.mu.Unlock() + + if err := s.enc.Encode(req); err != nil { + return HostInfo{}, err + } + var resp rpcResponse + if err := s.dec.Decode(&resp); err != nil { + return HostInfo{}, err + } + if resp.Error != nil { + return HostInfo{}, fmt.Errorf("%s", resp.Error.Message) + } + var info HostInfo + if err := json.Unmarshal(resp.Result, &info); err != nil { + return HostInfo{}, err + } + return info, nil +} + // ping verifies the remote server is responsive. func (s *Server) ping(ctx context.Context) error { id := s.nextID.Add(1)