Skip to content

Commit 76b82f6

Browse files
committed
feat: make edit flag-only and clarify body replacement semantics
1 parent c3e9d9a commit 76b82f6

File tree

5 files changed

+52
-23
lines changed

5 files changed

+52
-23
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ felt tree # dependency tree
165165
### Editing
166166

167167
```bash
168-
felt edit <id> # open in $EDITOR
168+
felt edit <id> --body "text" # replace full body (destructive overwrite)
169169
felt edit <id> --title "new" # set title
170170
felt edit <id> -s active # set status
171171
felt edit <id> -o "outcome" # set outcome

cmd/edit.go

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package cmd
22

33
import (
44
"fmt"
5-
"os"
6-
"os/exec"
75
"time"
86

97
"github.com/cailmdaley/felt/internal/felt"
@@ -25,13 +23,13 @@ var linkLabel string
2523

2624
var editCmd = &cobra.Command{
2725
Use: "edit <id>",
28-
Short: "Modify a felt's properties or open in $EDITOR",
29-
Long: `Modifies a felt's properties via flags, or opens in $EDITOR if no flags given.
26+
Short: "Modify a felt's properties via flags",
27+
Long: `Modifies a felt's properties via flags.
3028
3129
Examples:
3230
felt edit abc123 --title "New title" -s active
3331
felt edit abc123 --depends-on other-fiber-id
34-
felt edit abc123 # opens in $EDITOR`,
32+
felt edit abc123 --body "Full replacement body text" # overwrites body`,
3533
Args: cobra.ExactArgs(1),
3634
RunE: func(cmd *cobra.Command, args []string) error {
3735
root, err := felt.FindProjectRoot()
@@ -54,21 +52,12 @@ Examples:
5452
cmd.Flags().Changed("outcome")
5553

5654
if !hasFlags {
57-
// No flags: open in editor
58-
editor := os.Getenv("EDITOR")
59-
if editor == "" {
60-
editor = "vim"
61-
}
62-
63-
path := storage.Path(f.ID)
64-
editorCmd := exec.Command(editor, path)
65-
editorCmd.Stdin = os.Stdin
66-
editorCmd.Stdout = os.Stdout
67-
editorCmd.Stderr = os.Stderr
68-
69-
return editorCmd.Run()
55+
return fmt.Errorf("no changes requested: use edit flags (use --body only when you intend to overwrite the full body)")
7056
}
7157

58+
bodyOverwritten := false
59+
bodyCleared := false
60+
7261
// Apply flag modifications
7362
if cmd.Flags().Changed("title") {
7463
f.Title = editTitle
@@ -96,6 +85,12 @@ Examples:
9685
}
9786
}
9887
if cmd.Flags().Changed("body") {
88+
if f.Body != "" && editBody != f.Body {
89+
bodyOverwritten = true
90+
}
91+
if f.Body != "" && editBody == "" {
92+
bodyCleared = true
93+
}
9994
f.Body = editBody
10095
}
10196
if cmd.Flags().Changed("outcome") {
@@ -148,7 +143,14 @@ Examples:
148143
return err
149144
}
150145

