Skip to content

feat(skills): add int-aws integration — SNS/SES/S3/CloudWatch#89

Open
dvlexp wants to merge 1 commit into
evolution-foundation:mainfrom
dvlexp:feat/skill-int-aws
Open

feat(skills): add int-aws integration — SNS/SES/S3/CloudWatch#89
dvlexp wants to merge 1 commit into
evolution-foundation:mainfrom
dvlexp:feat/skill-int-aws

Conversation

@dvlexp
Copy link
Copy Markdown

@dvlexp dvlexp commented May 23, 2026

Summary

Adds a new int-aws skill that provides EvoNexus agents with access to four core AWS services:

  • SNS — SMS delivery to 200+ countries (transactional alerts, OTP, notifications)
  • SES — Transactional email with high deliverability (receipts, reports, drip sequences)
  • S3 — Object storage for file uploads, exports, backups
  • CloudWatch — Custom metric publishing for operational dashboards

Files Added

File Purpose
.claude/skills/int-aws/SKILL.md Skill manifest — triggers, env vars, usage examples
.claude/skills/int-aws/scripts/sns_sms.py Python helper for SNS SMS dispatch

Configuration

All credentials are sourced from environment variables — no secrets in code:

AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_DEFAULT_REGION
AWS_SES_FROM_EMAIL

Test Plan

  • Invoke /int-aws skill — help text renders correctly
  • Set env vars with a test IAM user (SNS + SES permissions only)
  • Send a test SMS via sns_sms.py to a verified number
  • Send a test SES email — confirm delivery in SES console
  • No credentials appear in any committed file (grep check)

Summary by Sourcery

Introduce a new int-aws skill that integrates core AWS services into the project and documents how to use them.

New Features:

  • Add an SNS SMS helper script for sending, bulk sending, and managing verification/status of SMS via Amazon SNS.
  • Define an int-aws skill manifest that wires AWS credentials and tooling into the skill system for SNS, SES, S3, and CloudWatch usage.

Documentation:

  • Document setup and usage of the int-aws skill, including SNS SMS flows, SES email usage, sandbox vs production behavior, and cost estimates.

Provides sns_sms.py for SMS send (single + bulk via CSV), sandbox
verification, and account status. Credentials loaded from .env (IAM
user evonexus, SNSFullAccess + SESFullAccess).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 23, 2026

Reviewer's Guide

Adds a new int-aws Claude skill integrating AWS SNS/SES/S3/CloudWatch, including a Python SNS SMS helper script and a skill manifest documenting environment-based configuration and usage patterns.

Sequence diagram for sns_sms.py sending an SMS via AWS SNS

sequenceDiagram
  actor User
  participant Script as sns_sms.py
  participant Boto3 as boto3_SNS_client
  participant SNS as AWS_SNS

  User->>Script: invoke main(send, phone, message)
  Script->>Script: get_client()
  Script->>Boto3: client(region_name, aws_access_key_id, aws_secret_access_key)
  Script->>Boto3: publish(PhoneNumber, Message, MessageAttributes)
  Boto3->>SNS: Publish
  SNS-->>Boto3: MessageId
  Boto3-->>Script: publish response
  Script-->>User: print(MessageId)
Loading

File-Level Changes

Change Details Files
Introduce a Python helper CLI for sending and managing SMS via AWS SNS, including single, bulk, and sandbox-related operations.
  • Create boto3-based SNS client using credentials and region from environment variables.
  • Normalize phone numbers (with Brazilian locale defaults) before sending SMS messages.
  • Implement subcommands for single SMS send, CSV-based bulk sends with basic error handling and throttling, and inspecting SNS SMS account attributes and sandbox status.
  • Add commands for listing and verifying sandbox phone numbers using SNS APIs.
  • Provide a simple argparse-based CLI entrypoint that routes subcommands to their handlers.
.claude/skills/int-aws/scripts/sns_sms.py
Define the int-aws skill manifest with required environment variables, toolchain dependencies, and documented workflows for SNS and SES usage.
  • Declare skill metadata, required AWS environment variables, and dependency on aws CLI and python3 along with bundled scripts.
  • Document SNS SMS workflows including single send, bulk send, sandbox verification, and account status inspection using the new helper script.
  • Describe SES transactional email usage patterns and outline expected helper scripts (e.g., ses_email.py) even if not yet present in this diff.
  • Capture notes on current sandbox constraints, steps to move to production, and rough cost estimates for SNS, SES, and S3 services.
.claude/skills/int-aws/SKILL.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The skill manifest and docs reference different region env vars (AWS_REGION vs AWS_DEFAULT_REGION), and sns_sms.py hardcodes AWS_REGION; it would be good to standardize on a single env var name across code and SKILL.md to avoid subtle misconfigurations.
  • SKILL.md references ses_email.py and SES commands, but that script is not part of this diff or listed under files in the manifest; consider either adding the script in this PR or trimming the SES-specific CLI examples to avoid broken instructions.
  • sns_sms.py builds the SNS client using AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY directly but does not validate their presence, so a quick check with a clear error message when these env vars are missing would make failures easier to debug.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The skill manifest and docs reference different region env vars (`AWS_REGION` vs `AWS_DEFAULT_REGION`), and `sns_sms.py` hardcodes `AWS_REGION`; it would be good to standardize on a single env var name across code and SKILL.md to avoid subtle misconfigurations.
