# Padua CLI

A unified CLI for AWS infrastructure management—authentication, tunneling, and container tools in one place.

## Installation

```bash
npm install -g @padua/cli
```

### Prerequisites

**Required:**
- [Node.js](https://nodejs.org/) **24, 25, or 26** (24 recommended - Active LTS) - see below
- [AWS CLI v2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)

**Node.js 24 or 26 - no build tools needed on any OS:**

`@padua/cli` depends on `isolated-vm`, which ships **prebuilt** native binaries
for **Node 24** (abi137) and **Node 26** (abi147). On those versions the binary
loads on Windows, macOS, and Linux with no compiler involved. **Node 25** is also
supported, but has no prebuilt binary (it is an odd, non-LTS "Current" release),
so there npm compiles `isolated-vm` from source via `node-gyp` - which needs a C++
toolchain (Visual Studio Build Tools on Windows; `build-essential` + `python3` on
Linux). Older majors (23 and below) and future ones (27+) are refused at install
with a clear, actionable message.

Install Node 24 (Active LTS) before installing the CLI:

```bash
# Windows
winget install OpenJS.NodeJS.LTS       # or use nvm-windows: nvm install 24 && nvm use 24

# macOS / Linux (nvm)
nvm install 24 && nvm use 24
```

> **Which version?** Use **24** or **26** for a prebuilt, compiler-free install on
> Windows, macOS, and Linux - `isolated-vm` 7.x ships prebuilds for both. Node 25
> works too, but compiles `isolated-vm` from source (needs a C++ toolchain). A
> `.nvmrc` (pinned to 24) is provided - run `nvm use` in the repo.

**Optional:**
- [npm](https://nodejs.org/) (for CodeArtifact registry authentication)
- [Docker](https://www.docker.com/) (for ECR registry authentication)
- [Session Manager Plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) (for `padua tunnel` command)

> **Note:** `padua init` validates prerequisites and displays install URLs for any missing tools.

## Quick Start

> **⚠️ WARNING:** `padua init` will delete your existing `~/.aws/config` file to ensure a clean configuration. A timestamped backup is automatically created at `~/.aws/config.backup.<timestamp>`. Use the `--keep-existing` flag to preserve custom non-Padua profiles.

```bash
# 1. Initialize configuration (discovers SSO accounts automatically)
padua init

# 2. Login to all services
padua login
```

The `init` command will:
- Check for required prerequisites
- Detect AWS environment variables and warn if present
- Authenticate with AWS SSO
- Discover all accounts and roles you have access to
- Create a timestamped backup of `~/.aws/config`
- Delete and regenerate `~/.aws/config` with discovered profiles
- Generate `~/.padua/padua.config.json`

## Features

| Category | Feature | Description |
|----------|---------|-------------|
| **Auth** | SSO Login | AWS SSO authentication and session management |
| | Profiles | Interactive AWS profile selection with shell integration |
| | CodeArtifact | Automatic npm registry authentication |
| | ECR | Docker registry authentication |
| **Config** | Init | Automatic SSO discovery and profile generation with backup |
| | Status | Health check for all authentication services |
| | Doctor | Configuration validation and diagnostics with fuzzy profile matching |
| **Tunnel** | RDS | Secure tunnels to RDS/Aurora databases via SSM |
| | LB | Port forwarding to internal ALB via SSM |
| | OpenSearch | Port forwarding to OpenSearch/Elasticsearch domains via SSM |
| **Bedrock** | Enable | Configure Claude Code to use AWS Bedrock inference (paduafg-production, ap-southeast-2) |
| | Disable | Restore previous Claude config from Bedrock mode |
| | Status | Report current Bedrock env state |
| | Setup | Install `pbedrock` shell function (bash/zsh/PowerShell) |
| **Exec** | ECS | Interactive shell in running ECS containers via SSM |
| **MCP** | Server | Built-in MCP server with 56 tools — GitLab, Atlassian, Grafana, and code execution |
| | Daemon | Background process management via `padua login` |
| | Diagnostics | MCP health checks via `padua doctor` |
| | Migration | Token migration from legacy Go server |

## MCP Server

`@padua/cli` v2 includes a built-in [MCP](https://modelcontextprotocol.io/) (Model Context Protocol) server that exposes 56 tools and 6 resource templates for GitLab, Atlassian, Grafana, and code execution. Any MCP-compatible client — Claude Code, Claude Desktop, Cursor, Windsurf, VS Code, etc. — can call these tools directly.

### Setup

```bash
padua login          # authenticates everything and starts the daemon
padua doctor         # verify the server is healthy
```

`padua login` runs an 8-step flow:

| Step | What happens |
|------|--------------|
| 1. AWS SSO | Authenticate to AWS |
| 2. S3 Config | Download MCP provider configuration to `~/.padua/mcp-providers.json` |
| 3. CodeArtifact | Configure npm registry |
| 4. ECR | Configure Docker registry |
| 5. GitLab | OAuth2 PKCE authentication |
| 6. Atlassian | OAuth2 confidential client authentication |
| 7. Grafana | Service-account token authentication. Prompts for a token unless a shared `token` is set in the `grafana` provider config (then it just validates, no prompt); skipped entirely when Grafana isn't configured |
| 8. MCP Daemon | Start background HTTP server on port 8919 |

Steps 5-8 are non-blocking — if a provider fails, the rest of Padua still works.

### Architecture

```
┌─────────────────┐    HTTP POST     ┌──────────────────────┐
│  Claude Code /  │ ──────────────→  │  Padua MCP Daemon    │
│  Claude Desktop │  Bearer token    │  127.0.0.1:8919/mcp  │
│  Cursor / etc.  │ ←────────────── │                      │
└─────────────────┘    JSON-RPC      │  ┌────────────────┐  │
                                     │  │ GitLab (OAuth)  │  │
                                     │  │ 30 tools        │  │
                                     │  └────────────────┘  │
                                     │  ┌────────────────┐  │
                                     │  │ Atlassian (OAuth│) │
                                     │  │ 19 tools        │  │
                                     │  └────────────────┘  │
                                     │  ┌────────────────┐  │
                                     │  │ Grafana (token) │  │
                                     │  │ 5 tools         │  │
                                     │  └────────────────┘  │
                                     └──────────────────────┘
```

- **Transport:** HTTP with [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http) transport at `POST /mcp`
- **Auth:** `Authorization: Bearer padua-mcp-local` (fixed token — the server only binds to localhost)
- **Tokens:** OAuth tokens persisted to `~/.padua/tokens.db` (AES-256-GCM encrypted), proactively refreshed before expiry and lazily refreshed on tool calls
- **Sessions:** Multi-session support — multiple Claude Code instances share the daemon concurrently, each with an independent session. Stale sessions are swept after 30 minutes, max 10 concurrent sessions with LRU eviction
- **Response format:** All tools default to TOON (Token-Oriented Object Notation) for 40-60% token savings on list/search responses. Pass `"format": "json"` on any tool call to receive raw JSON instead
- **Code mode:** `call_tool_chain` executes JavaScript in a secure V8 sandbox with all MCP tools available as synchronous namespaced functions (`gitlab.*`, `jira.*`, `confluence.*`, `grafana.*`). `search_tools` provides keyword search over all registered tools. The server sends `instructions` during initialization that guide LLM clients to prefer `call_tool_chain` for multi-step operations automatically

### IDE Configuration

#### Claude Code (`~/.claude/settings.json`)

Claude Code discovers MCP servers from its settings file. Add the `padua` server:

```json
{
  "mcpServers": {
    "padua": {
      "type": "url",
      "url": "http://127.0.0.1:8919/mcp",
      "headers": {
        "Authorization": "Bearer padua-mcp-local"
      }
    }
  }
}
```

After adding the config, restart Claude Code or run `/mcp` to verify the server is connected.

#### Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_config.json`)

```json
{
  "mcpServers": {
    "padua": {
      "url": "http://127.0.0.1:8919/mcp",
      "headers": {
        "Authorization": "Bearer padua-mcp-local"
      }
    }
  }
}
```

#### Cursor (`.cursor/mcp.json`)

```json
{
  "mcpServers": {
    "padua": {
      "url": "http://127.0.0.1:8919/mcp",
      "headers": {
        "Authorization": "Bearer padua-mcp-local"
      }
    }
  }
}
```

#### Windsurf (`~/.windsurf/mcp.json`)

```json
{
  "mcpServers": {
    "padua": {
      "serverUrl": "http://127.0.0.1:8919/mcp",
      "headers": {
        "Authorization": "Bearer padua-mcp-local"
      }
    }
  }
}
```

#### Any MCP Client (manual HTTP)

```bash
curl -X POST http://127.0.0.1:8919/mcp \
  -H "Authorization: Bearer padua-mcp-local" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

### Tool Reference

#### GitLab — Repository (10 tools)

| Tool | Description |
|------|-------------|
| `gitlab_list_projects` | List projects accessible to the authenticated user |
| `gitlab_get_project` | Get project metadata by ID |
| `gitlab_get_file` | Get file content from a repository |
| `gitlab_create_file` | Create a new file in a repository |
| `gitlab_update_file` | Update an existing file in a repository |
| `gitlab_list_branches` | List branches in a project |
| `gitlab_get_branch` | Get a single branch by name |
| `gitlab_search_code` | Search for code across projects |
| `gitlab_get_repository_tree` | List files and directories in a repository tree |
| `gitlab_get_clone_urls` | Get clone URLs (SSH and HTTPS) for a project |

#### GitLab — Issues (4 tools)

| Tool | Description |
|------|-------------|
| `gitlab_list_issues` | List issues with filtering (project, state, labels, assignee) |
| `gitlab_get_issue` | Get a single issue by IID |
| `gitlab_create_issue` | Create a new issue |
| `gitlab_update_issue` | Update an existing issue (title, description, labels, state) |

#### GitLab — Merge Requests (11 tools)

| Tool | Description |
|------|-------------|
| `gitlab_list_merge_requests` | List merge requests with filtering |
| `gitlab_get_merge_request` | Get a single merge request by IID |
| `gitlab_create_merge_request` | Create a new merge request |
| `gitlab_update_merge_request` | Update MR title, description, labels, assignees |
| `gitlab_get_merge_request_diffs` | Get the diff/changes in a merge request |
| `gitlab_list_merge_request_notes` | List comments on a merge request |
| `gitlab_create_merge_request_note` | Add a comment to a merge request |
| `gitlab_get_merge_request_approvals` | Get approval status and required approvers |
| `gitlab_approve_merge_request` | Approve a merge request |
| `gitlab_merge_merge_request` | Merge a merge request |
| `gitlab_rebase_merge_request` | Rebase a merge request onto its target branch |

#### GitLab — Pipelines (5 tools)

| Tool | Description |
|------|-------------|
| `gitlab_list_pipelines` | List CI/CD pipelines for a project |
| `gitlab_get_pipeline` | Get pipeline details (status, duration, stages) |
| `gitlab_list_pipeline_jobs` | List jobs within a pipeline |
| `gitlab_get_job_log` | Get the log output of a CI/CD job |
| `gitlab_retry_pipeline` | Retry a failed pipeline |

#### Atlassian — Jira (11 tools)

| Tool | Description |
|------|-------------|
| `atlassian_jira_search` | Search issues using JQL |
| `atlassian_jira_get_issue` | Get a single issue by key (e.g., `PROJ-123`) |
| `atlassian_jira_create_issue` | Create a new issue (Bug, Story, Task, Epic, etc.) |
| `atlassian_jira_update_issue` | Update issue fields (summary, description, labels, etc.) |
| `atlassian_jira_link_issues` | Create a link between two issues (Blocks, Relates, etc.) |
| `atlassian_jira_add_comment` | Add a comment to an issue (supports Markdown) |
| `atlassian_jira_transition_issue` | Move an issue to a new status |
| `atlassian_jira_list_projects` | List accessible Jira projects |
| `atlassian_jira_add_worklog` | Log time spent on an issue |
| `atlassian_jira_get_transitions` | Get available transitions for an issue |
| `atlassian_jira_get_issue_types` | Get available issue types for a project |

#### Atlassian — Confluence (8 tools)

| Tool | Description |
|------|-------------|
| `atlassian_confluence_search` | Search Confluence using CQL |
| `atlassian_confluence_get_page` | Get page content by ID |
| `atlassian_confluence_create_page` | Create a new page in a space |
| `atlassian_confluence_update_page` | Update an existing page |
| `atlassian_confluence_delete_page` | Delete a page |
| `atlassian_confluence_list_spaces` | List accessible Confluence spaces |
| `atlassian_confluence_get_space` | Get space details by key |
| `atlassian_confluence_search_content` | Search page content |

#### Grafana — Observability (5 tools)

| Tool | Description |
|------|-------------|
| `grafana_list_datasources` | Lists all Grafana datasources so callers can discover the Loki/Prometheus/Tempo datasource UIDs that the other tools require |
| `grafana_query_prometheus` | Queries a Prometheus datasource through Grafana; performs an instant query when `start`/`end` are omitted, or a range query when both `start` and `end` are provided |
| `grafana_query_loki` | Queries a Loki datasource through Grafana using LogQL and returns log streams for the given time range |
| `grafana_loki_label_values` | Lists Loki label names (when `label` is omitted) or the values for a specific label, used to discover available log-filtering dimensions |
| `grafana_query_tempo` | Queries a Tempo datasource through Grafana; fetches a specific trace by `traceId`, or searches for traces using TraceQL when `traceId` is omitted |

#### Code Mode (2 tools)

| Tool | Description |
|------|-------------|
| `call_tool_chain` | **Preferred for multi-step operations.** Execute JavaScript in a secure isolated-vm V8 sandbox with all MCP tools available as synchronous namespaced functions (`gitlab.*`, `jira.*`, `confluence.*`, `grafana.*`). Supports configurable timeout and memory limit. Use `__interfaces` or `__getToolInterface(name)` for tool schema introspection |
| `search_tools` | Search registered MCP tools by keyword — matches against name and description, returns full schema information |

### Resource Templates

Resources provide direct read access to specific entities by URI:

| URI Template | Description |
|--------------|-------------|
| `gitlab://project/{projectId}` | GitLab project metadata |
| `gitlab://project/{projectId}/file/{filePath}` | File content from a GitLab repository |
| `atlassian://jira/issue/{issueKey}` | Jira issue details |
| `atlassian://jira/project/{projectKey}` | Jira project details |
| `atlassian://confluence/page/{pageId}` | Confluence page content |
| `atlassian://confluence/space/{spaceKey}` | Confluence space details |

### Server Endpoints

| Endpoint | Method | Auth | Description |
|----------|--------|------|-------------|
| `/mcp` | POST | Required | MCP protocol (JSON-RPC over Streamable HTTP) |
| `/health` | GET | Not required | Health check and provider status |

**Health check:**

```bash
curl http://127.0.0.1:8919/health
```

Returns:

```json
{
  "healthy": true,
  "status": "ok",
  "providers": {
    "gitlab": "ready",
    "atlassian": "ready",
    "grafana": "ready"
  },
  "uptime": 3600,
  "version": "3.2.0"
}
```

### Daemon Files

| File | Purpose |
|------|---------|
| `~/.padua/mcp-auth.json` | Bearer token, port, PID, start time |
| `~/.padua/mcp-server.pid` | Daemon process ID |
| `~/.padua/mcp-providers.json` | Provider configuration (downloaded from S3). Add a `grafana` provider block (with an `https`-only `baseUrl`) to enable the Grafana provider and its `padua login` auth step. Optionally include a read-only/Viewer-scoped `token` (shared service-account token) so developers skip the login prompt — note it is a shared secret stored in plaintext here (mode 0600) and in S3 |
| `~/.padua/tokens.db` | Encrypted OAuth tokens (AES-256-GCM) |
| `~/.padua/encryption.key` | Auto-generated encryption key |
| `~/Library/Logs/padua-mcp/server.log` | Daemon log (macOS) — upstream API calls, tool errors, token refresh |
| `~/.local/share/padua/logs/server.log` | Daemon log (Linux) |

### Diagnostics

```bash
padua doctor
```

MCP-specific checks:
- Node.js version (>= 20.0.0)
- MCP provider configuration
- Token store integrity
- Encryption key availability
- Port availability
- Daemon health
- GitLab and Atlassian API connectivity

### Token Migration

If upgrading from the standalone `padua-mcp` Go server:

```bash
padua doctor --migrate-tokens
```

### Troubleshooting

| Issue | Solution |
|-------|----------|
| Daemon not starting | Run `padua doctor` for diagnostics |
| Port 8919 in use | `padua login` auto-kills orphaned processes; or set `mcp.port` in `~/.padua/padua.config.json` |
| Token issues | Delete `~/.padua/tokens.db` and `~/.padua/encryption.key`, then run `padua login` |
| Legacy daemon running | `pkill -f padua-mcp-daemon` |
| Tools not appearing | Verify `padua login` completed steps 5-8; check `padua doctor` for provider status |
| 401 Unauthorized | Ensure your IDE config includes the `Authorization: Bearer padua-mcp-local` header; check daemon log for upstream API errors |
| Debugging API errors | Check `~/Library/Logs/padua-mcp/server.log` (macOS) or `~/.local/share/padua/logs/server.log` (Linux). Set `PADUA_MCP_LOG_LEVEL=debug` for verbose output |
| MCP reconnection fails | Upgrade to v2.0.15+ — the daemon now handles ungraceful disconnects automatically |

### Downgrade to 1.x

```bash
npm install -g @padua/cli@1
pkill -f padua-mcp-daemon
```

## Init Command

Interactive configuration wizard that discovers all AWS accounts and roles via SSO, then generates AWS CLI profiles.

### Basic Usage

```bash
padua init                      # Standard initialization
padua init --keep-existing      # Preserve custom non-Padua profiles
padua init --force              # Skip confirmation prompts
```

### Options

| Flag | Description |
|------|-------------|
| `--force` | Overwrite existing config without prompting |
| `--keep-existing` | Preserve non-Padua profiles in ~/.aws/config |
| `--no-browser` | Don't auto-open browser for SSO authentication |
| `-v, --verbose` | Show detailed progress information |
| `--no-color` | Disable colored output |

**Note:** `--force` and `--keep-existing` are mutually exclusive.

### What It Does

1. **Prerequisite Check** - Validates required tools are installed (AWS CLI v2, npm, Docker, Session Manager Plugin)
2. **Environment Variable Detection** - Warns if AWS environment variables are set that may interfere with SSO
3. **SSO Authentication** - Authenticates with AWS SSO or reuses cached token
4. **Account Discovery** - Discovers all AWS accounts and roles available via SSO
5. **Role Selection** - Prompts user to select which role to use for profiles
6. **Backup Creation** - Creates timestamped backup of existing `~/.aws/config`
7. **Config Generation** - Deletes old config and generates fresh profiles
8. **Profile Preservation** - Optionally merges back custom non-Padua profiles

### Backup Behavior

- **Location:** `~/.aws/config.backup.YYYYMMDD-HHMMSS`
- **Auto-cleanup:** Keeps last 5 backups, removes older ones automatically
- **Restoration:** `cp ~/.aws/config.backup.* ~/.aws/config`

### Profile Preservation

By default, `padua init` deletes **all** existing profiles to ensure a clean state. Use `--keep-existing` to preserve custom profiles:

```bash
padua init --keep-existing
```

**How it works:**
- Identifies Padua-managed profiles by `sso_session = padua` reference
- Preserves all other profiles (custom configurations)
- Merges custom profiles back after generating Padua profiles

### Environment Variable Warnings

If AWS environment variables are detected, you'll see:

```
WARNING: AWS environment variables detected
The following environment variables may interfere with SSO authentication:
  AWS_PROFILE=development
  AWS_DEFAULT_REGION=us-east-1

To unset (bash/zsh):
  unset AWS_PROFILE AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_DEFAULT_REGION

Continue anyway? (y/N)
```

### Exit Codes

| Code | Meaning |
|------|---------|
| 0 | Success |
| 1 | General error or mutually exclusive flags |
| 2 | Missing required prerequisites |
| 4 | Config write failed |

### Troubleshooting

**"SSO session 'padua' already exists"**
- Run with `--force` flag to overwrite

**Environment variables interfering**
- Unset AWS variables before running init:
  ```bash
  unset AWS_PROFILE AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_DEFAULT_REGION
  ```

**Lost custom profiles**
- Check backup: `ls -la ~/.aws/config.backup.*`
- Restore backup: `cp ~/.aws/config.backup.20260128-143055 ~/.aws/config`
- Or run `padua init --keep-existing` to preserve custom profiles

## Login Command

Authenticate to AWS SSO, CodeArtifact (npm), and ECR (Docker) with a single command.

Automatically reads `AWS_PROFILE` environment variable when no `--profile` flag is provided (works with `paws`).

### Basic Usage

```bash
padua login                    # Full login (SSO + CodeArtifact + ECR)
padua login --profile staging  # Use specific AWS profile
padua login --sso-only         # SSO only, skip CodeArtifact and ECR
padua login --no-browser --profile paduafg  # Print SSO URL instead of opening a browser (useful for a different identity)
```

### Options

| Flag | Description |
|------|-------------|
| `-p, --profile <name>` | AWS SSO profile to use |
| `--sso-only` | Only authenticate to SSO |
| `--local` | Write CodeArtifact config to project `.npmrc` instead of global `~/.npmrc` |
| `--no-browser` | Print the SSO verification URL instead of opening a browser |
| `-v, --verbose` | Show detailed output |
| `-q, --quiet` | Suppress non-error output |
| `--no-color` | Disable colored output |

### Exit Codes

| Code | Meaning |
|------|---------|
| 0 | Success (SSO + at least one other service) |
| 1 | SSO succeeded but CodeArtifact and ECR both failed |
| 2 | SSO failed (other services not attempted) |

### What `padua login` replaces

```bash
# Before: 3 separate commands
aws sso login --profile development
aws codeartifact login --tool npm --repository npm-repo --domain my-domain \
  --domain-owner 123456789012 --region ap-southeast-2 --profile development
aws ecr get-login-password --region ap-southeast-2 | \
  docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-southeast-2.amazonaws.com

# After: 1 command
padua login
```

### Example Output

```
[1/8] AWS SSO ...
      ✓ authenticated (profile: paduafg-development)
[2/8] S3 Config ...
      ✓ downloaded
[3/8] CodeArtifact (npm) ...
      ✓ configured
[4/8] ECR (Docker) ...
      ✓ configured
[5/8] GitLab Auth ...
      ✓ authenticated
[6/8] Atlassian Auth ...
      ✓ authenticated
[7/8] Grafana Auth ...
      ✓ authenticated
[8/8] MCP Daemon ...
      ✓ running (http://127.0.0.1:8919/mcp)

=== Login Results ===
SSO: ✓ OK
S3 Config: ✓ OK
CodeArtifact: ✓ OK
ECR: ✓ OK
GitLab: ✓ OK
Atlassian: ✓ OK
Grafana: ✓ OK
MCP Daemon: ✓ OK

Total: 8/8 services authenticated
```

## Configuration

Configuration is stored in `~/.padua/padua.config.json` (created by `padua init`):

```json
{
  "defaultProfile": "development",
  "region": "ap-southeast-2",
  "codeartifact": {
    "domain": "my-domain",
    "domainOwner": "123456789012",
    "repository": "npm-repo"
  },
  "ecr": {
    "accountId": "123456789012",
    "region": "ap-southeast-2"
  }
}
```

### Profile Resolution Priority

1. CLI flags (`--profile`)
2. `PADUA_PROFILE` environment variable
3. `AWS_PROFILE` environment variable
4. `defaultProfile` in config file
5. `'default'`

This means `padua status` automatically uses the profile set by `paws` or `export AWS_PROFILE=...`.

## Status Command

Check authentication status for all configured services with detailed AWS identity information.

Automatically reads `AWS_PROFILE` environment variable when no `--profile` flag is provided (works with `paws`).

### Basic Usage

```bash
padua status                     # Check all services
padua status -p staging          # Check with specific profile
padua status -q                  # Quiet mode (exit code only)
```

### Options

| Flag | Description |
|------|-------------|
| `-p, --profile <name>` | AWS profile to check |
| `-q, --quiet` | Only output if there are issues |
| `--no-color` | Disable colored output |

### Example Output

```
AWS SSO
  Status:  Authenticated
  Profile: paduafg-development
  Account: 361485743373 (development)
  Region:  ap-southeast-2
  Role:    Developers
  User:    user@example.com
  User ID: AROA123456789:user@example.com

CodeArtifact (npm)
  Status:   Authenticated
  Registry: https://paduafg-361485743373.d.codeartifact.ap-southeast-2.amazonaws.com/npm/paduafg/
  Expires:  12/4/2025, 7:30:00 PM (in 11 hours)

ECR (Docker)
  Status:   Authenticated
  Registry: 361485743373.dkr.ecr.ap-southeast-2.amazonaws.com
```

### Exit Codes

| Code | Meaning |
|------|---------|
| 0 | All services healthy |
| 1 | One or more services have issues |
| 2 | Configuration not found |

## Doctor Command

Validate Padua CLI configuration and diagnose common issues. Helps troubleshoot problems with profiles, authentication, and prerequisites.

### Basic Usage

```bash
padua doctor                     # Run all diagnostic checks
padua doctor --verbose           # Show detailed diagnostic information
```

### Options

| Flag | Description |
|------|-------------|
| `--fix` | Attempt to auto-fix common issues (not yet implemented) |
| `-v, --verbose` | Show detailed diagnostic information |
| `--no-color` | Disable colored output |

### Diagnostic Checks

The doctor command performs comprehensive validation:

1. **Config Validation** - Verifies `~/.padua/padua.config.json` exists and contains valid JSON
2. **Config Schema** - Validates field formats (account IDs, regions, domains, repositories)
3. **Profile Validation** - Checks if `defaultProfile` exists in `~/.aws/config`, suggests corrections for typos
4. **Prerequisites** - Checks AWS CLI, npm, Docker, Session Manager Plugin installation status and versions
5. **Authentication** - Validates SSO, CodeArtifact, and ECR authentication status
6. **Installation** - Compares current CLI version with latest published version on npm
7. **MCP Server** - Node.js version (>= 20.0.0), MCP provider config, token store integrity, encryption key, port availability, daemon health, GitLab and Atlassian API connectivity

Use `--migrate-tokens` to migrate encrypted tokens from a legacy `padua-mcp` Go server database.

### Example Output

```
🏥 Padua CLI Doctor

Config Validation
  [✓] Config file: Found at ~/.padua/padua.config.json
  [✓] Config schema: All fields valid
  [✗] Default profile 'development' not found in ~/.aws/config
      → Did you mean 'paduafg-development'?

Prerequisites
  [✓] AWS CLI: Installed (2.15.0)
  [✓] npm: Installed (10.9.2)
  [✓] Docker: Installed (29.1.3)
  [⚠] Session Manager Plugin: Not installed
      → Required for tunneling features. Install: https://docs.aws.amazon.com/...

Authentication
  [✓] SSO authentication: Authenticated as user@example.com
  [✓] CodeArtifact authentication: Authenticated (project .npmrc)
  [✓] ECR authentication: Authenticated

Installation
  [⚠] CLI version: Current: 1.10.0, Latest: 1.11.0
      → Update with: npm install -g @padua/cli@latest

Summary: 1 error, 2 warnings
```

### Exit Codes

| Code | Meaning |
|------|---------|
| 0 | All checks passed |
| 1 | Issues or warnings found |
| 2 | Config not found or invalid |

### Use Cases

**Troubleshooting login issues:**
```bash
# Check why "padua login" fails
padua doctor
```

**Verifying setup after installation:**
```bash
# Ensure all prerequisites are installed
padua doctor
```

**Finding profile typos:**
```bash
# Get suggestions for misspelled profile names
padua doctor
# Example: Suggests 'paduafg-development' if you typed 'development'
```

## Profile Command

Interactive AWS profile selection with shell integration.

### Basic Usage

```bash
padua profile                    # Interactive selection
padua profile --install          # Install/upgrade the paws shell function (idempotent)
padua profile --setup            # Output shell function for paws command
padua profile --list-only        # List profiles (for scripts)
padua profile --select-only      # Select and output name only
```

### Options

| Flag | Description |
|------|-------------|
| `--install` | Install/upgrade the `paws` shell function in your shell profile (idempotent — safe to re-run) |
| `--setup` | Output the shell function (manual install; prints the marker-wrapped block) |
| `--list-only` | Output profile names only (one per line) |
| `--select-only` | Interactive select, output only the name |
| `--no-color` | Disable colored output |

### Shell Integration (paws)

`paws` is a shell function that switches AWS profiles by setting `AWS_PROFILE` in your current shell session. All Padua commands (`padua login`, `padua status`, `padua tunnel`) automatically pick up the active profile.

#### Installation

**Bash / Zsh** — one idempotent command installs or upgrades the function:

```bash
padua profile --install && source ~/.zshrc   # or: source ~/.bashrc
```

`--install` writes the `paws` function (and tab completion) to your shell rc
inside managed markers. It is safe to re-run: it replaces the existing block in
place and migrates any older hand-installed block, so you never end up with a
duplicate. After it runs, `source` the rc file (or open a new terminal).

> **Upgrading from a previous version?** Just run `padua profile --install`
> again — it replaces the old block and removes any pre-marker `paws` you
> installed manually. (Versions before 2.5.4 left the interactive `paws` picker
> invisible — it appeared to hang until `Ctrl+C`; `--install` picks up that fix.)

**PowerShell** — auto-install isn't supported yet; print the block and add it to
your `$PROFILE`:

```powershell
padua profile --install        # prints the managed block + instructions
# paste the block into $PROFILE, then:
. $PROFILE
```

The manual `padua profile --setup` (which prints the function for you to append)
still works for any shell, but `--install` is preferred for Bash/Zsh.

#### Usage

```bash
paws                        # Interactive profile selection (arrow keys)
paws <profile-name>         # Switch to a specific profile directly
paws clear                  # Unset all AWS environment variables
```

| Command | Description |
|---------|-------------|
| `paws` | Opens an interactive list of available SSO profiles |
| `paws <name>` | Sets `AWS_PROFILE` to the given profile name |
| `paws clear` | Unsets `AWS_PROFILE`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`, `AWS_DEFAULT_REGION`, `AWS_REGION`, and `AWS_DEFAULT_PROFILE` |

#### Tab Completion

Tab completion is included automatically with the shell function. Press `Tab` after typing `paws ` to see available profiles and the `clear` subcommand.

#### Example Workflow

```bash
# Switch to development profile
paws paduafg-development
# AWS_PROFILE set to: paduafg-development

# Login using the active profile
padua login

# Check status
padua status

# Clear profile when done
paws clear
# AWS environment variables cleared
```

## Tunnel Command

Create secure tunnels to AWS resources via SSM Session Manager.

Automatically reads `AWS_PROFILE` environment variable when no `--profile` flag is provided (works with `paws`).

### Targets

| Target | Description | System Required |
|--------|-------------|-----------------|
| `rds` | Tunnel to RDS/Aurora databases | Yes (e.g., `roma`, `padua-iam`) |
| `lb` | Tunnel to internal ALB (host derived per account, e.g. `preprod.int.paduasolutions.net:443`) | No |
| `opensearch` | Tunnel to AWS OpenSearch / Elasticsearch domain (port 443 → local 9200) | Yes (used as logical name in output) |
| `elasticsearch` | Alias for `opensearch` | Yes |

### Basic Usage

```bash
# RDS tunnel (requires system argument)
padua tunnel roma rds                    # Interactive (prompts for port/cluster)
padua tunnel roma rds -p paduafg-development  # Use specific profile

# Load balancer tunnel (no system needed)
padua tunnel lb                          # Interactive (prompts for port, default 8443)
padua tunnel lb --local-port 9443        # Use specific local port
```

### Options

| Flag | Description |
|------|-------------|
| `-p, --profile <name>` | AWS profile to use |
| `--local-port <port>` | Local port for the tunnel |
| `--cluster <name>` | RDS cluster or instance name (RDS only) |
| `--domain <name>` | OpenSearch domain name (OpenSearch only) — skips interactive selection |
| `-v, --verbose` | Show detailed output |
| `--no-color` | Disable colored output |

### RDS System Configuration

Different systems use different EC2 instances as tunnel endpoints:

| System | EC2 Tag | Default Port | Default Cluster |
|--------|---------|--------------|-----------------|
| roma | bastion-host | 3306 (MySQL) | roma-backend |
| (others) | `${alias}-jumpbox` | 5432 (PostgreSQL) | (none) |

> **Note:** All three tunnel targets (`lb`, `rds`, `opensearch`) derive the jumpbox EC2 Name tag via `aws sts get-caller-identity` and look up the returned account ID in the embedded `PADUA_ACCOUNT_ALIASES` map (`src/shared/aws-accounts.ts`). The tag is constructed as `<alias>-jumpbox` (e.g. account `361485743373` → alias `development` → tag `development-jumpbox`). `roma` is the only system with an explicit override (`bastion-host`). If the account ID is not present in the map, the command fails with a clear error — there is no silent fallback.
>
> The `lb` target additionally derives the **internal ALB host** from the same account, using the per-account sub-domain map mirrored from the infrastructure `networking` layer: `development` → `dev.int.paduasolutions.net`, `preproduction` → `preprod.int.paduasolutions.net`, `playground` → `play.int.paduasolutions.net`, `production` → `int.paduasolutions.net` (zone apex). Accounts without an internal ALB fail with a clear error rather than tunnelling to the wrong environment (IOPS-1312).

### LB Tunnel Usage

The internal ALB uses path-based routing for most services. Once the tunnel is running:

```bash
# Path-based services (most services)
curl -k https://localhost:8443/echo/test
curl -k https://localhost:8443/wealth-summaries/healthz
curl -k https://localhost:8443/entity-management/api/v1/status

# Host-based services (permify)
curl -k https://localhost:8443/v1/permissions/check \
  -H "Host: permify.dev.int.paduasolutions.net"
```

### Example Output (RDS)

```
Checking port 3306 availability...
✓ Port 3306 is available
Looking up bastion-host instance...
✓ Found instance: i-0abc123def456
Looking up RDS endpoint for roma-backend...
✓ Found endpoint: roma-backend.cluster-xxx.ap-southeast-2.rds.amazonaws.com

Starting tunnel...
  System: roma
  Profile: paduafg-development
  Local port: 3306
  Remote: roma-backend.cluster-xxx.ap-southeast-2.rds.amazonaws.com:3306
  Via: i-0abc123def456 (bastion-host)

Press Ctrl+C to close the tunnel
```

### Example Output (LB)

```
Checking port 8443 availability...
✓ Port 8443 is available
Looking up jumpbox instance...
✓ Found instance: i-0abc123def456

Starting tunnel...
  Profile: paduafg-development
  Local port: 8443
  Remote: dev.int.paduasolutions.net:443
  Via: i-0abc123def456 (development-jumpbox)

Usage (path-based services):
  curl -k https://localhost:8443/<service>/path
  e.g. curl -k https://localhost:8443/echo/test

Usage (host-based services like permify):
  curl -k https://localhost:8443/path -H "Host: <service>.dev.int.paduasolutions.net"

Press Ctrl+C to close the tunnel
```

### OpenSearch Tunnel Usage

```bash
# Interactive (lists domains, prompts for selection)
padua tunnel logs opensearch
padua tunnel logs opensearch -p paduafg-development

# Skip selection
padua tunnel logs opensearch --domain my-domain
padua tunnel logs opensearch --domain my-domain --local-port 9201

# elasticsearch alias works identically
padua tunnel logs elasticsearch --domain legacy-es-domain
```

The first positional argument (e.g. `logs`) is a logical system name shown in output — it does **not** affect domain discovery. Domains are listed via `aws opensearch list-domain-names` for the current profile/region.

**Endpoint resolution:** `Endpoints.vpc` is preferred. If the domain is public-only, the public `Endpoint` is used and a warning is printed. If neither exists, the command throws.

**Ports:** Remote is always `443` (HTTPS). Default local port is `9200`.

#### Curl Examples

```bash
# Cluster health
curl -k https://localhost:9200/

# List indices
curl -k https://localhost:9200/_cat/indices?v

# OpenSearch Dashboards (formerly Kibana)
open https://localhost:9200/_dashboards/
```

#### TLS and SigV4 Notes

- **`-k` is required.** The domain's TLS certificate is for `*.<region>.es.amazonaws.com`, not `localhost`. `curl` rejects the mismatch without `-k` (or `--insecure`).
- **SigV4 over the tunnel breaks IAM signing.** AWS SigV4 signs the `Host` header. The signed host is `<domain>.<region>.es.amazonaws.com`, but the tunneled request hits `localhost:9200`, so signature verification fails on the AWS side.
  - **Workarounds:**
    - Use a domain with **Fine-Grained Access Control (FGAC)** and authenticate with basic auth (`-u user:pass`), not IAM.
    - Or use a domain with an **open access policy** (no auth) for read-only browse from a VPC.
    - If you must use IAM, query the domain directly without a tunnel (from an EC2 in the VPC or via a SigV4-aware proxy).

## Exec Command

Open an interactive shell in a running ECS container via SSM Execute Command.

### Basic Usage

```bash
padua exec                                    # Interactive (prompts for cluster/service/task/container)
padua exec -c my-cluster -s my-service        # Specify cluster and service
padua exec -c my-cluster -s my-service -t abc123 --container app  # Fully non-interactive
```

### Options

| Flag | Description |
|------|-------------|
| `-p, --profile <name>` | AWS profile to use |
| `-c, --cluster <name>` | ECS cluster name |
| `-s, --service <name>` | ECS service name |
| `-t, --task <id>` | ECS task ID |
| `--container <name>` | Container name |
| `--command <cmd>` | Command to run (default: `/bin/bash`) |
| `-v, --verbose` | Show detailed output |
| `--no-color` | Disable colored output |

Auto-selects when only one option exists at each level (cluster, service, task, container).

## Bedrock Command

Configure Claude Code to use AWS Bedrock inference inside the `paduafg-production` account. All inference stays in `ap-southeast-2` (AU data residency), and every `bedrock:InvokeModel*` call is logged in CloudTrail under `paduafg-production`.

### Prerequisites

- Active `paduafg-production` SSO session (`padua login -p paduafg-production`)
- IAM permissions: `bedrock:ListInferenceProfiles`, `bedrock:InvokeModel`, `bedrock:InvokeModelWithResponseStream`, `bedrock:GetInferenceProfile`

### Setup (one-time)

```bash
# Install pbedrock shell function
eval "$(padua bedrock --setup)"

# Add permanently to your shell profile
padua bedrock --setup >> ~/.zshrc  # or ~/.bashrc
source ~/.zshrc
```

### Daily Use

```bash
pbedrock on       # Enable Bedrock mode (discovers profiles, sets 8 env vars)
pbedrock off      # Disable Bedrock mode (restores previous env)
pbedrock          # Toggle
pbedrock status   # Show current state
```

Or directly:

```bash
eval "$(padua bedrock enable --eval -p paduafg-production)"
eval "$(padua bedrock disable --eval)"
padua bedrock status
```

### Options

| Flag | Description |
|------|-------------|
| `-p, --profile <name>` | AWS SSO profile (default: `paduafg-production`) |
| `-r, --region <region>` | AWS region (default: `ap-southeast-2`) |
| `--config-dir <path>` | Custom Claude config directory |
| `--eval` | Output shell export statements only |
| `--allow-non-prod` | Allow non-production profiles |
| `--setup` | Output pbedrock shell function |
| `--shell <shell>` | Shell type for `--setup`: `zsh`, `bash`, or `pwsh` |
| `-v, --verbose` | Verbose output (never dumps env vars) |
| `--no-color` | Disable colored output |

### Security

- Production-only default — non-`paduafg-production` profiles are rejected unless `--allow-non-prod` is passed
- Region pinned to `ap-southeast-2`; inference profile ARNs must start with `ap-southeast-`
- All exported values are shell-escaped to prevent injection via `eval`
- `$HOME` containment guard on the default config directory
- Subprocess env hardened via `getSubprocessEnv()` whitelist — no secret leakage to child processes
- Verbose mode never dumps environment variables

### What `pbedrock on` sets

| Variable | Value |
|----------|-------|
| `CLAUDE_CODE_USE_BEDROCK` | `1` |
| `CLAUDE_CONFIG_DIR` | `~/.claude-bedrock` |
| `CLAUDE_CONFIG_DIR_BEFORE_BEDROCK` | Previous `CLAUDE_CONFIG_DIR` value |
| `ANTHROPIC_MODEL` | Latest APAC claude-3-5-sonnet profile ID |
| `ANTHROPIC_DEFAULT_OPUS_MODEL` | Latest APAC claude-opus profile ID |
| `ANTHROPIC_DEFAULT_SONNET_MODEL` | Latest APAC claude-3-5-sonnet profile ID |
| `ANTHROPIC_DEFAULT_HAIKU_MODEL` | Latest APAC claude-3-5-haiku profile ID |
| `AWS_REGION` | `ap-southeast-2` |

## Planned Features

The following commands are planned for future releases:

```bash
padua tunnel ec2 <name>          # SSM session to EC2 instance
```

## Development

```bash
git clone <repository-url>
cd padua-cli
npm install
npm run build
npm link           # Makes 'padua' available globally
npm test           # Run tests
npm run test:coverage  # Run tests with coverage
```

## License

MIT
