Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
d0c5cb6
define LoaderParams and ConfigureAgentLoader
jordanstephens Nov 8, 2025
70d878d
setup tools for genkit
jordanstephens Nov 8, 2025
bcdf620
translate genkit tools for mcp
jordanstephens Nov 8, 2025
2decca8
setup agent
jordanstephens Nov 8, 2025
5ff86c2
expose agent through root defang cmd
jordanstephens Nov 8, 2025
a24c268
skip in-process mcp server tests for now
jordanstephens Nov 8, 2025
5ff1fdb
setup agent with tools
jordanstephens Nov 8, 2025
cedf969
synchronous deployment
jordanstephens Nov 8, 2025
67e95ce
add TailAndMonitor method to MockDeployCLI and update test expectations
jordanstephens Nov 13, 2025
82b763a
implement additional MockCLI methods
jordanstephens Nov 13, 2025
6a7081b
capture deployment output
jordanstephens Nov 13, 2025
0e9f1ec
move debug into new pkg
jordanstephens Nov 14, 2025
ed8cfeb
conditionally invoke ai agent during debug
jordanstephens Nov 14, 2025
832154e
start debug agent sessions with an initial message
jordanstephens Nov 14, 2025
8de2af3
split tool output to buffer and stdout
jordanstephens Nov 14, 2025
05409c8
s/CollectTools/CollectDefangTools/g
jordanstephens Nov 14, 2025
23942d0
store prompt with cwd when agent is initialized
jordanstephens Nov 15, 2025
8b12c8c
add local fs tools
jordanstephens Nov 15, 2025
eaa3b09
prompt
jordanstephens Nov 15, 2025
2ed544a
update system prompt
jordanstephens Nov 15, 2025
7366953
reduce tool error verbosity
jordanstephens Nov 15, 2025
97b9a75
allow multiple turns
jordanstephens Nov 15, 2025
dde47d7
consolidate printing to generate
jordanstephens Nov 15, 2025
807553b
tell model how to handle failed deployments
jordanstephens Nov 19, 2025
1fd0989
refactor message handling and printing
jordanstephens Nov 19, 2025
1a6d411
remove confusing error prefix
jordanstephens Nov 19, 2025
e1ca0d5
debug print generate errors
jordanstephens Nov 19, 2025
8266c99
ignore empty responses from the model
jordanstephens Nov 19, 2025
72f1da3
fix usage of system prompt
jordanstephens Nov 19, 2025
8f65747
reduce model temperature
jordanstephens Nov 19, 2025
ecd8f61
add deployment_id to failed deployment errors
jordanstephens Nov 19, 2025
ae13414
print deployment_id when deployment starts
jordanstephens Nov 19, 2025
025e6b7
cleanup
jordanstephens Nov 19, 2025
8a5bb47
ensure tool errors are added to message history
jordanstephens Nov 19, 2025
400513d
Revert "print deployment_id when deployment starts"
jordanstephens Nov 20, 2025
368a653
update nix vendor hash
jordanstephens Nov 20, 2025
e2d207c
prevent repeat tool calls within single cycle
jordanstephens Nov 20, 2025
79b3f5a
make a few methods private
jordanstephens Nov 20, 2025
e6f1f90
refactor
jordanstephens Nov 20, 2025
88ace33
unit test
jordanstephens Nov 20, 2025
f22d983
refactor EqualPrevious
jordanstephens Nov 20, 2025
7cc1b4a
increase MaxTurns
jordanstephens Nov 20, 2025
6a7b503
only tee output for deploy tool
jordanstephens Nov 20, 2025
4486af7
avoid wrapping nil error
jordanstephens Nov 20, 2025
0d5f588
push toolcall short-circuit into toolmanager
jordanstephens Nov 21, 2025
dd27045
update system prompt
jordanstephens Nov 21, 2025
3aae724
debug until now
jordanstephens Nov 21, 2025
4b1c3b8
move msgs into generator
jordanstephens Nov 21, 2025
9e799b0
ask to continue beyond max turns
jordanstephens Nov 21, 2025
2128898
replace one-shot debugger with debug agent
jordanstephens Nov 21, 2025
bf63b81
setup debug tests
jordanstephens Nov 22, 2025
b44a145
avoid shadowing err
jordanstephens Nov 24, 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-me/fzAIa1ONgn/9rn13zd2kujzuqrCvbBaAmoqjL90c="; # TODO: use fetchFromGitHub
vendorHash = "sha256-sD/pczoX98Z+DgaLVCADwu4qWiGnNjlebKDOKjV2HnU="; # TODO: use fetchFromGitHub

