feat(v0.5): launch agent inside project subdirectory via launch_dir
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>
This commit is contained in:
+22
-3
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/warrenronsiek/ctask/internal/lockfile"
|
||||
"github.com/warrenronsiek/ctask/internal/shell"
|
||||
"github.com/warrenronsiek/ctask/internal/workspace"
|
||||
)
|
||||
|
||||
// LaunchOpts configures a session launch.
|
||||
@@ -20,6 +21,12 @@ type LaunchOpts struct {
|
||||
Slug string
|
||||
Shell bool // true = interactive shell, false = agent
|
||||
|
||||
// LaunchDir is the workspace-relative launch directory (v0.5). Empty for
|
||||
// tasks and pre-v0.5 projects. When set, Run resolves the absolute path
|
||||
// via workspace.ResolveLaunch and uses it as the child's working dir;
|
||||
// a security violation (absolute path or .. escape) aborts the session.
|
||||
LaunchDir string
|
||||
|
||||
// Force suppresses both the active-session warning (Layer 1) and the
|
||||
// stale-workspace warning (Layer 3). It does NOT disable the metadata
|
||||
// write lock or the session summary. Used for scripted/automated runs.
|
||||
@@ -138,15 +145,27 @@ func Run(opts LaunchOpts) error {
|
||||
hb = StartHeartbeat(leasePath, HeartbeatInterval)
|
||||
}
|
||||
|
||||
// ---- Resolve launch directory (v0.5) ----
|
||||
// Security violations (absolute path, ..-escape) abort the session
|
||||
// before we hand control to the child. Missing/non-dir falls back to
|
||||
// wsDir with a warning.
|
||||
launchAbs, launchWarn, launchErr := workspace.ResolveLaunch(opts.WsDir, opts.LaunchDir)
|
||||
if launchErr != nil {
|
||||
return fmt.Errorf("resolving launch_dir: %w", launchErr)
|
||||
}
|
||||
if launchWarn != "" {
|
||||
fmt.Fprintln(os.Stderr, launchWarn)
|
||||
}
|
||||
|
||||
// ---- Run the child ----
|
||||
var childErr error
|
||||
if opts.Shell {
|
||||
childErr = shell.ExecShell(opts.WsDir, opts.EnvVars, opts.Slug, opts.Mode)
|
||||
childErr = shell.ExecShell(launchAbs, opts.EnvVars, opts.Slug, opts.Mode)
|
||||
} else {
|
||||
for _, line := range shell.BannerLines(opts.Mode, opts.Slug, opts.WsDir) {
|
||||
for _, line := range shell.BannerLines(opts.Mode, opts.Slug, opts.WsDir, opts.LaunchDir) {
|
||||
fmt.Println(line)
|
||||
}
|
||||
childErr = shell.ExecAgent(opts.Agent, opts.WsDir, opts.EnvVars)
|
||||
childErr = shell.ExecAgent(opts.Agent, launchAbs, opts.EnvVars)
|
||||
}
|
||||
|
||||
if hb != nil {
|
||||
|
||||
Reference in New Issue
Block a user