Repository navigation
Expand file tree
/
Copy pathcli_baton.go
More file actions
149 lines (138 loc) · 4.31 KB
/
Copy pathcli_baton.go
File metadata and controls
149 lines (138 loc) · 4.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package main
import (
"errors"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/jmwri/flockdeck/internal/baton"
)
// batonFlags are the flags of `flockdeck baton show`.
type batonFlags struct {
path bool
}
func batonShowFlagSet(f *batonFlags) *flag.FlagSet {
fs := flag.NewFlagSet("baton show", flag.ContinueOnError)
fs.SetOutput(os.Stderr)
fs.BoolVar(&f.path, "path", false, "print the file the baton is kept in, and nothing else")
fs.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: flockdeck baton show [-path] <baton-id>\n\n")
fmt.Fprintf(os.Stderr, "Prints a baton, the handoff another agent started this work from. A pane that\n")
fmt.Fprintf(os.Stderr, "was started from one is told its id; `flockdeck baton list` shows them all.\n\nFlags:\n")
fs.PrintDefaults()
}
return fs
}
// runBaton implements `flockdeck baton`: show prints one baton and list lists
// them, newest first. Batons are files in the state directory, so it needs no
// running instance, and works from inside a pane or outside one.
func runBaton(args []string, out io.Writer) error {
s, err := baton.Open()
if err != nil {
return err
}
return runBatonIn(s, args, out)
}
// runBatonIn is runBaton over a store it is given, so a test can point it at a
// temporary one.
func runBatonIn(s *baton.Store, args []string, out io.Writer) error {
if len(args) == 0 || args[0] == "-h" || args[0] == "-help" || args[0] == "--help" {
batonUsage()
if len(args) == 0 {
return errReported
}
return nil
}
switch args[0] {
case "list":
return listBatons(s, args[1:], out)
case "show":
return showBaton(s, args[1:], out)
}
batonUsage()
return fmt.Errorf("baton has no subcommand %q", args[0])
}
func batonUsage() {
fmt.Fprintf(os.Stderr, "Usage: flockdeck baton list\n")
fmt.Fprintf(os.Stderr, " flockdeck baton show [-path] <baton-id>\n\n")
fmt.Fprintf(os.Stderr, "A baton is a short document one agent hands to the next: what the work is for,\n")
fmt.Fprintf(os.Stderr, "where it stands, the files and commands, and what must hold. They are kept in\n")
fmt.Fprintf(os.Stderr, "Flockdeck's state directory, on this machine only. Start an agent from one with\n")
fmt.Fprintf(os.Stderr, "`flockdeck spawn -baton <self | pane-id | baton-id | file> <task>`.\n")
}
func listBatons(s *baton.Store, args []string, out io.Writer) error {
if len(args) > 0 {
batonUsage()
return fmt.Errorf("baton list takes no arguments")
}
all, err := s.List()
if err != nil {
return err
}
if len(all) == 0 {
fmt.Fprintln(out, "no batons; make one from a pane with Make baton in the command palette (folder:", s.Dir()+")")
return nil
}
for _, b := range all {
when := "-"
if !b.Created.IsZero() {
when = b.Created.Local().Format("2006-01-02 15:04")
}
agent := b.FromAgent
if agent == "" {
agent = "-"
}
// Printed to a terminal, so nothing in a stored file may move the cursor.
fmt.Fprint(out, baton.CleanText(fmt.Sprintf("%s %s %-12s %s\n", b.ID, when, agent, oneLine(b.Title))))
}
return nil
}
func showBaton(s *baton.Store, args []string, out io.Writer) error {
var f batonFlags
fs := batonShowFlagSet(&f)
if err := fs.Parse(args); err != nil {
if errors.Is(err, flag.ErrHelp) {
return nil
}
return errReported
}
if fs.NArg() != 1 {
fs.Usage()
return errReported
}
id := strings.TrimSpace(fs.Arg(0))
b, err := s.Load(id)
if errors.Is(err, baton.ErrNotFound) {
return fmt.Errorf("no baton %q; flockdeck baton list shows what there is", id)
}
if err != nil {
return err
}
s.Touch(id)
if f.path {
fmt.Fprintln(out, s.Path(id))
return nil
}
fmt.Fprint(out, baton.CleanText(baton.Render(b)))
return nil
}
// batonRef turns what `spawn -baton` was given into the reference the
// application resolves: "self" and a baton id as they are, a file that exists
// as "path:" and its absolute path, and anything else as it was written, which
// the application takes for a pane id and refuses if it is none.
func batonRef(ref string) string {
ref = strings.TrimSpace(ref)
if ref == "self" || baton.ValidID(ref) {
return ref
}
if fi, err := os.Stat(ref); err == nil && !fi.IsDir() {
if abs, err := filepath.Abs(ref); err == nil {
return "path:" + abs
}
}
return ref
}
// oneLine keeps a title to the one line a list row has.
func oneLine(s string) string { return strings.Join(strings.Fields(s), " ") }