151-
fmt.Printf("Updated %s\n", f.ID)
146+
switch {
147+
case bodyCleared:
148+
fmt.Printf("Updated %s (body cleared; previous content removed)\n", f.ID)
149+
case bodyOverwritten:
150+
fmt.Printf("Updated %s (body overwritten)\n", f.ID)
151+
default:
152+
fmt.Printf("Updated %s\n", f.ID)
153+
}
152154
return nil
153155
},
154156
}
@@ -311,7 +313,7 @@ func init() {
311313
// Edit command flags
312314
editCmd.Flags().StringVarP(&editTitle, "title", "t", "", "Set title")
313315
editCmd.Flags().StringVarP(&editStatus, "status", "s", "", "Set status (open, active, closed)")
314-
editCmd.Flags().StringVarP(&editBody, "body", "b", "", "Set body text")
316+
editCmd.Flags().StringVarP(&editBody, "body", "b", "", "Replace full body text (destructive overwrite)")
315317
editCmd.Flags().StringVarP(&editOutcome, "outcome", "o", "", "Set outcome")
316318
editCmd.Flags().StringVarP(&editDue, "due", "D", "", "Set due date (YYYY-MM-DD, empty to clear)")
317319
editCmd.Flags().StringArrayVarP(&editDeps, "depends-on", "a", nil, "Add dependency (repeatable)")

cmd/hook.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ func minimalOutput() string {
6666
` + cliReference() + `## Core Rules
6767
- **Use felt for everything** — tasks, decisions, questions, detours, bugs you can't chase now. If it might matter, it's a fiber.
6868
- Outcome (` + "`-o`" + `) is the documentation: the conclusion, the reasoning, what was learned
69+
- ` + "`felt edit`" + ` is non-interactive (flags only); for patch edits, modify ` + "`.felt/<id>.md`" + ` directly
6970
- **Leave a wake** — file as you go
7071
- **Titles are DAG node labels: 2-3 words.** Body and outcome carry full content.
7172
`
@@ -149,6 +150,7 @@ func formatSessionOutput(felts []*felt.Felt, g *felt.Graph) string {
149150
sb.WriteString("## Core Rules\n")
150151
sb.WriteString("- **Use felt for everything** — tasks, decisions, questions, detours, bugs you can't chase now. If it might matter, it's a fiber.\n")
151152
sb.WriteString("- Outcome (`-o`) is the documentation: the conclusion, the reasoning, what was learned\n")
153+
sb.WriteString("- `felt edit` is non-interactive (flags only); for patch edits, modify `.felt/<id>.md` directly\n")
152154
sb.WriteString("- **Leave a wake** — file as you go\n")
153155
sb.WriteString("- **Titles are DAG node labels: 2-3 words.** Body and outcome carry full content.\n")
154156

@@ -213,6 +215,7 @@ felt "title" # create fiber
213215
felt add "title" -s open -t tag -a <dep-id> -o "outcome"
214216
felt edit <id> -s active # enter tracking / mark active
215217
felt edit <id> -s closed -o "outcome" # close with outcome
218+
felt edit <id> --body "text" # full body replacement (overwrite)
216219
felt comment <id> "note" # add comment
217220
felt show <id> # full details
218221
felt ls # tracked fibers (open/active)
@@ -227,4 +230,3 @@ To patch body text (not replace), edit .felt/<id>.md directly.
227230
228231
`
229232
}
230-

cmd/integration_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,31 @@ func TestIntegration(t *testing.T) {
9898
t.Fatalf("edit: expected active status, got: %s", out)
9999
}
100100

101+
// edit with no flags should fail (agent-first, non-interactive)
102+
out, err := felt(dir, "edit", fiberID)
103+
if err == nil {
104+
t.Fatal("edit without flags: expected error")
105+
}
106+
if !strings.Contains(out, "no changes requested") {
107+
t.Fatalf("edit without flags: expected helpful error, got: %s", out)
108+
}
109+
110+
// initial body set on empty body should be a normal update (not overwrite warning)
111+
out = mustFelt(t, dir, "edit", fiberID, "--body", "initial body")
112+
if strings.Contains(out, "body overwritten") {
113+
t.Fatalf("edit --body first set: should not warn overwrite, got: %s", out)
114+
}
115+
116+
// replacing non-empty body should be called out as overwrite
117+
out = mustFelt(t, dir, "edit", fiberID, "--body", "replacement body")
118+
if !strings.Contains(out, "body overwritten") {
119+
t.Fatalf("edit --body replace: expected overwrite message, got: %s", out)
120+
}
121+
out = mustFelt(t, dir, "show", fiberID, "--body")
122+
if !strings.Contains(out, "replacement body") {
123+
t.Fatalf("edit --body: expected replacement content, got: %s", out)
124+
}
125+
101126
// comment
102127
mustFelt(t, dir, "comment", fiberID, "a test comment")
103128

docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ felt tree # dependency tree
151151
### Editing
152152

153153
```bash
154-
felt edit <id> # open in $EDITOR
154+
felt edit <id> --body "text" # replace full body (destructive overwrite)
155155
felt edit <id> --title "new" # set title
156156
felt edit <id> -s active # set status
157157
felt edit <id> -o "outcome" # set outcome

0 commit comments

Comments
 (0)