swa 0.8.6 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.beads/.gitignore +18 -0
  3. data/.beads/issues.jsonl +5 -0
  4. data/.rubocop.yml +39 -0
  5. data/AGENTS.md +541 -0
  6. data/CLAUDE.md +5 -0
  7. data/Gemfile +9 -1
  8. data/README.md +0 -5
  9. data/Rakefile +2 -0
  10. data/bin/console +1 -0
  11. data/exe/swa +2 -1
  12. data/lib/swa/athena/catalog.rb +4 -0
  13. data/lib/swa/athena/database.rb +4 -0
  14. data/lib/swa/athena/query_execution.rb +4 -0
  15. data/lib/swa/athena/work_group.rb +4 -0
  16. data/lib/swa/cli/athena_command.rb +93 -42
  17. data/lib/swa/cli/base_command.rb +22 -14
  18. data/lib/swa/cli/cloud_formation_command.rb +11 -4
  19. data/lib/swa/cli/cloudtrail_command.rb +130 -0
  20. data/lib/swa/cli/collection_behaviour.rb +4 -2
  21. data/lib/swa/cli/data_output.rb +12 -11
  22. data/lib/swa/cli/ec2_command.rb +21 -37
  23. data/lib/swa/cli/elb_command.rb +5 -3
  24. data/lib/swa/cli/filter_options.rb +6 -1
  25. data/lib/swa/cli/glue_command.rb +84 -36
  26. data/lib/swa/cli/iam_command.rb +42 -33
  27. data/lib/swa/cli/item_behaviour.rb +4 -2
  28. data/lib/swa/cli/kms_command.rb +26 -5
  29. data/lib/swa/cli/lake_formation_command.rb +72 -11
  30. data/lib/swa/cli/main_command.rb +19 -9
  31. data/lib/swa/cli/s3_command.rb +32 -24
  32. data/lib/swa/cli/selector.rb +4 -0
  33. data/lib/swa/cli/tag_filter_options.rb +6 -2
  34. data/lib/swa/cloud_formation/stack.rb +5 -1
  35. data/lib/swa/cloud_trail/event.rb +49 -0
  36. data/lib/swa/data_presentation.rb +23 -22
  37. data/lib/swa/ec2/image.rb +7 -5
  38. data/lib/swa/ec2/instance.rb +5 -1
  39. data/lib/swa/ec2/key_pair.rb +4 -0
  40. data/lib/swa/ec2/security_group.rb +5 -3
  41. data/lib/swa/ec2/snapshot.rb +6 -4
  42. data/lib/swa/ec2/subnet.rb +5 -3
  43. data/lib/swa/ec2/tagged_resource.rb +4 -0
  44. data/lib/swa/ec2/volume.rb +7 -5
  45. data/lib/swa/ec2/vpc.rb +5 -3
  46. data/lib/swa/elb/load_balancer.rb +4 -0
  47. data/lib/swa/glue/crawl.rb +5 -1
  48. data/lib/swa/glue/crawler.rb +5 -1
  49. data/lib/swa/glue/database.rb +5 -0
  50. data/lib/swa/glue/job.rb +4 -0
  51. data/lib/swa/glue/job_bookmark_entry.rb +4 -0
  52. data/lib/swa/glue/job_run.rb +5 -1
  53. data/lib/swa/glue/partition.rb +5 -1
  54. data/lib/swa/glue/table.rb +4 -0
  55. data/lib/swa/iam/credentials.rb +7 -7
  56. data/lib/swa/iam/group.rb +5 -3
  57. data/lib/swa/iam/instance_profile.rb +5 -3
  58. data/lib/swa/iam/policy.rb +5 -3
  59. data/lib/swa/iam/role.rb +10 -3
  60. data/lib/swa/iam/role_policy.rb +5 -3
  61. data/lib/swa/iam/user.rb +5 -3
  62. data/lib/swa/kms/alias.rb +4 -0
  63. data/lib/swa/kms/key.rb +4 -0
  64. data/lib/swa/lake_formation/data_lake_settings.rb +15 -0
  65. data/lib/swa/lake_formation/permission.rb +5 -1
  66. data/lib/swa/lake_formation/resource_info.rb +4 -0
  67. data/lib/swa/lake_formation/tag.rb +25 -0
  68. data/lib/swa/polyfill.rb +3 -1
  69. data/lib/swa/record.rb +5 -3
  70. data/lib/swa/resource.rb +3 -1
  71. data/lib/swa/s3/bucket.rb +5 -3
  72. data/lib/swa/s3/object.rb +7 -5
  73. data/lib/swa/s3/object_list_entry.rb +4 -0
  74. data/lib/swa/s3/object_version.rb +13 -7
  75. data/lib/swa/version.rb +5 -1
  76. data/lib/swa.rb +2 -0
  77. data/swa.gemspec +29 -25
  78. metadata +63 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c07723e018108852c4b08553e806bdd53e80e88988e2fa1dbdef99b8aebbab3
4
- data.tar.gz: 2835192e6d525d1bdb79ae94b698f8435f3311bba1deb145d20a5ab9257e373e
3
+ metadata.gz: 7881424f25a25e4b4eaabbeb23b23661ca08294c2b7a766a8463ad111a0a31c7
4
+ data.tar.gz: b35dde4b7c9e44486eb61b31f9cec0bf3656d8f7c539e2e9f80a1dc128e49fc5
5
5
  SHA512:
