Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ A terminal UI for AWS resource management
- **Cross-resource navigation** - Jump from VPC to subnets, Lambda to CloudWatch
- **Filtering & sorting** - Fuzzy search, tag filtering, column sorting
- **Resource comparison** - Side-by-side diff view
- **AI Chat** - AI assistant with AWS context (via Bedrock)
- **6 color themes** - dark, light, nord, dracula, gruvbox, catppuccin

## Screenshots
Expand All @@ -31,6 +32,12 @@ A terminal UI for AWS resource management

![multi-region](docs/images/multi-account-region.png)

### AI Chat (Bedrock)

![ai-chat](docs/images/ai-chat.png)

Press `A` in list/detail/diff views to open AI chat. The assistant analyzes resources, compares configurations, and identifies risks using AWS Bedrock.

## Installation

### Homebrew (macOS/Linux)
Expand Down Expand Up @@ -89,6 +96,7 @@ claws --read-only
| `:` | Command mode (e.g., `:ec2/instances`) |
| `/` | Filter mode (fuzzy search) |
| `a` | Open actions menu |
| `A` | AI Chat (in list/detail/diff views) |
| `R` | Select region(s) |
| `P` | Select profile(s) |
| `?` | Show help |
Expand All @@ -104,6 +112,7 @@ See [docs/keybindings.md](docs/keybindings.md) for complete reference.
| [Supported Services](docs/services.md) | All 69 services and 163 resources |
| [Configuration](docs/configuration.md) | Config file, themes, and options |
| [IAM Permissions](docs/iam-permissions.md) | Required AWS permissions |
| [AI Chat](docs/ai-chat.md) | AI assistant usage and features |
| [Architecture](docs/architecture.md) | Internal design and structure |
| [Adding Resources](docs/adding-resources.md) | Guide for contributors |

Expand Down
1 change: 1 addition & 0 deletions cmd/claws/imports_custom.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions custom/accessanalyzer/analyzers/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ type AnalyzerResource struct {
func NewAnalyzerResource(analyzer types.AnalyzerSummary) *AnalyzerResource {
return &AnalyzerResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(analyzer.Name),
ARN: appaws.Str(analyzer.Arn),
ID: appaws.Str(analyzer.Name),
ARN: appaws.Str(analyzer.Arn),
Data: analyzer,
},
Summary: &analyzer,
}
Expand All @@ -96,8 +97,9 @@ func NewAnalyzerResource(analyzer types.AnalyzerSummary) *AnalyzerResource {
func NewAnalyzerResourceFromDetail(analyzer types.AnalyzerSummary) *AnalyzerResource {
return &AnalyzerResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(analyzer.Name),
ARN: appaws.Str(analyzer.Arn),
ID: appaws.Str(analyzer.Name),
ARN: appaws.Str(analyzer.Arn),
Data: analyzer,
},
Detail: &analyzer,
}
Expand Down
6 changes: 4 additions & 2 deletions custom/accessanalyzer/findings/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ type FindingResource struct {
func NewFindingResource(finding types.FindingSummary, analyzerArn string) *FindingResource {
return &FindingResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(finding.Id),
ID: appaws.Str(finding.Id),
Data: finding,
},
Summary: &finding,
AnalyzerArn: analyzerArn,
Expand All @@ -104,7 +105,8 @@ func NewFindingResource(finding types.FindingSummary, analyzerArn string) *Findi
func NewFindingResourceFromDetail(finding types.Finding, analyzerArn string) *FindingResource {
return &FindingResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(finding.Id),
ID: appaws.Str(finding.Id),
Data: finding,
},
Detail: &finding,
AnalyzerArn: analyzerArn,
Expand Down
5 changes: 3 additions & 2 deletions custom/apprunner/operations/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,9 @@ type OperationResource struct {
func NewOperationResource(op types.OperationSummary) *OperationResource {
return &OperationResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(op.Id),
ARN: appaws.Str(op.TargetArn),
ID: appaws.Str(op.Id),
ARN: appaws.Str(op.TargetArn),
Data: op,
},
Item: op,
}
Expand Down
10 changes: 6 additions & 4 deletions custom/apprunner/services/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ type ServiceResource struct {
func NewServiceResource(svc types.ServiceSummary) *ServiceResource {
return &ServiceResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(svc.ServiceName),
ARN: appaws.Str(svc.ServiceArn),
ID: appaws.Str(svc.ServiceName),
ARN: appaws.Str(svc.ServiceArn),
Data: svc,
},
Summary: &svc,
}
Expand All @@ -96,8 +97,9 @@ func NewServiceResource(svc types.ServiceSummary) *ServiceResource {
func NewServiceResourceFromDetail(svc types.Service) *ServiceResource {
return &ServiceResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(svc.ServiceName),
ARN: appaws.Str(svc.ServiceArn),
ID: appaws.Str(svc.ServiceName),
ARN: appaws.Str(svc.ServiceArn),
Data: svc,
},
Detail: &svc,
}
Expand Down
5 changes: 3 additions & 2 deletions custom/appsync/data-sources/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ type DataSourceResource struct {
func NewDataSourceResource(ds types.DataSource, apiId string) *DataSourceResource {
return &DataSourceResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(ds.Name),
ARN: appaws.Str(ds.DataSourceArn),
ID: appaws.Str(ds.Name),
ARN: appaws.Str(ds.DataSourceArn),
Data: ds,
},
DataSource: &ds,
apiId: apiId,
Expand Down
5 changes: 3 additions & 2 deletions custom/appsync/graphql-apis/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@ type GraphQLApiResource struct {
func NewGraphQLApiResource(api types.GraphqlApi) *GraphQLApiResource {
return &GraphQLApiResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(api.ApiId),
ARN: appaws.Str(api.Arn),
ID: appaws.Str(api.ApiId),
ARN: appaws.Str(api.Arn),
Data: api,
},
Api: &api,
}
Expand Down
2 changes: 1 addition & 1 deletion custom/appsync/graphql-apis/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ func (r *GraphQLApiRenderer) Navigations(resource dao.Resource) []render.Navigat
}
return []render.Navigation{
{
Key: "d",
Key: "D",
Label: "Data Sources",
Service: "appsync",
Resource: "data-sources",
Expand Down
5 changes: 3 additions & 2 deletions custom/athena/query-executions/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,9 @@ type QueryExecutionResource struct {
func NewQueryExecutionResource(qe types.QueryExecution) *QueryExecutionResource {
return &QueryExecutionResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(qe.QueryExecutionId),
ARN: "",
ID: appaws.Str(qe.QueryExecutionId),
ARN: "",
Data: qe,
},
Item: qe,
}
Expand Down
10 changes: 6 additions & 4 deletions custom/athena/workgroups/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ type WorkgroupResource struct {
func NewWorkgroupResource(wg types.WorkGroupSummary) *WorkgroupResource {
return &WorkgroupResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(wg.Name),
ARN: "",
ID: appaws.Str(wg.Name),
ARN: "",
Data: wg,
},
Summary: &wg,
}
Expand All @@ -96,8 +97,9 @@ func NewWorkgroupResource(wg types.WorkGroupSummary) *WorkgroupResource {
func NewWorkgroupResourceFromDetail(wg types.WorkGroup) *WorkgroupResource {
return &WorkgroupResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(wg.Name),
ARN: "",
ID: appaws.Str(wg.Name),
ARN: "",
Data: wg,
},
Detail: &wg,
}
Expand Down
5 changes: 3 additions & 2 deletions custom/batch/compute-environments/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,9 @@ type ComputeEnvironmentResource struct {
func NewComputeEnvironmentResource(env types.ComputeEnvironmentDetail) *ComputeEnvironmentResource {
return &ComputeEnvironmentResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(env.ComputeEnvironmentName),
ARN: appaws.Str(env.ComputeEnvironmentArn),
ID: appaws.Str(env.ComputeEnvironmentName),
ARN: appaws.Str(env.ComputeEnvironmentArn),
Data: env,
},
Env: &env,
}
Expand Down
5 changes: 3 additions & 2 deletions custom/batch/job-definitions/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ func NewJobDefinitionResource(def types.JobDefinition) *JobDefinitionResource {
}
return &JobDefinitionResource{
BaseResource: dao.BaseResource{
ID: name,
ARN: appaws.Str(def.JobDefinitionArn),
ID: name,
ARN: appaws.Str(def.JobDefinitionArn),
Data: def,
},
Def: &def,
}
Expand Down
5 changes: 3 additions & 2 deletions custom/batch/job-queues/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,9 @@ type JobQueueResource struct {
func NewJobQueueResource(queue types.JobQueueDetail) *JobQueueResource {
return &JobQueueResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(queue.JobQueueName),
ARN: appaws.Str(queue.JobQueueArn),
ID: appaws.Str(queue.JobQueueName),
ARN: appaws.Str(queue.JobQueueArn),
Data: queue,
},
Queue: &queue,
}
Expand Down
10 changes: 6 additions & 4 deletions custom/batch/jobs/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,9 @@ func (d *JobDAO) Get(ctx context.Context, id string) (dao.Resource, error) {

return &JobResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(job.JobId),
ARN: appaws.Str(job.JobArn),
ID: appaws.Str(job.JobId),
ARN: appaws.Str(job.JobArn),
Data: job,
},
Job: &types.JobSummary{
JobId: job.JobId,
Expand Down Expand Up @@ -154,8 +155,9 @@ type JobResource struct {
func NewJobResource(job types.JobSummary) *JobResource {
return &JobResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(job.JobId),
ARN: appaws.Str(job.JobArn),
ID: appaws.Str(job.JobId),
ARN: appaws.Str(job.JobArn),
Data: job,
},
Job: &job,
}
Expand Down
5 changes: 3 additions & 2 deletions custom/budgets/budgets/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,9 @@ type BudgetResource struct {
func NewBudgetResource(budget types.Budget, accountID string) *BudgetResource {
return &BudgetResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(budget.BudgetName),
ARN: fmt.Sprintf("arn:aws:budgets::%s:budget/%s", accountID, appaws.Str(budget.BudgetName)),
ID: appaws.Str(budget.BudgetName),
ARN: fmt.Sprintf("arn:aws:budgets::%s:budget/%s", accountID, appaws.Str(budget.BudgetName)),
Data: budget,
},
Item: budget,
AccountID: accountID,
Expand Down
5 changes: 3 additions & 2 deletions custom/budgets/notifications/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ func NewNotificationResource(notif types.Notification, budgetName string, index
)
return &NotificationResource{
BaseResource: dao.BaseResource{
ID: id,
ARN: "",
ID: id,
ARN: "",
Data: notif,
},
Item: notif,
BudgetName: budgetName,
Expand Down
3 changes: 2 additions & 1 deletion custom/ce/costs/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@ func NewCostResource(group types.Group, start, end string) *CostResource {
ID: serviceName,
// Pseudo-ARN: Cost Explorer aggregates don't have real ARNs.
// Format "ce::<service>" enables internal resource identification.
ARN: fmt.Sprintf("ce::%s", serviceName),
ARN: fmt.Sprintf("ce::%s", serviceName),
Data: serviceName,
},
ServiceName: serviceName,
Cost: cost,
Expand Down
5 changes: 3 additions & 2 deletions custom/cloudtrail/events/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ type EventResource struct {
func NewEventResource(event types.Event) *EventResource {
return &EventResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(event.EventId),
ARN: appaws.Str(event.EventId),
ID: appaws.Str(event.EventId),
ARN: appaws.Str(event.EventId),
Data: event,
},
Item: event,
}
Expand Down
5 changes: 3 additions & 2 deletions custom/cloudtrail/trails/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ type TrailResource struct {
func NewTrailResource(trail types.Trail) *TrailResource {
return &TrailResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(trail.Name),
ARN: appaws.Str(trail.TrailARN),
ID: appaws.Str(trail.Name),
ARN: appaws.Str(trail.TrailARN),
Data: trail,
},
Item: trail,
}
Expand Down
5 changes: 3 additions & 2 deletions custom/configservice/rules/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ type RuleResource struct {
func NewRuleResource(rule types.ConfigRule) *RuleResource {
return &RuleResource{
BaseResource: dao.BaseResource{
ID: appaws.Str(rule.ConfigRuleName),
ARN: appaws.Str(rule.ConfigRuleArn),
ID: appaws.Str(rule.ConfigRuleName),
ARN: appaws.Str(rule.ConfigRuleArn),
Data: rule,
},
Item: rule,
}
Expand Down
5 changes: 3 additions & 2 deletions custom/datasync/locations/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ func NewLocationResource(loc types.LocationListEntry) *LocationResource {
arn := appaws.Str(loc.LocationArn)
return &LocationResource{
BaseResource: dao.BaseResource{
ID: extractLocationID(arn),
ARN: arn,
ID: extractLocationID(arn),
ARN: arn,
Data: loc,
},
Location: &loc,
}
Expand Down
5 changes: 3 additions & 2 deletions custom/datasync/task-executions/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ func NewTaskExecutionResource(exec types.TaskExecutionListEntry) *TaskExecutionR
arn := appaws.Str(exec.TaskExecutionArn)
return &TaskExecutionResource{
BaseResource: dao.BaseResource{
ID: extractExecutionID(arn),
ARN: arn,
ID: extractExecutionID(arn),
ARN: arn,
Data: exec,
},
Execution: &exec,
}
Expand Down
10 changes: 6 additions & 4 deletions custom/datasync/tasks/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,9 @@ func (d *TaskDAO) Get(ctx context.Context, id string) (dao.Resource, error) {

return &TaskResource{
BaseResource: dao.BaseResource{
ID: extractTaskID(appaws.Str(output.TaskArn)),
ARN: appaws.Str(output.TaskArn),
ID: extractTaskID(appaws.Str(output.TaskArn)),
ARN: appaws.Str(output.TaskArn),
Data: output,
},
Task: &types.TaskListEntry{
TaskArn: output.TaskArn,
Expand Down Expand Up @@ -150,8 +151,9 @@ func NewTaskResource(task types.TaskListEntry) *TaskResource {
arn := appaws.Str(task.TaskArn)
return &TaskResource{
BaseResource: dao.BaseResource{
ID: extractTaskID(arn),
ARN: arn,
ID: extractTaskID(arn),
ARN: arn,
Data: task,
},
Task: &task,
}
Expand Down
5 changes: 3 additions & 2 deletions custom/detective/graphs/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,9 @@ func NewGraphResource(graph types.Graph) *GraphResource {

return &GraphResource{
BaseResource: dao.BaseResource{
ID: id,
ARN: arn,
ID: id,
ARN: arn,
Data: graph,
},
Graph: &graph,
}
Expand Down
5 changes: 3 additions & 2 deletions custom/detective/investigations/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,9 @@ func NewInvestigationResource(inv types.InvestigationDetail, graphArn string) *I
id := appaws.Str(inv.InvestigationId)
return &InvestigationResource{
BaseResource: dao.BaseResource{
ID: id,
ARN: graphArn + "/investigation/" + id,
ID: id,
ARN: graphArn + "/investigation/" + id,
Data: inv,
},
Investigation: &inv,
graphArn: graphArn,
Expand Down
Loading
Loading