@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.
Files changed (70) hide show
  1. package/NPM_README.md +78 -0
  2. package/README.md +341 -0
  3. package/bin/dcode-bin +0 -0
  4. package/bin/dcode.js +44 -0
  5. package/cmd/agent_v2.go +448 -0
  6. package/cmd/analyze.go +97 -0
  7. package/cmd/auth.go +338 -0
  8. package/cmd/compose.go +284 -0
  9. package/cmd/context.go +111 -0
  10. package/cmd/edit.go +116 -0
  11. package/cmd/env.go +10 -0
  12. package/cmd/fix.go +145 -0
  13. package/cmd/gemini.go +20 -0
  14. package/cmd/generate.go +47 -0
  15. package/cmd/interactive.go +33 -0
  16. package/cmd/mcp.go +196 -0
  17. package/cmd/patch.go +19 -0
  18. package/cmd/providers.go +67 -0
  19. package/cmd/root.go +41 -0
  20. package/cmd/search.go +61 -0
  21. package/cmd/server.go +36 -0
  22. package/cmd/switch.go +122 -0
  23. package/cmd/terminal.go +277 -0
  24. package/go.mod +42 -0
  25. package/go.sum +86 -0
  26. package/internal/agent/agent.go +332 -0
  27. package/internal/agent/parse.go +25 -0
  28. package/internal/agents/base.go +154 -0
  29. package/internal/agents/documenter.go +77 -0
  30. package/internal/agents/generalist.go +266 -0
  31. package/internal/agents/investigator.go +60 -0
  32. package/internal/agents/registry.go +34 -0
  33. package/internal/agents/reviewer.go +67 -0
  34. package/internal/agents/tester.go +73 -0
  35. package/internal/ai/client.go +634 -0
  36. package/internal/ai/tools.go +332 -0
  37. package/internal/auth/adc.go +108 -0
  38. package/internal/auth/apikey.go +67 -0
  39. package/internal/auth/factory.go +145 -0
  40. package/internal/auth/oauth2.go +227 -0
  41. package/internal/auth/store.go +216 -0
  42. package/internal/auth/types.go +79 -0
  43. package/internal/auth/vertex.go +138 -0
  44. package/internal/config/config.go +428 -0
  45. package/internal/config/policy.go +251 -0
  46. package/internal/context/builder.go +312 -0
  47. package/internal/detector/detector.go +204 -0
  48. package/internal/diffutil/diffutil.go +30 -0
  49. package/internal/fsutil/fsutil.go +35 -0
  50. package/internal/mcp/client.go +314 -0
  51. package/internal/mcp/manager.go +221 -0
  52. package/internal/policy/policy.go +89 -0
  53. package/internal/prompt/interactive.go +338 -0
  54. package/internal/registry/agent.go +201 -0
  55. package/internal/registry/tool.go +181 -0
  56. package/internal/scheduler/scheduler.go +250 -0
  57. package/internal/server/server.go +167 -0
  58. package/internal/tools/file.go +183 -0
  59. package/internal/tools/filesystem.go +286 -0
  60. package/internal/tools/git.go +355 -0
  61. package/internal/tools/memory.go +269 -0
  62. package/internal/tools/registry.go +49 -0
  63. package/internal/tools/search.go +230 -0
  64. package/internal/tools/shell.go +84 -0
  65. package/internal/websearch/search.go +40 -0
  66. package/internal/websearch/tavily.go +79 -0
  67. package/main.go +19 -0
  68. package/package.json +57 -0
  69. package/scripts/install.js +59 -0
  70. 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 &registry.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 &registry.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 &registry.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 &registry.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 &registry.ToolOutput{
78
+ Success: false,
79
+ Error: fmt.Sprintf("failed to delete: %v", err),
80
+ }, err
81
+ }
82
+
83
+ return &registry.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 &registry.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 &registry.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 &registry.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 &registry.ToolOutput{
142
+ Success: false,
143
+ Error: fmt.Sprintf("failed to move: %v", err),
144
+ }, err
145
+ }
146
+
147
+ return &registry.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 &registry.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 &registry.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 &registry.ToolOutput{
209
+ Success: false,
210
+ Error: fmt.Sprintf("failed to create directory: %v", err),
211
+ }, err
212
+ }
213
+
214
+ return &registry.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 &registry.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 &registry.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 &registry.ToolOutput{
272
+ Success: false,
273
+ Error: fmt.Sprintf("glob failed: %v", err),
274
+ }, err
275
+ }
276
+
277
+ return &registry.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 &registry.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 &registry.ToolOutput{
50
+ Success: false,
51
+ Error: fmt.Sprintf("git status failed: %v", err),
52
+ }, nil
53
+ }
54
+
55
+ return &registry.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 &registry.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 &registry.ToolOutput{
116
+ Success: false,
117
+ Error: fmt.Sprintf("git diff failed: %v", err),
118
+ }, nil
119
+ }
120
+
121
+ return &registry.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 &registry.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 &registry.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 &registry.ToolOutput{
182
+ Success: false,
183
+ Data: string(output),
184
+ Error: fmt.Sprintf("git commit failed: %v", err),
185
+ }, nil
186
+ }
187
+
188
+ return &registry.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 &registry.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 &registry.ToolOutput{
246
+ Success: false,
247
+ Error: fmt.Sprintf("git log failed: %v", err),
248
+ }, nil
249
+ }
250
+
251
+ return &registry.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 &registry.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 &registry.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 &registry.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 &registry.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 &registry.ToolOutput{
341
+ Success: false,
342
+ Data: string(output),
343
+ Error: fmt.Sprintf("git branch failed: %v", err),
344
+ }, nil
345
+ }
346
+
347
+ return &registry.ToolOutput{
348
+ Success: true,
349
+ Data: string(output),
350
+ Metadata: map[string]interface{}{
351
+ "path": path,
352
+ "action": action,
353
+ },
354
+ }, nil
355
+ }