@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/cmd/context.go
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
package cmd
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"os"
|
|
6
|
+
|
|
7
|
+
"github.com/ddhanush1/dcode/internal/context"
|
|
8
|
+
"github.com/spf13/cobra"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
var (
|
|
12
|
+
contextListAll bool
|
|
13
|
+
contextAdd []string
|
|
14
|
+
contextRemove []string
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
var contextCmd = &cobra.Command{
|
|
18
|
+
Use: "context",
|
|
19
|
+
Short: "Manage context for AI operations",
|
|
20
|
+
Long: `Smart context management for better AI understanding
|
|
21
|
+
|
|
22
|
+
The context system helps DCode understand your codebase by:
|
|
23
|
+
• Auto-detecting relevant files
|
|
24
|
+
• Tracking dependencies and imports
|
|
25
|
+
• Building a knowledge graph
|
|
26
|
+
• Remembering conversation history
|
|
27
|
+
|
|
28
|
+
Subcommands:
|
|
29
|
+
analyze - Analyze project and build context
|
|
30
|
+
list - List current context files
|
|
31
|
+
clear - Clear context cache
|
|
32
|
+
`,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
var contextAnalyzeCmd = &cobra.Command{
|
|
36
|
+
Use: "analyze",
|
|
37
|
+
Short: "Analyze project and build context",
|
|
38
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
39
|
+
wd, _ := os.Getwd()
|
|
40
|
+
|
|
41
|
+
fmt.Println("\033[1;36m🔍 Analyzing project...\033[0m\n")
|
|
42
|
+
|
|
43
|
+
builder := context.NewBuilder(wd)
|
|
44
|
+
projectCtx, err := builder.Build()
|
|
45
|
+
if err != nil {
|
|
46
|
+
return fmt.Errorf("failed to analyze: %w", err)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Print detailed summary
|
|
50
|
+
fmt.Println(projectCtx.Summary)
|
|
51
|
+
|
|
52
|
+
fmt.Println("\n\033[90m💡 Use 'dcode agent' to interact with this project\033[0m")
|
|
53
|
+
|
|
54
|
+
return nil
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
var contextListCmd = &cobra.Command{
|
|
59
|
+
Use: "list",
|
|
60
|
+
Short: "List current context files",
|
|
61
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
62
|
+
wd, _ := os.Getwd()
|
|
63
|
+
|
|
64
|
+
builder := context.NewBuilder(wd)
|
|
65
|
+
projectCtx, err := builder.Build()
|
|
66
|
+
if err != nil {
|
|
67
|
+
return fmt.Errorf("failed to analyze: %w", err)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
files := projectCtx.RelevantFiles
|
|
71
|
+
|
|
72
|
+
if len(files) == 0 {
|
|
73
|
+
fmt.Println("\033[90mNo files in context\033[0m")
|
|
74
|
+
return nil
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fmt.Printf("\033[1;36mContext files (%d):\033[0m\n\n", len(files))
|
|
78
|
+
for i, f := range files {
|
|
79
|
+
if contextListAll || i < 20 {
|
|
80
|
+
fmt.Printf(" %3d. %.1f%% %s\n", i+1, f.Relevance*100, f.RelPath)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if !contextListAll && len(files) > 20 {
|
|
85
|
+
fmt.Printf("\n\033[90m... and %d more (use --all to show all)\033[0m\n", len(files)-20)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return nil
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
var contextClearCmd = &cobra.Command{
|
|
93
|
+
Use: "clear",
|
|
94
|
+
Short: "Clear context cache",
|
|
95
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
96
|
+
// For now, this is a no-op since we don't persist context yet
|
|
97
|
+
// In future: clear cache, embeddings, etc.
|
|
98
|
+
fmt.Println("\033[32m✓ Context cleared\033[0m")
|
|
99
|
+
return nil
|
|
100
|
+
},
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
func init() {
|
|
104
|
+
contextListCmd.Flags().BoolVarP(&contextListAll, "all", "a", false, "Show all files")
|
|
105
|
+
|
|
106
|
+
contextCmd.AddCommand(contextAnalyzeCmd)
|
|
107
|
+
contextCmd.AddCommand(contextListCmd)
|
|
108
|
+
contextCmd.AddCommand(contextClearCmd)
|
|
109
|
+
|
|
110
|
+
rootCmd.AddCommand(contextCmd)
|
|
111
|
+
}
|
package/cmd/edit.go
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
package cmd
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"fmt"
|
|
6
|
+
"os"
|
|
7
|
+
"path/filepath"
|
|
8
|
+
"strings"
|
|
9
|
+
"time"
|
|
10
|
+
|
|
11
|
+
"github.com/ddhanush1/dcode/internal/agent"
|
|
12
|
+
"github.com/ddhanush1/dcode/internal/fsutil"
|
|
13
|
+
"github.com/spf13/cobra"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
var (
|
|
17
|
+
editFile string
|
|
18
|
+
editPrompt string
|
|
19
|
+
aiProvider string
|
|
20
|
+
aiModel string
|
|
21
|
+
useWebSearch bool
|
|
22
|
+
dryRun bool
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
var editCmd = &cobra.Command{
|
|
26
|
+
Use: "edit",
|
|
27
|
+
Short: "Edit code using AI",
|
|
28
|
+
Long: `Edit code intelligently using AI providers (OpenAI, Gemini, Claude).
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
dcode edit --file main.go --prompt "Add error handling" --provider openai --model gpt-4
|
|
32
|
+
dcode edit --file app.py --prompt "Optimize this function" --provider gemini
|
|
33
|
+
dcode edit --file api.js --prompt "Add validation" --provider claude --search`,
|
|
34
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
35
|
+
if strings.TrimSpace(editFile) == "" {
|
|
36
|
+
return fmt.Errorf("--file is required")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Allow overriding provider/model via flags.
|
|
40
|
+
if aiProvider != "" {
|
|
41
|
+
_ = os.Setenv("AI_PROVIDER", aiProvider)
|
|
42
|
+
}
|
|
43
|
+
if aiModel != "" {
|
|
44
|
+
_ = os.Setenv("AI_MODEL", aiModel)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
content, err := fsutil.ReadFile(editFile)
|
|
48
|
+
if err != nil {
|
|
49
|
+
return err
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
ag, err := agent.New()
|
|
53
|
+
if err != nil {
|
|
54
|
+
return err
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
|
58
|
+
defer cancel()
|
|
59
|
+
|
|
60
|
+
resp, err := ag.Edit(ctx, agent.EditRequest{
|
|
61
|
+
Prompt: editPrompt,
|
|
62
|
+
Files: []agent.FileInput{{Path: filepath.ToSlash(editFile), Content: content}},
|
|
63
|
+
WebSearch: agent.WebSearchRequest{Enabled: useWebSearch},
|
|
64
|
+
})
|
|
65
|
+
if err != nil {
|
|
66
|
+
return err
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if len(resp.Edits) == 0 {
|
|
70
|
+
fmt.Println("No edits returned.")
|
|
71
|
+
return nil
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for _, e := range resp.Edits {
|
|
75
|
+
if filepath.Clean(e.Path) != filepath.Clean(filepath.ToSlash(editFile)) && filepath.Clean(e.Path) != filepath.Clean(editFile) {
|
|
76
|
+
fmt.Printf("Skipping unexpected edit path: %s\n", e.Path)
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
if e.NewContent == content {
|
|
80
|
+
fmt.Println("No changes needed.")
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if dryRun {
|
|
85
|
+
fmt.Printf("--- %s (dry-run, not written) ---\n", editFile)
|
|
86
|
+
fmt.Println(e.NewContent)
|
|
87
|
+
continue
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
perm := fsutil.FilePermOrDefault(editFile, 0o644)
|
|
91
|
+
if err := fsutil.WriteFileAtomic(editFile, []byte(e.NewContent), perm); err != nil {
|
|
92
|
+
return err
|
|
93
|
+
}
|
|
94
|
+
fmt.Printf("Updated %s\n", editFile)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if strings.TrimSpace(resp.Explanation) != "" {
|
|
98
|
+
fmt.Println("\nExplanation:")
|
|
99
|
+
fmt.Println(resp.Explanation)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return nil
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func init() {
|
|
107
|
+
editCmd.Flags().StringVarP(&editFile, "file", "f", "", "File to edit")
|
|
108
|
+
editCmd.Flags().StringVarP(&editPrompt, "prompt", "p", "", "Editing instructions (required)")
|
|
109
|
+
editCmd.Flags().StringVar(&aiProvider, "provider", "", "AI provider: openai, gemini, claude (default from .env)")
|
|
110
|
+
editCmd.Flags().StringVar(&aiModel, "model", "", "Model name (default from .env)")
|
|
111
|
+
editCmd.Flags().BoolVar(&useWebSearch, "search", false, "Enable web search for context")
|
|
112
|
+
editCmd.Flags().BoolVar(&dryRun, "dry-run", false, "Print new content but do not write")
|
|
113
|
+
|
|
114
|
+
editCmd.MarkFlagRequired("prompt")
|
|
115
|
+
rootCmd.AddCommand(editCmd)
|
|
116
|
+
}
|
package/cmd/env.go
ADDED
package/cmd/fix.go
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
package cmd
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"context"
|
|
6
|
+
"fmt"
|
|
7
|
+
"os"
|
|
8
|
+
"os/exec"
|
|
9
|
+
"path/filepath"
|
|
10
|
+
"strings"
|
|
11
|
+
"time"
|
|
12
|
+
|
|
13
|
+
"github.com/ddhanush1/dcode/internal/agent"
|
|
14
|
+
"github.com/ddhanush1/dcode/internal/fsutil"
|
|
15
|
+
"github.com/spf13/cobra"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
var (
|
|
19
|
+
fixFile string
|
|
20
|
+
fixPrompt string
|
|
21
|
+
fixCommand string
|
|
22
|
+
fixDryRun bool
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
var fixCmd = &cobra.Command{
|
|
26
|
+
Use: "fix",
|
|
27
|
+
Short: "Find and fix errors",
|
|
28
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
29
|
+
if strings.TrimSpace(fixFile) == "" {
|
|
30
|
+
return fmt.Errorf("--file is required")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
content, err := fsutil.ReadFile(fixFile)
|
|
34
|
+
if err != nil {
|
|
35
|
+
return err
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Allow overriding provider/model via flags inherited from edit if set.
|
|
39
|
+
if aiProvider != "" {
|
|
40
|
+
_ = os.Setenv("AI_PROVIDER", aiProvider)
|
|
41
|
+
}
|
|
42
|
+
if aiModel != "" {
|
|
43
|
+
_ = os.Setenv("AI_MODEL", aiModel)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
var testOutput string
|
|
47
|
+
if strings.TrimSpace(fixCommand) != "" {
|
|
48
|
+
out, err := runShellCommand(fixCommand)
|
|
49
|
+
testOutput = out
|
|
50
|
+
if err != nil {
|
|
51
|
+
// keep output; model can still try to fix
|
|
52
|
+
fmt.Printf("Command failed (continuing): %v\n", err)
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
// If this looks like a Go workspace, run tests by default.
|
|
56
|
+
if _, err := os.Stat("go.mod"); err == nil {
|
|
57
|
+
out, err := runShellCommand("go test ./...")
|
|
58
|
+
testOutput = out
|
|
59
|
+
if err != nil {
|
|
60
|
+
fmt.Printf("go test failed (continuing): %v\n", err)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
ag, err := agent.New()
|
|
66
|
+
if err != nil {
|
|
67
|
+
return err
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
ctx, cancel := context.WithTimeout(context.Background(), 8*time.Minute)
|
|
71
|
+
defer cancel()
|
|
72
|
+
|
|
73
|
+
resp, err := ag.Fix(ctx, agent.FixRequest{
|
|
74
|
+
Prompt: fixPrompt,
|
|
75
|
+
Files: []agent.FileInput{{Path: filepath.ToSlash(fixFile), Content: content}},
|
|
76
|
+
TestOutput: testOutput,
|
|
77
|
+
})
|
|
78
|
+
if err != nil {
|
|
79
|
+
return err
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
for _, e := range resp.Edits {
|
|
83
|
+
if filepath.Clean(e.Path) != filepath.Clean(filepath.ToSlash(fixFile)) && filepath.Clean(e.Path) != filepath.Clean(fixFile) {
|
|
84
|
+
fmt.Printf("Skipping unexpected edit path: %s\n", e.Path)
|
|
85
|
+
continue
|
|
86
|
+
}
|
|
87
|
+
if e.NewContent == content {
|
|
88
|
+
fmt.Println("No changes needed.")
|
|
89
|
+
continue
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if fixDryRun {
|
|
93
|
+
fmt.Printf("--- %s (dry-run, not written) ---\n", fixFile)
|
|
94
|
+
fmt.Println(e.NewContent)
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
perm := fsutil.FilePermOrDefault(fixFile, 0o644)
|
|
99
|
+
if err := fsutil.WriteFileAtomic(fixFile, []byte(e.NewContent), perm); err != nil {
|
|
100
|
+
return err
|
|
101
|
+
}
|
|
102
|
+
fmt.Printf("Updated %s\n", fixFile)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if strings.TrimSpace(resp.Explanation) != "" {
|
|
106
|
+
fmt.Println("\nExplanation:")
|
|
107
|
+
fmt.Println(resp.Explanation)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return nil
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
func runShellCommand(command string) (string, error) {
|
|
115
|
+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
|
116
|
+
defer cancel()
|
|
117
|
+
|
|
118
|
+
// Use `bash -lc` when available (WSL/Linux/mac). On Windows, users can pass a single command supported by their shell.
|
|
119
|
+
var cmd *exec.Cmd
|
|
120
|
+
if _, err := exec.LookPath("bash"); err == nil {
|
|
121
|
+
cmd = exec.CommandContext(ctx, "bash", "-lc", command)
|
|
122
|
+
} else {
|
|
123
|
+
parts := strings.Fields(command)
|
|
124
|
+
if len(parts) == 0 {
|
|
125
|
+
return "", fmt.Errorf("empty command")
|
|
126
|
+
}
|
|
127
|
+
cmd = exec.CommandContext(ctx, parts[0], parts[1:]...)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
var buf bytes.Buffer
|
|
131
|
+
cmd.Stdout = &buf
|
|
132
|
+
cmd.Stderr = &buf
|
|
133
|
+
cmd.Dir = "."
|
|
134
|
+
cmd.Env = os.Environ()
|
|
135
|
+
err := cmd.Run()
|
|
136
|
+
return buf.String(), err
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
func init() {
|
|
140
|
+
fixCmd.Flags().StringVarP(&fixFile, "file", "f", "", "File to fix (required)")
|
|
141
|
+
fixCmd.Flags().StringVarP(&fixPrompt, "prompt", "p", "", "Optional extra instructions")
|
|
142
|
+
fixCmd.Flags().StringVar(&fixCommand, "command", "", "Command to run for error output (default: go test ./... if go.mod exists)")
|
|
143
|
+
fixCmd.Flags().BoolVar(&fixDryRun, "dry-run", false, "Print new content but do not write")
|
|
144
|
+
rootCmd.AddCommand(fixCmd)
|
|
145
|
+
}
|
package/cmd/gemini.go
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
package cmd
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
|
|
6
|
+
"github.com/spf13/cobra"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
var geminiCmd = &cobra.Command{
|
|
10
|
+
Use: "gemini",
|
|
11
|
+
Short: "Start agent with Google Gemini (shortcut)",
|
|
12
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
13
|
+
_ = os.Setenv("AI_PROVIDER", "gemini")
|
|
14
|
+
return agentV2Cmd.RunE(cmd, args)
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
func init() {
|
|
19
|
+
rootCmd.AddCommand(geminiCmd)
|
|
20
|
+
}
|
package/cmd/generate.go
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
package cmd
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"github.com/spf13/cobra"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
var generateCmd = &cobra.Command{
|
|
9
|
+
Use: "generate",
|
|
10
|
+
Short: "Generate code using AI",
|
|
11
|
+
Long: `Generate code from natural language descriptions using AI.
|
|
12
|
+
|
|
13
|
+
Examples:
|
|
14
|
+
dcode generate --prompt "REST API with authentication" --language go --provider openai
|
|
15
|
+
dcode generate --prompt "Binary search algorithm" --language python --provider gemini
|
|
16
|
+
dcode generate --prompt "React component for login form" --language typescript --provider claude`,
|
|
17
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
18
|
+
genPrompt, _ := cmd.Flags().GetString("prompt")
|
|
19
|
+
genLang, _ := cmd.Flags().GetString("language")
|
|
20
|
+
provider, _ := cmd.Flags().GetString("provider")
|
|
21
|
+
model, _ := cmd.Flags().GetString("model")
|
|
22
|
+
|
|
23
|
+
if provider == "" {
|
|
24
|
+
provider = getEnv("AI_PROVIDER", "openai")
|
|
25
|
+
}
|
|
26
|
+
if model == "" {
|
|
27
|
+
model = getEnv("AI_MODEL", "gpt-4")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fmt.Printf(" Using %s (%s)\n", provider, model)
|
|
31
|
+
fmt.Printf(" Generating %s code...\n", genLang)
|
|
32
|
+
fmt.Printf(" Prompt: %s\n\n", genPrompt)
|
|
33
|
+
|
|
34
|
+
fmt.Println("Generated code will appear here...")
|
|
35
|
+
return nil
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
func init() {
|
|
40
|
+
generateCmd.Flags().StringP("prompt", "p", "", "Description of code to generate (required)")
|
|
41
|
+
generateCmd.Flags().StringP("language", "l", "go", "Programming language")
|
|
42
|
+
generateCmd.Flags().String("provider", "", "AI provider: openai, gemini, claude")
|
|
43
|
+
generateCmd.Flags().String("model", "", "Model name")
|
|
44
|
+
|
|
45
|
+
generateCmd.MarkFlagRequired("prompt")
|
|
46
|
+
rootCmd.AddCommand(generateCmd)
|
|
47
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
package cmd
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
|
|
6
|
+
"github.com/ddhanush1/dcode/internal/prompt"
|
|
7
|
+
"github.com/spf13/cobra"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
var interactiveCmd = &cobra.Command{
|
|
11
|
+
Use: "interactive",
|
|
12
|
+
Short: "Start interactive mode with slash command suggestions",
|
|
13
|
+
Long: `Start an interactive session with a prompt that shows command suggestions
|
|
14
|
+
when you type /. Use arrow keys to navigate, Tab or Enter to select.
|
|
15
|
+
|
|
16
|
+
Features:
|
|
17
|
+
• Type / to see all available commands
|
|
18
|
+
• Autocomplete filters as you type
|
|
19
|
+
• Use ↑↓ arrow keys to navigate suggestions
|
|
20
|
+
• Press Tab or Enter to select a command
|
|
21
|
+
• Type regular prompts without / for normal input`,
|
|
22
|
+
Run: func(cmd *cobra.Command, args []string) {
|
|
23
|
+
fmt.Println("Starting interactive mode...")
|
|
24
|
+
fmt.Println()
|
|
25
|
+
if err := prompt.StartInteractive(); err != nil {
|
|
26
|
+
fmt.Printf("Error: %v\n", err)
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
func init() {
|
|
32
|
+
rootCmd.AddCommand(interactiveCmd)
|
|
33
|
+
}
|
package/cmd/mcp.go
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
package cmd
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
|
|
6
|
+
"github.com/spf13/cobra"
|
|
7
|
+
"github.com/ddhanush1/dcode/internal/mcp"
|
|
8
|
+
"github.com/ddhanush1/dcode/internal/registry"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
var mcpCmd = &cobra.Command{
|
|
12
|
+
Use: "mcp",
|
|
13
|
+
Short: "Manage MCP (Model Context Protocol) servers",
|
|
14
|
+
Long: `Manage MCP servers that provide additional tools, resources, and prompts.
|
|
15
|
+
|
|
16
|
+
MCP servers extend dcode's capabilities by providing:
|
|
17
|
+
- External tools (file systems, APIs, databases)
|
|
18
|
+
- Resources (documents, URLs, data)
|
|
19
|
+
- Prompts (reusable templates)`,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
var mcpListCmd = &cobra.Command{
|
|
23
|
+
Use: "list",
|
|
24
|
+
Short: "List all configured MCP servers",
|
|
25
|
+
Run: func(cmd *cobra.Command, args []string) {
|
|
26
|
+
// Create tool registry (needed for manager)
|
|
27
|
+
toolRegistry := registry.NewToolRegistry()
|
|
28
|
+
|
|
29
|
+
// Create MCP manager
|
|
30
|
+
manager := mcp.NewMCPManager(toolRegistry)
|
|
31
|
+
|
|
32
|
+
// List servers
|
|
33
|
+
servers := manager.ListServers()
|
|
34
|
+
|
|
35
|
+
if len(servers) == 0 {
|
|
36
|
+
fmt.Println("No MCP servers configured.")
|
|
37
|
+
fmt.Println("\nAdd a server with: dcode mcp add <url>")
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
fmt.Println("╔════════════════════════════════════════════════════════════╗")
|
|
42
|
+
fmt.Println("║ MCP Servers ║")
|
|
43
|
+
fmt.Println("╚════════════════════════════════════════════════════════════╝")
|
|
44
|
+
fmt.Println()
|
|
45
|
+
|
|
46
|
+
for _, server := range servers {
|
|
47
|
+
status := "❌ Disabled"
|
|
48
|
+
if server.Enabled {
|
|
49
|
+
status = "✅ Enabled"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fmt.Printf("📡 %s (%s)\n", server.Name, server.ID)
|
|
53
|
+
fmt.Printf(" URL: %s\n", server.URL)
|
|
54
|
+
fmt.Printf(" Status: %s\n", status)
|
|
55
|
+
fmt.Println()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fmt.Printf("Total: %d server(s)\n", len(servers))
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
var mcpAddCmd = &cobra.Command{
|
|
63
|
+
Use: "add <url>",
|
|
64
|
+
Short: "Add a new MCP server",
|
|
65
|
+
Args: cobra.ExactArgs(1),
|
|
66
|
+
Run: func(cmd *cobra.Command, args []string) {
|
|
67
|
+
url := args[0]
|
|
68
|
+
name, _ := cmd.Flags().GetString("name")
|
|
69
|
+
id, _ := cmd.Flags().GetString("id")
|
|
70
|
+
|
|
71
|
+
// Generate ID if not provided
|
|
72
|
+
if id == "" {
|
|
73
|
+
id = fmt.Sprintf("mcp_%d", len(args))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Use URL as name if not provided
|
|
77
|
+
if name == "" {
|
|
78
|
+
name = url
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Create tool registry
|
|
82
|
+
toolRegistry := registry.NewToolRegistry()
|
|
83
|
+
|
|
84
|
+
// Create MCP manager
|
|
85
|
+
manager := mcp.NewMCPManager(toolRegistry)
|
|
86
|
+
|
|
87
|
+
// Add server
|
|
88
|
+
server := &mcp.MCPServer{
|
|
89
|
+
ID: id,
|
|
90
|
+
Name: name,
|
|
91
|
+
URL: url,
|
|
92
|
+
Enabled: true,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if err := manager.AddServer(server); err != nil {
|
|
96
|
+
fmt.Printf("❌ Failed to add server: %v\n", err)
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
fmt.Printf("✅ Added MCP server: %s\n", name)
|
|
101
|
+
fmt.Printf(" ID: %s\n", id)
|
|
102
|
+
fmt.Printf(" URL: %s\n", url)
|
|
103
|
+
fmt.Println("\nDiscovering tools...")
|
|
104
|
+
|
|
105
|
+
// Enable and discover tools
|
|
106
|
+
if err := manager.EnableServer(id); err != nil {
|
|
107
|
+
fmt.Printf("⚠️ Warning: failed to discover tools: %v\n", err)
|
|
108
|
+
} else {
|
|
109
|
+
fmt.Println("✅ Tools discovered and registered")
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
var mcpRemoveCmd = &cobra.Command{
|
|
115
|
+
Use: "remove <id>",
|
|
116
|
+
Short: "Remove an MCP server",
|
|
117
|
+
Args: cobra.ExactArgs(1),
|
|
118
|
+
Run: func(cmd *cobra.Command, args []string) {
|
|
119
|
+
id := args[0]
|
|
120
|
+
|
|
121
|
+
// Create tool registry
|
|
122
|
+
toolRegistry := registry.NewToolRegistry()
|
|
123
|
+
|
|
124
|
+
// Create MCP manager
|
|
125
|
+
manager := mcp.NewMCPManager(toolRegistry)
|
|
126
|
+
|
|
127
|
+
// Remove server
|
|
128
|
+
if err := manager.RemoveServer(id); err != nil {
|
|
129
|
+
fmt.Printf("❌ Failed to remove server: %v\n", err)
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
fmt.Printf("✅ Removed MCP server: %s\n", id)
|
|
134
|
+
},
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
var mcpEnableCmd = &cobra.Command{
|
|
138
|
+
Use: "enable <id>",
|
|
139
|
+
Short: "Enable an MCP server",
|
|
140
|
+
Args: cobra.ExactArgs(1),
|
|
141
|
+
Run: func(cmd *cobra.Command, args []string) {
|
|
142
|
+
id := args[0]
|
|
143
|
+
|
|
144
|
+
// Create tool registry
|
|
145
|
+
toolRegistry := registry.NewToolRegistry()
|
|
146
|
+
|
|
147
|
+
// Create MCP manager
|
|
148
|
+
manager := mcp.NewMCPManager(toolRegistry)
|
|
149
|
+
|
|
150
|
+
// Enable server
|
|
151
|
+
if err := manager.EnableServer(id); err != nil {
|
|
152
|
+
fmt.Printf("❌ Failed to enable server: %v\n", err)
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
fmt.Printf("✅ Enabled MCP server: %s\n", id)
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
var mcpDisableCmd = &cobra.Command{
|
|
161
|
+
Use: "disable <id>",
|
|
162
|
+
Short: "Disable an MCP server",
|
|
163
|
+
Args: cobra.ExactArgs(1),
|
|
164
|
+
Run: func(cmd *cobra.Command, args []string) {
|
|
165
|
+
id := args[0]
|
|
166
|
+
|
|
167
|
+
// Create tool registry
|
|
168
|
+
toolRegistry := registry.NewToolRegistry()
|
|
169
|
+
|
|
170
|
+
// Create MCP manager
|
|
171
|
+
manager := mcp.NewMCPManager(toolRegistry)
|
|
172
|
+
|
|
173
|
+
// Disable server
|
|
174
|
+
if err := manager.DisableServer(id); err != nil {
|
|
175
|
+
fmt.Printf("❌ Failed to disable server: %v\n", err)
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
fmt.Printf("✅ Disabled MCP server: %s\n", id)
|
|
180
|
+
},
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
func init() {
|
|
184
|
+
rootCmd.AddCommand(mcpCmd)
|
|
185
|
+
|
|
186
|
+
// Add subcommands
|
|
187
|
+
mcpCmd.AddCommand(mcpListCmd)
|
|
188
|
+
mcpCmd.AddCommand(mcpAddCmd)
|
|
189
|
+
mcpCmd.AddCommand(mcpRemoveCmd)
|
|
190
|
+
mcpCmd.AddCommand(mcpEnableCmd)
|
|
191
|
+
mcpCmd.AddCommand(mcpDisableCmd)
|
|
192
|
+
|
|
193
|
+
// Add flags
|
|
194
|
+
mcpAddCmd.Flags().String("name", "", "Server name")
|
|
195
|
+
mcpAddCmd.Flags().String("id", "", "Server ID (auto-generated if not provided)")
|
|
196
|
+
}
|
package/cmd/patch.go
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
package cmd
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"github.com/spf13/cobra"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
var patchCmd = &cobra.Command{
|
|
9
|
+
Use: "patch",
|
|
10
|
+
Short: "Generate or apply patches",
|
|
11
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
12
|
+
fmt.Println("Patch operations - coming soon!")
|
|
13
|
+
return nil
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
func init() {
|
|
18
|
+
rootCmd.AddCommand(patchCmd)
|
|
19
|
+
}
|