Files
ctask/cmd/open.go
T
typebasedio b75b82e676 feat(v0.6): launch path carries ResolvedAgent (command + args + env)
LaunchOpts.Agent (string) and WorkspaceEntryOptions.Agent (string) are
replaced by *agent.Resolved, carrying Command, Args, and Env. The five
entry commands (new, resume, last, open, attach) each construct an
AgentSpec from the workspace metadata, apply --agent as a one-shot
agent.command override (Open Q 1 — keeps muscle memory for users
passing executable paths), call agent.Resolve, and pass the result
through. resolveEntryAgent centralises the resume/last/open/attach path.

shell.ExecAgent and shell.ExecTmuxAgent gain an args parameter; agent.env
is merged into the env map at the session.Run launch switch, AFTER
ctask's exported CTASK_* vars (per spec §5: agent.env wins on collision).
mergeAgentEnv is the centralised merge.

Lease, manifest, write lock, heartbeat, summary, and provisional
cleanup are unchanged. The Agent string fields on Lease, SessionSummary,
and SessionInfo continue to record the launched command for diagnostics.
2026-05-15 11:08:03 -04:00

66 lines
1.9 KiB
Go

package cmd
import (
"fmt"
"path/filepath"
"time"
"github.com/spf13/cobra"
"github.com/warrenronsiek/ctask/internal/config"
"github.com/warrenronsiek/ctask/internal/workspace"
)
var openCmd = &cobra.Command{
Use: "open <query>",
Short: "Open a workspace directory without launching the agent",
Args: cobra.ExactArgs(1),
SilenceUsage: true,
RunE: runOpen,
}
var (
openAll bool
openForce bool
openDirect bool
)
func init() {
openCmd.Flags().BoolVarP(&openAll, "all", "a", false, "Include archived workspaces in query resolution")
openCmd.Flags().BoolVar(&openForce, "force", false, "Skip active-session and stale-workspace warnings")
openCmd.Flags().BoolVar(&openDirect, "direct", false, "Bypass persistent session mode for this command")
openCmd.ValidArgsFunction = completeWorkspaces(completionActive)
rootCmd.AddCommand(openCmd)
}
func runOpen(cmd *cobra.Command, args []string) error {
roots := config.SearchRoots()
// PRESERVED v0.5.2 behavior: open's archive resolution is opt-in via --all.
// resolveOne(roots, query, includeArchived) — distinct from resume's
// archived-inclusive-with-restore-hint behavior.
ws := resolveOne(roots, args[0], openAll)
// updated_at bump (existing v0.4 behavior).
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)
}
resolved, err := resolveEntryAgent(ws.Meta.Agent, "")
if err != nil {
return err
}
return runWorkspaceEntry(WorkspaceEntryOptions{
WsPath: ws.Path,
WsRoot: ws.Root,
WsMeta: ws.Meta,
ResolvedAgent: resolved,
Shell: true, // open always launches a shell
Force: openForce,
Direct: openDirect,
CommandName: "open",
})
}