|
2 | 2 | "Core implementation for the file-write tool. |
3 | 3 | This namespace contains the pure functionality without any MCP-specific code." |
4 | 4 | (:require |
5 | | - [clojure.java.io :as io] |
6 | | - [clojure-mcp.tools.form-edit.pipeline :as pipeline] |
7 | | - [clojure-mcp.utils.diff :as diff-utils] |
8 | | - [clojure-mcp.linting :as linting] |
9 | | - [clojure-mcp.utils.valid-paths :as valid-paths] |
10 | | - [rewrite-clj.zip :as z])) |
| 5 | + [clojure.java.io :as io] |
| 6 | + [clojure-mcp.tools.form-edit.pipeline :as pipeline] |
| 7 | + [clojure-mcp.utils.diff :as diff-utils] |
| 8 | + [clojure-mcp.linting :as linting] |
| 9 | + [clojure-mcp.utils.valid-paths :as valid-paths] |
| 10 | + [rewrite-clj.zip :as z])) |
11 | 11 |
|
12 | | - (defn is-clojure-file? |
13 | | - "Check if a file is a Clojure-related file based on its extension or Babashka shebang. |
| 12 | +(defn is-clojure-file? |
| 13 | + "Check if a file is a Clojure-related file based on its extension or Babashka shebang. |
14 | 14 |
|
15 | 15 | Parameters: |
16 | 16 | - file-path: Path to the file to check |
17 | 17 |
|
18 | 18 | Returns true for Clojure extensions (.clj, .cljs, .cljc, .edn, .bb) or files with a `bb` shebang." |
19 | | - [file-path] |
20 | | - (boolean (valid-paths/clojure-file? file-path))) |
| 19 | + [file-path] |
| 20 | + (boolean (valid-paths/clojure-file? file-path))) |
21 | 21 |
|
22 | 22 | (defn write-clojure-file |
23 | 23 | "Write content to a Clojure file, with linting, formatting, and diffing. |
24 | 24 | |
25 | 25 | Parameters: |
26 | 26 | - file-path: Validated path to the file to write |
27 | 27 | - content: Content to write to the file |
| 28 | + - dry_run: Optional preview mode ('diff' or 'new-source') |
28 | 29 | |
29 | 30 | Returns: |
30 | 31 | - A map with :error, :type, :file-path, and :diff keys" |
31 | | - [nrepl-client-atom file-path content] |
| 32 | + [nrepl-client-atom file-path content dry_run] |
32 | 33 | (let [file (io/file file-path) |
33 | 34 | file-exists? (.exists file) |
34 | 35 | old-content (if file-exists? (slurp file) "") |
|
50 | 51 | pipeline/format-source ;; Format the content |
51 | 52 | pipeline/generate-diff ;; Generate diff between old and new content |
52 | 53 | pipeline/determine-file-type ;; Determine if creating or updating |
53 | | - pipeline/save-file)] ;; Save the file and get offsets |
| 54 | + ;; Conditionally skip file save if dry_run is set |
| 55 | + (fn [ctx] |
| 56 | + (if dry_run |
| 57 | + ctx |
| 58 | + (pipeline/save-file ctx))))] |
54 | 59 |
|
55 | 60 | ;; Format the result for tool consumption |
56 | 61 | (if (::pipeline/error result) |
57 | 62 | {:error true |
58 | 63 | :message (::pipeline/message result)} |
59 | | - {:error false |
60 | | - :type (::pipeline/type result) |
61 | | - :file-path (::pipeline/file-path result) |
62 | | - :diff (::pipeline/diff result)}))) |
| 64 | + (cond |
| 65 | + ;; Return new-source for dry_run="new-source" |
| 66 | + (= dry_run "new-source") |
| 67 | + {:error false |
| 68 | + :dry_run "new-source" |
| 69 | + :new-source (::pipeline/output-source result)} |
| 70 | + |
| 71 | + ;; Return just diff for dry_run="diff" |
| 72 | + (= dry_run "diff") |
| 73 | + {:error false |
| 74 | + :dry_run "diff" |
| 75 | + :diff (::pipeline/diff result)} |
| 76 | + |
| 77 | + ;; Return full result for normal operation |
| 78 | + :else |
| 79 | + {:error false |
| 80 | + :type (::pipeline/type result) |
| 81 | + :file-path (::pipeline/file-path result) |
| 82 | + :diff (::pipeline/diff result)})))) |
63 | 83 |
|
64 | 84 | (defn write-text-file |
65 | 85 | "Write content to a non-Clojure text file, with diffing but no linting or formatting. |
66 | 86 | |
67 | 87 | Parameters: |
68 | 88 | - file-path: Validated path to the file to write |
69 | 89 | - content: Content to write to the file |
| 90 | + - dry_run: Optional preview mode ('diff' or 'new-source') |
70 | 91 | |
71 | 92 | Returns: |
72 | 93 | - A map with :error, :type, :file-path, and :diff keys" |
73 | | - [file-path content] |
| 94 | + [file-path content dry_run] |
74 | 95 | (try |
75 | 96 | (let [file (io/file file-path) |
76 | 97 | file-exists? (.exists file) |
|
82 | 103 | (diff-utils/generate-unified-diff old-content content)) |
83 | 104 | "")] |
84 | 105 |
|
85 | | - ;; Write the content directly |
86 | | - (spit file content) |
| 106 | + (cond |
| 107 | + ;; Return new-source if dry_run="new-source" |
| 108 | + (= dry_run "new-source") |
| 109 | + {:error false |
| 110 | + :dry_run "new-source" |
| 111 | + :new-source content} |
| 112 | + |
| 113 | + ;; Return just diff for dry_run="diff" |
| 114 | + (= dry_run "diff") |
| 115 | + {:error false |
| 116 | + :dry_run "diff" |
| 117 | + :diff diff} |
87 | 118 |
|
88 | | - {:error false |
89 | | - :type (if file-exists? "update" "create") |
90 | | - :file-path file-path |
91 | | - :diff diff}) |
| 119 | + ;; Normal operation - write and return full result |
| 120 | + :else |
| 121 | + (do |
| 122 | + (spit file content) |
| 123 | + {:error false |
| 124 | + :type (if file-exists? "update" "create") |
| 125 | + :file-path file-path |
| 126 | + :diff diff}))) |
92 | 127 | (catch Exception e |
93 | 128 | {:error true |
94 | 129 | :message (str "Error writing file: " (.getMessage e))}))) |
|
101 | 136 | Parameters: |
102 | 137 | - file-path: Validated path to the file to write |
103 | 138 | - content: Content to write to the file |
| 139 | + - dry_run: Optional preview mode ('diff' or 'new-source') |
104 | 140 | |
105 | 141 | Returns: |
106 | 142 | - A map with :error, :type, :file-path, and :diff keys" |
107 | | - [nrepl-client-atom file-path content] |
| 143 | + [nrepl-client-atom file-path content dry_run] |
108 | 144 | (if (is-clojure-file? file-path) |
109 | | - (write-clojure-file nrepl-client-atom file-path content) |
110 | | - (write-text-file file-path content))) |
| 145 | + (write-clojure-file nrepl-client-atom file-path content dry_run) |
| 146 | + (write-text-file file-path content dry_run))) |
0 commit comments