@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
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
package tools
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
|
|
8
|
+
"github.com/ddhanush1/dcode/internal/registry"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// DeleteFileTool deletes a file
|
|
12
|
+
type DeleteFileTool struct{}
|
|
13
|
+
|
|
14
|
+
func NewDeleteFileTool() *registry.ToolDefinition {
|
|
15
|
+
return ®istry.ToolDefinition{
|
|
16
|
+
ID: "delete_file",
|
|
17
|
+
Name: "Delete File",
|
|
18
|
+
Description: "Delete a file or directory",
|
|
19
|
+
InputSchema: map[string]interface{}{
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": map[string]interface{}{
|
|
22
|
+
"path": map[string]interface{}{
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "Path to file or directory to delete",
|
|
25
|
+
},
|
|
26
|
+
"recursive": map[string]interface{}{
|
|
27
|
+
"type": "boolean",
|
|
28
|
+
"description": "Delete directory recursively",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
"required": []string{"path"},
|
|
32
|
+
},
|
|
33
|
+
RequiresConfirmation: true,
|
|
34
|
+
RiskLevel: "high",
|
|
35
|
+
Category: "file",
|
|
36
|
+
Executor: executeDeleteFile,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
func executeDeleteFile(input *registry.ToolInput) (*registry.ToolOutput, error) {
|
|
41
|
+
path, ok := input.Parameters["path"].(string)
|
|
42
|
+
if !ok {
|
|
43
|
+
return ®istry.ToolOutput{
|
|
44
|
+
Success: false,
|
|
45
|
+
Error: "path parameter is required",
|
|
46
|
+
}, fmt.Errorf("invalid path parameter")
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
recursive := false
|
|
50
|
+
if r, ok := input.Parameters["recursive"].(bool); ok {
|
|
51
|
+
recursive = r
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check if path exists
|
|
55
|
+
info, err := os.Stat(path)
|
|
56
|
+
if err != nil {
|
|
57
|
+
return ®istry.ToolOutput{
|
|
58
|
+
Success: false,
|
|
59
|
+
Error: fmt.Sprintf("path not found: %v", err),
|
|
60
|
+
}, err
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Handle directory
|
|
64
|
+
if info.IsDir() {
|
|
65
|
+
if !recursive {
|
|
66
|
+
return ®istry.ToolOutput{
|
|
67
|
+
Success: false,
|
|
68
|
+
Error: "path is a directory, use recursive=true to delete",
|
|
69
|
+
}, fmt.Errorf("directory requires recursive flag")
|
|
70
|
+
}
|
|
71
|
+
err = os.RemoveAll(path)
|
|
72
|
+
} else {
|
|
73
|
+
err = os.Remove(path)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if err != nil {
|
|
77
|
+
return ®istry.ToolOutput{
|
|
78
|
+
Success: false,
|
|
79
|
+
Error: fmt.Sprintf("failed to delete: %v", err),
|
|
80
|
+
}, err
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return ®istry.ToolOutput{
|
|
84
|
+
Success: true,
|
|
85
|
+
Data: fmt.Sprintf("Deleted: %s", path),
|
|
86
|
+
Metadata: map[string]interface{}{
|
|
87
|
+
"path": path,
|
|
88
|
+
"recursive": recursive,
|
|
89
|
+
},
|
|
90
|
+
}, nil
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// MoveFileTool moves/renames files
|
|
94
|
+
type MoveFileTool struct{}
|
|
95
|
+
|
|
96
|
+
func NewMoveFileTool() *registry.ToolDefinition {
|
|
97
|
+
return ®istry.ToolDefinition{
|
|
98
|
+
ID: "move_file",
|
|
99
|
+
Name: "Move/Rename File",
|
|
100
|
+
Description: "Move or rename a file",
|
|
101
|
+
InputSchema: map[string]interface{}{
|
|
102
|
+
"type": "object",
|
|
103
|
+
"properties": map[string]interface{}{
|
|
104
|
+
"source": map[string]interface{}{
|
|
105
|
+
"type": "string",
|
|
106
|
+
"description": "Source path",
|
|
107
|
+
},
|
|
108
|
+
"destination": map[string]interface{}{
|
|
109
|
+
"type": "string",
|
|
110
|
+
"description": "Destination path",
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
"required": []string{"source", "destination"},
|
|
114
|
+
},
|
|
115
|
+
RequiresConfirmation: true,
|
|
116
|
+
RiskLevel: "medium",
|
|
117
|
+
Category: "file",
|
|
118
|
+
Executor: executeMoveFile,
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
func executeMoveFile(input *registry.ToolInput) (*registry.ToolOutput, error) {
|
|
123
|
+
source, ok := input.Parameters["source"].(string)
|
|
124
|
+
if !ok {
|
|
125
|
+
return ®istry.ToolOutput{
|
|
126
|
+
Success: false,
|
|
127
|
+
Error: "source parameter is required",
|
|
128
|
+
}, fmt.Errorf("invalid source parameter")
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
dest, ok := input.Parameters["destination"].(string)
|
|
132
|
+
if !ok {
|
|
133
|
+
return ®istry.ToolOutput{
|
|
134
|
+
Success: false,
|
|
135
|
+
Error: "destination parameter is required",
|
|
136
|
+
}, fmt.Errorf("invalid destination parameter")
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
err := os.Rename(source, dest)
|
|
140
|
+
if err != nil {
|
|
141
|
+
return ®istry.ToolOutput{
|
|
142
|
+
Success: false,
|
|
143
|
+
Error: fmt.Sprintf("failed to move: %v", err),
|
|
144
|
+
}, err
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return ®istry.ToolOutput{
|
|
148
|
+
Success: true,
|
|
149
|
+
Data: fmt.Sprintf("Moved %s to %s", source, dest),
|
|
150
|
+
Metadata: map[string]interface{}{
|
|
151
|
+
"source": source,
|
|
152
|
+
"destination": dest,
|
|
153
|
+
},
|
|
154
|
+
}, nil
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// CreateDirectoryTool creates directories
|
|
158
|
+
type CreateDirectoryTool struct{}
|
|
159
|
+
|
|
160
|
+
func NewCreateDirectoryTool() *registry.ToolDefinition {
|
|
161
|
+
return ®istry.ToolDefinition{
|
|
162
|
+
ID: "create_directory",
|
|
163
|
+
Name: "Create Directory",
|
|
164
|
+
Description: "Create a directory",
|
|
165
|
+
InputSchema: map[string]interface{}{
|
|
166
|
+
"type": "object",
|
|
167
|
+
"properties": map[string]interface{}{
|
|
168
|
+
"path": map[string]interface{}{
|
|
169
|
+
"type": "string",
|
|
170
|
+
"description": "Directory path to create",
|
|
171
|
+
},
|
|
172
|
+
"parents": map[string]interface{}{
|
|
173
|
+
"type": "boolean",
|
|
174
|
+
"description": "Create parent directories",
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
"required": []string{"path"},
|
|
178
|
+
},
|
|
179
|
+
RequiresConfirmation: false,
|
|
180
|
+
RiskLevel: "low",
|
|
181
|
+
Category: "file",
|
|
182
|
+
Executor: executeCreateDirectory,
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
func executeCreateDirectory(input *registry.ToolInput) (*registry.ToolOutput, error) {
|
|
187
|
+
path, ok := input.Parameters["path"].(string)
|
|
188
|
+
if !ok {
|
|
189
|
+
return ®istry.ToolOutput{
|
|
190
|
+
Success: false,
|
|
191
|
+
Error: "path parameter is required",
|
|
192
|
+
}, fmt.Errorf("invalid path parameter")
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
parents := true
|
|
196
|
+
if p, ok := input.Parameters["parents"].(bool); ok {
|
|
197
|
+
parents = p
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
var err error
|
|
201
|
+
if parents {
|
|
202
|
+
err = os.MkdirAll(path, 0755)
|
|
203
|
+
} else {
|
|
204
|
+
err = os.Mkdir(path, 0755)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if err != nil {
|
|
208
|
+
return ®istry.ToolOutput{
|
|
209
|
+
Success: false,
|
|
210
|
+
Error: fmt.Sprintf("failed to create directory: %v", err),
|
|
211
|
+
}, err
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return ®istry.ToolOutput{
|
|
215
|
+
Success: true,
|
|
216
|
+
Data: fmt.Sprintf("Created directory: %s", path),
|
|
217
|
+
Metadata: map[string]interface{}{
|
|
218
|
+
"path": path,
|
|
219
|
+
"parents": parents,
|
|
220
|
+
},
|
|
221
|
+
}, nil
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// GlobTool finds files by pattern
|
|
225
|
+
type GlobTool struct{}
|
|
226
|
+
|
|
227
|
+
func NewGlobTool() *registry.ToolDefinition {
|
|
228
|
+
return ®istry.ToolDefinition{
|
|
229
|
+
ID: "glob",
|
|
230
|
+
Name: "Glob Pattern Match",
|
|
231
|
+
Description: "Find files matching a pattern",
|
|
232
|
+
InputSchema: map[string]interface{}{
|
|
233
|
+
"type": "object",
|
|
234
|
+
"properties": map[string]interface{}{
|
|
235
|
+
"pattern": map[string]interface{}{
|
|
236
|
+
"type": "string",
|
|
237
|
+
"description": "Glob pattern (e.g., '**/*.go')",
|
|
238
|
+
},
|
|
239
|
+
"base_path": map[string]interface{}{
|
|
240
|
+
"type": "string",
|
|
241
|
+
"description": "Base path to search from",
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
"required": []string{"pattern"},
|
|
245
|
+
},
|
|
246
|
+
RequiresConfirmation: false,
|
|
247
|
+
RiskLevel: "low",
|
|
248
|
+
Category: "file",
|
|
249
|
+
Executor: executeGlob,
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
func executeGlob(input *registry.ToolInput) (*registry.ToolOutput, error) {
|
|
254
|
+
pattern, ok := input.Parameters["pattern"].(string)
|
|
255
|
+
if !ok {
|
|
256
|
+
return ®istry.ToolOutput{
|
|
257
|
+
Success: false,
|
|
258
|
+
Error: "pattern parameter is required",
|
|
259
|
+
}, fmt.Errorf("invalid pattern parameter")
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
basePath := "."
|
|
263
|
+
if p, ok := input.Parameters["base_path"].(string); ok {
|
|
264
|
+
basePath = p
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Use filepath.Glob with full pattern
|
|
268
|
+
fullPattern := filepath.Join(basePath, pattern)
|
|
269
|
+
matches, err := filepath.Glob(fullPattern)
|
|
270
|
+
if err != nil {
|
|
271
|
+
return ®istry.ToolOutput{
|
|
272
|
+
Success: false,
|
|
273
|
+
Error: fmt.Sprintf("glob failed: %v", err),
|
|
274
|
+
}, err
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return ®istry.ToolOutput{
|
|
278
|
+
Success: true,
|
|
279
|
+
Data: matches,
|
|
280
|
+
Metadata: map[string]interface{}{
|
|
281
|
+
"pattern": pattern,
|
|
282
|
+
"base_path": basePath,
|
|
283
|
+
"count": len(matches),
|
|
284
|
+
},
|
|
285
|
+
}, nil
|
|
286
|
+
}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
package tools
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"fmt"
|
|
6
|
+
"os/exec"
|
|
7
|
+
"time"
|
|
8
|
+
|
|
9
|
+
"github.com/ddhanush1/dcode/internal/registry"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// GitStatusTool shows git repository status
|
|
13
|
+
type GitStatusTool struct{}
|
|
14
|
+
|
|
15
|
+
func NewGitStatusTool() *registry.ToolDefinition {
|
|
16
|
+
return ®istry.ToolDefinition{
|
|
17
|
+
ID: "git_status",
|
|
18
|
+
Name: "Git Status",
|
|
19
|
+
Description: "Show git repository status",
|
|
20
|
+
InputSchema: map[string]interface{}{
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": map[string]interface{}{
|
|
23
|
+
"path": map[string]interface{}{
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "Path to git repository (default: current directory)",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
RequiresConfirmation: false,
|
|
30
|
+
RiskLevel: "low",
|
|
31
|
+
Category: "git",
|
|
32
|
+
Executor: executeGitStatus,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func executeGitStatus(input *registry.ToolInput) (*registry.ToolOutput, error) {
|
|
37
|
+
path := "."
|
|
38
|
+
if p, ok := input.Parameters["path"].(string); ok {
|
|
39
|
+
path = p
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
43
|
+
defer cancel()
|
|
44
|
+
|
|
45
|
+
cmd := exec.CommandContext(ctx, "git", "-C", path, "status", "--short")
|
|
46
|
+
output, err := cmd.CombinedOutput()
|
|
47
|
+
|
|
48
|
+
if err != nil {
|
|
49
|
+
return ®istry.ToolOutput{
|
|
50
|
+
Success: false,
|
|
51
|
+
Error: fmt.Sprintf("git status failed: %v", err),
|
|
52
|
+
}, nil
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return ®istry.ToolOutput{
|
|
56
|
+
Success: true,
|
|
57
|
+
Data: string(output),
|
|
58
|
+
Metadata: map[string]interface{}{
|
|
59
|
+
"path": path,
|
|
60
|
+
},
|
|
61
|
+
}, nil
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// GitDiffTool shows git diff
|
|
65
|
+
type GitDiffTool struct{}
|
|
66
|
+
|
|
67
|
+
func NewGitDiffTool() *registry.ToolDefinition {
|
|
68
|
+
return ®istry.ToolDefinition{
|
|
69
|
+
ID: "git_diff",
|
|
70
|
+
Name: "Git Diff",
|
|
71
|
+
Description: "Show git diff",
|
|
72
|
+
InputSchema: map[string]interface{}{
|
|
73
|
+
"type": "object",
|
|
74
|
+
"properties": map[string]interface{}{
|
|
75
|
+
"path": map[string]interface{}{
|
|
76
|
+
"type": "string",
|
|
77
|
+
"description": "Path to git repository",
|
|
78
|
+
},
|
|
79
|
+
"cached": map[string]interface{}{
|
|
80
|
+
"type": "boolean",
|
|
81
|
+
"description": "Show staged changes",
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
RequiresConfirmation: false,
|
|
86
|
+
RiskLevel: "low",
|
|
87
|
+
Category: "git",
|
|
88
|
+
Executor: executeGitDiff,
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
func executeGitDiff(input *registry.ToolInput) (*registry.ToolOutput, error) {
|
|
93
|
+
path := "."
|
|
94
|
+
if p, ok := input.Parameters["path"].(string); ok {
|
|
95
|
+
path = p
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
cached := false
|
|
99
|
+
if c, ok := input.Parameters["cached"].(bool); ok {
|
|
100
|
+
cached = c
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
104
|
+
defer cancel()
|
|
105
|
+
|
|
106
|
+
args := []string{"-C", path, "diff"}
|
|
107
|
+
if cached {
|
|
108
|
+
args = append(args, "--cached")
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
cmd := exec.CommandContext(ctx, "git", args...)
|
|
112
|
+
output, err := cmd.CombinedOutput()
|
|
113
|
+
|
|
114
|
+
if err != nil {
|
|
115
|
+
return ®istry.ToolOutput{
|
|
116
|
+
Success: false,
|
|
117
|
+
Error: fmt.Sprintf("git diff failed: %v", err),
|
|
118
|
+
}, nil
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return ®istry.ToolOutput{
|
|
122
|
+
Success: true,
|
|
123
|
+
Data: string(output),
|
|
124
|
+
Metadata: map[string]interface{}{
|
|
125
|
+
"path": path,
|
|
126
|
+
"cached": cached,
|
|
127
|
+
},
|
|
128
|
+
}, nil
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// GitCommitTool commits changes
|
|
132
|
+
type GitCommitTool struct{}
|
|
133
|
+
|
|
134
|
+
func NewGitCommitTool() *registry.ToolDefinition {
|
|
135
|
+
return ®istry.ToolDefinition{
|
|
136
|
+
ID: "git_commit",
|
|
137
|
+
Name: "Git Commit",
|
|
138
|
+
Description: "Commit changes to git",
|
|
139
|
+
InputSchema: map[string]interface{}{
|
|
140
|
+
"type": "object",
|
|
141
|
+
"properties": map[string]interface{}{
|
|
142
|
+
"message": map[string]interface{}{
|
|
143
|
+
"type": "string",
|
|
144
|
+
"description": "Commit message",
|
|
145
|
+
},
|
|
146
|
+
"path": map[string]interface{}{
|
|
147
|
+
"type": "string",
|
|
148
|
+
"description": "Path to git repository",
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
"required": []string{"message"},
|
|
152
|
+
},
|
|
153
|
+
RequiresConfirmation: true,
|
|
154
|
+
RiskLevel: "high",
|
|
155
|
+
Category: "git",
|
|
156
|
+
Executor: executeGitCommit,
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
func executeGitCommit(input *registry.ToolInput) (*registry.ToolOutput, error) {
|
|
161
|
+
message, ok := input.Parameters["message"].(string)
|
|
162
|
+
if !ok {
|
|
163
|
+
return ®istry.ToolOutput{
|
|
164
|
+
Success: false,
|
|
165
|
+
Error: "message parameter is required",
|
|
166
|
+
}, fmt.Errorf("invalid message parameter")
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
path := "."
|
|
170
|
+
if p, ok := input.Parameters["path"].(string); ok {
|
|
171
|
+
path = p
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
175
|
+
defer cancel()
|
|
176
|
+
|
|
177
|
+
cmd := exec.CommandContext(ctx, "git", "-C", path, "commit", "-m", message)
|
|
178
|
+
output, err := cmd.CombinedOutput()
|
|
179
|
+
|
|
180
|
+
if err != nil {
|
|
181
|
+
return ®istry.ToolOutput{
|
|
182
|
+
Success: false,
|
|
183
|
+
Data: string(output),
|
|
184
|
+
Error: fmt.Sprintf("git commit failed: %v", err),
|
|
185
|
+
}, nil
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return ®istry.ToolOutput{
|
|
189
|
+
Success: true,
|
|
190
|
+
Data: string(output),
|
|
191
|
+
Metadata: map[string]interface{}{
|
|
192
|
+
"path": path,
|
|
193
|
+
"message": message,
|
|
194
|
+
},
|
|
195
|
+
}, nil
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// GitLogTool shows commit history
|
|
199
|
+
type GitLogTool struct{}
|
|
200
|
+
|
|
201
|
+
func NewGitLogTool() *registry.ToolDefinition {
|
|
202
|
+
return ®istry.ToolDefinition{
|
|
203
|
+
ID: "git_log",
|
|
204
|
+
Name: "Git Log",
|
|
205
|
+
Description: "Show git commit history",
|
|
206
|
+
InputSchema: map[string]interface{}{
|
|
207
|
+
"type": "object",
|
|
208
|
+
"properties": map[string]interface{}{
|
|
209
|
+
"path": map[string]interface{}{
|
|
210
|
+
"type": "string",
|
|
211
|
+
"description": "Path to git repository",
|
|
212
|
+
},
|
|
213
|
+
"limit": map[string]interface{}{
|
|
214
|
+
"type": "number",
|
|
215
|
+
"description": "Number of commits to show",
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
RequiresConfirmation: false,
|
|
220
|
+
RiskLevel: "low",
|
|
221
|
+
Category: "git",
|
|
222
|
+
Executor: executeGitLog,
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
func executeGitLog(input *registry.ToolInput) (*registry.ToolOutput, error) {
|
|
227
|
+
path := "."
|
|
228
|
+
if p, ok := input.Parameters["path"].(string); ok {
|
|
229
|
+
path = p
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
limit := 10
|
|
233
|
+
if l, ok := input.Parameters["limit"].(float64); ok {
|
|
234
|
+
limit = int(l)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
238
|
+
defer cancel()
|
|
239
|
+
|
|
240
|
+
cmd := exec.CommandContext(ctx, "git", "-C", path, "log",
|
|
241
|
+
fmt.Sprintf("-%d", limit), "--oneline", "--decorate")
|
|
242
|
+
output, err := cmd.CombinedOutput()
|
|
243
|
+
|
|
244
|
+
if err != nil {
|
|
245
|
+
return ®istry.ToolOutput{
|
|
246
|
+
Success: false,
|
|
247
|
+
Error: fmt.Sprintf("git log failed: %v", err),
|
|
248
|
+
}, nil
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return ®istry.ToolOutput{
|
|
252
|
+
Success: true,
|
|
253
|
+
Data: string(output),
|
|
254
|
+
Metadata: map[string]interface{}{
|
|
255
|
+
"path": path,
|
|
256
|
+
"limit": limit,
|
|
257
|
+
},
|
|
258
|
+
}, nil
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// GitBranchTool manages branches
|
|
262
|
+
type GitBranchTool struct{}
|
|
263
|
+
|
|
264
|
+
func NewGitBranchTool() *registry.ToolDefinition {
|
|
265
|
+
return ®istry.ToolDefinition{
|
|
266
|
+
ID: "git_branch",
|
|
267
|
+
Name: "Git Branch",
|
|
268
|
+
Description: "List or switch git branches",
|
|
269
|
+
InputSchema: map[string]interface{}{
|
|
270
|
+
"type": "object",
|
|
271
|
+
"properties": map[string]interface{}{
|
|
272
|
+
"path": map[string]interface{}{
|
|
273
|
+
"type": "string",
|
|
274
|
+
"description": "Path to git repository",
|
|
275
|
+
},
|
|
276
|
+
"action": map[string]interface{}{
|
|
277
|
+
"type": "string",
|
|
278
|
+
"description": "Action: list, switch, create",
|
|
279
|
+
},
|
|
280
|
+
"branch": map[string]interface{}{
|
|
281
|
+
"type": "string",
|
|
282
|
+
"description": "Branch name (for switch/create)",
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
RequiresConfirmation: false,
|
|
287
|
+
RiskLevel: "low",
|
|
288
|
+
Category: "git",
|
|
289
|
+
Executor: executeGitBranch,
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
func executeGitBranch(input *registry.ToolInput) (*registry.ToolOutput, error) {
|
|
294
|
+
path := "."
|
|
295
|
+
if p, ok := input.Parameters["path"].(string); ok {
|
|
296
|
+
path = p
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
action := "list"
|
|
300
|
+
if a, ok := input.Parameters["action"].(string); ok {
|
|
301
|
+
action = a
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
305
|
+
defer cancel()
|
|
306
|
+
|
|
307
|
+
var cmd *exec.Cmd
|
|
308
|
+
|
|
309
|
+
switch action {
|
|
310
|
+
case "list":
|
|
311
|
+
cmd = exec.CommandContext(ctx, "git", "-C", path, "branch", "-a")
|
|
312
|
+
case "switch":
|
|
313
|
+
branch, ok := input.Parameters["branch"].(string)
|
|
314
|
+
if !ok {
|
|
315
|
+
return ®istry.ToolOutput{
|
|
316
|
+
Success: false,
|
|
317
|
+
Error: "branch parameter required for switch action",
|
|
318
|
+
}, nil
|
|
319
|
+
}
|
|
320
|
+
cmd = exec.CommandContext(ctx, "git", "-C", path, "checkout", branch)
|
|
321
|
+
case "create":
|
|
322
|
+
branch, ok := input.Parameters["branch"].(string)
|
|
323
|
+
if !ok {
|
|
324
|
+
return ®istry.ToolOutput{
|
|
325
|
+
Success: false,
|
|
326
|
+
Error: "branch parameter required for create action",
|
|
327
|
+
}, nil
|
|
328
|
+
}
|
|
329
|
+
cmd = exec.CommandContext(ctx, "git", "-C", path, "checkout", "-b", branch)
|
|
330
|
+
default:
|
|
331
|
+
return ®istry.ToolOutput{
|
|
332
|
+
Success: false,
|
|
333
|
+
Error: "invalid action: " + action,
|
|
334
|
+
}, nil
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
output, err := cmd.CombinedOutput()
|
|
338
|
+
|
|
339
|
+
if err != nil {
|
|
340
|
+
return ®istry.ToolOutput{
|
|
341
|
+
Success: false,
|
|
342
|
+
Data: string(output),
|
|
343
|
+
Error: fmt.Sprintf("git branch failed: %v", err),
|
|
344
|
+
}, nil
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return ®istry.ToolOutput{
|
|
348
|
+
Success: true,
|
|
349
|
+
Data: string(output),
|
|
350
|
+
Metadata: map[string]interface{}{
|
|
351
|
+
"path": path,
|
|
352
|
+
"action": action,
|
|
353
|
+
},
|
|
354
|
+
}, nil
|
|
355
|
+
}
|