@dcode-dev/dcode-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NPM_README.md +78 -0
- package/README.md +341 -0
- package/bin/dcode-bin +0 -0
- package/bin/dcode.js +44 -0
- package/cmd/agent_v2.go +448 -0
- package/cmd/analyze.go +97 -0
- package/cmd/auth.go +338 -0
- package/cmd/compose.go +284 -0
- package/cmd/context.go +111 -0
- package/cmd/edit.go +116 -0
- package/cmd/env.go +10 -0
- package/cmd/fix.go +145 -0
- package/cmd/gemini.go +20 -0
- package/cmd/generate.go +47 -0
- package/cmd/interactive.go +33 -0
- package/cmd/mcp.go +196 -0
- package/cmd/patch.go +19 -0
- package/cmd/providers.go +67 -0
- package/cmd/root.go +41 -0
- package/cmd/search.go +61 -0
- package/cmd/server.go +36 -0
- package/cmd/switch.go +122 -0
- package/cmd/terminal.go +277 -0
- package/go.mod +42 -0
- package/go.sum +86 -0
- package/internal/agent/agent.go +332 -0
- package/internal/agent/parse.go +25 -0
- package/internal/agents/base.go +154 -0
- package/internal/agents/documenter.go +77 -0
- package/internal/agents/generalist.go +266 -0
- package/internal/agents/investigator.go +60 -0
- package/internal/agents/registry.go +34 -0
- package/internal/agents/reviewer.go +67 -0
- package/internal/agents/tester.go +73 -0
- package/internal/ai/client.go +634 -0
- package/internal/ai/tools.go +332 -0
- package/internal/auth/adc.go +108 -0
- package/internal/auth/apikey.go +67 -0
- package/internal/auth/factory.go +145 -0
- package/internal/auth/oauth2.go +227 -0
- package/internal/auth/store.go +216 -0
- package/internal/auth/types.go +79 -0
- package/internal/auth/vertex.go +138 -0
- package/internal/config/config.go +428 -0
- package/internal/config/policy.go +251 -0
- package/internal/context/builder.go +312 -0
- package/internal/detector/detector.go +204 -0
- package/internal/diffutil/diffutil.go +30 -0
- package/internal/fsutil/fsutil.go +35 -0
- package/internal/mcp/client.go +314 -0
- package/internal/mcp/manager.go +221 -0
- package/internal/policy/policy.go +89 -0
- package/internal/prompt/interactive.go +338 -0
- package/internal/registry/agent.go +201 -0
- package/internal/registry/tool.go +181 -0
- package/internal/scheduler/scheduler.go +250 -0
- package/internal/server/server.go +167 -0
- package/internal/tools/file.go +183 -0
- package/internal/tools/filesystem.go +286 -0
- package/internal/tools/git.go +355 -0
- package/internal/tools/memory.go +269 -0
- package/internal/tools/registry.go +49 -0
- package/internal/tools/search.go +230 -0
- package/internal/tools/shell.go +84 -0
- package/internal/websearch/search.go +40 -0
- package/internal/websearch/tavily.go +79 -0
- package/main.go +19 -0
- package/package.json +57 -0
- package/scripts/install.js +59 -0
- package/scripts/uninstall.js +28 -0
package/go.sum
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho=
|
|
2
|
+
al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
|
|
3
|
+
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
|
4
|
+
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
|
5
|
+
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
|
6
|
+
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
|
7
|
+
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
|
8
|
+
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
|
9
|
+
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
|
|
10
|
+
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
|
|
11
|
+
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
|
|
12
|
+
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
|
|
13
|
+
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
|
|
14
|
+
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
|
|
15
|
+
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
|
|
16
|
+
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
|
17
|
+
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
|
18
|
+
github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0=
|
|
19
|
+
github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8=
|
|
20
|
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
21
|
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
22
|
+
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
|
23
|
+
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
|
24
|
+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
|
25
|
+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
|
26
|
+
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
|
27
|
+
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
|
28
|
+
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
|
29
|
+
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
|
30
|
+
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
|
31
|
+
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
|
32
|
+
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
|
33
|
+
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
|
34
|
+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
|
35
|
+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
|
36
|
+
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
|
37
|
+
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
|
38
|
+
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
|
39
|
+
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
|
40
|
+
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
|
41
|
+
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
|
42
|
+
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
|
43
|
+
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
|
44
|
+
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
|
45
|
+
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
|
46
|
+
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
|
47
|
+
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
|
48
|
+
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
|
49
|
+
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
|
50
|
+
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
|
51
|
+
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
|
52
|
+
github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw=
|
|
53
|
+
github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI=
|
|
54
|
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
55
|
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
56
|
+
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
|
57
|
+
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
|
58
|
+
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
|
59
|
+
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
|
60
|
+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
|
61
|
+
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
|
62
|
+
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
|
63
|
+
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|
64
|
+
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
65
|
+
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
|
66
|
+
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
|
67
|
+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
|
68
|
+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
|
69
|
+
github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
|
|
70
|
+
github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI=
|
|
71
|
+
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
|
|
72
|
+
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
|
73
|
+
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
|
74
|
+
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
|
75
|
+
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
76
|
+
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
77
|
+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
78
|
+
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
|
79
|
+
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
|
80
|
+
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
|
81
|
+
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
|
82
|
+
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
|
83
|
+
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
|
84
|
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
85
|
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
86
|
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
package agent
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"encoding/base64"
|
|
6
|
+
"fmt"
|
|
7
|
+
"os"
|
|
8
|
+
"path/filepath"
|
|
9
|
+
"strings"
|
|
10
|
+
|
|
11
|
+
"github.com/ddhanush1/dcode/internal/ai"
|
|
12
|
+
"github.com/ddhanush1/dcode/internal/websearch"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
type Attachment struct {
|
|
16
|
+
Type string `json:"type"` // "image" supported
|
|
17
|
+
Mime string `json:"mime,omitempty"`
|
|
18
|
+
DataBase64 string `json:"data_base64,omitempty"`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type WebSearchRequest struct {
|
|
22
|
+
Enabled bool `json:"enabled"`
|
|
23
|
+
Query string `json:"query,omitempty"`
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type ChatRequest struct {
|
|
27
|
+
Messages []ai.Message `json:"messages"`
|
|
28
|
+
Attach []Attachment `json:"attachments,omitempty"`
|
|
29
|
+
WebSearch WebSearchRequest `json:"web_search,omitempty"`
|
|
30
|
+
ProjectDir string `json:"project_dir,omitempty"` // optional, for future
|
|
31
|
+
Metadata map[string]string `json:"metadata,omitempty"`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type WebResult struct {
|
|
35
|
+
Title string `json:"title"`
|
|
36
|
+
URL string `json:"url"`
|
|
37
|
+
Snippet string `json:"snippet"`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type ChatResponse struct {
|
|
41
|
+
Assistant string `json:"assistant"`
|
|
42
|
+
WebResults []WebResult `json:"web_results,omitempty"`
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type FileInput struct {
|
|
46
|
+
Path string `json:"path"`
|
|
47
|
+
Content string `json:"content"`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type EditRequest struct {
|
|
51
|
+
Prompt string `json:"prompt"`
|
|
52
|
+
Files []FileInput `json:"files"`
|
|
53
|
+
WebSearch WebSearchRequest `json:"web_search,omitempty"`
|
|
54
|
+
Instructions string `json:"instructions,omitempty"` // optional extra system guidance
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type FileEdit struct {
|
|
58
|
+
Path string `json:"path"`
|
|
59
|
+
NewContent string `json:"new_content"`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type EditResponse struct {
|
|
63
|
+
Explanation string `json:"explanation"`
|
|
64
|
+
Edits []FileEdit `json:"edits"`
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
type Diagnostic struct {
|
|
68
|
+
Path string `json:"path"`
|
|
69
|
+
Message string `json:"message"`
|
|
70
|
+
Source string `json:"source,omitempty"`
|
|
71
|
+
Code string `json:"code,omitempty"`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
type FixRequest struct {
|
|
75
|
+
Prompt string `json:"prompt,omitempty"`
|
|
76
|
+
Files []FileInput `json:"files"`
|
|
77
|
+
Diagnostics []Diagnostic `json:"diagnostics,omitempty"`
|
|
78
|
+
TestOutput string `json:"test_output,omitempty"`
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
type FixResponse = EditResponse
|
|
82
|
+
|
|
83
|
+
type Agent struct {
|
|
84
|
+
client *ai.Client
|
|
85
|
+
search websearch.Searcher
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
func New() (*Agent, error) {
|
|
89
|
+
client, err := ai.NewClient()
|
|
90
|
+
if err != nil {
|
|
91
|
+
return nil, err
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
searcher := websearch.NewFromEnv()
|
|
95
|
+
return &Agent{client: client, search: searcher}, nil
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
func (a *Agent) Client() *ai.Client {
|
|
99
|
+
return a.client
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
func (a *Agent) Chat(ctx context.Context, req ChatRequest) (ChatResponse, error) {
|
|
103
|
+
var webResults []WebResult
|
|
104
|
+
if req.WebSearch.Enabled && a.search != nil {
|
|
105
|
+
query := strings.TrimSpace(req.WebSearch.Query)
|
|
106
|
+
if query == "" {
|
|
107
|
+
// fallback: use last user content
|
|
108
|
+
for i := len(req.Messages) - 1; i >= 0; i-- {
|
|
109
|
+
if req.Messages[i].Role == "user" {
|
|
110
|
+
query = req.Messages[i].Content
|
|
111
|
+
break
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if query != "" {
|
|
116
|
+
results, err := a.search.Search(ctx, query)
|
|
117
|
+
if err == nil {
|
|
118
|
+
webResults = make([]WebResult, 0, len(results))
|
|
119
|
+
for _, r := range results {
|
|
120
|
+
webResults = append(webResults, WebResult{Title: r.Title, URL: r.URL, Snippet: r.Snippet})
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
messages := make([]ai.Message, 0, len(req.Messages)+1)
|
|
127
|
+
if len(webResults) > 0 {
|
|
128
|
+
messages = append(messages, ai.Message{Role: "system", Content: "Web search results (use as optional context):\n" + formatWebResults(webResults)})
|
|
129
|
+
}
|
|
130
|
+
messages = append(messages, req.Messages...)
|
|
131
|
+
|
|
132
|
+
if len(req.Attach) > 0 {
|
|
133
|
+
images, err := convertAttachmentsToImages(req.Attach)
|
|
134
|
+
if err != nil {
|
|
135
|
+
return ChatResponse{}, err
|
|
136
|
+
}
|
|
137
|
+
text, err := a.client.ChatWithImages(ctx, messages, images)
|
|
138
|
+
if err != nil {
|
|
139
|
+
return ChatResponse{}, err
|
|
140
|
+
}
|
|
141
|
+
return ChatResponse{Assistant: text, WebResults: webResults}, nil
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
text, err := a.client.ChatWithContext(ctx, messages)
|
|
145
|
+
if err != nil {
|
|
146
|
+
return ChatResponse{}, err
|
|
147
|
+
}
|
|
148
|
+
return ChatResponse{Assistant: text, WebResults: webResults}, nil
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
func (a *Agent) Edit(ctx context.Context, req EditRequest) (EditResponse, error) {
|
|
152
|
+
if strings.TrimSpace(req.Prompt) == "" {
|
|
153
|
+
return EditResponse{}, fmt.Errorf("prompt is required")
|
|
154
|
+
}
|
|
155
|
+
if len(req.Files) == 0 {
|
|
156
|
+
return EditResponse{}, fmt.Errorf("files is required")
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
var webContext string
|
|
160
|
+
if req.WebSearch.Enabled && a.search != nil {
|
|
161
|
+
query := strings.TrimSpace(req.WebSearch.Query)
|
|
162
|
+
if query == "" {
|
|
163
|
+
query = req.Prompt
|
|
164
|
+
}
|
|
165
|
+
if query != "" {
|
|
166
|
+
results, err := a.search.Search(ctx, query)
|
|
167
|
+
if err == nil {
|
|
168
|
+
webContext = formatWebResultsSimple(results)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
system := "You are an AI coding agent. Return ONLY a JSON object with shape: {\"explanation\":string,\"edits\":[{\"path\":string,\"new_content\":string}]} .\n" +
|
|
174
|
+
"Rules: preserve unrelated code; keep formatting; ensure code compiles; if you can't, explain in explanation and return the original content."
|
|
175
|
+
if req.Instructions != "" {
|
|
176
|
+
system += "\nExtra instructions: " + req.Instructions
|
|
177
|
+
}
|
|
178
|
+
if webContext != "" {
|
|
179
|
+
system += "\n\nWeb context:\n" + webContext
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
var b strings.Builder
|
|
183
|
+
for _, f := range req.Files {
|
|
184
|
+
cleanPath := filepath.ToSlash(f.Path)
|
|
185
|
+
b.WriteString("FILE: " + cleanPath + "\n")
|
|
186
|
+
b.WriteString("```\n")
|
|
187
|
+
b.WriteString(f.Content)
|
|
188
|
+
if !strings.HasSuffix(f.Content, "\n") {
|
|
189
|
+
b.WriteString("\n")
|
|
190
|
+
}
|
|
191
|
+
b.WriteString("```\n\n")
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
messages := []ai.Message{
|
|
195
|
+
{Role: "system", Content: system},
|
|
196
|
+
{Role: "user", Content: "Task: " + req.Prompt + "\n\n" + b.String()},
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
text, err := a.client.ChatWithContext(ctx, messages)
|
|
200
|
+
if err != nil {
|
|
201
|
+
return EditResponse{}, err
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
resp, err := parseEditResponse(text)
|
|
205
|
+
if err != nil {
|
|
206
|
+
return EditResponse{}, err
|
|
207
|
+
}
|
|
208
|
+
return resp, nil
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
func (a *Agent) Fix(ctx context.Context, req FixRequest) (FixResponse, error) {
|
|
212
|
+
if len(req.Files) == 0 {
|
|
213
|
+
return FixResponse{}, fmt.Errorf("files is required")
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
prompt := strings.TrimSpace(req.Prompt)
|
|
217
|
+
if prompt == "" {
|
|
218
|
+
prompt = "Fix the reported problems and make the code compile/tests pass."
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
system := "You are an AI coding agent fixing code. Return ONLY JSON: {\"explanation\":string,\"edits\":[{\"path\":string,\"new_content\":string}]}. " +
|
|
222
|
+
"Keep changes minimal and targeted; do not refactor unrelated code."
|
|
223
|
+
|
|
224
|
+
var b strings.Builder
|
|
225
|
+
if len(req.Diagnostics) > 0 {
|
|
226
|
+
b.WriteString("DIAGNOSTICS:\n")
|
|
227
|
+
for _, d := range req.Diagnostics {
|
|
228
|
+
b.WriteString("- " + filepath.ToSlash(d.Path) + ": " + d.Message)
|
|
229
|
+
if d.Source != "" {
|
|
230
|
+
b.WriteString(" (" + d.Source + ")")
|
|
231
|
+
}
|
|
232
|
+
b.WriteString("\n")
|
|
233
|
+
}
|
|
234
|
+
b.WriteString("\n")
|
|
235
|
+
}
|
|
236
|
+
if req.TestOutput != "" {
|
|
237
|
+
b.WriteString("TEST OUTPUT:\n")
|
|
238
|
+
b.WriteString(req.TestOutput)
|
|
239
|
+
b.WriteString("\n\n")
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
for _, f := range req.Files {
|
|
243
|
+
cleanPath := filepath.ToSlash(f.Path)
|
|
244
|
+
b.WriteString("FILE: " + cleanPath + "\n")
|
|
245
|
+
b.WriteString("```\n")
|
|
246
|
+
b.WriteString(f.Content)
|
|
247
|
+
if !strings.HasSuffix(f.Content, "\n") {
|
|
248
|
+
b.WriteString("\n")
|
|
249
|
+
}
|
|
250
|
+
b.WriteString("```\n\n")
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
messages := []ai.Message{
|
|
254
|
+
{Role: "system", Content: system},
|
|
255
|
+
{Role: "user", Content: "Task: " + prompt + "\n\n" + b.String()},
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
text, err := a.client.ChatWithContext(ctx, messages)
|
|
259
|
+
if err != nil {
|
|
260
|
+
return FixResponse{}, err
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
resp, err := parseEditResponse(text)
|
|
264
|
+
if err != nil {
|
|
265
|
+
return FixResponse{}, err
|
|
266
|
+
}
|
|
267
|
+
return resp, nil
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
type imageInput struct {
|
|
271
|
+
Mime string
|
|
272
|
+
DataBase64 string
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
func convertAttachmentsToImages(attachments []Attachment) ([]ai.ImageInput, error) {
|
|
276
|
+
images := make([]ai.ImageInput, 0, len(attachments))
|
|
277
|
+
for _, a := range attachments {
|
|
278
|
+
if a.Type != "image" {
|
|
279
|
+
continue
|
|
280
|
+
}
|
|
281
|
+
mime := a.Mime
|
|
282
|
+
if mime == "" {
|
|
283
|
+
mime = "image/png"
|
|
284
|
+
}
|
|
285
|
+
data := strings.TrimSpace(a.DataBase64)
|
|
286
|
+
if data == "" {
|
|
287
|
+
return nil, fmt.Errorf("image attachment missing data_base64")
|
|
288
|
+
}
|
|
289
|
+
// validate base64
|
|
290
|
+
if _, err := base64.StdEncoding.DecodeString(stripDataURLPrefix(data)); err != nil {
|
|
291
|
+
// might already be data url
|
|
292
|
+
if _, err2 := base64.StdEncoding.DecodeString(data); err2 != nil {
|
|
293
|
+
return nil, fmt.Errorf("invalid base64 image")
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
images = append(images, ai.ImageInput{Mime: mime, DataBase64: data})
|
|
297
|
+
}
|
|
298
|
+
if len(images) == 0 {
|
|
299
|
+
return nil, fmt.Errorf("no supported attachments")
|
|
300
|
+
}
|
|
301
|
+
return images, nil
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
func stripDataURLPrefix(s string) string {
|
|
305
|
+
if i := strings.Index(s, ","); i != -1 && strings.Contains(s[:i], "base64") {
|
|
306
|
+
return s[i+1:]
|
|
307
|
+
}
|
|
308
|
+
return s
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
func formatWebResults(results []WebResult) string {
|
|
312
|
+
var b strings.Builder
|
|
313
|
+
for i, r := range results {
|
|
314
|
+
b.WriteString(fmt.Sprintf("%d. %s\n %s\n %s\n", i+1, r.Title, r.URL, r.Snippet))
|
|
315
|
+
}
|
|
316
|
+
return b.String()
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
func formatWebResultsSimple(results []websearch.Result) string {
|
|
320
|
+
var b strings.Builder
|
|
321
|
+
for i, r := range results {
|
|
322
|
+
b.WriteString(fmt.Sprintf("%d. %s\n %s\n %s\n", i+1, r.Title, r.URL, r.Snippet))
|
|
323
|
+
}
|
|
324
|
+
return b.String()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
func mustGetEnv(key, fallback string) string {
|
|
328
|
+
if v := os.Getenv(key); v != "" {
|
|
329
|
+
return v
|
|
330
|
+
}
|
|
331
|
+
return fallback
|
|
332
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package agent
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"fmt"
|
|
6
|
+
"strings"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
func parseEditResponse(text string) (EditResponse, error) {
|
|
10
|
+
trimmed := strings.TrimSpace(text)
|
|
11
|
+
// Some models wrap JSON in markdown fences
|
|
12
|
+
trimmed = strings.TrimPrefix(trimmed, "```json")
|
|
13
|
+
trimmed = strings.TrimPrefix(trimmed, "```")
|
|
14
|
+
trimmed = strings.TrimSuffix(trimmed, "```")
|
|
15
|
+
trimmed = strings.TrimSpace(trimmed)
|
|
16
|
+
|
|
17
|
+
var resp EditResponse
|
|
18
|
+
if err := json.Unmarshal([]byte(trimmed), &resp); err != nil {
|
|
19
|
+
return EditResponse{}, fmt.Errorf("model did not return valid JSON: %w", err)
|
|
20
|
+
}
|
|
21
|
+
if len(resp.Edits) == 0 {
|
|
22
|
+
return EditResponse{}, fmt.Errorf("model returned no edits")
|
|
23
|
+
}
|
|
24
|
+
return resp, nil
|
|
25
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
package agents
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"fmt"
|
|
6
|
+
|
|
7
|
+
"github.com/ddhanush1/dcode/internal/registry"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// BaseAgent provides common functionality for all agents
|
|
11
|
+
type BaseAgent struct {
|
|
12
|
+
definition *registry.AgentDefinition
|
|
13
|
+
history []registry.Message
|
|
14
|
+
context map[string]interface{}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// NewBaseAgent creates a new base agent
|
|
18
|
+
func NewBaseAgent(def *registry.AgentDefinition) *BaseAgent {
|
|
19
|
+
return &BaseAgent{
|
|
20
|
+
definition: def,
|
|
21
|
+
history: make([]registry.Message, 0),
|
|
22
|
+
context: make(map[string]interface{}),
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// AddMessage adds a message to history
|
|
27
|
+
func (a *BaseAgent) AddMessage(role, content string, data map[string]interface{}) {
|
|
28
|
+
a.history = append(a.history, registry.Message{
|
|
29
|
+
Role: role,
|
|
30
|
+
Content: content,
|
|
31
|
+
Data: data,
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// GetHistory returns conversation history
|
|
36
|
+
func (a *BaseAgent) GetHistory() []registry.Message {
|
|
37
|
+
return a.history
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ClearHistory clears conversation history
|
|
41
|
+
func (a *BaseAgent) ClearHistory() {
|
|
42
|
+
a.history = make([]registry.Message, 0)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// SetContext sets context data
|
|
46
|
+
func (a *BaseAgent) SetContext(key string, value interface{}) {
|
|
47
|
+
a.context[key] = value
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// GetContext gets context data
|
|
51
|
+
func (a *BaseAgent) GetContext(key string) interface{} {
|
|
52
|
+
return a.context[key]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// GetDefinition returns the agent definition
|
|
56
|
+
func (a *BaseAgent) GetDefinition() *registry.AgentDefinition {
|
|
57
|
+
return a.definition
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// AgentManager manages agent lifecycle and execution
|
|
61
|
+
type AgentManager struct {
|
|
62
|
+
registry *registry.AgentRegistry
|
|
63
|
+
toolRegistry *registry.ToolRegistry
|
|
64
|
+
currentAgent string
|
|
65
|
+
activeAgents map[string]*BaseAgent
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// NewAgentManager creates a new agent manager
|
|
69
|
+
func NewAgentManager(agentRegistry *registry.AgentRegistry, toolRegistry *registry.ToolRegistry) *AgentManager {
|
|
70
|
+
return &AgentManager{
|
|
71
|
+
registry: agentRegistry,
|
|
72
|
+
toolRegistry: toolRegistry,
|
|
73
|
+
currentAgent: "generalist",
|
|
74
|
+
activeAgents: make(map[string]*BaseAgent),
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// SwitchAgent switches to a different agent
|
|
79
|
+
func (m *AgentManager) SwitchAgent(agentID string) error {
|
|
80
|
+
if !m.registry.Has(agentID) {
|
|
81
|
+
return fmt.Errorf("agent not found: %s", agentID)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
m.currentAgent = agentID
|
|
85
|
+
return nil
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// GetCurrentAgent returns the current agent
|
|
89
|
+
func (m *AgentManager) GetCurrentAgent() (*registry.AgentDefinition, error) {
|
|
90
|
+
return m.registry.Get(m.currentAgent)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Execute executes the current agent with input
|
|
94
|
+
func (m *AgentManager) Execute(ctx context.Context, input *registry.AgentInput) (*registry.AgentOutput, error) {
|
|
95
|
+
agent, err := m.GetCurrentAgent()
|
|
96
|
+
if err != nil {
|
|
97
|
+
return nil, err
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Get or create active agent instance
|
|
101
|
+
activeAgent, ok := m.activeAgents[agent.ID]
|
|
102
|
+
if !ok {
|
|
103
|
+
activeAgent = NewBaseAgent(agent)
|
|
104
|
+
m.activeAgents[agent.ID] = activeAgent
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Add to history only if query is not empty
|
|
108
|
+
if input.Query != "" {
|
|
109
|
+
activeAgent.AddMessage("user", input.Query, nil)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Execute agent
|
|
113
|
+
output, err := agent.Executor(input)
|
|
114
|
+
if err != nil {
|
|
115
|
+
return nil, err
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Add response to history only if message is not empty
|
|
119
|
+
if output.Message != "" {
|
|
120
|
+
activeAgent.AddMessage("assistant", output.Message, output.Data)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return output, nil
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// GetHistory returns history for an agent
|
|
127
|
+
func (m *AgentManager) GetHistory(agentID string) []registry.Message {
|
|
128
|
+
if agent, ok := m.activeAgents[agentID]; ok {
|
|
129
|
+
return agent.GetHistory()
|
|
130
|
+
}
|
|
131
|
+
return []registry.Message{}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ClearHistory clears history for an agent
|
|
135
|
+
func (m *AgentManager) ClearHistory(agentID string) {
|
|
136
|
+
if agent, ok := m.activeAgents[agentID]; ok {
|
|
137
|
+
agent.ClearHistory()
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// AddMessage adds a message to an agent's history
|
|
142
|
+
func (m *AgentManager) AddMessage(agentID, role, content string, data map[string]interface{}) {
|
|
143
|
+
agent, ok := m.activeAgents[agentID]
|
|
144
|
+
if !ok {
|
|
145
|
+
// Create if doesn't exist
|
|
146
|
+
agentDef, err := m.registry.Get(agentID)
|
|
147
|
+
if err != nil {
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
agent = NewBaseAgent(agentDef)
|
|
151
|
+
m.activeAgents[agentID] = agent
|
|
152
|
+
}
|
|
153
|
+
agent.AddMessage(role, content, data)
|
|
154
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
package agents
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"github.com/ddhanush1/dcode/internal/ai"
|
|
5
|
+
"github.com/ddhanush1/dcode/internal/registry"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
const documenterSystemPrompt = `You are a Documentation Specialist, an AI agent specialized in creating clear, comprehensive documentation.
|
|
9
|
+
|
|
10
|
+
Your documentation skills:
|
|
11
|
+
- README files and project overviews
|
|
12
|
+
- API documentation (REST, GraphQL, gRPC)
|
|
13
|
+
- Code comments and inline documentation
|
|
14
|
+
- Architecture documentation
|
|
15
|
+
- User guides and tutorials
|
|
16
|
+
- Contributing guidelines
|
|
17
|
+
- Changelog management
|
|
18
|
+
|
|
19
|
+
When writing documentation:
|
|
20
|
+
1. Start with a clear purpose and audience
|
|
21
|
+
2. Use clear, concise language
|
|
22
|
+
3. Include practical examples
|
|
23
|
+
4. Structure information logically
|
|
24
|
+
5. Keep it up-to-date with code changes
|
|
25
|
+
6. Use appropriate formatting (Markdown, etc.)
|
|
26
|
+
|
|
27
|
+
Documentation types:
|
|
28
|
+
- **README**: Project overview, setup, usage, examples
|
|
29
|
+
- **API Docs**: Endpoints, parameters, responses, examples
|
|
30
|
+
- **Code Comments**: Explain WHY, not WHAT
|
|
31
|
+
- **Architecture**: System design, component relationships
|
|
32
|
+
- **Guides**: Step-by-step tutorials for common tasks
|
|
33
|
+
- **Reference**: Comprehensive API/function reference
|
|
34
|
+
|
|
35
|
+
Best practices:
|
|
36
|
+
- Write for your audience's level
|
|
37
|
+
- Include code examples that actually work
|
|
38
|
+
- Use diagrams where helpful
|
|
39
|
+
- Keep documentation close to code
|
|
40
|
+
- Update docs when code changes
|
|
41
|
+
- Make it searchable and navigable
|
|
42
|
+
|
|
43
|
+
Use available tools to:
|
|
44
|
+
- read_file: Understand existing code/docs
|
|
45
|
+
- write_file: Create/update documentation
|
|
46
|
+
- glob: Find files to document
|
|
47
|
+
- git_log: Understand project evolution
|
|
48
|
+
- grep: Search for patterns
|
|
49
|
+
- ls: Explore project structure
|
|
50
|
+
|
|
51
|
+
Create documentation that helps people understand and use the code effectively.`
|
|
52
|
+
|
|
53
|
+
// GetDocumenterTools returns tools best suited for documentation
|
|
54
|
+
func GetDocumenterTools() []string {
|
|
55
|
+
return []string{
|
|
56
|
+
"read_file",
|
|
57
|
+
"write_file",
|
|
58
|
+
"glob",
|
|
59
|
+
"ls",
|
|
60
|
+
"git_log",
|
|
61
|
+
"grep",
|
|
62
|
+
"save_memory",
|
|
63
|
+
"recall_memory",
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// RegisterDocumenterAgent registers the documenter agent
|
|
68
|
+
func RegisterDocumenterAgent(agentRegistry *registry.AgentRegistry, aiClient *ai.Client) error {
|
|
69
|
+
return agentRegistry.Register(®istry.AgentDefinition{
|
|
70
|
+
ID: "documenter",
|
|
71
|
+
Name: "Documentation Specialist",
|
|
72
|
+
Description: "Specialized in creating clear, comprehensive documentation",
|
|
73
|
+
SystemPrompt: documenterSystemPrompt,
|
|
74
|
+
Tools: GetDocumenterTools(),
|
|
75
|
+
Executor: CreateGeneralistExecutor(aiClient),
|
|
76
|
+
})
|
|
77
|
+
}
|