Skip to content

Commit 586fe6b

Browse files
bhaumanBruce Hauman
authored andcommitted
feat: detect babashka scripts as clojure
1 parent 9cd4df1 commit 586fe6b

File tree

6 files changed

+74
-39
lines changed

6 files changed

+74
-39
lines changed

src/clojure_mcp/tools/file_write/core.clj

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,22 @@
22
"Core implementation for the file-write tool.
33
This namespace contains the pure functionality without any MCP-specific code."
44
(:require
5-
[clojure.java.io :as io]
6-
[clojure.string :as string]
7-
[clojure-mcp.tools.form-edit.pipeline :as pipeline]
8-
[clojure-mcp.utils.diff :as diff-utils]
9-
[clojure-mcp.linting :as linting]
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]))
1111

12-
(defn is-clojure-file?
13-
"Check if a file is a Clojure-related file based on its extension.
14-
15-
Parameters:
16-
- file-path: Path to the file to check
17-
18-
Returns true if the file has a Clojure-related extension (.clj, .cljs, .cljc, .edn, .bb),
19-
false otherwise."
20-
[file-path]
21-
(let [lower-path (string/lower-case file-path)]
22-
(some #(string/ends-with? lower-path %) [".clj" ".cljs" ".cljc" ".edn" ".bb"])))
12+
(defn is-clojure-file?
13+
"Check if a file is a Clojure-related file based on its extension or Babashka shebang.
14+
15+
Parameters:
16+
- file-path: Path to the file to check
17+
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)))
2321

2422
(defn write-clojure-file
2523
"Write content to a Clojure file, with linting, formatting, and diffing.

src/clojure_mcp/tools/unified_read_file/tool.clj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030
;; Helper functions
3131

3232
(defn collapsible-clojure-file?
33-
"Determines if a file is a collapsible Clojure source file based on its extension."
33+
"Determines if a file is a collapsible Clojure source file."
3434
[file-path]
3535
(when file-path
36-
(let [extension (last (str/split file-path #"\."))]
37-
(contains? #{"clj" "cljc" "cljs" "bb" "lpy"} extension))))
36+
(and (valid-paths/clojure-file? file-path)
37+
(not (str/ends-with? (str/lower-case file-path) ".edn")))))
3838

3939
;; Implement the required multimethods for the unified read file tool
4040
(defmethod tool-system/tool-name :unified-read-file [_]

src/clojure_mcp/utils/valid_paths.clj

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -82,24 +82,39 @@
8282

8383
(validate-path path current-dir allowed-dirs)))
8484

85-
(defn clojure-file?
86-
"Checks if a file path has a Clojure-related extension.
87-
88-
Supported extensions:
89-
- .clj (Clojure)
90-
- .cljs (ClojureScript)
91-
- .cljc (Clojure/ClojureScript shared)
92-
- .bb (Babashka)
93-
- .edn (Extensible Data Notation)"
94-
[file-path]
95-
(when file-path
96-
(let [lower-path (str/lower-case file-path)]
97-
(or (str/ends-with? lower-path ".clj")
98-
(str/ends-with? lower-path ".cljs")
99-
(str/ends-with? lower-path ".cljc")
100-
(str/ends-with? lower-path ".bb")
101-
(str/ends-with? lower-path ".lpy")
102-
(str/ends-with? lower-path ".edn")))))
85+
(defn- babashka-shebang?
86+
[file-path]
87+
(when (path-exists? file-path)
88+
(try
89+
(with-open [r (io/reader file-path)]
90+
(let [line (-> r line-seq first)]
91+
(and line
92+
(or (str/starts-with? line "#!/usr/bin/env bb")
93+
(str/starts-with? line "#!/usr/bin/bb")
94+
(str/starts-with? line "#!/bin/bb")))))
95+
(catch Exception _ false))))
96+
97+
(defn clojure-file?
98+
"Checks if a file path has a Clojure-related extension or Babashka shebang."
99+
100+
Supported extensions:
101+
- .clj (Clojure)
102+
- .cljs (ClojureScript)
103+
- .cljc (Clojure/ClojureScript shared)
104+
- .bb (Babashka)
105+
- .edn (Extensible Data Notation)
106+
- .lpy (Librepl)
107+
Also detects files starting with a Babashka shebang (`bb`)."
108+
[file-path]
109+
(when file-path
110+
(let [lower-path (str/lower-case file-path)]
111+
(or (str/ends-with? lower-path ".clj")
112+
(str/ends-with? lower-path ".cljs")
113+
(str/ends-with? lower-path ".cljc")
114+
(str/ends-with? lower-path ".bb")
115+
(str/ends-with? lower-path ".lpy")
116+
(str/ends-with? lower-path ".edn")
117+
(babashka-shebang? file-path)))))
103118
104119
(defn extract-paths-from-bash-command
105120
"Extract file/directory paths from a bash command string.

test/clojure_mcp/tools/file_write/core_test.clj

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,13 @@
5454
(is (file-write-core/is-clojure-file? "C:\\Path\\To\\File.CLJ")) ; Case insensitive
5555
(is (not (file-write-core/is-clojure-file? "test.txt")))
5656
(is (not (file-write-core/is-clojure-file? "test.md")))
57-
(is (not (file-write-core/is-clojure-file? "test.js")))))
57+
(is (not (file-write-core/is-clojure-file? "test.js"))))
58+
59+
(testing "Detecting Babashka shebang"
60+
(let [tmp (io/file *test-dir* "script.sh")]
61+
(spit tmp "#!/usr/bin/env bb\n(println :hi)")
62+
(is (file-write-core/is-clojure-file? (.getPath tmp)))
63+
(.delete tmp))))
5864

5965
(deftest write-text-file-test
6066
(testing "Creating a new text file"

test/clojure_mcp/tools/unified_read_file/tool_test.clj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@
6161
(is (unified-read-file-tool/collapsible-clojure-file? "test.cljc"))
6262
(is (unified-read-file-tool/collapsible-clojure-file? "test.bb"))
6363
(is (unified-read-file-tool/collapsible-clojure-file? "/path/to/file.clj"))
64+
(let [tmp (io/file *test-dir* "script.sh")]
65+
(spit tmp "#!/usr/bin/env bb\n(println :hi)")
66+
(is (unified-read-file-tool/collapsible-clojure-file? (.getPath tmp)))
67+
(.delete tmp))
6468
(is (not (unified-read-file-tool/collapsible-clojure-file? "test.edn"))) ; EDN files not collapsible
6569
(is (not (unified-read-file-tool/collapsible-clojure-file? "test.txt")))
6670
(is (not (unified-read-file-tool/collapsible-clojure-file? "test.md")))

test/clojure_mcp/utils/valid_paths_test.clj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,18 @@
102102
(is (= ".."
103103
(valid-paths/preprocess-path "..")))))
104104

105+
(deftest clojure-file?-test
106+
(testing "Detect Babashka shebang"
107+
(let [tmp (io/file (System/getProperty "java.io.tmpdir") "bb-script.sh")]
108+
(spit tmp "#!/usr/bin/env bb\n(println :hi)")
109+
(is (valid-paths/clojure-file? (.getPath tmp)))
110+
(.delete tmp)))
111+
(testing "Regular bash script not detected"
112+
(let [tmp (io/file (System/getProperty "java.io.tmpdir") "bash-script.sh")]
113+
(spit tmp "#!/bin/bash\necho hi")
114+
(is (not (valid-paths/clojure-file? (.getPath tmp))))
115+
(.delete tmp))))
116+
105117
(deftest validate-bash-command-paths-test
106118
(let [test-dir (.getCanonicalPath (io/file (System/getProperty "java.io.tmpdir")))
107119
home-dir (System/getProperty "user.home")]

0 commit comments

Comments
 (0)