Skip to content

Commit 80db62b

Browse files
committed
Support AGENT.md file if found
1 parent c4ec9c5 commit 80db62b

File tree

8 files changed

+69
-34
lines changed

8 files changed

+69
-34
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 auto read `AGENT.md` from workspace root and global eca dir, considering as context for chat prompts.
6+
57
## 0.26.3
68

79
- breaking: Replace configs `ollama host` and `ollama port` with `ollamaApiUrl`.

docs/configuration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ interface Config {
237237
chat?: {
238238
welcomeMessage: string;
239239
};
240+
agentFileRelativePath: string;
240241
index?: {
241242
ignoreFiles: [{
242243
type: string;
@@ -274,6 +275,7 @@ interface Config {
274275
"chat" : {
275276
"welcomeMessage" : "Welcome to ECA!\n\nType '/' for commands\n\n"
276277
},
278+
"agentFileRelativePath": "AGENT.md"
277279
"index" : {
278280
"ignoreFiles" : [ {
279281
"type" : "gitignore"

docs/features.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ Here are the current supported contexts types:
5454
- `repoMap`: a summary view of workspaces files and folders, server will calculate this and pass to LLM. Currently, the repo-map includes only the file paths in git.
5555
- `mcpResource`: resources provided by running MCPs servers.
5656

57+
#### AGENT.md automatic context
58+
59+
ECA will always include if found the `AGENT.md` file (configurable via `agentFileRelativePath` config) as context, searching for both `/project-root/AGENT.md` and `~/.config/eca/AGENT.md`.
60+
5761
### Commands
5862

5963
![](./images/commands.png)

src/eca/config.clj

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
[clojure.core.memoize :as memoize]
1212
[clojure.java.io :as io]
1313
[clojure.string :as string]
14-
[eca.shared :as shared]))
14+
[eca.shared :as shared])
15+
(:import
16+
[java.io File]))
1517

1618
(set! *warn-on-reflection* true)
1719

@@ -42,6 +44,7 @@
4244
:ollama {:useTools true
4345
:think true}
4446
:chat {:welcomeMessage "Welcome to ECA!\n\nType '/' for commands\n\n"}
47+
:agentFileRelativePath "AGENT.md"
4548
:customProviders {}
4649
:index {:ignoreFiles [{:type :gitignore}]}})
4750

@@ -64,10 +67,13 @@
6467

6568
(def ^:private config-from-envvar (memoize config-from-envvar*))
6669

67-
(defn ^:private config-from-global-file* []
70+
(defn global-config-dir ^File []
6871
(let [xdg-config-home (or (get-env "XDG_CONFIG_HOME")
69-
(io/file (get-property "user.home") ".config"))
70-
config-file (io/file xdg-config-home "eca" "config.json")]
72+
(io/file (get-property "user.home") ".config"))]
73+
(io/file xdg-config-home "eca")))
74+
75+
(defn ^:private config-from-global-file* []
76+
(let [config-file (io/file (global-config-dir) "config.json")]
7177
(when (.exists config-file)
7278
(safe-read-json-string (slurp config-file)))))
7379

src/eca/features/chat.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109
all-models (models/all)
110110
provider (get-in all-models [model :provider])
111111
rules (f.rules/all config (:workspace-folders db))
112-
refined-contexts (f.context/raw-contexts->refined contexts db)
112+
refined-contexts (f.context/raw-contexts->refined contexts db config)
113113
repo-map* (delay (f.index/repo-map db {:as-string? true}))
114114
instructions (f.prompt/build-instructions refined-contexts rules repo-map* (or behavior (:chat-default-behavior db)) config)
115115
past-messages (get-in db [:chats chat-id :messages] [])

src/eca/features/context.clj

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
(ns eca.features.context
22
(:require
33
[babashka.fs :as fs]
4+
[eca.config :as config]
45
[eca.features.index :as f.index]
56
[eca.features.tools.mcp :as f.mcp]
67
[eca.llm-api :as llm-api]
@@ -11,32 +12,52 @@
1112

1213
(def ^:private logger-tag "[CONTEXT]")
1314

14-
(defn raw-contexts->refined [contexts db]
15-
(mapcat (fn [{:keys [type path lines-range uri]}]
16-
(case (name type)
17-
"file" [{:type :file
18-
:path path
19-
:partial (boolean lines-range)
20-
:content (llm-api/refine-file-context path lines-range)}]
21-
"directory" (->> (fs/glob path "**")
22-
(remove fs/directory?)
23-
(map (fn [path]
24-
(let [filename (str (fs/canonicalize path))]
25-
{:type :file
26-
:path filename
27-
:content (llm-api/refine-file-context filename nil)}))))
28-
"repoMap" [{:type :repoMap}]
29-
"mcpResource" (try
30-
(mapv
31-
(fn [{:keys [text]}]
32-
{:type :mcpResource
33-
:uri uri
34-
:content text})
35-
(:contents (f.mcp/get-resource! uri db)))
36-
(catch Exception e
37-
(logger/warn logger-tag (format "Error getting MCP resource %s: %s" uri (.getMessage e)))
38-
[]))))
39-
contexts))
15+
(defn ^:private agents-file-contexts
16+
"Search for AGENT.md file both in workspaceRoot and global config dir."
17+
[db config]
18+
(let [local-agent-files (keep (fn [{:keys [uri]}]
19+
(let [agent-file (fs/path (shared/uri->filename uri) (:agentFileRelativePath config))]
20+
(when (fs/readable? agent-file)
21+
(fs/canonicalize agent-file))))
22+
(:workspace-folders db))
23+
global-agent-file (let [agent-file (fs/path (config/global-config-dir) (:agentFileRelativePath config))]
24+
(when (fs/readable? agent-file)
25+
(fs/canonicalize agent-file)))]
26+
(mapv (fn [path]
27+
{:type :file
28+
:path (str path)
29+
:partial false
30+
:content (llm-api/refine-file-context (str path) nil)})
31+
(concat local-agent-files
32+
(when global-agent-file [global-agent-file])))))
33+
34+
(defn raw-contexts->refined [contexts db config]
35+
(concat (agents-file-contexts db config)
36+
(mapcat (fn [{:keys [type path lines-range uri]}]
37+
(case (name type)
38+
"file" [{:type :file
39+
:path path
40+
:partial (boolean lines-range)
41+
:content (llm-api/refine-file-context path lines-range)}]
42+
"directory" (->> (fs/glob path "**")
43+
(remove fs/directory?)
44+
(map (fn [path]
45+
(let [filename (str (fs/canonicalize path))]
46+
{:type :file
47+
:path filename
48+
:content (llm-api/refine-file-context filename nil)}))))
49+
"repoMap" [{:type :repoMap}]
50+
"mcpResource" (try
51+
(mapv
52+
(fn [{:keys [text]}]
53+
{:type :mcpResource
54+
:uri uri
55+
:content text})
56+
(:contents (f.mcp/get-resource! uri db)))
57+
(catch Exception e
58+
(logger/warn logger-tag (format "Error getting MCP resource %s: %s" uri (.getMessage e)))
59+
[]))))
60+
contexts)))
4061

4162
(defn ^:private contexts-for [root-filename query config]
4263
(let [all-files (fs/glob root-filename (str "**" (or query "") "**"))

src/eca/features/prompt.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
rules)
4444
"</rules>"
4545
""
46-
"<contexts>"
46+
"<contexts description=\"Manually provided by user, usually when provided user knows that your task is related to those files, so consider reliying on it but use tools to read/find any extra files/contexts if really needed.\">"
4747
(reduce
4848
(fn [context-str {:keys [type path content partial uri]}]
4949
(str context-str (case type

test/eca/features/prompt_test.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
(is (string/includes? result "<rules>"))
2020
(is (string/includes? result "<rule name=\"rule1\">First rule</rule>"))
2121
(is (string/includes? result "<rule name=\"rule2\">Second rule</rule>"))
22-
(is (string/includes? result "<contexts>"))
22+
(is (string/includes? result "<contexts description=\"Manually provided by user, usually when provided user knows that your task is related to those files, so consider reliying on it but use tools to read/find any extra files/contexts if really needed.\">"))
2323
(is (string/includes? result "<file path=\"foo.clj\">(ns foo)</file>"))
2424
(is (string/includes? result "<file partial=true path=\"bar.clj\">...\n(def a 1)\n...</file>"))
2525
(is (string/includes? result "<repoMap description=\"Workspaces structure in a tree view, spaces represent file hierarchy\" >TREE</repoMap>"))
@@ -40,7 +40,7 @@
4040
(is (string/includes? result "<rules>"))
4141
(is (string/includes? result "<rule name=\"rule1\">First rule</rule>"))
4242
(is (string/includes? result "<rule name=\"rule2\">Second rule</rule>"))
43-
(is (string/includes? result "<contexts>"))
43+
(is (string/includes? result "<contexts description=\"Manually provided by user, usually when provided user knows that your task is related to those files, so consider reliying on it but use tools to read/find any extra files/contexts if really needed.\">"))
4444
(is (string/includes? result "<file path=\"foo.clj\">(ns foo)</file>"))
4545
(is (string/includes? result "<file partial=true path=\"bar.clj\">...\n(def a 1)\n...</file>"))
4646
(is (string/includes? result "<repoMap description=\"Workspaces structure in a tree view, spaces represent file hierarchy\" >TREE</repoMap>"))

0 commit comments

Comments
 (0)