Skip to content

fix: [PRO-470] add --path option and auto-stop to deploy commands#115

Merged
xpander-moriel merged 10 commits into
mainfrom
dt-20251210-pro-470
Dec 15, 2025
Merged

fix: [PRO-470] add --path option and auto-stop to deploy commands#115
xpander-moriel merged 10 commits into
mainfrom
dt-20251210-pro-470

Conversation

@dudutwizer

@dudutwizer dudutwizer commented Dec 10, 2025

Copy link
Copy Markdown
Contributor

Summary

This PR includes comprehensive improvements to the deployment workflow and command handling, fixing multiple critical issues with profile credentials, agent creation, non-interactive mode, and command options.

Key Fixes

1. Profile Credential Precedence

Problem: Even with --profile flag, CLI was using .env credentials instead of profile credentials
Root Cause: Commander.js returns undefined when an option's value matches its default
Solution: Detect explicit --profile flag using command.parent?.rawArgs.includes('--profile')

2. Agent Naming

Problem: Agents were created with UUID as name instead of directory name
Solution: Use path.basename(currentDirectory) for agent name during creation

3. .env File Management

Problem: Deployment failed when .env didn't exist; .env not updated after agent creation
Solution:

  • Auto-create .env with CLI credentials if missing
  • Update .env with new agent ID immediately after creation

4. Race Condition in Agent Creation (Critical Fix)

Problem: After creating new agent, old agentId variable was used for stop/build/deploy operations
Symptom: "This agent is currently running in production" error
Solution: Added agentId = agent.id after agent creation

5. --path Option Support

Problem: Only deploy command had --path option; stop/restart/dev commands required being in agent directory
Solution: Added --path option to all agent management commands with proper working directory handling

6. Non-Interactive Mode for Delete

Problem: Agent delete asked for confirmation even with XPANDER_NON_INTERACTIVE=true
Solution: Check both --confirm flag and XPANDER_NON_INTERACTIVE environment variable

Files Modified

  • src/commands/deploy.ts
  • src/commands/agent/interactive/deploy.ts
  • src/commands/agent/commands/delete.ts
  • src/commands/agent/index.ts
  • src/commands/stop.ts
  • src/commands/restart.ts
  • src/commands/dev.ts
  • src/commands/agent/interactive/dev.ts

Test plan

  • Deploy agent without .env file - creates .env automatically
  • Deploy with --profile flag - uses profile credentials
  • Deploy creates agent with directory name (not UUID)
  • Deploy updates .env with new agent ID
  • Race condition fixed - new agent ID used for all operations
  • Non-interactive delete works without prompts
  • --path option works for stop/restart/dev commands
  • Successful deployment completes without conflicts

Breaking Changes

None - all changes are backwards compatible

🤖 Generated with Claude Code


Note

Enhances agent deploy/dev/stop/restart commands with --path support, proper credential precedence, non-interactive handling, .env management, and pre-deploy auto-stop with robust agent ID resolution.

  • CLI commands:
    • agent deploy, root deploy, agent dev, agent stop, agent restart now accept --path to target a specific directory; plumbed through to handlers.
    • Detect explicit --profile via raw args to ensure profile credentials are honored over .env when specified.
  • Deployment flow (src/commands/agent/interactive/deploy.ts):
    • Auto-create .env with CLI credentials if missing; update XPANDER_AGENT_ID after agent creation.
    • Prefer .env credentials unless --profile explicitly set; resolve agent from arg/.env/selection or directory name (non-interactive).
    • Auto-create agent when missing and use directory name; fix ID handling by updating agentId to the newly created agent's ID.
    • Stop existing deployment before building/uploading via stopDeployment.
    • Optional post-deploy log tail; non-interactive mode skips prompts with notices.
  • Local dev (src/commands/agent/interactive/dev.ts):
    • Support optional working directory; validate initialization when no agent ID provided.
  • Restart/Stop:
    • Support --path by temporarily chdir to resolve agent from .env, then restore CWD.
  • Delete:
    • Respect --confirm and XPANDER_NON_INTERACTIVE to skip confirmation; show notice when skipping.
  • Agent resolution utils:
    • getAgentIdFromEnvOrSelection accepts workingDirectory for .env lookup; improved resolution flow.

Written by Cursor Bugbot for commit ccfded4. This will update automatically on new commits. Configure here.

Fixed two deployment issues:
1. Added --path option to both 'xpander deploy' and 'xpander agent deploy'
   to allow specifying agent directory (defaults to current directory)
2. Added automatic stop of existing deployment before deploying new version
   to prevent conflicts with running agents

Changes:
- Updated deploy.ts to add --path option
- Updated agent/index.ts to add --path option for agent deploy
- Modified deployAgent() to accept workingDirectory parameter
- Added automatic stopDeployment() call before building/deploying
- Gracefully handles cases where no deployment is running

