Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
e47ca3e
introduce agent package
jordanstephens Nov 28, 2025
206d1d4
delegate tool calls to toolmanager
jordanstephens Nov 28, 2025
e2c1608
setup defang cmd as agent entrypoint
jordanstephens Nov 28, 2025
366f4e1
def stacks.Load and stacks.Read
jordanstephens Nov 29, 2025
1b8c091
delete setup tools
jordanstephens Nov 29, 2025
b169034
delete setup prompts
jordanstephens Dec 1, 2025
d50bdc1
delete login tool
jordanstephens Nov 29, 2025
3cd4650
upgrade mcp-go
jordanstephens Dec 1, 2025
e7c969e
remove tool descriptions from mcp server instructions
jordanstephens Dec 2, 2025
8ca53cf
create elicitations package
jordanstephens Dec 2, 2025
efb2105
expose elicitations controller to tool collector
jordanstephens Dec 2, 2025
8dcc0c7
Tail should only need project name
jordanstephens Dec 2, 2025
ebbcee8
pass StackConfig and ElicitationsController to mcp tools
jordanstephens Dec 2, 2025
f8fbc28
avoid opening browser
jordanstephens Dec 2, 2025
842665e
blocking deployments
jordanstephens Dec 2, 2025
3531791
define tools with genkit, translate to mcp
jordanstephens Dec 2, 2025
7dee286
configure agent provider with elicitations
jordanstephens Dec 2, 2025
d46a302
configure tool providers with elicitations
jordanstephens Dec 2, 2025
ae65eb0
expose defang tools to cli agent
jordanstephens Dec 2, 2025
51db9cd
remove support for stackless .defang file
jordanstephens Dec 2, 2025
62eeffb
get tests green
jordanstephens Dec 2, 2025
2c8fa24
load profiles from disk
jordanstephens Dec 3, 2025
86344d3
re-use stack between tool calls
jordanstephens Dec 3, 2025
5d816a1
stacks cannot have "-" or "_"
jordanstephens Dec 3, 2025
97f396e
use survey for chat prompts
jordanstephens Dec 3, 2025
3d18b14
ensure stack and provider auth are shared between tool calls
jordanstephens Dec 3, 2025
cc125ba
setup agent debugger
jordanstephens Dec 3, 2025
e2cfa54
validate etags on tail
jordanstephens Dec 3, 2025
7c5cb80
re-iterate deployment_id to agent after deployment
jordanstephens Dec 3, 2025
a2403c8
increase MaxCompletionTokens
jordanstephens Dec 4, 2025
5159131
interactively ask for missing config
jordanstephens Dec 4, 2025
cb334b6
Merge branch 'main' into jordan/agent-2
lionello Dec 7, 2025
527008c
track debug prompted
jordanstephens Dec 8, 2025
ee3a1a1
remove support for .defang/.defang
jordanstephens Dec 8, 2025
bb335ec
rename provider to aiProvider
jordanstephens Dec 8, 2025
87a52e4
remove debug logging
jordanstephens Dec 8, 2025
ec2b6cf
rename tc to sc
jordanstephens Dec 8, 2025
4f72db8
avoid uppercase error messages
jordanstephens Dec 8, 2025
a966467
name interface method parameters
jordanstephens Dec 8, 2025
4165002
avoid swallowing errors
jordanstephens Dec 8, 2025
55c5873
avoid breaking up user messages into two with different media
jordanstephens Dec 8, 2025
f11256e
EqualPrevious handles multiple tool calls
jordanstephens Dec 8, 2025
ebf7f52
messages
jordanstephens Dec 8, 2025
ad26d72
guard against nil pointers
jordanstephens Dec 8, 2025
f964644
close file at the end of each loop cycle
jordanstephens Dec 8, 2025
37567ac
remove unused Connecter interface
jordanstephens Dec 8, 2025
93667ad
ensure a space before deployment id
jordanstephens Dec 8, 2025
dd08100
error message capitalization
jordanstephens Dec 8, 2025
5f584d2
safer schema parsing
jordanstephens Dec 8, 2025
9cb4629
refactor prepareQuestions
jordanstephens Dec 8, 2025
bfc0136
caps
jordanstephens Dec 8, 2025
672d2d2
replace debug.go with debugger
jordanstephens Dec 8, 2025
5b77675
avoid nil ptr access
jordanstephens Dec 9, 2025
5b80db6
avoid a few more potential nil ptr accesses
jordanstephens Dec 9, 2025
85a5198
typos
jordanstephens Dec 9, 2025
088f0f6
avoid concat without space
jordanstephens Dec 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkgs/defang/cli.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ buildGo124Module {
pname = "defang-cli";
version = "git";
src = lib.cleanSource ../../src;
vendorHash = "sha256-le/O0WhOXO8ItgiG1ZRqDzMNV/2mOwWATn8T/xfn6dM="; # TODO: use fetchFromGitHub
vendorHash = "sha256-/1a2G+8JQUYhMv2zHlhDoDsFLXhxA//AwVfIWw5fqeo="; # TODO: use fetchFromGitHub

subPackages = [ "cmd/cli" ];

Expand Down
37 changes: 31 additions & 6 deletions src/cmd/cli/command/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ import (

"github.com/AlecAivazis/survey/v2"
"github.com/DefangLabs/defang/src/pkg"
"github.com/DefangLabs/defang/src/pkg/agent"
"github.com/DefangLabs/defang/src/pkg/cli"
cliClient "github.com/DefangLabs/defang/src/pkg/cli/client"
"github.com/DefangLabs/defang/src/pkg/cli/client/byoc"
"github.com/DefangLabs/defang/src/pkg/cli/client/byoc/gcp"
"github.com/DefangLabs/defang/src/pkg/cli/compose"
"github.com/DefangLabs/defang/src/pkg/clouds/aws"
"github.com/DefangLabs/defang/src/pkg/debug"
"github.com/DefangLabs/defang/src/pkg/dryrun"
"github.com/DefangLabs/defang/src/pkg/login"
"github.com/DefangLabs/defang/src/pkg/logs"
Expand Down Expand Up @@ -406,6 +408,24 @@ var RootCmd = &cobra.Command{

return err
},
RunE: func(cmd *cobra.Command, args []string) error {
if global.NonInteractive {
return cmd.Help()
}

ctx := cmd.Context()
err := login.InteractiveRequireLoginAndToS(ctx, global.Client, getCluster())
if err != nil {
return err
}

prompt := "Welcome to Defang. I can help you deploy your project to the cloud"
ag, err := agent.New(ctx, getCluster(), &global.ProviderID, &global.Stack)
if err != nil {
return err
}
return ag.StartWithUserPrompt(ctx, prompt)
},
}

var loginCmd = &cobra.Command{
Expand Down Expand Up @@ -852,6 +872,7 @@ var debugCmd = &cobra.Command{
Hidden: true,
Short: "Debug a build, deployment, or service failure",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
etag, _ := cmd.Flags().GetString("etag")
deployment, _ := cmd.Flags().GetString("deployment")
since, _ := cmd.Flags().GetString("since")
Expand All @@ -862,12 +883,17 @@ var debugCmd = &cobra.Command{
}

loader := configureLoader(cmd)
provider, err := newProviderChecked(cmd.Context(), loader)
_, err := newProviderChecked(ctx, loader)
if err != nil {
return err
}

project, err := loader.LoadProject(cmd.Context())
project, err := loader.LoadProject(ctx)
if err != nil {
return err
}

debugger, err := debug.NewDebugger(ctx, getCluster(), &global.ProviderID, &global.Stack)
if err != nil {
return err
}
Expand All @@ -882,16 +908,15 @@ var debugCmd = &cobra.Command{
return fmt.Errorf("invalid 'until' time: %w", err)
}

debugConfig := cli.DebugConfig{
debugConfig := debug.DebugConfig{
Deployment: deployment,
FailedServices: args,
ModelId: global.ModelID,
Project: project,
Provider: provider,
ProviderID: &global.ProviderID,
Since: sinceTs.UTC(),
Until: untilTs.UTC(),
}
return cli.DebugDeployment(cmd.Context(), global.Client, debugConfig)
return debugger.DebugDeployment(ctx, debugConfig)
},
}

Expand Down
44 changes: 29 additions & 15 deletions src/cmd/cli/command/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
cliClient "github.com/DefangLabs/defang/src/pkg/cli/client"
"github.com/DefangLabs/defang/src/pkg/cli/client/byoc"
"github.com/DefangLabs/defang/src/pkg/cli/compose"
"github.com/DefangLabs/defang/src/pkg/debug"
"github.com/DefangLabs/defang/src/pkg/dryrun"
"github.com/DefangLabs/defang/src/pkg/logs"
"github.com/DefangLabs/defang/src/pkg/modes"
Expand Down Expand Up @@ -160,14 +161,20 @@ func makeComposeUpCmd() *cobra.Command {
tailOptions := newTailOptionsForDeploy(deploy.Etag, since, global.Verbose)
serviceStates, err := cli.TailAndMonitor(ctx, project, provider, time.Duration(waitTimeout)*time.Second, tailOptions)
if err != nil {
handleTailAndMonitorErr(ctx, err, global.Client, cli.DebugConfig{
deploymentErr := err
debugger, err := debug.NewDebugger(ctx, getCluster(), &global.ProviderID, &global.Stack)
if err != nil {
return err
}
handleTailAndMonitorErr(ctx, deploymentErr, debugger, debug.DebugConfig{
Deployment: deploy.Etag,
ModelId: global.ModelID,
Project: project,
Provider: provider,
ProviderID: &global.ProviderID,
Stack: &global.Stack,
Since: since,
Until: time.Now(),
})
return err
return deploymentErr
}

for _, service := range deploy.Services {
Expand Down Expand Up @@ -229,25 +236,27 @@ func handleComposeUpErr(ctx context.Context, err error, project *compose.Project
return cli.InteractiveDebugForClientError(ctx, global.Client, project, err)
}

func handleTailAndMonitorErr(ctx context.Context, err error, client *cliClient.GrpcClient, debugConfig cli.DebugConfig) {
func handleTailAndMonitorErr(ctx context.Context, err error, debugger *debug.Debugger, debugConfig debug.DebugConfig) {
var errDeploymentFailed cliClient.ErrDeploymentFailed
if errors.As(err, &errDeploymentFailed) {
// Tail got canceled because of deployment failure: prompt to show the debugger
term.Warn(errDeploymentFailed)
if errDeploymentFailed.Service != "" {
debugConfig.FailedServices = []string{errDeploymentFailed.Service}
}

if global.NonInteractive {
printDefangHint("To debug the deployment, do:", debugConfig.String())
} else {
track.Evt("Debug Prompted", P("failedServices", debugConfig.FailedServices), P("etag", debugConfig.Deployment), P("reason", errDeploymentFailed))
return
}

// Call the AI debug endpoint using the original command context (not the tail ctx which is canceled)
if nil != cli.InteractiveDebugDeployment(ctx, client, debugConfig) {
// don't show this defang hint if debugging was successful
tailOptions := newTailOptionsForDeploy(debugConfig.Deployment, debugConfig.Since, true)
printDefangHint("To see the logs of the failed service, run:", "logs "+tailOptions.String())
}
track.Evt("Debug Prompted", P("failedServices", debugConfig.FailedServices), P("etag", debugConfig.Deployment), P("reason", errDeploymentFailed))

// Call the AI debug endpoint using the original command context (not the tail ctx which is canceled)
if nil != debugger.DebugDeployment(ctx, debugConfig) {
// don't show this defang hint if debugging was successful
tailOptions := newTailOptionsForDeploy(debugConfig.Deployment, debugConfig.Since, true)
printDefangHint("To see the logs of the failed service, run:", "logs "+tailOptions.String())
}
}
}
Expand Down Expand Up @@ -448,8 +457,13 @@ func makeComposeConfigCmd() *cobra.Command {
return err
}

track.Evt("Debug Prompted", P("loadErr", loadErr))
return cli.InteractiveDebugForClientError(ctx, global.Client, project, loadErr)
debugger, err := debug.NewDebugger(ctx, getCluster(), &global.ProviderID, &global.Stack)
if err != nil {
return err
}
return debugger.DebugComposeLoadError(ctx, debug.DebugConfig{
Project: project,
}, loadErr)
}

provider, err := newProvider(ctx, loader)
Expand Down
33 changes: 13 additions & 20 deletions src/cmd/cli/command/globals.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package command

import (
"fmt"
"os"
"path/filepath"
"strconv"
Expand All @@ -10,6 +9,7 @@ import (
"github.com/DefangLabs/defang/src/pkg/cluster"
"github.com/DefangLabs/defang/src/pkg/migrate"
"github.com/DefangLabs/defang/src/pkg/modes"
"github.com/DefangLabs/defang/src/pkg/stacks"
"github.com/DefangLabs/defang/src/pkg/term"
"github.com/joho/godotenv"
"github.com/spf13/pflag"
Expand Down Expand Up @@ -254,28 +254,21 @@ godotenv.Load respects existing environment variables. Stack-specific RC files
are considered required when specified, while the general RC file is optional.
*/
func (r *GlobalConfig) loadDotDefang(stackName string) error {
dotfile := ".defang"
if stackName != "" {
// If a stack name is provided, load the stack-specific RC file but return error if it fails or does not exist
dotfile = filepath.Join(dotfile, stackName)
if abs, err := filepath.Abs(dotfile); err == nil {
dotfile = abs
}
if err := godotenv.Load(dotfile); err != nil {
return fmt.Errorf("could not load stack %q: %w", stackName, err)
}
} else {
// If no stack name is provided, trying load the general .defang file
if abs, err := filepath.Abs(dotfile); err == nil {
dotfile = abs
}
// An error here is non-fatal since the file is optional
if err := godotenv.Load(dotfile); err != nil {
term.Debugf("could not load stack %q; continuing without env file: %v", stackName, err)
return nil // continue if no general env file
}
return stacks.Load(stackName) // ensure stack exists
}
dotfile := ".defang"
// If no stack name is provided, trying load the general .defang file
if abs, err := filepath.Abs(dotfile); err == nil {
dotfile = abs
}
// An error here is non-fatal since the file is optional
if err := godotenv.Load(dotfile); err != nil {
term.Debugf("could not load stack %q; continuing without env file: %v", stackName, err)
return nil // continue if no general env file
}

term.Debugf("loaded globals from %s", dotfile)

return nil
}
6 changes: 5 additions & 1 deletion src/cmd/cli/command/mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ var mcpServerCmd = &cobra.Command{

// Create a new MCP server
term.Debug("Creating MCP server")
s, err := mcp.NewDefangMCPServer(RootCmd.Version, getCluster(), &global.ProviderID, mcpClient, tools.DefaultToolCLI{})
s, err := mcp.NewDefangMCPServer(RootCmd.Version, mcpClient, tools.DefaultToolCLI{}, mcp.StackConfig{
Cluster: getCluster(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lionello base on your comment on my PR the other day should this be getCluster() or &global.Cluster

#1646 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@raphaeltm is deleting getCluster in #1684

ProviderID: &global.ProviderID,
Stack: &global.Stack,
})
if err != nil {
return fmt.Errorf("failed to create MCP server: %w", err)
}
Expand Down
Loading
Loading