MCPcopy Create free account
hub / github.com/diillson/chatcli / handleMultipatch

Method handleMultipatch

pkg/coder/engine/multipatch.go:78–254  ·  view source on GitHub ↗

handleMultipatch is the engine entry-point for transactional multi-file edits. Contract: 1. The --edits flag carries a JSON array of MultipatchEdit objects. 2. Phase 1 (validate): every edit's path passes validatePath, the file exists, and the search string is found in the content the edit would se

(args []string)

Source from the content-addressed store, hash-verified

76// that, a process kill mid-write leaves a partial multipatch — the
77// agent can use @coder rollback to recover from individual .bak files.
78func (e *Engine) handleMultipatch(args []string) error {
79 fs := flag.NewFlagSet("multipatch", flag.ContinueOnError)
80 editsJSON := fs.String("edits", "", "JSON array of {file,search,replace,encoding?}")
81 if err := parseFlags(fs, args); err != nil {
82 return err
83 }
84 if strings.TrimSpace(*editsJSON) == "" {
85 return errors.New("--edits required")
86 }
87
88 var edits []MultipatchEdit
89 if err := json.Unmarshal([]byte(*editsJSON), &edits); err != nil {
90 return fmt.Errorf("invalid edits JSON: %w", err)
91 }
92 if len(edits) == 0 {
93 return errors.New("edits array cannot be empty")
94 }
95
96 // Phase 0: normalize and group by absolute path. Keep declaration
97 // order within a file because later edits may depend on earlier
98 // edits' replacements.
99 type stagedEdit struct {
100 decl int // 1-indexed declaration position for error reporting
101 edit MultipatchEdit
102 search string // decoded search text
103 replace string
104 }
105 groups := make(map[string][]stagedEdit)
106 groupKeys := make([]string, 0)
107
108 for i, ed := range edits {
109 idx := i + 1
110 if ed.File == "" {
111 return fmt.Errorf("edit #%d: file is required", idx)
112 }
113 if ed.Search == "" {
114 return fmt.Errorf("edit #%d: search is required (use --diff if you have a unified diff)", idx)
115 }
116
117 enc := ed.Encoding
118 if enc == "" {
119 enc = "text"
120 }
121 sBytes, err := smartDecode(ed.Search, enc)
122 if err != nil {
123 return fmt.Errorf("edit #%d: search decode failed: %w", idx, err)
124 }
125 rBytes, err := smartDecode(ed.Replace, enc)
126 if err != nil {
127 return fmt.Errorf("edit #%d: replace decode failed: %w", idx, err)
128 }
129
130 // ed.File arrives inside the --edits JSON, so it bypasses the
131 // path-flag expansion in parseFlags; expand it here too or a
132 // "~/x" / "$CHATCLI_AGENT_TMPDIR/x" edit target would resolve
133 // to a literal directory under the cwd.
134 abs, err := filepath.Abs(expandPath(ed.File))
135 if err != nil {

Callers 1

ExecuteMethod · 0.95

Calls 11

validatePathMethod · 0.95
printfMethod · 0.95
smartDecodeFunction · 0.85
lockFileFunction · 0.85
ErrorfMethod · 0.80
LockMethod · 0.80
UnlockMethod · 0.80
parseFlagsFunction · 0.70
expandPathFunction · 0.70
ModeMethod · 0.65
StringMethod · 0.45

Tested by

no test coverage detected