- SKILL.md references `ses_email.py` and SES commands, but that script is not part of this diff or listed under `files` in the manifest; consider either adding the script in this PR or trimming the SES-specific CLI examples to avoid broken instructions.
- `sns_sms.py` builds the SNS client using `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY` directly but does not validate their presence, so a quick check with a clear error message when these env vars are missing would make failures easier to debug.

## Individual Comments

### Comment 1
<location path=".claude/skills/int-aws/scripts/sns_sms.py" line_range="50-53" />
<code_context>
+    print(f"✅ Enviado! MessageId: {resp['MessageId']}")
+
+
+def cmd_bulk(args):
+    with open(args.file, newline="", encoding="utf-8") as f:
+        reader = csv.DictReader(f)
+        rows = list(reader)
+
+    print(f"Enviando para {len(rows)} números...")
</code_context>
<issue_to_address>
**suggestion (performance):** Avoid loading the entire CSV into memory; iterate over the reader to support large files.

Converting the `DictReader` to a list loads the whole file into memory, which can be a problem for large bulk sends. Instead, iterate over `reader` directly:

```python
def cmd_bulk(args):
    with open(args.file, newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        print("Enviando...")
        sent, failed = 0, 0
        for row in reader:
            ...
```

If you need the total count, consider a `--dry-run` mode to count first, or otherwise avoid materializing all rows just to print the total up front.

Suggested implementation:

```python
def cmd_bulk(args):
    sent, failed = 0, 0

    with open(args.file, newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        print("Enviando...")

        for row in reader:
            phone = row.get("phone") or row.get("telefone") or row.get("celular", "")
            message = row.get("message") or row.get("mensagem", "")

            if not phone or not message:
                print(f"  ⚠️  Linha ignorada (phone ou message vazio): {row}")
                failed += 1
                continue

            try:
                send_sms(phone, message)
                sent += 1
                print(f"{phone}")
            except Exception as exc:
                failed += 1
                print(f"{phone} - erro ao enviar: {exc}")

    print(f"Resumo: {sent} enviados, {failed} falharam")

```

If there is existing error-handling or summary logic for `cmd_bulk` further down in the file (beyond the snippet provided), ensure it is removed or adjusted to avoid duplicate logging or conflicting counters, since this implementation now handles per-row errors and prints a final summary.
</issue_to_address>

### Comment 2
<location path=".claude/skills/int-aws/scripts/sns_sms.py" line_range="14-20" />
<code_context>
+REGION = os.environ.get("AWS_REGION", "us-east-1")
+
+
+def get_client():
+    return boto3.client(
+        "sns",
+        region_name=REGION,
+        aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID"),
+        aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY"),
+    )
+
+
</code_context>
<issue_to_address>
**suggestion (performance):** Reuse a single SNS client instead of creating a new one for each operation.

Each helper call creates a fresh SNS client, which adds unnecessary overhead and connection churn in bulk usage. Consider caching the client (e.g., module-level singleton or memoizing `get_client()`) so subsequent calls reuse the same instance.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +50 to +53
def cmd_bulk(args):
with open(args.file, newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
rows = list(reader)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (performance): Avoid loading the entire CSV into memory; iterate over the reader to support large files.

Converting the DictReader to a list loads the whole file into memory, which can be a problem for large bulk sends. Instead, iterate over reader directly:

def cmd_bulk(args):
    with open(args.file, newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        print("Enviando...")
        sent, failed = 0, 0
        for row in reader:
            ...

If you need the total count, consider a --dry-run mode to count first, or otherwise avoid materializing all rows just to print the total up front.

Suggested implementation:

def cmd_bulk(args):
    sent, failed = 0, 0

    with open(args.file, newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        print("Enviando...")

        for row in reader:
            phone = row.get("phone") or row.get("telefone") or row.get("celular", "")
            message = row.get("message") or row.get("mensagem", "")

            if not phone or not message:
                print(f"  ⚠️  Linha ignorada (phone ou message vazio): {row}")
                failed += 1
                continue

            try:
                send_sms(phone, message)
                sent += 1
                print(f"  ✅ {phone}")
            except Exception as exc:
                failed += 1
                print(f"  ❌ {phone} - erro ao enviar: {exc}")

    print(f"Resumo: {sent} enviados, {failed} falharam")

If there is existing error-handling or summary logic for cmd_bulk further down in the file (beyond the snippet provided), ensure it is removed or adjusted to avoid duplicate logging or conflicting counters, since this implementation now handles per-row errors and prints a final summary.

Comment on lines +14 to +20
def get_client():
return boto3.client(
"sns",
region_name=REGION,
aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID"),
aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY"),
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (performance): Reuse a single SNS client instead of creating a new one for each operation.

Each helper call creates a fresh SNS client, which adds unnecessary overhead and connection churn in bulk usage. Consider caching the client (e.g., module-level singleton or memoizing get_client()) so subsequent calls reuse the same instance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant