refactor: config permissions and done shortcut

Amolith created

- Config file now written with 0o600 instead of 0o644
- done shortcut creates fresh command copy instead of mutating global
  state

Assisted-by: Claude Opus 4.5 via Amp

Change summary

cmd/done.go               | 14 ++++++++------
internal/config/config.go |  3 ++-
2 files changed, 10 insertions(+), 7 deletions(-)

Detailed changes

cmd/done.go 🔗

@@ -11,16 +11,18 @@ import (
 
 var doneCmd = &cobra.Command{
 	Use:     "done ID",
-	Short:   "Mark a task as completed",
+	Short:   "Mark a task completed",
 	GroupID: "shortcuts",
 	Args:    cobra.ExactArgs(1),
 	RunE: func(cmd *cobra.Command, args []string) error {
-		_ = task.UpdateCmd.Flags().Set("status", "completed")
+		updateCmd := *task.UpdateCmd
+		updateCmd.Flags().AddFlagSet(task.UpdateCmd.Flags())
+		_ = updateCmd.Flags().Set("status", "completed")
 
-		task.UpdateCmd.SetIn(cmd.InOrStdin())
-		task.UpdateCmd.SetOut(cmd.OutOrStdout())
-		task.UpdateCmd.SetErr(cmd.ErrOrStderr())
+		updateCmd.SetIn(cmd.InOrStdin())
+		updateCmd.SetOut(cmd.OutOrStdout())
+		updateCmd.SetErr(cmd.ErrOrStderr())
 
-		return task.UpdateCmd.RunE(task.UpdateCmd, args)
+		return updateCmd.RunE(&updateCmd, args)
 	},
 }

internal/config/config.go 🔗

@@ -106,7 +106,8 @@ func (c *Config) Save() error {
 		return fmt.Errorf("creating config dir: %w", err)
 	}
 
-	f, err := os.Create(path) //nolint:gosec // path is from user config dir
+	//nolint:gosec,mnd // path is from user config dir; 0o600 is intentional
+	f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
 	if err != nil {
 		return fmt.Errorf("creating config file: %w", err)
 	}