10ab9efc80
Refactor new/resume/open to use session.Run() which wraps child process launch with pre/post manifest capture and append-only session logging to logs/sessions.log. Bump version to 0.2.0. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
102 lines
2.7 KiB
Go
102 lines
2.7 KiB
Go
package session
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/warrenronsiek/ctask/internal/shell"
|
|
)
|
|
|
|
// LaunchOpts configures a session launch.
|
|
type LaunchOpts struct {
|
|
WsDir string
|
|
EnvVars map[string]string
|
|
Agent string
|
|
Mode string
|
|
Slug string
|
|
Shell bool // true = interactive shell, false = agent
|
|
}
|
|
|
|
// manifestStartPath returns the path to the start manifest file.
|
|
func manifestStartPath(wsDir string) string {
|
|
return filepath.Join(wsDir, ".ctask", "manifest-start.json")
|
|
}
|
|
|
|
// Run launches an agent or shell session with pre/post manifest capture and session logging.
|
|
// Returns the child process error (for exit code propagation).
|
|
func Run(opts LaunchOpts) error {
|
|
startTime := time.Now().UTC().Truncate(time.Second)
|
|
|
|
// Pre-session: capture start manifest
|
|
startManifest, err := CaptureManifest(opts.WsDir)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "[ctask] warning: failed to capture start manifest: %v\n", err)
|
|
// Continue anyway — never block the user
|
|
}
|
|
|
|
if startManifest != nil {
|
|
mPath := manifestStartPath(opts.WsDir)
|
|
if err := WriteManifest(mPath, startManifest); err != nil {
|
|
fmt.Fprintf(os.Stderr, "[ctask] warning: failed to write start manifest: %v\n", err)
|
|
}
|
|
}
|
|
|
|
// Launch the session
|
|
var childErr error
|
|
if opts.Shell {
|
|
childErr = shell.ExecShell(opts.WsDir, opts.EnvVars, opts.Slug, opts.Mode)
|
|
} else {
|
|
// Print banner before agent launch
|
|
for _, line := range shell.BannerLines(opts.Mode, opts.Slug, opts.WsDir) {
|
|
fmt.Println(line)
|
|
}
|
|
childErr = shell.ExecAgent(opts.Agent, opts.WsDir, opts.EnvVars)
|
|
}
|
|
|
|
// Post-session: capture end manifest and log
|
|
endTime := time.Now().UTC().Truncate(time.Second)
|
|
|
|
if startManifest != nil {
|
|
if logErr := captureAndLog(opts, startManifest, startTime, endTime); logErr != nil {
|
|
fmt.Fprintf(os.Stderr, "[ctask] warning: session logging failed: %v\n", logErr)
|
|
// Keep manifest-start.json for debugging
|
|
} else {
|
|
// Clean up manifest-start.json on success
|
|
os.Remove(manifestStartPath(opts.WsDir))
|
|
}
|
|
}
|
|
|
|
return childErr
|
|
}
|
|
|
|
// captureAndLog captures the end manifest, diffs, and appends to session log.
|
|
func captureAndLog(opts LaunchOpts, startManifest *Manifest, startTime, endTime time.Time) error {
|
|
endManifest, err := CaptureManifest(opts.WsDir)
|
|
if err != nil {
|
|
return fmt.Errorf("capturing end manifest: %w", err)
|
|
}
|
|
|
|
diff := DiffManifests(startManifest, endManifest)
|
|
|
|
info := &SessionInfo{
|
|
Agent: opts.Agent,
|
|
Mode: opts.Mode,
|
|
StartTime: startTime,
|
|
EndTime: endTime,
|
|
Diff: diff,
|
|
}
|
|
|
|
// For shell sessions, record "shell" as agent
|
|
if opts.Shell {
|
|
info.Agent = "shell"
|
|
}
|
|
|
|
if err := AppendSessionLog(opts.WsDir, info); err != nil {
|
|
return fmt.Errorf("appending session log: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|