103f2cd33e
LaunchOpts gains LaunchDir. session.Run resolves it via workspace.ResolveLaunch, prints any fallback warning, and passes the absolute path as the child process's working directory. Security violations (absolute paths, .. escape) abort the session. The banner gains a 'project dir: <name>/' line when launch_dir is set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
78 lines
2.2 KiB
Go
78 lines
2.2 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/warrenronsiek/ctask/internal/config"
|
|
"github.com/warrenronsiek/ctask/internal/session"
|
|
"github.com/warrenronsiek/ctask/internal/shell"
|
|
"github.com/warrenronsiek/ctask/internal/workspace"
|
|
)
|
|
|
|
var resumeCmd = &cobra.Command{
|
|
Use: "resume <query>",
|
|
Short: "Reopen an existing workspace and launch the agent",
|
|
Args: cobra.ExactArgs(1),
|
|
SilenceUsage: true,
|
|
RunE: runResume,
|
|
}
|
|
|
|
var (
|
|
resumeContainer bool
|
|
resumeShell bool
|
|
resumeAgent string
|
|
resumeForce bool
|
|
)
|
|
|
|
func init() {
|
|
resumeCmd.Flags().BoolVar(&resumeContainer, "container", false, "Resume in container mode (deferred)")
|
|
resumeCmd.Flags().BoolVar(&resumeShell, "shell", false, "Open shell instead of agent")
|
|
resumeCmd.Flags().StringVarP(&resumeAgent, "agent", "a", "", "Override agent command")
|
|
resumeCmd.Flags().BoolVar(&resumeForce, "force", false, "Skip active-session and stale-workspace warnings")
|
|
rootCmd.AddCommand(resumeCmd)
|
|
}
|
|
|
|
func runResume(cmd *cobra.Command, args []string) error {
|
|
return doResume(args[0], resumeContainer, resumeShell, resumeForce, resumeAgent)
|
|
}
|
|
|
|
// doResume is the shared resume logic used by both resume and last commands.
|
|
func doResume(query string, container, useShell, force bool, agentOverride string) error {
|
|
if container {
|
|
fmt.Println(shell.ContainerNotice())
|
|
return nil
|
|
}
|
|
|
|
roots := config.SearchRoots()
|
|
ws := resolveOne(roots, query, false)
|
|
|
|
// Update updated_at
|
|
now := time.Now().UTC().Truncate(time.Second)
|
|
ws.Meta.UpdatedAt = now
|
|
metaPath := filepath.Join(ws.Path, "task.yaml")
|
|
if err := workspace.WriteMetaLocked(metaPath, ws.Meta); err != nil {
|
|
return fmt.Errorf("updating metadata: %w", err)
|
|
}
|
|
|
|
agent := agentOverride
|
|
if agent == "" {
|
|
agent = ws.Meta.Agent
|
|
}
|
|
|
|
envVars := config.EnvVars(ws.Meta.Slug, ws.Meta.Mode, ws.Root, ws.Path, ws.Meta.Category, workspace.EffectiveType(ws.Meta), ws.Meta.LaunchDir)
|
|
|
|
return session.Run(session.LaunchOpts{
|
|
WsDir: ws.Path,
|
|
EnvVars: envVars,
|
|
Agent: agent,
|
|
Mode: ws.Meta.Mode,
|
|
Slug: ws.Meta.Slug,
|
|
Shell: useShell,
|
|
LaunchDir: ws.Meta.LaunchDir,
|
|
Force: force,
|
|
})
|
|
}
|