Skip to content

Commit 7b03fd9

Browse files
committed
Add global rules support
1 parent 0d88069 commit 7b03fd9

File tree

5 files changed

+62
-16
lines changed

5 files changed

+62
-16
lines changed

CHANGELOG.md

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

33
## Unreleased
44

5+
- Add support for global rules.
6+
57
## 0.3.1
68

79
- Improve default model logic to check for configs and env vars of known models.

docs/configuration.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ ECA_CONFIG='{"myConfig": "my_value"}' eca server
4343

4444
## Rules
4545

46-
Rules are contexts that are passed to the LLM during a prompt.
47-
There are 2 possible ways following this order of priority:
46+
Rules are contexts that are passed to the LLM during a prompt and are useful to tune prompts or LLM behavior.
47+
There are 3 possible ways following this order of priority:
4848

4949
### Project file
5050

@@ -59,6 +59,19 @@ name: Funny rule
5959
- Talk funny like Mickey!
6060
```
6161

62+
### Global file
63+
64+
A `$XDG_CONFIG_HOME/eca/rules` or `~/.config/eca/rules` folder containing `.md` files with the rules.
65+
66+
`~/.config/eca/rules/talk_funny.md`
67+
```markdown
68+
---
69+
name: Funny rule
70+
---
71+
72+
- Talk funny like Mickey!
73+
```
74+
6275
### Config
6376

6477
Just add to your config the `:rules` pointing to `.md` files that will be searched from the workspace root if not an absolute path:

docs/protocol.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ interface ChatPromptParams {
252252
/**
253253
* The chat behavior used by server to handle chat communication and actions.
254254
*/
255-
behavior?: 'agent' | 'ask';
255+
behavior?: ChatBehavior;
256256

257257
/**
258258
* Optional contexts about the current workspace.

src/eca/features/rules.clj

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
11
(ns eca.features.rules
22
(:require
33
[babashka.fs :as fs]
4+
[clojure.java.io :as io]
45
[clojure.string :as string]
6+
[eca.config :as config]
57
[eca.shared :as shared]))
68

79
(set! *warn-on-reflection* true)
810

9-
(defn ^:private file-rules [roots]
11+
(defn ^:private global-file-rules []
12+
(let [xdg-config-home (or (config/get-env "XDG_CONFIG_HOME")
13+
(io/file (config/get-property "user.home") ".config"))
14+
rules-dir (io/file xdg-config-home "eca" "rules")]
15+
(when (fs/exists? rules-dir)
16+
(map (fn [file]
17+
{:name (fs/file-name file)
18+
:path (str (fs/canonicalize file))
19+
:type :user-global-file
20+
:content (slurp (fs/file file))})
21+
(fs/list-dir rules-dir)))))
22+
23+
(defn ^:private local-file-rules [roots]
1024
(->> roots
1125
(mapcat (fn [{:keys [uri]}]
1226
(let [rules-dir (fs/file (shared/uri->filename uri) ".eca" "rules")]
@@ -15,7 +29,7 @@
1529
(map (fn [file]
1630
{:name (fs/file-name file)
1731
:path (str (fs/canonicalize file))
18-
:type :user-file
32+
:type :user-local-file
1933
:content (slurp (fs/file file))}))))
2034

2135
(defn ^:private config-rules [config roots]
@@ -55,4 +69,5 @@
5569
variables))
5670
(concat (system-rules)
5771
(config-rules config roots)
58-
(file-rules roots))))
72+
(global-file-rules)
73+
(local-file-rules roots))))

test/eca/features/rules_test.clj

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
(:require
33
[babashka.fs :as fs]
44
[clojure.test :refer [deftest is testing]]
5+
[eca.config :as config]
56
[eca.features.rules :as f.rules]
67
[eca.test-helper :as h]
78
[matcher-combinators.matchers :as m]
@@ -20,7 +21,7 @@
2021
(testing "absolute config rule"
2122
(with-redefs [clojure.core/slurp (constantly "MY_RULE_CONTENT")
2223
fs/absolute? (constantly true)
23-
fs/exists? (constantly true)
24+
fs/exists? #(= (h/file-path "/path/to/my-rule.md") %)
2425
fs/canonicalize identity
2526
fs/file-name (constantly "cool-name")]
2627
(let [config {:rules [{:path (h/file-path "/path/to/my-rule.md")}]}]
@@ -33,29 +34,44 @@
3334

3435
(testing "relative config rule"
3536
(with-redefs [fs/absolute? (constantly false)
36-
fs/exists? (constantly true)
37+
fs/exists? #(contains? #{(h/file-path "/my/project/.eca/rules")
38+
(h/file-path "/my/project/.foo/cool-rule.md")} (str %))
3739
fs/list-dir (constantly [])
3840
fs/canonicalize identity
3941
fs/file-name (constantly "cool-name")
4042
clojure.core/slurp (constantly "MY_RULE_CONTENT")]
4143
(let [config {:rules [{:path ".foo/cool-rule.md"}]}
4244
roots [{:uri (h/file-uri "file:///my/project")}]]
4345
(is (match?
44-
(m/embeds [{:type :user-config
45-
:path (h/file-path "/my/project/.foo/cool-rule.md")
46-
:name "cool-name"
47-
:content "MY_RULE_CONTENT"}])
48-
(f.rules/all config roots vars))))))
46+
(m/embeds [{:type :user-config
47+
:path (h/file-path "/my/project/.foo/cool-rule.md")
48+
:name "cool-name"
49+
:content "MY_RULE_CONTENT"}])
50+
(f.rules/all config roots vars))))))
4951

50-
(testing "file rules"
51-
(with-redefs [fs/exists? (constantly true)
52+
(testing "local file rules"
53+
(with-redefs [fs/exists? #(= (h/file-path "/my/project/.eca/rules") (str %))
54+
fs/list-dir (constantly [(fs/path "cool.md")])
55+
fs/canonicalize identity
56+
fs/file-name (constantly "cool-name")
57+
clojure.core/slurp (constantly "MY_RULE_CONTENT")]
58+
(let [roots [{:uri (h/file-uri "file:///my/project")}]]
59+
(is (match?
60+
(m/embeds [{:type :user-local-file
61+
:path "cool.md"
62+
:name "cool-name"
63+
:content "MY_RULE_CONTENT"}])
64+
(f.rules/all {} roots vars))))))
65+
(testing "global file rules"
66+
(with-redefs [config/get-env (constantly (h/file-path "/home/someuser/.config"))
67+
fs/exists? #(= (h/file-path "/home/someuser/.config/eca/rules") (str %))
5268
fs/list-dir (constantly [(fs/path "cool.md")])
5369
fs/canonicalize identity
5470
fs/file-name (constantly "cool-name")
5571
clojure.core/slurp (constantly "MY_RULE_CONTENT")]
5672
(let [roots [{:uri (h/file-uri "file:///my/project")}]]
5773
(is (match?
58-
(m/embeds [{:type :user-file
74+
(m/embeds [{:type :user-global-file
5975
:path "cool.md"
6076
:name "cool-name"
6177
:content "MY_RULE_CONTENT"}])

0 commit comments

Comments
 (0)