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.
This commit is contained in:
2026-05-15 11:08:03 -04:00
parent 24f213449e
commit b75b82e676
15 changed files with 317 additions and 110 deletions
+22 -13
View File
@@ -5,6 +5,7 @@ import (
"os"
"github.com/spf13/cobra"
"github.com/warrenronsiek/ctask/internal/agent"
"github.com/warrenronsiek/ctask/internal/config"
"github.com/warrenronsiek/ctask/internal/shell"
"github.com/warrenronsiek/ctask/internal/workspace"
@@ -53,9 +54,12 @@ func runNew(cmd *cobra.Command, args []string) error {
return err
}
agent := newAgent
if agent == "" {
agent = config.ResolveAgent()
// Task 3: minimal wiring — the --agent type-selector rework lands in
// Task 4. For now the resolved type drives both the workspace AgentSpec
// and the launch-time agent.Resolve.
agentType := newAgent
if agentType == "" {
agentType = config.ResolveAgent()
}
title := ""
@@ -92,7 +96,7 @@ func runNew(cmd *cobra.Command, args []string) error {
Title: title,
Category: category,
Mode: "local",
AgentSpec: workspace.AgentSpec{Type: agent},
AgentSpec: workspace.AgentSpec{Type: agentType},
IsProject: newProject,
SeedDir: config.ResolveSeedDir(),
SkipCategoryDir: skipCategoryDir,
@@ -127,20 +131,25 @@ func runNew(cmd *cobra.Command, args []string) error {
return nil
}
resolved, err := agent.Resolve(workspace.AgentSpec{Type: agentType}, agentType)
if err != nil {
return err
}
// Re-set the workspace's root: workspace.Create returned ws but our
// computed `root` is what should be exported via CTASK_ROOT (it may
// differ from ws-derived defaults when --project + CTASK_PROJECT_ROOT
// are in play).
return runWorkspaceEntry(WorkspaceEntryOptions{
WsPath: ws.Path,
WsRoot: root,
WsMeta: ws.Meta,
Agent: agent,
Shell: newShell,
Direct: newDirect,
CommandName: "new",
TmuxPath: tmuxPath,
NewlyCreated: true,
WsPath: ws.Path,
WsRoot: root,
WsMeta: ws.Meta,
ResolvedAgent: resolved,
Shell: newShell,
Direct: newDirect,
CommandName: "new",
TmuxPath: tmuxPath,
NewlyCreated: true,
})
}