Both 'xpander deploy' and 'xpander agent deploy' now work consistently
with same options and behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
@notion-workspace

Copy link
Copy Markdown

CLI Bug: Xpander Deploy

deploymentSpinner.info(
`No existing deployment to stop, proceeding with deployment...`,
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Spinner not restarted after stop failure in catch block

When stopDeployment fails and the code enters the catch block, deploymentSpinner.info() is called which stops the spinner animation. However, unlike the success path (which calls deploymentSpinner.start() on line 83), the error path doesn't restart the spinner. This means subsequent operations like buildAndSaveDockerImage will update spinner.text on a stopped spinner, resulting in no visual progress indication during the Docker build and deployment process.

Fix in Cursor Fix in Web

deploymentSpinner.info(
`No existing deployment to stop, proceeding with deployment...`,
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Spinner not restarted after stop failure in catch block

When stopDeployment fails and the code enters the catch block, deploymentSpinner.info() is called which stops the spinner animation. However, unlike the success path (which calls deploymentSpinner.start() on line 83), the error path doesn't restart the spinner. This means subsequent operations like buildAndSaveDockerImage will update spinner.text on a stopped spinner, resulting in no visual progress indication during the Docker build and deployment process.

Fix in Cursor Fix in Web

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Path option not used when resolving agent ID

When --path is provided without an explicit agent ID, getAgentIdFromEnvOrSelection looks for the .env file in process.cwd() instead of the specified options.path directory. This means running xpander deploy --path /path/to/agent will fail to find the agent ID from the target directory's .env file. The options.path value needs to be passed to getAgentIdFromEnvOrSelection so it can read the .env from the correct directory.

src/commands/deploy.ts#L24-L28

const resolvedAgentId = await getAgentIdFromEnvOrSelection(
client,
agentId,
);

src/commands/agent/index.ts#L79-L83

const client = createClient(options.profile);
const resolvedAgentId = await getAgentIdFromEnvOrSelection(
client,
agentId,
);

Fix in Cursor Fix in Web


When deploying an agent, the CLI now prioritizes credentials from the
agent's .env file over the configured profile credentials. This allows
deploying agents that belong to different organizations without switching
profiles.

Changes:
- Modified deployAgent() to read credentials from .env file
- Creates new XpanderClient with .env credentials if available
- Falls back to profile credentials if .env doesn't have them
- Shows info message when using .env credentials

Behavior:
- If .env contains XPANDER_API_KEY and XPANDER_ORGANIZATION_ID, uses them
- Otherwise, uses credentials from --profile or default profile
- Applies to both 'xpander deploy' and 'xpander agent deploy' commands

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Fixed deployment flow to properly use .env file configuration:
- Agent ID now always comes from .env file (not command line)
- Removed agent selection prompt when deploying from agent directory
- Moved deployment confirmation after .env is read
- Simplified command handlers to pass agent ID directly

This ensures that when deploying with --path, the CLI uses the
complete configuration from the agent's .env file including
credentials and agent ID.

Test results:
- CLI correctly shows "Using credentials from .env file"
- Agent ID from .env is used (not from command line or selection)
- Deployment flow is streamlined without redundant prompts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
When deploying and no XPANDER_AGENT_ID is found in .env file, the CLI
now prompts the user to select an existing agent or create a new one.

Changes:
- Modified deployAgent() to gracefully handle missing XPANDER_AGENT_ID
- Still reads credentials from .env if available
- Falls back to agent selection prompt if no agent ID found
- Uses getAgentIdFromEnvOrSelection() with correct client credentials

Behavior:
- If .env has agent ID: uses it directly
- If .env missing agent ID: prompts user to select/create agent
- Credentials from .env are still respected during selection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
…eployment

When deploying with credentials from .env file, if the agent doesn't exist
in that organization, the CLI now offers to create it automatically.

Changes:
- Modified deployAgent() to check if agent exists
- Prompts user to create agent if not found (unless --confirm flag)
- Creates agent with container deployment type
- Uses agent ID from .env as the agent name
- Continues with deployment after agent creation

Behavior:
- If agent exists: deploys normally
- If agent doesn't exist: prompts to create, then deploys
- In non-interactive mode: skips confirmation prompts

This completes the deployment workflow for new agents using .env credentials.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
The --confirm flag now properly skips the agent creation confirmation
prompt, allowing fully automated deployment when agent doesn't exist.

Changes:
- Agent creation prompt respects skipDeploymentConfirmation flag
- Shows message: "Agent does not exist, creating automatically..."
- Works in both --confirm and non-interactive modes

Test results:
- Agent successfully auto-created with --confirm flag
- Used credentials from .env file
- No user interaction required for full deployment flow

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Comment thread src/commands/agent/interactive/deploy.ts Outdated
Comment thread src/commands/agent/interactive/deploy.ts
Comment thread src/commands/agent/interactive/deploy.ts Outdated
When --profile is explicitly specified, the CLI now uses profile credentials
instead of .env credentials. This gives users control over which credentials
to use for deployment.

Changes:
- Added useProfileCredentials parameter to deployAgent()
- Profile credentials take precedence when --profile flag is set
- Shows message: "Using credentials from profile (--profile flag set)"
- Otherwise uses .env credentials if available

Credential Priority (updated):
1. --profile flag credentials (if --profile specified)
2. .env file credentials (if no --profile flag)
3. Default profile credentials (fallback)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
…esolution

Fixed three bugs identified by Cursor Bugbot code review:

1. Spinner not restarted after stop failure
   - Added deploymentSpinner.start() in catch block after info message
   - Ensures visual progress indication continues after stop failures

2. Command-line agent ID precedence
   - Changed from config.agent_id || _agentId to _agentId || config.agent_id
   - Command-line arguments now properly override .env configuration

3. Agent name resolution
   - Added resolveAgentId() call for command-line agent arguments
   - Users can now deploy by agent name: xpander deploy my-agent-name
   - Maintains backward compatibility with agent name lookups

All three bugs fixed and tested. Build passes with all tests green.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
// Command-line argument provided - resolve name to ID
const { resolveAgentId } = await import('../../../utils/agent-resolver');
const resolved = await resolveAgentId(deployClient, _agentId, true);
agentId = resolved || undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Missing early return when agent resolution fails

When _agentId is provided but resolveAgentId cannot find a matching agent, it returns null. The code sets agentId = resolved || undefined but doesn't return early. This causes the deployment to continue with agentId being undefined, resulting in getAgent(undefined) returning null, and then createAgent(undefined, 'container') being called, which creates an agent with the name "undefined". The original code always returned early if resolution failed (if (!resolvedAgentId) return), but this check was removed.

Fix in Cursor Fix in Web

## Summary
- Fixed profile credential precedence with --profile flag
- Fixed agent naming to use directory name instead of UUID
- Fixed .env file auto-creation and updates during deployment
- Fixed race condition in agent creation workflow
- Added --path option to stop, restart, and dev commands
- Fixed non-interactive mode for agent delete command

## Changes

### Profile Credential Handling
- Modified deploy command to detect explicit --profile flag using rawArgs
- When --profile is explicitly set, CLI credentials take precedence over .env
- Fixes issue where Commander.js returns undefined for default values

### Agent Creation and Naming
- Agents now created with directory name instead of agent ID
- Fixed agentId variable update after agent creation to prevent race condition
- Ensures subsequent operations (stop, build, deploy) use correct new agent ID

### .env File Management
- Auto-creates .env file with CLI credentials if it doesn't exist
- Updates .env file with new agent ID after agent creation
- Supports deployment workflow without pre-existing .env file

### Non-Interactive Mode Support
- Agent delete now respects XPANDER_NON_INTERACTIVE environment variable
- Allows automated deletion without confirmation prompts
- Maintains security with --confirm flag requirement

### --path Option Support
- Added --path option to stop, restart, and dev commands
- Allows operating on agents from any directory
- Implements proper working directory switching with restoration

### Race Condition Fix
- Fixed critical bug where old agentId was used after creating new agent
- Added `agentId = agent.id` after agent creation
- Prevents conflicts and "agent is currently running" errors

## Files Modified
- src/commands/deploy.ts
- src/commands/agent/interactive/deploy.ts
- src/commands/agent/commands/delete.ts
- src/commands/agent/index.ts
- src/commands/stop.ts
- src/commands/restart.ts
- src/commands/dev.ts
- src/commands/agent/interactive/dev.ts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Comment thread src/commands/stop.ts
// Restore original working directory
if (workingDirectory) {
process.chdir(originalCwd);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Working directory not restored on exception

If getAgentIdFromEnvOrSelection throws an error after process.chdir(workingDirectory) is called, the original working directory is never restored because the restoration code on lines 31-33 won't execute. The outer try-catch handles the error but doesn't restore the directory, leaving the process in the wrong working directory for any subsequent operations. A try-finally pattern around the chdir/restore logic would ensure cleanup.

Additional Locations (1)

Fix in Cursor Fix in Web

… support

- Added optional workingDirectory parameter to getAgentIdFromEnvOrSelection
- Now uses workingDirectory when reading .env file instead of process.cwd()
- Fixed deploy command to pass currentDirectory when resolving agent ID
- Resolves Cursor bug where --path option wasn't being used for agent resolution
Comment thread src/commands/agent/interactive/deploy.ts
@xpander-moriel
xpander-moriel merged commit 59c43e4 into main Dec 15, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants