feat(v0.5.3): cmd new -- persistent preflight before workspace.Create; delegate to runWorkspaceEntry

This commit is contained in:
2026-05-08 14:01:07 -04:00
parent f5746df314
commit 2d3ebfbc3a
2 changed files with 92 additions and 9 deletions
+43 -9
View File
@@ -6,7 +6,6 @@ import (
"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"
)
@@ -27,6 +26,7 @@ var (
newAgent string
newNoLaunch bool
newProject bool
newDirect bool
)
func init() {
@@ -36,6 +36,7 @@ func init() {
newCmd.Flags().BoolVar(&newShell, "shell", false, "Open interactive shell instead of agent")
newCmd.Flags().StringVarP(&newAgent, "agent", "a", "", "Command to exec as the agent")
newCmd.Flags().BoolVar(&newNoLaunch, "no-launch", false, "Create workspace only, do not launch")
newCmd.Flags().BoolVar(&newDirect, "direct", false, "Bypass persistent session mode for this command")
rootCmd.AddCommand(newCmd)
}
@@ -45,6 +46,13 @@ func runNew(cmd *cobra.Command, args []string) error {
return nil
}
// Persistent-mode preflight runs BEFORE workspace.Create — a missing
// tmux install must not leave a half-initialized workspace on disk.
tmuxPath, err := newPersistentPreflight(newDirect)
if err != nil {
return err
}
agent := newAgent
if agent == "" {
agent = config.ResolveAgent()
@@ -119,16 +127,42 @@ func runNew(cmd *cobra.Command, args []string) error {
return nil
}
envVars := config.EnvVars(ws.Meta.Slug, ws.Meta.Mode, root, ws.Path, ws.Meta.Category, workspace.EffectiveType(ws.Meta), ws.Meta.LaunchDir)
return session.Run(session.LaunchOpts{
WsDir: ws.Path,
EnvVars: envVars,
// 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,
Mode: ws.Meta.Mode,
Slug: ws.Meta.Slug,
Shell: newShell,
LaunchDir: ws.Meta.LaunchDir,
Direct: newDirect,
CommandName: "new",
TmuxPath: tmuxPath,
NewlyCreated: true,
})
}
// newPersistentPreflight runs the persistent-mode preflight for `ctask new`,
// returning the validated tmux path on success or "" when persistent mode
// is not in effect (or --direct was passed). When persistent mode IS in
// effect and --direct was passed, prints the bypass warning and returns
// ("", nil) — the workspace can still be created in direct mode.
//
// `new` is the only command where preflight runs *before* the workspace
// exists; a tmux failure must not leave a half-initialized directory on
// disk. (resume / last / open / attach run preflight inside
// runWorkspaceEntry, after their own resolution step.)
func newPersistentPreflight(directFlag bool) (string, error) {
mode := config.ResolveSessionMode()
if mode != "persistent" {
return "", nil
}
if directFlag {
fmt.Fprintln(os.Stderr,
"[ctask] warning: --direct bypassing persistent mode (no tmux session exists for this workspace)")
return "", nil
}
return preflightPersistentEntry("new")
}