6
- metadata.gz: 0cc932811e45b02d1d1b254ca52ba54e188281f59764e4b0efaae4164bdc2695f7c7cfcbab3f0ab92c6cbcfd417b3ef17e05e5a76ce2b29820c7c936cfb65a47
7
- data.tar.gz: fc6105d43f360c2db2be74a594bd855a4567618143edbefb6343f46c5dd137af87971468a1d85c9cad5ef2a0b7d2c952f8b9b10c02b60f29f90c910f615e8587
6
+ metadata.gz: f494cf53c307bb2a6c9603ad59c6912a0371192184c5a2934a55aa0c8cadb692e18148481d306ee124b7113ce02b646e7972fbd2aa1ee2b0def09f778eb1e9be
7
+ data.tar.gz: 94576ae0691f6b37caccaf7da735b02f107737da10c5ac2781825c90e0fd8439fbb86843039f12a96266ee0c1bd2530f1f39224be21c83823ae0ab8a755cc1b1
data/.beads/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ # SQLite databases
2
+ *.db
3
+ *.db-journal
4
+ *.db-wal
5
+ *.db-shm
6
+
7
+ # Daemon runtime files
8
+ daemon.lock
9
+ daemon.log
10
+ daemon.pid
11
+ bd.sock
12
+
13
+ # Legacy database files
14
+ db.sqlite
15
+ bd.db
16
+
17
+ # Keep JSONL exports (source of truth for git)
18
+ !*.jsonl
@@ -0,0 +1,5 @@
1
+ {"id":"swa-1","title":"Add CloudTrail support with \"swa cloudtrail query\" command","description":"Implement CloudTrail support in swa to enable querying CloudTrail events. Initial implementation should support \"swa cloudtrail query\" to return the most recent 50 CloudTrail entries. Future enhancements will add filtering and ordering options.","design":"Follow the existing swa architecture patterns:\n\n1. Add aws-sdk-cloudtrail dependency to swa.gemspec\n2. Create lib/swa/cloud_trail/ directory for resource models\n3. Create lib/swa/cloud_trail/event.rb to wrap CloudTrail event records\n4. Create lib/swa/cli/cloud_trail_command.rb following pattern of other service commands\n5. Implement \"query\" subcommand using CollectionBehaviour mixin\n6. Use CloudTrail LookupEvents API to fetch recent events (default limit: 50)\n7. Implement summary() method for one-line event display\n8. Support standard data output (YAML/JSON with JMESPath)\n\nThe command structure should be:\n- swa cloudtrail query summary (default)\n- swa cloudtrail query data [jmespath-query]","acceptance_criteria":"- Running \"swa cloudtrail query\" returns the 50 most recent CloudTrail events\n- Output defaults to summary format (one line per event)\n- \"swa cloudtrail query data\" returns full event data in YAML\n- \"swa cloudtrail query data --json\" returns full event data in JSON\n- JMESPath queries work with the data output\n- Event summary shows key fields (timestamp, event name, username, source IP)\n- Follows existing swa architectural patterns and code style","notes":"Implemented and tested CloudTrail support. All acceptance criteria met:\n- Returns 50 most recent events by default\n- --limit option works correctly (fixed in commit 3012838)\n- Summary format shows timestamp, event name, username, event source\n- Data output in YAML/JSON works\n- JMESPath queries work\n- Follows swa architectural patterns","status":"closed","priority":2,"issue_type":"feature","assignee":"mikewilliams","created_at":"2025-10-25T16:03:46.156464+11:00","updated_at":"2025-10-26T10:19:01.802715+11:00","closed_at":"2025-10-26T10:19:01.802715+11:00"}
2
+ {"id":"swa-2","title":"Add filtering options for cloudtrail events","description":"Add command-line options to filter CloudTrail events by common criteria like event source and event name. This will make it easier to narrow down results without piping through grep/jq.","design":"Add filtering options to the cloudtrail events command:\n\nOptions to add:\n- --source SERVICE - filter by event source (e.g., kms.amazonaws.com, sts.amazonaws.com)\n- --name EVENT_NAME - filter by event name (e.g., Decrypt, AssumeRole)\n\nImplementation approach:\n1. Add option declarations in cloudtrail_command.rb\n2. Apply filters to the collection before .take(max)\n3. Use lazy evaluation to maintain efficiency\n4. Consider using AWS CloudTrail LookupEvents API filters if available (check API docs)\n - If API supports filtering, pass to lookup_events\n - Otherwise, filter in Ruby after fetching\n\nFuture enhancements could add:\n- --user USERNAME - filter by username\n- --since/--until - time range filtering\n- --resource - filter by resource type/ARN","acceptance_criteria":"- Can filter by event source: swa cloudtrail events --source kms.amazonaws.com\n- Can filter by event name: swa cloudtrail events --name Decrypt\n- Can combine filters: swa cloudtrail events --source sts.amazonaws.com --name AssumeRole\n- Filters work with all output modes (summary, data)\n- Filters work correctly with --max option\n- Performance is reasonable (uses API filters if available)","notes":"Implemented filtering options for CloudTrail events:\n- Added --source option to filter by event source (e.g. kms.amazonaws.com)\n- Added --name option to filter by event name (e.g. Decrypt)\n- Handles CloudTrail API limitation (only one lookup_attribute at a time) by using most selective filter at API level and applying second filter in Ruby\n- All tests passing with various filter combinations\n- Works with all output modes (summary, data)","status":"closed","priority":2,"issue_type":"feature","assignee":"mikewilliams","created_at":"2025-10-25T16:55:05.923645+11:00","updated_at":"2025-10-25T22:34:46.729002+11:00","closed_at":"2025-10-25T22:34:46.729002+11:00"}
3
+ {"id":"swa-3","title":"Add filtering by caller for cloudtrail events","description":"Add ability to filter CloudTrail events by the identity/caller that made the API call. This is useful for tracking down specific users, roles, or service accounts.","design":"Add --user/--caller option to filter CloudTrail events by the identity making the request.\n\nThe CloudTrail event structure has multiple identity-related fields:\n- username (from envelope) - simplified identity string\n- userIdentity.principalId (in CloudTrail event)\n- userIdentity.arn (in CloudTrail event) \n- userIdentity.userName (in CloudTrail event)\n\nImplementation approach:\n1. Add --user USERNAME option (or --caller)\n2. Consider matching against multiple fields for flexibility:\n - Match against username field (envelope)\n - Could also match against userIdentity.principalId or userName\n3. Support partial matching (substring) for convenience\n4. Investigate if CloudTrail LookupEvents API supports username filtering\n\nExamples:\n- swa cloudtrail events --user snowflake\n- swa cloudtrail events --user kafkatopicarchive\n- swa cloudtrail events --user system:serviceaccount\n\nConsider if this should be combined with swa-2 (general filtering) or kept separate.","acceptance_criteria":"- Can filter by caller/user: swa cloudtrail events --user USERNAME\n- Partial matching works (e.g., --user kafka matches kafkatopicarchive-*)\n- Works with other filters and --max option\n- Works with all output modes (summary, data)\n- Helpful error message if no events match the filter","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-10-25T16:55:56.999516+11:00","updated_at":"2025-10-25T22:48:57.404314+11:00","closed_at":"2025-10-25T22:48:57.404314+11:00"}
4
+ {"id":"swa-4","title":"Add general --where filtering for CloudTrail events","description":"Add a flexible --where option that allows filtering CloudTrail events by any field in the CloudTrail event structure using a key=value or key=pattern syntax. This provides more flexible filtering than the specific --source and --name options.","design":"Add --where option for general field matching:\n\nSyntax: --where FIELD=VALUE\n- FIELD: JMESPath-style field path (e.g., awsRegion, userIdentity.type)\n- VALUE: String or wildcard pattern (supports * and ?)\n- Can be specified multiple times (AND logic)\n\nExamples:\n- swa cloudtrail events --where awsRegion=us-west-2\n- swa cloudtrail events --where userIdentity.type=AssumedRole\n- swa cloudtrail events --where userIdentity.sessionContext.sessionIssuer.arn=*:role/emr-iceberg-stats\n- swa cloudtrail events --where eventType=AwsApiCall --where readOnly=false\n\nImplementation approach:\n1. Accept multiple --where options (Clamp supports this)\n2. Parse each into field path and pattern\n3. Extract field values from CloudTrail event using JMESPath or nested hash access\n4. Match using compiled patterns (same as --name and --source)\n5. All --where conditions must match (AND logic)\n\nSince CloudTrail API doesn't support arbitrary field filtering, all filtering will be done in Ruby after fetching events.\n\nNote: This is complementary to --source and --name, which use API filtering when possible.","acceptance_criteria":"- Can filter by any field: swa cloudtrail events --where awsRegion=us-west-2\n- Supports nested fields: --where userIdentity.type=AssumedRole\n- Supports wildcards: --where sourceIPAddress=10.0.*\n- Multiple --where conditions work (AND logic)\n- Works with --source, --name, and --max options\n- Works with all output modes\n- Clear error message if field path is invalid","status":"closed","priority":2,"issue_type":"feature","assignee":"mikewilliams","created_at":"2025-10-25T22:38:44.843229+11:00","updated_at":"2025-10-26T10:16:23.846637+11:00","closed_at":"2025-10-26T10:16:23.846637+11:00"}
5
+ {"id":"swa-5","title":"Add time-based filtering with --after and --before options","description":"Add --after and --before options to cloudtrail events command to filter events by time range. Use the existing parse_datetime function from base_command.rb to parse time arguments and pass StartTime and EndTime parameters to the CloudTrail LookupEvents API.","design":"Add time-based filtering options to cloudtrail events command:\n\nOptions to add:\n- --after TIME - filter events after this time\n- --before TIME - filter events before this time\n\nImplementation approach:\n1. Add option declarations in cloudtrail_command.rb\n2. Use parse_datetime function from base_command.rb (line 66) to parse time arguments\n3. Pass StartTime and EndTime parameters to CloudTrail LookupEvents API\n - StartTime: corresponds to --after\n - EndTime: corresponds to --before\n4. CloudTrail API supports these natively, so filtering happens at API level\n\nparse_datetime supports various formats:\n- Absolute: \"2025-01-15\", \"2025-01-15 14:30\"\n- Relative: \"yesterday\", \"last week\", \"2 days ago\"\n- Uses Chronic gem for natural language parsing\n\nExamples:\n- swa cloudtrail events --after \"last week\"\n- swa cloudtrail events --before \"2025-01-20\"\n- swa cloudtrail events --after \"yesterday\" --before \"today\"","acceptance_criteria":"- Can filter events after a time: swa cloudtrail events --after \"last week\"\n- Can filter events before a time: swa cloudtrail events --before \"2025-01-20\"\n- Can combine time filters: swa cloudtrail events --after \"yesterday\" --before \"today\"\n- Supports relative time expressions via parse_datetime\n- Works with other filters (--source, --name, --max)\n- Works with all output modes (summary, data)\n- Clear error message if time parsing fails","status":"closed","priority":2,"issue_type":"feature","assignee":"mikewilliams","created_at":"2025-10-25T22:42:48.949746+11:00","updated_at":"2025-10-25T22:54:47.74188+11:00","closed_at":"2025-10-25T22:54:47.74188+11:00"}
data/.rubocop.yml ADDED
@@ -0,0 +1,39 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ SuggestExtensions: false
4
+
5
+ Layout/EmptyLinesAroundBlockBody:
6
+ Enabled: false
7
+ EnforcedStyle: empty_lines
8
+ Layout/EmptyLinesAroundClassBody:
9
+ EnforcedStyle: empty_lines
10
+ Layout/EmptyLinesAroundModuleBody:
11
+ EnforcedStyle: empty_lines
12
+
13
+ Lint/DuplicateBranch:
14
+ Enabled: false
15
+ Lint/NestedMethodDefinition:
16
+ Enabled: false
17
+
18
+ Metrics/AbcSize:
19
+ Max: 40
20
+ Metrics/BlockLength:
21
+ Max: 25
22
+ AllowedMethods:
23
+ - subcommand
24
+ Metrics/ClassLength:
25
+ Enabled: false
26
+ Metrics/CyclomaticComplexity:
27
+ Max: 15
28
+ Metrics/MethodLength:
29
+ Max: 30
30
+ Metrics/PerceivedComplexity:
31
+ Max: 15
32
+
33
+ Naming/HeredocDelimiterNaming:
34
+ Enabled: false
35
+
36
+ Style/Documentation:
37
+ Enabled: false
38
+ Style/StringLiterals:
39
+ EnforcedStyle: double_quotes
data/AGENTS.md ADDED
@@ -0,0 +1,541 @@
1
+ # swa - AWS CLI (backwards)
2
+
3
+ ## Overview
4
+
5
+ **swa** is an alternative CLI for AWS that inverts the typical command structure by placing verbs at the end rather than the beginning. The name "swa" is "AWS" spelled backwards, reflecting this design philosophy.
6
+
7
+ **Version:** 0.8.6
8
+ **Repository:** https://github.com/mdub/swa
9
+ **License:** MIT
10
+ **Language:** Ruby
11
+
12
+ ### Command structure comparison
13
+
14
+ ```bash
15
+ # Standard AWS CLI
16
+ aws ec2 terminate-instances --instance-ids i-9336f049
17
+
18
+ # swa
19
+ swa ec2 instance i-9336f049 terminate
20
+ ```
21
+
22
+ ## What swa does
23
+
24
+ swa provides a more intuitive interface to AWS services by organizing commands hierarchically by service, resource type, and then action. It supports:
25
+
26
+ - **Resource inspection** - View details of AWS resources in YAML or JSON format
27
+ - **Resource filtering** - Filter by tags, states, dates, and custom predicates
28
+ - **Batch operations** - List and query collections of resources
29
+ - **JMESPath queries** - Extract specific fields from resource data
30
+ - **Smart resource resolution** - Automatically detect resource types from IDs (e.g., `swa i-123456` → EC2 instance)
31
+ - **Resource actions** - Perform operations like terminate, delete, put, get, etc.
32
+
33
+ ### Supported AWS services
34
+
35
+ | Service | Resource types |
36
+ |---------|----------------|
37
+ | **EC2** | Instances, AMIs, Key-pairs, Security Groups, Snapshots, Volumes, Subnets, VPCs |
38
+ | **S3** | Buckets, Objects, Object versions |
39
+ | **IAM** | Users, Roles, Groups, Policies, Instance Profiles |
40
+ | **Glue** | Databases, Tables, Crawlers, Jobs, Job Runs, Partitions |
41
+ | **Athena** | Catalogs, Databases, Query Executions |
42
+ | **KMS** | Keys, Aliases |
43
+ | **CloudFormation** | Stacks, Templates, Parameters, Outputs, Resources |
44
+ | **ELB** | Load Balancers (Classic) |
45
+ | **LakeFormation** | Data Lake Settings, LF-Tags, Permissions, Resources |
46
+
47
+ ## Codebase architecture
48
+
49
+ ### Directory structure
50
+
51
+ ```
52
+ /
53
+ ├── exe/
54
+ │ └── swa # Main executable entry point
55
+ ├── lib/
56
+ │ └── swa/
57
+ │ ├── cli/ # Command-line interface layer
58
+ │ │ ├── main_command.rb # Top-level command dispatcher
59
+ │ │ ├── base_command.rb # Base class for all commands
60
+ │ │ ├── *_command.rb # Service-specific commands (9 services)
61
+ │ │ ├── collection_behaviour.rb # Mixin for listing resources
62
+ │ │ ├── item_behaviour.rb # Mixin for single resource ops
63
+ │ │ ├── data_output.rb # Output formatting (YAML/JSON)
64
+ │ │ ├── filter_options.rb # AWS filtering
65
+ │ │ ├── tag_filter_options.rb # Tag-based filtering
66
+ │ │ └── selector.rb # Custom filter predicates
67
+ │ ├── athena/ # Athena resource models
68
+ │ ├── cloud_formation/ # CloudFormation resource models
69
+ │ ├── ec2/ # EC2 resource models
70
+ │ ├── elb/ # ELB resource models
71
+ │ ├── glue/ # Glue resource models
72
+ │ ├── iam/ # IAM resource models
73
+ │ ├── kms/ # KMS resource models
74
+ │ ├── lake_formation/ # LakeFormation resource models
75
+ │ ├── s3/ # S3 resource models
76
+ │ ├── resource.rb # Base class for AWS SDK resources
77
+ │ ├── record.rb # Base class for API response records
78
+ │ ├── data_presentation.rb # Display formatting utilities
79
+ │ └── version.rb # Version constant
80
+ └── swa.gemspec # Gem specification
81
+ ```
82
+
83
+ ### Key architectural patterns
84
+
85
+ #### 1. Command hierarchy (Clamp framework)
86
+
87
+ ```
88
+ MainCommand (Swa::CLI::MainCommand)
89
+ ├── BaseCommand (authentication, AWS client setup)
90
+ ├── Service Commands (e.g., Ec2Command, S3Command)
91
+ │ ├── Collection Subcommands (e.g., "instances", "buckets")
92
+ │ │ └── Actions: summary, ids, data
93
+ │ └── Item Subcommands (e.g., "instance", "bucket")
94
+ │ └── Actions: summary, data, [resource-specific actions]
95
+ ```
96
+
97
+ - **MainCommand** - Top-level dispatcher, includes smart resource ID prefix matching
98
+ - **BaseCommand** - Shared functionality: AWS authentication, region config, logging
99
+ - **Service Commands** - One per AWS service (9 total)
100
+ - **Mixins:**
101
+ - `CollectionBehaviour` - Provides `summary`, `ids`, `data` subcommands for listings
102
+ - `ItemBehaviour` - Provides `summary`, `data` subcommands for individual items
103
+
104
+ #### 2. Resource wrapping pattern
105
+
106
+ Two base classes wrap AWS SDK objects:
107
+
108
+ **Resource** class (`lib/swa/resource.rb`):
109
+ - Used for AWS SDK resource objects (EC2, S3, IAM, CloudFormation, ELB)
110
+ - Delegates method calls to underlying SDK resource
111
+ - Provides standard interface:
112
+ - `summary()` - One-line display string
113
+ - `id` - Resource identifier
114
+ - `data()` - Full data as hash (for YAML/JSON output)
115
+
116
+ **Record** class (`lib/swa/record.rb`):
117
+ - Used for API response data structures (Glue, Athena, KMS, LakeFormation)
118
+ - Wraps plain Ruby hashes/structs from API responses
119
+ - Same standard interface as Resource
120
+
121
+ Each AWS service has specialized subclasses in its directory (e.g., `Swa::EC2::Instance`, `Swa::S3::Bucket`).
122
+
123
+ #### 3. Filtering and querying
124
+
125
+ Multiple filtering mechanisms work together:
126
+
127
+ - **FilterOptions** - AWS API-level filters (e.g., `--filter name=value`)
128
+ - **TagFilterOptions** - Tag-based filtering (e.g., `--tagged environment=prod`)
129
+ - **Selector** - Custom Ruby predicates for in-memory filtering
130
+ - **Date ranges** - Via Chronic gem for date/time parsing
131
+ - **JMESPath** - Query expressions for extracting specific fields from output
132
+
133
+ #### 4. Output formatting
134
+
135
+ **DataOutput** module provides:
136
+ - YAML output (default)
137
+ - JSON output (`--json` / `-J`)
138
+ - JMESPath query support (as command argument)
139
+ - Pretty-printing with indentation
140
+ - CSV support for certain data types
141
+
142
+ ### Core components
143
+
144
+ #### CLI layer (`lib/swa/cli/`)
145
+
146
+ | File | Purpose |
147
+ |------|---------|
148
+ | `main_command.rb` | Top-level command, service dispatch, smart ID prefix matching |
149
+ | `base_command.rb` | AWS authentication, region config, client setup |
150
+ | `athena_command.rb` | Athena catalogs, databases, queries |
151
+ | `cloud_formation_command.rb` | CloudFormation stacks, templates |
152
+ | `ec2_command.rb` | EC2 instances, images, volumes, security groups, etc. |
153
+ | `elb_command.rb` | Elastic Load Balancers |
154
+ | `glue_command.rb` | Glue databases, tables, jobs, crawlers |
155
+ | `iam_command.rb` | IAM users, roles, groups, policies |
156
+ | `kms_command.rb` | KMS keys, aliases |
157
+ | `lake_formation_command.rb` | LakeFormation tags, permissions |
158
+ | `s3_command.rb` | S3 buckets, objects |
159
+ | `collection_behaviour.rb` | Mixin: list resources, filter, output |
160
+ | `item_behaviour.rb` | Mixin: single resource inspection |
161
+ | `data_output.rb` | YAML/JSON formatting, JMESPath |
162
+ | `filter_options.rb` | AWS API filter options |
163
+ | `tag_filter_options.rb` | Tag-based filter options |
164
+ | `selector.rb` | Custom predicate filtering |
165
+
166
+ #### Resource models
167
+
168
+ Each AWS service has a directory with resource model classes:
169
+
170
+ - **EC2** - 9 resource types (Instance, Image, KeyPair, SecurityGroup, Snapshot, Volume, Subnet, Vpc, TaggedResource)
171
+ - **S3** - 6 types (Bucket, Object, ObjectVersion, ObjectListEntry, ObjectSummary, ObjectPrefix)
172
+ - **IAM** - 7 types (User, Role, Group, Policy, RolePolicy, InstanceProfile, Credentials)
173
+ - **Glue** - 8 types (Database, Table, Job, JobRun, JobBookmarkEntry, Crawler, Crawl, Partition)
174
+ - **Athena** - 4 types (Catalog, Database, QueryExecution, WorkGroup)
175
+ - **KMS** - 2 types (Key, Alias)
176
+ - **CloudFormation** - 1 type (Stack)
177
+ - **ELB** - 1 type (LoadBalancer)
178
+ - **LakeFormation** - 4 types (DataLakeSettings, Permission, ResourceInfo, Tag)
179
+
180
+ Each model:
181
+ - Extends `Resource` or `Record`
182
+ - Implements `summary()` for display
183
+ - Exposes attributes via method delegation
184
+ - May implement custom actions (e.g., `terminate`, `delete`, `put`)
185
+
186
+ ### Key dependencies
187
+
188
+ ```ruby
189
+ # AWS SDK (modular approach)
190
+ aws-sdk-athena, aws-sdk-cloudformation, aws-sdk-ec2,
191
+ aws-sdk-elasticloadbalancing, aws-sdk-glue, aws-sdk-iam,
192
+ aws-sdk-kms, aws-sdk-lakeformation, aws-sdk-s3
193
+
194
+ # CLI framework
195
+ clamp (~> 1.1.0) # Command-line parsing
196
+
197
+ # Data processing
198
+ multi_json # JSON serialization
199
+ jmespath # Query language for JSON
200
+ chronic # Natural language date/time parsing
201
+ ox # XML parsing
202
+
203
+ # Display formatting
204
+ console_logger # Structured logging
205
+ bytesize # Human-readable byte sizes
206
+
207
+ # Utilities
208
+ stackup (~> 1.0.0) # CloudFormation tooling
209
+ ```
210
+
211
+ ## How the codebase hangs together
212
+
213
+ ### 1. Command execution flow
214
+
215
+ ```
216
+ User runs: swa ec2 instance i-123456 data --json
217
+
218
+ MainCommand.run (lib/swa/cli/main_command.rb)
219
+
220
+ Ec2Command (lib/swa/cli/ec2_command.rb)
221
+
222
+ InstanceCommand (defined in Ec2Command via subcommand DSL)
223
+
224
+ ItemBehaviour#execute (lib/swa/cli/item_behaviour.rb)
225
+
226
+ find_item method (retrieves AWS::EC2::Instance)
227
+
228
+ Wrapped as Swa::EC2::Instance (lib/swa/ec2/instance.rb)
229
+
230
+ DataCommand.execute (outputs data as JSON)
231
+
232
+ Output formatted and printed to stdout
233
+ ```
234
+
235
+ ### 2. Smart prefix matching
236
+
237
+ MainCommand includes logic to detect resource prefixes and route commands automatically:
238
+
239
+ ```ruby
240
+ # In lib/swa/cli/main_command.rb
241
+ case parameter
242
+ when /^i-/ then ["ec2", "instance", parameter]
243
+ when /^ami-/ then ["ec2", "image", parameter]
244
+ when /^sg-/ then ["ec2", "security-group", parameter]
245
+ when /^vol-/ then ["ec2", "volume", parameter]
246
+ when /^snap-/ then ["ec2", "snapshot", parameter]
247
+ when /^s3:\/\// then s3_url_command_line(parameter)
248
+ when /^arn:.*:iam:.*:policy\// then ["iam", "policy", parameter]
249
+ # ... etc
250
+ ```
251
+
252
+ This allows shortcut commands like:
253
+ ```bash
254
+ swa i-123456 data # → swa ec2 instance i-123456 data
255
+ swa sg-789abc data # → swa ec2 security-group sg-789abc data
256
+ ```
257
+
258
+ ### 3. Collection vs. item commands
259
+
260
+ **Collection commands** (plural, e.g., "instances"):
261
+ - List multiple resources
262
+ - Apply filters (tags, state, date ranges, AWS API filters)
263
+ - Support three output modes:
264
+ - `summary` - One-line per resource (default)
265
+ - `ids` - Just resource identifiers
266
+ - `data` - Full YAML/JSON with optional JMESPath
267
+
268
+ **Item commands** (singular, e.g., "instance"):
269
+ - Address a single resource by ID
270
+ - Support inspection (`summary`, `data`)
271
+ - Support resource-specific actions (`terminate`, `delete`, `get`, `put`, etc.)
272
+
273
+ ### 4. AWS SDK integration
274
+
275
+ BaseCommand establishes AWS clients:
276
+ ```ruby
277
+ def aws_config
278
+ @aws_config ||= {}.tap do |config|
279
+ config[:region] = region if region
280
+ config[:credentials] = aws_credentials if aws_credentials
281
+ # ... other config
282
+ end
283
+ end
284
+ ```
285
+
286
+ Service commands create AWS SDK clients:
287
+ ```ruby
288
+ def ec2
289
+ @ec2 ||= Aws::EC2::Resource.new(aws_config)
290
+ end
291
+ ```
292
+
293
+ Resource wrappers delegate to SDK objects:
294
+ ```ruby
295
+ class Instance < Swa::Resource
296
+ def summary
297
+ [id, image_id, instance_type, state.name, private_ip_address].join(" ")
298
+ end
299
+ end
300
+ ```
301
+
302
+ ### 5. Data presentation
303
+
304
+ Resources implement `data()` method to return hash for serialization:
305
+ ```ruby
306
+ def data
307
+ {
308
+ "InstanceId" => id,
309
+ "ImageId" => image_id,
310
+ "InstanceType" => instance_type,
311
+ "State" => state.name,
312
+ # ... etc
313
+ }
314
+ end
315
+ ```
316
+
317
+ DataOutput handles formatting:
318
+ - Applies JMESPath query if specified
319
+ - Serializes to YAML (default) or JSON
320
+ - Pretty-prints with proper indentation
321
+
322
+ ### 6. Extension points
323
+
324
+ The architecture makes it easy to add new services or resources:
325
+
326
+ 1. **Add a new service:**
327
+ - Create `lib/swa/cli/new_service_command.rb`
328
+ - Extend `BaseCommand`
329
+ - Define collection and item subcommands
330
+ - Register in `MainCommand`
331
+
332
+ 2. **Add a new resource type:**
333
+ - Create `lib/swa/new_service/new_resource.rb`
334
+ - Extend `Resource` or `Record`
335
+ - Implement `summary()` and optionally `data()`
336
+ - Add command in service command class
337
+
338
+ 3. **Add a new action:**
339
+ - Define method in resource class
340
+ - Add subcommand in item command
341
+ - Handle action-specific options
342
+
343
+ ## Usage patterns
344
+
345
+ ### Basic inspection
346
+
347
+ ```bash
348
+ # List resources
349
+ swa ec2 instances summary
350
+ swa s3 buckets
351
+ swa iam users
352
+
353
+ # Inspect single resource
354
+ swa ec2 instance i-123456 data
355
+ swa s3 bucket my-bucket data
356
+ swa iam user alice data --json
357
+ ```
358
+
359
+ ### Filtering
360
+
361
+ ```bash
362
+ # By tag
363
+ swa ec2 instances --tagged environment=prod
364
+
365
+ # By state
366
+ swa ec2 instances --state running
367
+
368
+ # By AWS API filter
369
+ swa ec2 instances --filter availability-zone=us-east-1a
370
+
371
+ # By date range
372
+ swa ec2 snapshots --since "last week"
373
+ ```
374
+
375
+ ### Querying with JMESPath
376
+
377
+ ```bash
378
+ # Extract specific fields
379
+ swa ec2 instances data '[].{Id:InstanceId,Type:InstanceType,State:State.Name}'
380
+
381
+ # Get just IPs
382
+ swa ec2 instances data '[].PrivateIpAddress'
383
+
384
+ # Complex query
385
+ swa s3 buckets data '[?CreationDate > `2024-01-01`].Name'
386
+ ```
387
+
388
+ ### Resource actions
389
+
390
+ ```bash
391
+ # EC2
392
+ swa ec2 instance i-123456 terminate
393
+
394
+ # S3
395
+ swa s3 bucket my-bucket object mykey get > file.txt
396
+ echo "content" | swa s3 bucket my-bucket object mykey put
397
+
398
+ # IAM
399
+ swa iam role my-role assume --session-name mysession
400
+
401
+ # Glue
402
+ swa glue job my-job start
403
+ swa glue crawler my-crawler start
404
+ ```
405
+
406
+ ### Complex operations
407
+
408
+ ```bash
409
+ # Athena query execution
410
+ swa athena query "SELECT * FROM mytable" -D mydb -O s3://bucket/path/
411
+
412
+ # Glue database and table addressing
413
+ swa glue database mydb table mytable data
414
+
415
+ # LakeFormation permissions
416
+ swa glue database mydb table mytable lf-permissions
417
+
418
+ # CloudFormation stack inspection
419
+ swa cf stack my-stack template
420
+ swa cf stack my-stack outputs
421
+ ```
422
+
423
+ ### Smart shortcuts
424
+
425
+ ```bash
426
+ # Automatic resource type detection
427
+ swa i-123456 data # EC2 instance
428
+ swa ami-789abc data # EC2 AMI
429
+ swa sg-456def data # EC2 security group
430
+ swa s3://my-bucket/path/file get # S3 object
431
+ swa arn:aws:iam::123:policy/MyPolicy # IAM policy
432
+ ```
433
+
434
+ ## Development focus areas
435
+
436
+ Based on recent commit history:
437
+ - Glue and LakeFormation integration (active development)
438
+ - ARN-based resource addressing
439
+ - Enhanced filtering and pattern matching
440
+ - Catalog specification for Glue resources
441
+
442
+ The tool appears to be particularly focused on AWS data services (Glue, Athena, LakeFormation) while maintaining broad coverage of core AWS services.
443
+
444
+ ## Issue Tracking with bd (beads)
445
+
446
+ **IMPORTANT**: This project uses **bd (beads)** for ALL issue tracking. Do NOT use markdown TODOs, task lists, or other tracking methods.
447
+
448
+ ### Why bd?
449
+
450
+ - Dependency-aware: Track blockers and relationships between issues
451
+ - Git-friendly: Auto-syncs to JSONL for version control
452
+ - Agent-optimized: JSON output, ready work detection, discovered-from links
453
+ - Prevents duplicate tracking systems and confusion
454
+
455
+ ### Quick Start
456
+
457
+ **Check for ready work:**
458
+ ```bash
459
+ bd ready --json
460
+ ```
461
+
462
+ **Create new issues:**
463
+ ```bash
464
+ bd create "Issue title" -t bug|feature|task -p 0-4 --json
465
+ bd create "Issue title" -p 1 --deps discovered-from:bd-123 --json
466
+ ```
467
+
468
+ **Claim and update:**
469
+ ```bash
470
+ bd update bd-42 --status in_progress --json
471
+ bd update bd-42 --priority 1 --json
472
+ ```
473
+
474
+ **Complete work:**
475
+ ```bash
476
+ bd close bd-42 --reason "Completed" --json
477
+ ```
478
+
479
+ ### Issue Types
480
+
481
+ - `bug` - Something broken
482
+ - `feature` - New functionality
483
+ - `task` - Work item (tests, docs, refactoring)
484
+ - `epic` - Large feature with subtasks
485
+ - `chore` - Maintenance (dependencies, tooling)
486
+
487
+ ### Priorities
488
+
489
+ - `0` - Critical (security, data loss, broken builds)
490
+ - `1` - High (major features, important bugs)
491
+ - `2` - Medium (default, nice-to-have)
492
+ - `3` - Low (polish, optimization)
493
+ - `4` - Backlog (future ideas)
494
+
495
+ ### Workflow for AI Agents
496
+
497
+ 1. **Check ready work**: `bd ready` shows unblocked issues
498
+ 2. **Claim your task**: `bd update <id> --status in_progress`
499
+ 3. **Work on it**: Implement, test, document
500
+ 4. **Discover new work?** Create linked issue:
501
+ - `bd create "Found bug" -p 1 --deps discovered-from:<parent-id>`
502
+ 5. **Complete**: `bd close <id> --reason "Done"`
503
+
504
+ ### Auto-Sync
505
+
506
+ bd automatically syncs with git:
507
+ - Exports to `.beads/issues.jsonl` after changes (5s debounce)
508
+ - Imports from JSONL when newer (e.g., after `git pull`)
509
+ - No manual export/import needed!
510
+
511
+ ### MCP Server (Recommended)
512
+
513
+ If using Claude or MCP-compatible clients, install the beads MCP server:
514
+
515
+ ```bash
516
+ pip install beads-mcp
517
+ ```
518
+
519
+ Add to MCP config (e.g., `~/.config/claude/config.json`):
520
+ ```json
521
+ {
522
+ "beads": {
523
+ "command": "beads-mcp",
524
+ "args": []
525
+ }
526
+ }
527
+ ```
528
+
529
+ Then use `mcp__beads__*` functions instead of CLI commands.
530
+
531
+ ### Important Rules
532
+
533
+ - ✅ Use bd for ALL task tracking
534
+ - ✅ Always use `--json` flag for programmatic use
535
+ - ✅ Link discovered work with `discovered-from` dependencies
536
+ - ✅ Check `bd ready` before asking "what should I work on?"
537
+ - ❌ Do NOT create markdown TODO lists
538
+ - ❌ Do NOT use external issue trackers
539
+ - ❌ Do NOT duplicate tracking systems
540
+
541
+ For more details, see README.md and QUICKSTART.md.
data/CLAUDE.md ADDED
@@ -0,0 +1,5 @@
1
+ **Note**: This project uses [bd (beads)](https://github.com/steveyegge/beads)
2
+ for issue tracking. Use `bd` commands instead of markdown TODOs.
3
+ See AGENTS.md for workflow details.
4
+
5
+ @AGENTS.md
data/Gemfile CHANGED
@@ -1,4 +1,12 @@
1
- source 'https://rubygems.org'
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
2
4
 
3
5
  # Specify your gem's dependencies in swa.gemspec
4
6
  gemspec
7
+
8
+ group :development do
9
+ gem "bundler", "~> 2.1"
10
+ gem "rake", "~> 12.0"
11
+ gem "rubocop", "~> 1.0"
12
+ end
data/README.md CHANGED
@@ -82,11 +82,6 @@ SWA is packaged as a Ruby gem, and can be installed with:
82
82
 
83
83
  gem install swa
84
84
 
85
- On a Mac, I recommend [brew-gem](https://github.com/sportngin/brew-gem) for easy system-wide installation:
86
-
87
- brew install brew-gem
88
- brew gem install swa
89
-
90
85
  ## Contributing
91
86
 
92
87
  Bug reports and pull requests are welcome on GitHub at https://github.com/mdub/swa.
data/Rakefile CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
data/bin/console CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "bundler/setup"
4
5
  require "swa"
data/exe/swa CHANGED
@@ -1,6 +1,7 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- $LOAD_PATH << File.expand_path("../../lib", __FILE__)
4
+ $LOAD_PATH << File.expand_path("../lib", __dir__)
4
5
 
5
6
  require "swa/cli/main_command"
6
7