Work>work chooses its own task and merges from the interface; panes find their tools; shell follows the project folder

This commit is contained in:
Sam
2026-11-11 12:02:12 +11:00
parent 678c497064
commit 7198c34e42
13 changed files with 441 additions and 15 deletions

View File

@@ -88,3 +88,55 @@ func splitLines(s string) []string {
}
return out
}
// mergeLogPath is where a merge writes what it did, so a panel can show it
// without the person opening a shell or remembering a command.
func mergeLogPath() string {
return filepath.Join(os.TempDir(), "paperclip-harness-merge.log")
}
// startMerge runs bin/merge for one task, detached, and returns at once. The
// Work workspace tails the log; the merge itself takes a lock, so starting one
// twice by accident is refused rather than doubled.
func startMerge(folder, identifier string) error {
merge := filepath.Join(exeDir(), "merge")
if _, err := os.Stat(merge); err != nil {
return fmt.Errorf("no bin/merge next to the program (%s)", merge)
}
log, err := os.Create(mergeLogPath())
if err != nil {
return err
}
fmt.Fprintf(log, "merge of %s started %s\n", identifier, time.Now().Format("15:04:05"))
cmd := exec.Command(merge, identifier)
cmd.Dir = folder
cmd.Stdout = log
cmd.Stderr = log
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
if err := cmd.Start(); err != nil {
log.Close()
return err
}
go func() {
err := cmd.Wait()
if err != nil {
fmt.Fprintf(log, "\nmerge stopped: %v\n", err)
} else {
fmt.Fprintf(log, "\nmerge finished %s\n", time.Now().Format("15:04:05"))
}
log.Close()
}()
return nil
}
// mergeLogTail returns the end of the last merge, for the panel that shows it.
func mergeLogTail(lines int) []string {
b, err := os.ReadFile(mergeLogPath())
if err != nil {
return []string{"", " no merge has been run from this screen yet"}
}
all := splitLines(string(b))
start := max(0, len(all)-lines)
return all[start:]
}