Skill: aronchick/expanso-text-summarize (version 1.0.0)

# text-summarize Summarize text into 3-5 bullet points using AI ## Requirements - Expanso Edge installed (`expanso-edge` binary in PATH) - Install via: `clawhub install expanso-edge` ## Usage ### CLI Pipeline ```bash # Run standalone echo '<input>' | expanso-edge run pipeline-cli.yaml ``` ### MCP Pipeline ```bash # Start as MCP server expanso-edge run pipeline-mcp.yaml ``` ### Deploy to Expanso Cloud ```bash expanso-cli job deploy https://skills.expanso.io/text-summarize/pipeline-cli.yaml ``` ## Files | File | Purpose | |------|---------| | `skill.yaml` | Skill metadata (inputs, outputs, credentials) | | `pipeline-cli.yaml` | Standalone CLI pipeline | | `pipeline-mcp.yaml` | MCP server pipeline |

path: README.md
content: # text-summarize

> Summarize any text into 3-5 bullet points while keeping your API keys local.

This is the "Hello World" of Expanso + OpenClaw skills. It demonstrates the core value proposition:

- **Your API keys stay local** - `${OPENAI_API_KEY}` is resolved on your machine
- **Validated pipelines** - This skill passes `expanso-cli job validate`
- **Full audit trail** - Every invocation is logged with input hash and trace ID
- **Backend flexibility** - Use OpenAI or Ollama without code changes

## Quick Start

### CLI Mode (for shell scripting)

```bash
# Set your API key
export OPENAI_API_KEY=sk-...

# Summarize text from stdin
echo "Your long article or document text here..." | \
  expanso-edge run pipeline-cli.yaml

# Or pipe a file
cat my-article.txt | expanso-edge run pipeline-cli.yaml
```

### MCP Mode (for OpenClaw integration)

```bash
# Start the skill server
PORT=8080 expanso-edge run pipeline-mcp.yaml &

# Call from curl (or OpenClaw MCP)
curl -X POST http://localhost:8080/summarize \
  -H "Content-Type: application/json" \
  -d '{"text": "Your long article or document text here..."}'
```

## Configuration

| Environment Variable | Required | Description |
|---------------------|----------|-------------|
| `OPENAI_API_KEY` | Yes* | OpenAI API key |
| `PORT` | No | HTTP port for MCP mode (default: 8080) |

*Not required if using Ollama backend locally.

## Example Output

```json
{
  "summary": "• Key insight from the text\n• Another important point\n• Action item or recommendation\n• Supporting detail\n• Conclusion or next steps",
  "metadata": {
    "skill": "text-summarize",
    "mode": "cli",
    "model": "gpt-4o-mini",
    "input_hash": "a1b2c3d4e5f6...",
    "input_length": 2847,
    "trace_id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2026-01-31T12:00:00Z"
  }
}
```

## Using with Ollama (Local, No API Key)

For complete privacy, you can use Ollama instead of OpenAI. Edit the pipeline to replace `openai_chat_completion` with `ollama_chat`:

```yaml
# In pipeline-cli.yaml, replace:
- openai_chat_completion:
    [REDACTED_SECRET]"
    model: gpt-4o-mini

# With:
- ollama_chat:
    server_address: "http://localhost:11434"
    model: llama3.2
```

Make sure Ollama is running:

```bash
ollama run llama3.2
```

## How It Works

```
┌─────────────────────────────────────────────────────────────────┐
│                     YOUR MACHINE                                 │
│                                                                 │
│  ┌───────────┐     ┌─────────────────┐     ┌───────────────┐   │
│  │ Input     │────▶│ Expanso Edge    │────▶│ Output        │   │
│  │ (stdin or │     │                 │     │ (stdout or    │   │
│  │  HTTP)    │     │ ${OPENAI_API_KEY}│     │  HTTP resp)   │   │
│  └───────────┘     │ resolved HERE   │     └───────────────┘   │
│                    └────────┬────────┘                         │
│                             │                                   │
│                    ┌────────▼────────┐                         │
│                    │ Local Vault     │                         │
│                    │ (env vars)      │                         │
│                    └─────────────────┘                         │
└─────────────────────────────────────────────────────────────────┘
                              │
                              │ API call with key
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                     OPENAI API                                   │
│                                                                 │
│  Receives: text to summarize                                    │
│  Does NOT receive: who you are, where the key came from        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
```

The key insight: OpenAI only sees the text. Your identity and credential source are protected by the local execution model.

## Troubleshooting

### "OPENAI_API_KEY not set"

Make sure you've exported the environment variable:

```bash
export OPENAI_API_KEY=sk-your-key-here
```

### "text field is required"

For MCP mode, make sure you're sending JSON with a `text` field:

```bash
curl -X POST http://localhost:8080/summarize \
  -H "Content-Type: application/json" \
  -d '{"text": "Your text here"}'
```