subPackages = [ "cmd/cli" ];

Expand Down
36 changes: 30 additions & 6 deletions src/cmd/cli/command/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ 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"
pcluster "github.com/DefangLabs/defang/src/pkg/cluster"
"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 @@ -392,6 +394,23 @@ var RootCmd = &cobra.Command{

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

ctx := cmd.Context()
err := login.InteractiveRequireLoginAndToS(ctx, 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(), &providerID, agent.DefaultSystemPrompt)
if err != nil {
return err
}
return ag.StartWithUserPrompt(ctx, prompt)
},
}

var loginCmd = &cobra.Command{
Expand Down Expand Up @@ -838,6 +857,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 @@ -848,12 +868,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(), &providerID)
if err != nil {
return err
}
Expand All @@ -868,16 +893,15 @@ var debugCmd = &cobra.Command{
return fmt.Errorf("invalid 'until' time: %w", err)
}

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

Expand Down
66 changes: 45 additions & 21 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 @@ -55,6 +56,7 @@ func makeComposeUpCmd() *cobra.Command {
loader := configureLoader(cmd)

ctx := cmd.Context()

project, loadErr := loader.LoadProject(ctx)
if loadErr != nil {
if nonInteractive {
Expand All @@ -67,8 +69,13 @@ func makeComposeUpCmd() *cobra.Command {
return err
}

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

provider, err := newProviderChecked(ctx, loader)
Expand Down Expand Up @@ -136,7 +143,11 @@ func makeComposeUpCmd() *cobra.Command {
Mode: mode,
})
if err != nil {
return handleComposeUpErr(ctx, err, project, provider)
debugger, err := debug.NewDebugger(ctx, getCluster(), &providerID)
if err != nil {
return err
}
return handleComposeUpErr(ctx, debugger, project, provider, err)
}

if len(deploy.Services) == 0 {
Expand All @@ -160,14 +171,19 @@ func makeComposeUpCmd() *cobra.Command {
tailOptions := newTailOptionsForDeploy(deploy.Etag, since, verbose)
serviceStates, err := cli.TailAndMonitor(ctx, project, provider, time.Duration(waitTimeout)*time.Second, tailOptions)
if err != nil {
handleTailAndMonitorErr(ctx, err, client, cli.DebugConfig{
deploymentErr := err
debugger, err := debug.NewDebugger(ctx, getCluster(), &providerID)
if err != nil {
return err
}
handleTailAndMonitorErr(ctx, deploymentErr, debugger, debug.DebugConfig{
Deployment: deploy.Etag,
ModelId: modelId,
Project: project,
Provider: provider,
Since: since,
Until: time.Now(),
ProviderID: &providerID,
})
return err
return deploymentErr
}

for _, service := range deploy.Services {
Expand Down Expand Up @@ -199,7 +215,7 @@ func makeComposeUpCmd() *cobra.Command {
return composeUpCmd
}

func handleComposeUpErr(ctx context.Context, err error, project *compose.Project, provider cliClient.Provider) error {
func handleComposeUpErr(ctx context.Context, debugger *debug.Debugger, project *compose.Project, provider cliClient.Provider, err error) error {
if errors.Is(err, types.ErrComposeFileNotFound) {
// TODO: generate a compose file based on the current project
printDefangHint("To start a new project, do:", "new")
Expand All @@ -225,29 +241,32 @@ func handleComposeUpErr(ctx context.Context, err error, project *compose.Project
}

term.Error("Error:", cliClient.PrettyError(err))
track.Evt("Debug Prompted", P("composeErr", err))
return cli.InteractiveDebugForClientError(ctx, client, project, err)
return debugger.DebugComposeLoadError(ctx, debug.DebugConfig{
Project: 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 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 +467,13 @@ func makeComposeConfigCmd() *cobra.Command {
return err
}

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

provider, err := newProvider(ctx, loader)
Expand Down
Loading
Loading