### Validation Errors

Run the validation script:

```bash
uv run -s scripts/validate-skills.py text-summarize
```

## Cost Estimate

Using OpenAI GPT-4o-mini:
- ~$0.15 per 1M input tokens
- ~$0.60 per 1M output tokens
- Typical summary: ~$0.001 (less than a penny)

Using Ollama: **Free** (runs locally)

## Related Skills

- [json-extract](../json-extract/) - Extract structured JSON from text
- [pii-detect](../pii-detect/) - Detect PII in text

---

*Built with [Expanso Edge](https://expanso.io) - Your keys, your machine.*


path: pipeline-cli.yaml
content: # Expanso Pipeline: text-summarize (CLI mode)
# ============================================
#
# Summarize text into bullet points via command line.
#
# Usage:
#   echo "your long text here..." | expanso-edge run pipeline-cli.yaml
#   cat article.txt | expanso-edge run pipeline-cli.yaml
#
# Why Expanso Edge?
#   Your OPENAI_API_KEY is resolved locally on your machine.
#   It's never transmitted to Expanso Cloud or logged anywhere.
#   The AI service only sees the text - not who you are or where the key came from.
#
# Validates against: docs.expanso.io/schemas/pipeline.schema.json

name: text-summarize-cli
type: pipeline

config:
  input:
    stdin:
      codec: lines
      max_buffer: 1048576  # 1MB max input

  pipeline:
    processors:
      # 1. Prepare input with audit metadata
      - mapping: |
          # Generate audit trail data
          meta input_hash = content().hash("sha256").encode("hex")
          meta input_length = content().length()
          meta trace_id = uuid_v4()

          # Prepare prompt for summarization
          root.messages = [
            {
              "role": "system",
              "content": "You are a professional summarizer. Create a clear, actionable summary in 3-5 bullet points. Each bullet should capture a key insight or action item. Be concise but complete."
            },
            {
              "role": "user",
              "content": "Summarize the following text:\n\n" + content()
            }
          ]

      # 2. Call OpenAI (credentials resolved locally by Expanso Edge)
      - openai_chat_completion:
          [REDACTED_SECRET]"
          model: gpt-4o-mini

      # 3. Format output with metadata
      - mapping: |
          root.summary = this.choices.0.message.content
          root.metadata = {
            "skill": "text-summarize",
            "mode": "cli",
            "model": "gpt-4o-mini",
            "input_hash": meta("input_hash"),
            "input_length": meta("input_length"),
            "trace_id": meta("trace_id"),
            "timestamp": now()
          }

      # 4. Audit log (appears in expanso-edge logs, not in output)
      - log:
          level: INFO
          message: |
            [text-summarize] Summarized ${! meta("input_length") } chars → ${! this.summary.length() } chars (trace: ${! meta("trace_id").slice(0, 8) })

  output:
    stdout:
      codec: json_object


path: pipeline-mcp.yaml
content: # Expanso Pipeline: text-summarize (MCP mode)
# =============================================
#
# HTTP endpoint for MCP/OpenClaw integration.
#
# Usage:
#   # Start the server
#   PORT=8080 expanso-edge run pipeline-mcp.yaml
#
#   # Call from curl
#   curl -X POST http://localhost:8080/summarize \
#     -H "Content-Type: application/json" \
#     -d '{"text": "Your long text here..."}'
#
# Why this matters for OpenClaw:
#   When Claude (via MCP) calls this endpoint, it runs on YOUR machine.
#   Your OPENAI_API_KEY stays local - the MCP server only receives results.
#   This is the "12-factor app" pattern for AI agent security.
#
# Validates against: docs.expanso.io/schemas/pipeline.schema.json

name: text-summarize-mcp
type: pipeline

config:
  # HTTP server configuration
  http:
    enabled: true
    address: "0.0.0.0:${PORT:-8080}"

  input:
    http_server:
      path: /summarize
      allowed_verbs: [POST]
      timeout: 60s

  pipeline:
    processors:
      # 1. Parse JSON body and prepare with audit metadata
      - mapping: |
          # Extract text from JSON body {"text": "..."}
          let input_text = this.text.or(content())

          # Validate input
          let text_length = $input_text.length()
          root = if $text_length == 0 {
            throw("text field is required and cannot be empty")
          } else if $text_length > 1048576 {
            throw("text exceeds maximum length of 1MB")
          } else {
            {
              "messages": [
                {
                  "role": "system",
                  "content": "You are a professional summarizer. Create a clear, actionable summary in 3-5 bullet points. Each bullet should capture a key insight or action item. Be concise but complete."
                },
                {
                  "role": "user",
                  "content": "Summarize the following text:\n\n" + $input_text
                }
              ]
            }
          }

          # Audit metadata
          meta input_hash = $input_text.hash("sha256").encode("hex")
          meta input_length = $text_length
          meta trace_id = uuid_v4()

      # 2. Call OpenAI (credentials resolved locally by Expanso Edge)
      - openai_chat_completion:
          [REDACTED_SECRET]"
          model: gpt-4o-mini

      # 3. Format response with metadata
      - mapping: |
          root.summary = this.choices.0.message.content
          root.metadata = {
            "skill": "text-summarize",
            "mode": "mcp",
            "model": "gpt-4o-mini",
            "input_hash": meta("input_hash"),
            "input_length": meta("input_length"),
            "trace_id": meta("trace_id"),
            "timestamp": now()
          }

      # 4. Audit log
      - log:
          level: INFO
          message: |
            [text-summarize] MCP request: ${! meta("input_length") } chars (trace: ${! meta("trace_id").slice(0, 8) })

  output:
    # Return response synchronously to HTTP caller
    sync_response: {}

# Optional: Node selection for Expanso Cloud deployment
selector:
  match_labels:
    capability: llm


path: skill.yaml
content: # Skill: text-summarize
# Version: 1.0.0
#
# Summarize any text into concise bullet points while keeping your API keys local.
# This is the "Hello World" of Expanso + OpenClaw skills.

name: text-summarize
version: 1.0.0
description: Summarize text into 3-5 bullet points using AI

# Credentials (stored locally on Expanso Edge, never transmitted)
credentials:
  - name: OPENAI_API_KEY
    required: false  # Not required if using Ollama
    description: OpenAI API key for remote inference

# Skill inputs
inputs:
  - name: text
    type: string
    required: true
    description: Text to summarize (up to 1MB)
  - name: bullet_count
    type: integer
    default: 5
    description: Maximum number of bullet points (3-10)
  - name: style
    type: string
    default: concise
    enum: [concise, detailed, executive]
    description: Summary style

# Skill outputs
outputs:
  - name: summary
    type: string
    description: Bulleted summary of the input text
  - name: metadata
    type: object
    description: Audit metadata (model, timestamp, input_hash)

# Backend options (Expanso Edge flexibility)
backends:
  - name: openai
    type: remote
    requires: [OPENAI_API_KEY]
    description: OpenAI GPT-4o-mini (fast, ~$0.001 per summary)
  - name: ollama
    type: local
    models: [llama3.2, mistral, phi3]
    description: Local Ollama (private, free, slower)

# Expanso components used
components:
  inputs:
    - stdin          # CLI mode
    - http_server    # MCP mode
  processors:
    - mapping
    - openai_chat_completion
    - log
  outputs:
    - stdout         # CLI mode
    - sync_response  # MCP mode


path: test/fixtures/input.txt
content: Artificial intelligence is transforming how businesses operate across every industry. From healthcare to finance, AI-powered tools are automating routine tasks, providing deeper insights from data, and enabling new capabilities that were previously impossible.

In healthcare, AI is being used to analyze medical images, predict patient outcomes, and accelerate drug discovery. Radiologists are using AI assistants to detect tumors earlier and more accurately. Pharmaceutical companies are using machine learning to identify promising drug candidates in a fraction of the time it traditionally took.

The financial sector has embraced AI for fraud detection, algorithmic trading, and customer service. Banks use AI to analyze transaction patterns and flag suspicious activity in real-time. Robo-advisors are making investment advice accessible to millions of people who couldn't afford traditional financial advisors.

However, the rapid adoption of AI also raises important concerns. Questions about bias in AI systems, job displacement, and the concentration of power among tech giants are being debated by policymakers worldwide. The European Union has proposed comprehensive AI regulations, while the United States is taking a more sector-specific approach.

Looking ahead, experts predict that AI will become even more integrated into our daily lives. Autonomous vehicles, personalized education, and AI-powered scientific research are just a few of the developments on the horizon. The key challenge will be ensuring that these powerful tools benefit society broadly while minimizing potential harms.


path: test/test.yaml
content: # Test cases for text-summarize
# Run with: uv run -s scripts/test-skills.py text-summarize

name: text-summarize-tests
description: Test cases for the text-summarize skill

fixtures_dir: "./fixtures"

tests:
  - name: basic_article
    description: Summarize a news article
    input_file: fixtures/input.txt
    expected:
      has_field: summary
      metadata_has: [trace_id, input_hash, timestamp, model]
      summary_contains_bullets: true

  - name: short_text
    description: Summarize very short text
    input: "The quick brown fox jumps over the lazy dog. This is a test."
    expected:
      has_field: summary
      min_length: 10

  - name: empty_input
    description: Handle empty input gracefully
    input: ""
    expected:
      # MCP mode should return error
      # CLI mode may return empty or error
      error_or_empty: true

# Notes:
# - Tests run against pipeline-cli.yaml by default
# - Set OPENAI_API_KEY for live tests
# - Set MOCK_LLM=true for faster mock tests (no API call)


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)