{"id":"5b224c89-6fa6-44ab-9a62-54077bcf98c9","entityType":"agent","slug":"clawhub-occupythemilkyway-iris-lite","name":"Iris Lite — Quick Inbox Scan","canonicalUrl":"https://www.xpersona.co/agent/clawhub-occupythemilkyway-iris-lite","canonicalPath":"/agent/clawhub-occupythemilkyway-iris-lite","generatedAt":"2026-10-14T04:35:18.214Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-12T00:18:13.468Z","emptyReason":null},"description":"Iris Lite — Inbox Intelligence (Free). Scans your last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies. A free taste of... Skill: Iris Lite — Quick Inbox Scan Owner: occupythemilkyway Summary: Iris Lite — Inbox Intelligence (Free). Scans your last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies. A free taste of... Tags: email:1.0.0, free:1.0.0, gmail:1.0.0, inbox:1.0.0, iris:1.0.0, latest:1.0.4, lite:1.0.0, productivity:1.0.0, triage:1.0.0 Version history: v1.0.4 | 2026-05-24T18:50:55.243Z | user - F","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 969 downloads reported by the source. Last updated 10/12/2026.","installCommand":"clawhub skill install s1757zbbgb226m951fdfmj4s6s860nkn:iris-lite","sourceUrl":"https://clawhub.ai/occupythemilkyway/iris-lite","homepage":"https://clawhub.ai/occupythemilkyway/skills/iris-lite","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/occupythemilkyway/iris-lite","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/occupythemilkyway/skills/iris-lite","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":60,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Iris Lite — Inbox Intelligence (Free). Scans your last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies. A free taste of..."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-12T00:18:13.468Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-12T00:18:13.468Z","emptyReason":null},"stars":null,"forks":null,"downloads":969,"likes":null,"task":null,"library":null,"packageName":null,"latestVersion":"1.0.4","tractionLabel":"969 downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-12T00:18:13.455Z","emptyReason":null},"lastUpdatedAt":"2026-10-12T00:18:13.468Z","lastCrawledAt":"2026-10-12T00:18:13.455Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-13T00:18:13.455Z","lastVerifiedAt":null,"highlights":[{"version":"1.0.4","createdAt":"2026-05-24T18:50:55.243Z","changelog":"- Fixed character encoding for emoji and symbols throughout documentation and messages - Updated upgrade instructions and links, including bundle deal offer - Version bumped to 1.0.4 - No changes to code logic or functionality","fileCount":3,"zipByteSize":4967},{"version":"1.0.1","createdAt":"2026-05-17T00:17:14.638Z","changelog":"iris-lite 1.0.1 - Version bump with no file changes. - No new features, fixes, or documentation updates in this release.","fileCount":2,"zipByteSize":3720},{"version":"1.0.0","createdAt":"2026-05-10T16:02:47.585Z","changelog":"Initial Lite release: 25 email scan, 2 drafts, priority list, Pro upsell","fileCount":2,"zipByteSize":3721}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s1757zbbgb226m951fdfmj4s6s860nkn:iris-lite","setupComplexity":"low","setupSteps":["Setup complexity is LOW. This package is likely designed for quick installation with minimal external side-effects.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-occupythemilkyway-iris-lite/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-occupythemilkyway-iris-lite/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-occupythemilkyway-iris-lite/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-occupythemilkyway-iris-lite/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-occupythemilkyway-iris-lite/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-occupythemilkyway-iris-lite/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-14T04:35:18.214Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-occupythemilkyway-iris-lite/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-occupythemilkyway-iris-lite/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-occupythemilkyway-iris-lite/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-occupythemilkyway-iris-lite/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-12T00:18:13.468Z","emptyReason":null},"readme":"Skill: Iris Lite — Quick Inbox Scan\n\nOwner: occupythemilkyway\n\nSummary: Iris Lite — Inbox Intelligence (Free). Scans your last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies. A free taste of...\n\nTags: email:1.0.0, free:1.0.0, gmail:1.0.0, inbox:1.0.0, iris:1.0.0, latest:1.0.4, lite:1.0.0, productivity:1.0.0, triage:1.0.0\n\nVersion history:\n\nv1.0.4 | 2026-05-24T18:50:55.243Z | user\n\n- Fixed character encoding for emoji and symbols throughout documentation and messages  \n- Updated upgrade instructions and links, including bundle deal offer  \n- Version bumped to 1.0.4  \n- No changes to code logic or functionality\n\nv1.0.1 | 2026-05-17T00:17:14.638Z | user\n\niris-lite 1.0.1\n\n- Version bump with no file changes.\n- No new features, fixes, or documentation updates in this release.\n\nv1.0.0 | 2026-05-10T16:02:47.585Z | user\n\nInitial Lite release: 25 email scan, 2 drafts, priority list, Pro upsell\n\nArchive index:\n\nArchive v1.0.4: 3 files, 4967 bytes\n\nFiles: skill-card.md (2232b), SKILL.md (8218b), _meta.json (128b)\n\nFile v1.0.4:SKILL.md\n\n---\nname: iris-lite\ndescription: \"Iris Lite — Inbox Intelligence (Free). Scans your last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies. A free taste of what Iris Pro does for your full inbox every day.\"\nversion: \"1.0.4\"\nmetadata:\n  openclaw:\n    requires:\n      env: [GMAIL_ADDRESS, GMAIL_APP_PASSWORD]\n      bins: [python3, pip3]\n    primaryEnv: GMAIL_ADDRESS\n    emoji: \"🌈\"\n    homepage: https://clawhub.ai/occupythemilkyway/iris-lite\n    tags: [email, gmail, inbox, triage, productivity, free, lite, iris]\n    envVars:\n      - name: GMAIL_ADDRESS\n        required: true\n        description: Your Gmail address\n      - name: GMAIL_APP_PASSWORD\n        required: true\n        description: \"Gmail app password from myaccount.google.com/apppasswords\"\n      - name: YOUR_NAME\n        required: false\n        description: Your name for draft replies\n        default: \"\"\n---\n\n# Iris Lite — Free Inbox Triage\n\nScan your last 25 emails and get a quick priority list with 2 draft replies.\n\n## Free vs Pro\n\n| Feature | Iris Lite (Free) | Iris Pro |\n|---------|-----------------|----------|\n| Emails scanned | **25** | Up to 200 |\n| Draft replies | **2** | Every actionable email |\n| Email categories | ❌ | ✅ Sales, HR, Legal, Finance |\n| Reply tones | ❌ | Professional / Friendly / Brief |\n| Weekly analytics | ❌ | ✅ |\n| JSON export | ❌ | ✅ |\n\n👉 **Upgrade:** `openclaw skills install iris-pro` — get your key at **ko-fi.com/s/f75940a0ce**\n\n💰 **Bundle deal:** all 5 Pro skills for **$29** → **ko-fi.com/s/7625accf3f** (save $16)\n\n---\n\n## Step 1 — Install\n\n```bash\npip3 install rich --break-system-packages --quiet\n```\n\n---\n\n## Step 2 — Quick inbox scan (Lite)\n\n```python\nimport os, imaplib, email, re\nfrom email.header import decode_header\nfrom email.utils import parsedate_to_datetime\nfrom datetime import datetime, timezone\nfrom rich.console import Console\nfrom rich.table import Table\nfrom rich.panel import Panel\nfrom rich import box\n\nconsole = Console()\n\nGMAIL_ADDR = os.environ.get(\"GMAIL_ADDRESS\", \"\").strip()\nGMAIL_PASS = os.environ.get(\"GMAIL_APP_PASSWORD\", \"\").strip()\nYOUR_NAME  = os.environ.get(\"YOUR_NAME\", \"\").strip()\n\n# Lite hard limits\nSCAN_LIMIT   = 25\nDRAFT_LIMIT  = 2\n\nif not GMAIL_ADDR or not GMAIL_PASS:\n    console.print(Panel(\n        \"[red]GMAIL_ADDRESS and GMAIL_APP_PASSWORD are required.[/red]\\n\\n\"\n        \"App password: myaccount.google.com/apppasswords\",\n        title=\"Setup Required\", border_style=\"red\"))\n    raise SystemExit(1)\n\nURGENT_KEYWORDS = [\"urgent\",\"asap\",\"deadline\",\"immediately\",\"overdue\",\"payment\",\"invoice\",\"emergency\"]\nNOISE_PATTERNS  = [r\"unsubscribe\",r\"newsletter\",r\"no-reply@\",r\"noreply@\",r\"marketing@\"]\n\ndef decode_str(s):\n    if not s: return \"\"\n    parts = decode_header(s)\n    result = []\n    for part, enc in parts:\n        if isinstance(part, bytes):\n            result.append(part.decode(enc or \"utf-8\", errors=\"replace\"))\n        else:\n            result.append(str(part))\n    return \" \".join(result)\n\ndef is_noise(sender, subject):\n    return any(re.search(p, (sender+subject).lower()) for p in NOISE_PATTERNS)\n\ndef score_email(subject, snippet, sender, age_hours, has_replied):\n    score = 50\n    text  = (subject + \" \" + snippet).lower()\n    if any(k in text for k in URGENT_KEYWORDS): score += 20\n    if \"?\" in text: score += 10\n    if age_hours > 48: score -= 10\n    if has_replied: score -= 15\n    return max(0, min(score, 100))\n\nconsole.print(Panel.fit(\n    f\"[bold cyan]🌈 Iris Lite — Quick Inbox Scan[/bold cyan]\\n\"\n    f\"Scanning last [yellow]{SCAN_LIMIT}[/yellow] emails from [green]{GMAIL_ADDR}[/green]\\n\"\n    f\"[dim]Lite: 25 emails, 2 drafts — upgrade to Pro for full inbox coverage[/dim]\",\n    border_style=\"cyan\"\n))\n\ntry:\n    mail = imaplib.IMAP4_SSL(\"imap.gmail.com\", 993)\n    mail.login(GMAIL_ADDR, GMAIL_PASS)\n    mail.select(\"INBOX\", readonly=True)\nexcept Exception as e:\n    console.print(f\"[red]❌ Login failed: {e}\\nCheck GMAIL_ADDRESS and GMAIL_APP_PASSWORD.[/red]\")\n    raise SystemExit(1)\n\n_, msg_ids = mail.search(None, \"ALL\")\nall_ids    = msg_ids[0].split() if msg_ids and msg_ids[0] else []\nrecent_ids = list(reversed(all_ids[-SCAN_LIMIT:])) if len(all_ids) > SCAN_LIMIT else list(reversed(all_ids))\n\nemails_data = []\nnow = datetime.now(timezone.utc)\nfor uid in recent_ids:\n    try:\n        _, raw = mail.fetch(uid, \"(RFC822.HEADER FLAGS)\")\n        if not raw or not raw[0]: continue\n        raw_header  = raw[0][1] if isinstance(raw[0], tuple) else raw[0]\n        msg         = email.message_from_bytes(raw_header)\n        subject     = decode_str(msg.get(\"Subject\",\"(no subject)\"))\n        sender      = decode_str(msg.get(\"From\",\"\"))\n        flags_raw   = raw[0][0] if isinstance(raw[0], tuple) else b\"\"\n        has_replied = b\"\\\\Answered\" in flags_raw\n        try:\n            sent_dt   = parsedate_to_datetime(msg.get(\"Date\",\"\"))\n            if sent_dt.tzinfo is None: sent_dt = sent_dt.replace(tzinfo=timezone.utc)\n            age_hours = (now - sent_dt).total_seconds() / 3600\n        except Exception:\n            age_hours = 0\n        body_snippet = \"\"\n        try:\n            _, rb = mail.fetch(uid, \"(BODY[TEXT]<0.150>)\")\n            if rb and rb[0] and isinstance(rb[0], tuple) and rb[0][1]:\n                body_snippet = rb[0][1].decode(\"utf-8\", errors=\"replace\").strip()[:100]\n        except Exception:\n            pass\n        m = re.search(r'\"?([^\"<]+)\"?\\s*<([^>]+)>', sender)\n        sender_name = m.group(1).strip() if m else sender\n        noise   = is_noise(sender, subject)\n        urgency = score_email(subject, body_snippet, sender, age_hours, has_replied)\n        emails_data.append({\n            \"subject\": subject, \"sender\": sender_name, \"age_hours\": age_hours,\n            \"urgency\": urgency, \"is_noise\": noise, \"replied\": has_replied, \"snippet\": body_snippet,\n        })\n    except Exception:\n        continue\n\nmail.logout()\n\nactionable = sorted([e for e in emails_data if not e[\"is_noise\"]], key=lambda e: -e[\"urgency\"])\nnoise_count = sum(1 for e in emails_data if e[\"is_noise\"])\n\nconsole.print()\ntbl = Table(title=f\"📬 Priority Inbox — Top {min(len(actionable), 15)} of {len(actionable)}\",\n            box=box.ROUNDED, border_style=\"cyan\")\ntbl.add_column(\"Score\", width=7, justify=\"right\")\ntbl.add_column(\"From\",  width=22, style=\"yellow\")\ntbl.add_column(\"Subject\", width=42)\ntbl.add_column(\"Age\",   width=6, style=\"dim\")\n\nfor e in actionable[:15]:\n    sc  = \"red\" if e[\"urgency\"] >= 70 else \"yellow\" if e[\"urgency\"] >= 50 else \"dim\"\n    age = f\"{int(e['age_hours'])}h\" if e[\"age_hours\"] < 48 else f\"{int(e['age_hours']//24)}d\"\n    tbl.add_row(f\"[{sc}]{e['urgency']}[/{sc}]\", e[\"sender\"][:20], e[\"subject\"][:40], age)\nconsole.print(tbl)\n\n# 2 draft replies (Lite limit)\nunreplied = [e for e in actionable if not e[\"replied\"]]\nconsole.print()\nfor e in unreplied[:DRAFT_LIMIT]:\n    name   = e[\"sender\"].split()[0]\n    text   = (e[\"subject\"] + \" \" + e[\"snippet\"]).lower()\n    if any(k in text for k in [\"urgent\",\"deadline\",\"asap\"]):\n        body = \"Thank you for flagging this — I'll look into it and get back to you shortly.\"\n    elif \"?\" in text:\n        body = \"Thanks for your question. To answer: [your answer here]\\n\\nLet me know if you need more.\"\n    else:\n        body = \"Thanks for reaching out. [Your response here].\"\n    sig  = f\"\\n\\n— {YOUR_NAME}\" if YOUR_NAME else \"\"\n    console.print(Panel(\n        f\"Hi {name},\\n\\n{body}{sig}\",\n        title=f\"[bold]📝 Draft — Re: {e['subject'][:45]}[/bold]\",\n        border_style=\"yellow\"\n    ))\n\nconsole.print()\nconsole.print(Panel(\n    f\"Scanned: [yellow]{len(emails_data)}[/yellow]  \"\n    f\"Actionable: [cyan]{len(actionable)}[/cyan]  \"\n    f\"Noise: [dim]{noise_count}[/dim]  \"\n    f\"Drafts: [yellow]{min(len(unreplied), DRAFT_LIMIT)}/{DRAFT_LIMIT}[/yellow] (Lite limit)\\n\\n\"\n    f\"[bold yellow]🔓 Want more?[/bold yellow]\\n\"\n    f\"Iris Pro scans [bold]200 emails[/bold], drafts [bold]every reply[/bold], classifies by category, \"\n    f\"and gives you weekly analytics.\\n\\n\"\n    f\"[bold cyan]openclaw skills install iris-pro[/bold cyan]\\n\"\n    f\"Get your key → [bold]ko-fi.com/s/f75940a0ce[/bold]\",\n    title=\"Summary + Upgrade\",\n    border_style=\"cyan\"\n))\n```\n\nFile v1.0.4:_meta.json\n\n{\n  \"ownerId\": \"kn7474wrtsjp988869sw7mygz1860n2v\",\n  \"slug\": \"iris-lite\",\n  \"version\": \"1.0.4\",\n  \"publishedAt\": 1779648655243\n}\n\nFile v1.0.4:skill-card.md\n\n## Description:\n\nIris Lite scans the last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[occupythemilkyway](https://clawhub.ai/user/occupythemilkyway)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nExternal users can run this skill to triage a small Gmail inbox sample, prioritize recent messages, and generate quick reply drafts. It is suited for lightweight personal productivity workflows that can tolerate manual review before acting on results.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill requires sensitive Gmail mailbox credentials and reads message headers and snippets.\n\nMitigation: Use a dedicated Gmail app password that can be revoked, and review the mailbox access implications before running the skill.\n\nRisk: The documented dependency install targets the system Python environment.\n\nMitigation: Install and run the dependency in an isolated virtual environment instead of using the system Python environment.\n\nRisk: The release includes advertising links for a separate Pro component.\n\nMitigation: Treat the Pro install and purchase links as separate from the Lite inbox scan and review them independently.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/occupythemilkyway/skills/iris-lite)\n- [ClawHub metadata homepage](https://clawhub.ai/occupythemilkyway/iris-lite)\n- [Publisher profile](https://clawhub.ai/user/occupythemilkyway)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown with inline Python and shell code blocks]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [The skill produces a priority table, urgency scores, summary counts, and up to 2 draft replies for human review.]\n\n## Skill Version(s):\n\n1.0.4 (source: frontmatter and server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.0.1: 2 files, 3720 bytes\n\nFiles: SKILL.md (8218b), _meta.json (128b)\n\nFile v1.0.1:SKILL.md\n\n---\nname: iris-lite\ndescription: \"Iris Lite â Inbox Intelligence (Free). Scans your last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies. A free taste of what Iris Pro does for your full inbox every day.\"\nversion: \"1.0.1\"\nmetadata:\n  openclaw:\n    requires:\n      env: [GMAIL_ADDRESS, GMAIL_APP_PASSWORD]\n      bins: [python3, pip3]\n    primaryEnv: GMAIL_ADDRESS\n    emoji: \"ð\"\n    homepage: https://clawhub.ai/occupythemilkyway/iris-lite\n    tags: [email, gmail, inbox, triage, productivity, free, lite, iris]\n    envVars:\n      - name: GMAIL_ADDRESS\n        required: true\n        description: Your Gmail address\n      - name: GMAIL_APP_PASSWORD\n        required: true\n        description: \"Gmail app password from myaccount.google.com/apppasswords\"\n      - name: YOUR_NAME\n        required: false\n        description: Your name for draft replies\n        default: \"\"\n---\n\n# Iris Lite â Free Inbox Triage\n\nScan your last 25 emails and get a quick priority list with 2 draft replies.\n\n## Free vs Pro\n\n| Feature | Iris Lite (Free) | Iris Pro |\n|---------|-----------------|----------|\n| Emails scanned | **25** | Up to 200 |\n| Draft replies | **2** | Every actionable email |\n| Email categories | â | â Sales, HR, Legal, Finance |\n| Reply tones | â | Professional / Friendly / Brief |\n| Weekly analytics | â | â |\n| JSON export | â | â |\n\nð **Upgrade:** `openclaw skills install iris-pro` â get your key at **ko-fi.com/occupythemilkyway**\n\n---\n\n## Step 1 â Install\n\n```bash\npip3 install rich --break-system-packages --quiet\n```\n\n---\n\n## Step 2 â Quick inbox scan (Lite)\n\n```python\nimport os, imaplib, email, re\nfrom email.header import decode_header\nfrom email.utils import parsedate_to_datetime\nfrom datetime import datetime, timezone\nfrom rich.console import Console\nfrom rich.table import Table\nfrom rich.panel import Panel\nfrom rich import box\n\nconsole = Console()\n\nGMAIL_ADDR = os.environ.get(\"GMAIL_ADDRESS\", \"\").strip()\nGMAIL_PASS = os.environ.get(\"GMAIL_APP_PASSWORD\", \"\").strip()\nYOUR_NAME  = os.environ.get(\"YOUR_NAME\", \"\").strip()\n\n# Lite hard limits\nSCAN_LIMIT   = 25\nDRAFT_LIMIT  = 2\n\nif not GMAIL_ADDR or not GMAIL_PASS:\n    console.print(Panel(\n        \"[red]GMAIL_ADDRESS and GMAIL_APP_PASSWORD are required.[/red]\\n\\n\"\n        \"App password: myaccount.google.com/apppasswords\",\n        title=\"Setup Required\", border_style=\"red\"))\n    raise SystemExit(1)\n\nURGENT_KEYWORDS = [\"urgent\",\"asap\",\"deadline\",\"immediately\",\"overdue\",\"payment\",\"invoice\",\"emergency\"]\nNOISE_PATTERNS  = [r\"unsubscribe\",r\"newsletter\",r\"no-reply@\",r\"noreply@\",r\"marketing@\"]\n\ndef decode_str(s):\n    if not s: return \"\"\n    parts = decode_header(s)\n    result = []\n    for part, enc in parts:\n        if isinstance(part, bytes):\n            result.append(part.decode(enc or \"utf-8\", errors=\"replace\"))\n        else:\n            result.append(str(part))\n    return \" \".join(result)\n\ndef is_noise(sender, subject):\n    return any(re.search(p, (sender+subject).lower()) for p in NOISE_PATTERNS)\n\ndef score_email(subject, snippet, sender, age_hours, has_replied):\n    score = 50\n    text  = (subject + \" \" + snippet).lower()\n    if any(k in text for k in URGENT_KEYWORDS): score += 20\n    if \"?\" in text: score += 10\n    if age_hours > 48: score -= 10\n    if has_replied: score -= 15\n    return max(0, min(score, 100))\n\nconsole.print(Panel.fit(\n    f\"[bold cyan]ð Iris Lite â Quick Inbox Scan[/bold cyan]\\n\"\n    f\"Scanning last [yellow]{SCAN_LIMIT}[/yellow] emails from [green]{GMAIL_ADDR}[/green]\\n\"\n    f\"[dim]Lite: 25 emails, 2 drafts â upgrade to Pro for full inbox coverage[/dim]\",\n    border_style=\"cyan\"\n))\n\ntry:\n    mail = imaplib.IMAP4_SSL(\"imap.gmail.com\", 993)\n    mail.login(GMAIL_ADDR, GMAIL_PASS)\n    mail.select(\"INBOX\", readonly=True)\nexcept Exception as e:\n    console.print(f\"[red]â Login failed: {e}\\nCheck GMAIL_ADDRESS and GMAIL_APP_PASSWORD.[/red]\")\n    raise SystemExit(1)\n\n_, msg_ids = mail.search(None, \"ALL\")\nall_ids    = msg_ids[0].split() if msg_ids and msg_ids[0] else []\nrecent_ids = list(reversed(all_ids[-SCAN_LIMIT:])) if len(all_ids) > SCAN_LIMIT else list(reversed(all_ids))\n\nemails_data = []\nnow = datetime.now(timezone.utc)\nfor uid in recent_ids:\n    try:\n        _, raw = mail.fetch(uid, \"(RFC822.HEADER FLAGS)\")\n        if not raw or not raw[0]: continue\n        raw_header  = raw[0][1] if isinstance(raw[0], tuple) else raw[0]\n        msg         = email.message_from_bytes(raw_header)\n        subject     = decode_str(msg.get(\"Subject\",\"(no subject)\"))\n        sender      = decode_str(msg.get(\"From\",\"\"))\n        flags_raw   = raw[0][0] if isinstance(raw[0], tuple) else b\"\"\n        has_replied = b\"\\\\Answered\" in flags_raw\n        try:\n            sent_dt   = parsedate_to_datetime(msg.get(\"Date\",\"\"))\n            if sent_dt.tzinfo is None: sent_dt = sent_dt.replace(tzinfo=timezone.utc)\n            age_hours = (now - sent_dt).total_seconds() / 3600\n        except Exception:\n            age_hours = 0\n        body_snippet = \"\"\n        try:\n            _, rb = mail.fetch(uid, \"(BODY[TEXT]<0.150>)\")\n            if rb and rb[0] and isinstance(rb[0], tuple) and rb[0][1]:\n                body_snippet = rb[0][1].decode(\"utf-8\", errors=\"replace\").strip()[:100]\n        except Exception:\n            pass\n        m = re.search(r'\"?([^\"<]+)\"?\\s*<([^>]+)>', sender)\n        sender_name = m.group(1).strip() if m else sender\n        noise   = is_noise(sender, subject)\n        urgency = score_email(subject, body_snippet, sender, age_hours, has_replied)\n        emails_data.append({\n            \"subject\": subject, \"sender\": sender_name, \"age_hours\": age_hours,\n            \"urgency\": urgency, \"is_noise\": noise, \"replied\": has_replied, \"snippet\": body_snippet,\n        })\n    except Exception:\n        continue\n\nmail.logout()\n\nactionable = sorted([e for e in emails_data if not e[\"is_noise\"]], key=lambda e: -e[\"urgency\"])\nnoise_count = sum(1 for e in emails_data if e[\"is_noise\"])\n\nconsole.print()\ntbl = Table(title=f\"ð¬ Priority Inbox â Top {min(len(actionable), 15)} of {len(actionable)}\",\n            box=box.ROUNDED, border_style=\"cyan\")\ntbl.add_column(\"Score\", width=7, justify=\"right\")\ntbl.add_column(\"From\",  width=22, style=\"yellow\")\ntbl.add_column(\"Subject\", width=42)\ntbl.add_column(\"Age\",   width=6, style=\"dim\")\n\nfor e in actionable[:15]:\n    sc  = \"red\" if e[\"urgency\"] >= 70 else \"yellow\" if e[\"urgency\"] >= 50 else \"dim\"\n    age = f\"{int(e['age_hours'])}h\" if e[\"age_hours\"] < 48 else f\"{int(e['age_hours']//24)}d\"\n    tbl.add_row(f\"[{sc}]{e['urgency']}[/{sc}]\", e[\"sender\"][:20], e[\"subject\"][:40], age)\nconsole.print(tbl)\n\n# 2 draft replies (Lite limit)\nunreplied = [e for e in actionable if not e[\"replied\"]]\nconsole.print()\nfor e in unreplied[:DRAFT_LIMIT]:\n    name   = e[\"sender\"].split()[0]\n    text   = (e[\"subject\"] + \" \" + e[\"snippet\"]).lower()\n    if any(k in text for k in [\"urgent\",\"deadline\",\"asap\"]):\n        body = \"Thank you for flagging this â I'll look into it and get back to you shortly.\"\n    elif \"?\" in text:\n        body = \"Thanks for your question. To answer: [your answer here]\\n\\nLet me know if you need more.\"\n    else:\n        body = \"Thanks for reaching out. [Your response here].\"\n    sig  = f\"\\n\\nâ {YOUR_NAME}\" if YOUR_NAME else \"\"\n    console.print(Panel(\n        f\"Hi {name},\\n\\n{body}{sig}\",\n        title=f\"[bold]ð Draft â Re: {e['subject'][:45]}[/bold]\",\n        border_style=\"yellow\"\n    ))\n\nconsole.print()\nconsole.print(Panel(\n    f\"Scanned: [yellow]{len(emails_data)}[/yellow]  \"\n    f\"Actionable: [cyan]{len(actionable)}[/cyan]  \"\n    f\"Noise: [dim]{noise_count}[/dim]  \"\n    f\"Drafts: [yellow]{min(len(unreplied), DRAFT_LIMIT)}/{DRAFT_LIMIT}[/yellow] (Lite limit)\\n\\n\"\n    f\"[bold yellow]ð Want more?[/bold yellow]\\n\"\n    f\"Iris Pro scans [bold]200 emails[/bold], drafts [bold]every reply[/bold], classifies by category, \"\n    f\"and gives you weekly analytics.\\n\\n\"\n    f\"[bold cyan]openclaw skills install iris-pro[/bold cyan]\\n\"\n    f\"Get your key â [bold]ko-fi.com/occupythemilkyway[/bold]\",\n    title=\"Summary + Upgrade\",\n    border_style=\"cyan\"\n))\n```\n\nFile v1.0.1:_meta.json\n\n{\n  \"ownerId\": \"kn7474wrtsjp988869sw7mygz1860n2v\",\n  \"slug\": \"iris-lite\",\n  \"version\": \"1.0.1\",\n  \"publishedAt\": 1778977034638\n}\n\nArchive v1.0.0: 2 files, 3721 bytes\n\nFiles: SKILL.md (8218b), _meta.json (128b)\n\nFile v1.0.0:SKILL.md\n\n---\nname: iris-lite\ndescription: \"Iris Lite â Inbox Intelligence (Free). Scans your last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies. A free taste of what Iris Pro does for your full inbox every day.\"\nversion: \"1.0.0\"\nmetadata:\n  openclaw:\n    requires:\n      env: [GMAIL_ADDRESS, GMAIL_APP_PASSWORD]\n      bins: [python3, pip3]\n    primaryEnv: GMAIL_ADDRESS\n    emoji: \"ð\"\n    homepage: https://clawhub.ai/occupythemilkyway/iris-lite\n    tags: [email, gmail, inbox, triage, productivity, free, lite, iris]\n    envVars:\n      - name: GMAIL_ADDRESS\n        required: true\n        description: Your Gmail address\n      - name: GMAIL_APP_PASSWORD\n        required: true\n        description: \"Gmail app password from myaccount.google.com/apppasswords\"\n      - name: YOUR_NAME\n        required: false\n        description: Your name for draft replies\n        default: \"\"\n---\n\n# Iris Lite â Free Inbox Triage\n\nScan your last 25 emails and get a quick priority list with 2 draft replies.\n\n## Free vs Pro\n\n| Feature | Iris Lite (Free) | Iris Pro |\n|---------|-----------------|----------|\n| Emails scanned | **25** | Up to 200 |\n| Draft replies | **2** | Every actionable email |\n| Email categories | â | â Sales, HR, Legal, Finance |\n| Reply tones | â | Professional / Friendly / Brief |\n| Weekly analytics | â | â |\n| JSON export | â | â |\n\nð **Upgrade:** `openclaw skills install iris-pro` â get your key at **ko-fi.com/occupythemilkyway**\n\n---\n\n## Step 1 â Install\n\n```bash\npip3 install rich --break-system-packages --quiet\n```\n\n---\n\n## Step 2 â Quick inbox scan (Lite)\n\n```python\nimport os, imaplib, email, re\nfrom email.header import decode_header\nfrom email.utils import parsedate_to_datetime\nfrom datetime import datetime, timezone\nfrom rich.console import Console\nfrom rich.table import Table\nfrom rich.panel import Panel\nfrom rich import box\n\nconsole = Console()\n\nGMAIL_ADDR = os.environ.get(\"GMAIL_ADDRESS\", \"\").strip()\nGMAIL_PASS = os.environ.get(\"GMAIL_APP_PASSWORD\", \"\").strip()\nYOUR_NAME  = os.environ.get(\"YOUR_NAME\", \"\").strip()\n\n# Lite hard limits\nSCAN_LIMIT   = 25\nDRAFT_LIMIT  = 2\n\nif not GMAIL_ADDR or not GMAIL_PASS:\n    console.print(Panel(\n        \"[red]GMAIL_ADDRESS and GMAIL_APP_PASSWORD are required.[/red]\\n\\n\"\n        \"App password: myaccount.google.com/apppasswords\",\n        title=\"Setup Required\", border_style=\"red\"))\n    raise SystemExit(1)\n\nURGENT_KEYWORDS = [\"urgent\",\"asap\",\"deadline\",\"immediately\",\"overdue\",\"payment\",\"invoice\",\"emergency\"]\nNOISE_PATTERNS  = [r\"unsubscribe\",r\"newsletter\",r\"no-reply@\",r\"noreply@\",r\"marketing@\"]\n\ndef decode_str(s):\n    if not s: return \"\"\n    parts = decode_header(s)\n    result = []\n    for part, enc in parts:\n        if isinstance(part, bytes):\n            result.append(part.decode(enc or \"utf-8\", errors=\"replace\"))\n        else:\n            result.append(str(part))\n    return \" \".join(result)\n\ndef is_noise(sender, subject):\n    return any(re.search(p, (sender+subject).lower()) for p in NOISE_PATTERNS)\n\ndef score_email(subject, snippet, sender, age_hours, has_replied):\n    score = 50\n    text  = (subject + \" \" + snippet).lower()\n    if any(k in text for k in URGENT_KEYWORDS): score += 20\n    if \"?\" in text: score += 10\n    if age_hours > 48: score -= 10\n    if has_replied: score -= 15\n    return max(0, min(score, 100))\n\nconsole.print(Panel.fit(\n    f\"[bold cyan]ð Iris Lite â Quick Inbox Scan[/bold cyan]\\n\"\n    f\"Scanning last [yellow]{SCAN_LIMIT}[/yellow] emails from [green]{GMAIL_ADDR}[/green]\\n\"\n    f\"[dim]Lite: 25 emails, 2 drafts â upgrade to Pro for full inbox coverage[/dim]\",\n    border_style=\"cyan\"\n))\n\ntry:\n    mail = imaplib.IMAP4_SSL(\"imap.gmail.com\", 993)\n    mail.login(GMAIL_ADDR, GMAIL_PASS)\n    mail.select(\"INBOX\", readonly=True)\nexcept Exception as e:\n    console.print(f\"[red]â Login failed: {e}\\nCheck GMAIL_ADDRESS and GMAIL_APP_PASSWORD.[/red]\")\n    raise SystemExit(1)\n\n_, msg_ids = mail.search(None, \"ALL\")\nall_ids    = msg_ids[0].split() if msg_ids and msg_ids[0] else []\nrecent_ids = list(reversed(all_ids[-SCAN_LIMIT:])) if len(all_ids) > SCAN_LIMIT else list(reversed(all_ids))\n\nemails_data = []\nnow = datetime.now(timezone.utc)\nfor uid in recent_ids:\n    try:\n        _, raw = mail.fetch(uid, \"(RFC822.HEADER FLAGS)\")\n        if not raw or not raw[0]: continue\n        raw_header  = raw[0][1] if isinstance(raw[0], tuple) else raw[0]\n        msg         = email.message_from_bytes(raw_header)\n        subject     = decode_str(msg.get(\"Subject\",\"(no subject)\"))\n        sender      = decode_str(msg.get(\"From\",\"\"))\n        flags_raw   = raw[0][0] if isinstance(raw[0], tuple) else b\"\"\n        has_replied = b\"\\\\Answered\" in flags_raw\n        try:\n            sent_dt   = parsedate_to_datetime(msg.get(\"Date\",\"\"))\n            if sent_dt.tzinfo is None: sent_dt = sent_dt.replace(tzinfo=timezone.utc)\n            age_hours = (now - sent_dt).total_seconds() / 3600\n        except Exception:\n            age_hours = 0\n        body_snippet = \"\"\n        try:\n            _, rb = mail.fetch(uid, \"(BODY[TEXT]<0.150>)\")\n            if rb and rb[0] and isinstance(rb[0], tuple) and rb[0][1]:\n                body_snippet = rb[0][1].decode(\"utf-8\", errors=\"replace\").strip()[:100]\n        except Exception:\n            pass\n        m = re.search(r'\"?([^\"<]+)\"?\\s*<([^>]+)>', sender)\n        sender_name = m.group(1).strip() if m else sender\n        noise   = is_noise(sender, subject)\n        urgency = score_email(subject, body_snippet, sender, age_hours, has_replied)\n        emails_data.append({\n            \"subject\": subject, \"sender\": sender_name, \"age_hours\": age_hours,\n            \"urgency\": urgency, \"is_noise\": noise, \"replied\": has_replied, \"snippet\": body_snippet,\n        })\n    except Exception:\n        continue\n\nmail.logout()\n\nactionable = sorted([e for e in emails_data if not e[\"is_noise\"]], key=lambda e: -e[\"urgency\"])\nnoise_count = sum(1 for e in emails_data if e[\"is_noise\"])\n\nconsole.print()\ntbl = Table(title=f\"ð¬ Priority Inbox â Top {min(len(actionable), 15)} of {len(actionable)}\",\n            box=box.ROUNDED, border_style=\"cyan\")\ntbl.add_column(\"Score\", width=7, justify=\"right\")\ntbl.add_column(\"From\",  width=22, style=\"yellow\")\ntbl.add_column(\"Subject\", width=42)\ntbl.add_column(\"Age\",   width=6, style=\"dim\")\n\nfor e in actionable[:15]:\n    sc  = \"red\" if e[\"urgency\"] >= 70 else \"yellow\" if e[\"urgency\"] >= 50 else \"dim\"\n    age = f\"{int(e['age_hours'])}h\" if e[\"age_hours\"] < 48 else f\"{int(e['age_hours']//24)}d\"\n    tbl.add_row(f\"[{sc}]{e['urgency']}[/{sc}]\", e[\"sender\"][:20], e[\"subject\"][:40], age)\nconsole.print(tbl)\n\n# 2 draft replies (Lite limit)\nunreplied = [e for e in actionable if not e[\"replied\"]]\nconsole.print()\nfor e in unreplied[:DRAFT_LIMIT]:\n    name   = e[\"sender\"].split()[0]\n    text   = (e[\"subject\"] + \" \" + e[\"snippet\"]).lower()\n    if any(k in text for k in [\"urgent\",\"deadline\",\"asap\"]):\n        body = \"Thank you for flagging this â I'll look into it and get back to you shortly.\"\n    elif \"?\" in text:\n        body = \"Thanks for your question. To answer: [your answer here]\\n\\nLet me know if you need more.\"\n    else:\n        body = \"Thanks for reaching out. [Your response here].\"\n    sig  = f\"\\n\\nâ {YOUR_NAME}\" if YOUR_NAME else \"\"\n    console.print(Panel(\n        f\"Hi {name},\\n\\n{body}{sig}\",\n        title=f\"[bold]ð Draft â Re: {e['subject'][:45]}[/bold]\",\n        border_style=\"yellow\"\n    ))\n\nconsole.print()\nconsole.print(Panel(\n    f\"Scanned: [yellow]{len(emails_data)}[/yellow]  \"\n    f\"Actionable: [cyan]{len(actionable)}[/cyan]  \"\n    f\"Noise: [dim]{noise_count}[/dim]  \"\n    f\"Drafts: [yellow]{min(len(unreplied), DRAFT_LIMIT)}/{DRAFT_LIMIT}[/yellow] (Lite limit)\\n\\n\"\n    f\"[bold yellow]ð Want more?[/bold yellow]\\n\"\n    f\"Iris Pro scans [bold]200 emails[/bold], drafts [bold]every reply[/bold], classifies by category, \"\n    f\"and gives you weekly analytics.\\n\\n\"\n    f\"[bold cyan]openclaw skills install iris-pro[/bold cyan]\\n\"\n    f\"Get your key â [bold]ko-fi.com/occupythemilkyway[/bold]\",\n    title=\"Summary + Upgrade\",\n    border_style=\"cyan\"\n))\n```\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn7474wrtsjp988869sw7mygz1860n2v\",\n  \"slug\": \"iris-lite\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1778428967585\n}","readmeExcerpt":"Skill: Iris Lite — Quick Inbox Scan Owner: occupythemilkyway Summary: Iris Lite — Inbox Intelligence (Free). Scans your last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies. A free taste of... Tags: email:1.0.0, free:1.0.0, gmail:1.0.0, inbox:1.0.0, iris:1.0.0, latest:1.0.4, lite:1.0.0, productivity:1.0.0, triage:1.0.0 Version history: v1.0.4 | 2026-05-24T18:50:55.243Z | user - F","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"pip3 install rich --break-system-packages --quiet"},{"language":"python","snippet":"import os, imaplib, email, re\nfrom email.header import decode_header\nfrom email.utils import parsedate_to_datetime\nfrom datetime import datetime, timezone\nfrom rich.console import Console\nfrom rich.table import Table\nfrom rich.panel import Panel\nfrom rich import box\n\nconsole = Console()\n\nGMAIL_ADDR = os.environ.get(\"GMAIL_ADDRESS\", \"\").strip()\nGMAIL_PASS = os.environ.get(\"GMAIL_APP_PASSWORD\", \"\").strip()\nYOUR_NAME  = os.environ.get(\"YOUR_NAME\", \"\").strip()\n\n# Lite hard limits\nSCAN_LIMIT   = 25\nDRAFT_LIMIT  = 2\n\nif not GMAIL_ADDR or not GMAIL_PASS:\n    console.print(Panel(\n        \"[red]GMAIL_ADDRESS and GMAIL_APP_PASSWORD are required.[/red]\\n\\n\"\n        \"App password: myaccount.google.com/apppasswords\",\n        title=\"Setup Required\", border_style=\"red\"))\n    raise SystemExit(1)\n\nURGENT_KEYWORDS = [\"urgent\",\"asap\",\"deadline\",\"immediately\",\"overdue\",\"payment\",\"invoice\",\"emergency\"]\nNOISE_PATTERNS  = [r\"unsubscribe\",r\"newsletter\",r\"no-reply@\",r\"noreply@\",r\"marketing@\"]\n\ndef decode_str(s):\n    if not s: return \"\"\n    parts = decode_header(s)\n    result = []\n    for part, enc in parts:\n        if isinstance(part, bytes):\n            result.append(part.decode(enc or \"utf-8\", errors=\"replace\"))\n        else:\n            result.append(str(part))\n    return \" \".join(result)\n\ndef is_noise(sender, subject):\n    return any(re.search(p, (sender+subject).lower()) for p in NOISE_PATTERNS)\n\ndef score_email(subject, snippet, sender, age_hours, has_replied):\n    score = 50\n    text  = (subject + \" \" + snippet).lower()\n    if any(k in text for k in URGENT_KEYWORDS): score += 20\n    if \"?\" in text: score += 10\n    if age_hours > 48: score -= 10\n    if has_replied: score -= 15\n    return max(0, min(score, 100))\n\nconsole.print(Panel.fit(\n    f\"[bold cyan]🌈 Iris Lite — Quick Inbox Scan[/bold cyan]\\n\"\n    f\"Scanning last [yellow]{SCAN_LIMIT}[/yellow] emails from [green]{GMAIL_ADDR}[/green]\\n\"\n    f\"[dim]Lite: 25 emails, 2 drafts — upgrade to Pro for full inbox coverage[/dim]\",\n    borde"},{"language":"bash","snippet":"pip3 install rich --break-system-packages --quiet"},{"language":"python","snippet":"import os, imaplib, email, re\nfrom email.header import decode_header\nfrom email.utils import parsedate_to_datetime\nfrom datetime import datetime, timezone\nfrom rich.console import Console\nfrom rich.table import Table\nfrom rich.panel import Panel\nfrom rich import box\n\nconsole = Console()\n\nGMAIL_ADDR = os.environ.get(\"GMAIL_ADDRESS\", \"\").strip()\nGMAIL_PASS = os.environ.get(\"GMAIL_APP_PASSWORD\", \"\").strip()\nYOUR_NAME  = os.environ.get(\"YOUR_NAME\", \"\").strip()\n\n# Lite hard limits\nSCAN_LIMIT   = 25\nDRAFT_LIMIT  = 2\n\nif not GMAIL_ADDR or not GMAIL_PASS:\n    console.print(Panel(\n        \"[red]GMAIL_ADDRESS and GMAIL_APP_PASSWORD are required.[/red]\\n\\n\"\n        \"App password: myaccount.google.com/apppasswords\",\n        title=\"Setup Required\", border_style=\"red\"))\n    raise SystemExit(1)\n\nURGENT_KEYWORDS = [\"urgent\",\"asap\",\"deadline\",\"immediately\",\"overdue\",\"payment\",\"invoice\",\"emergency\"]\nNOISE_PATTERNS  = [r\"unsubscribe\",r\"newsletter\",r\"no-reply@\",r\"noreply@\",r\"marketing@\"]\n\ndef decode_str(s):\n    if not s: return \"\"\n    parts = decode_header(s)\n    result = []\n    for part, enc in parts:\n        if isinstance(part, bytes):\n            result.append(part.decode(enc or \"utf-8\", errors=\"replace\"))\n        else:\n            result.append(str(part))\n    return \" \".join(result)\n\ndef is_noise(sender, subject):\n    return any(re.search(p, (sender+subject).lower()) for p in NOISE_PATTERNS)\n\ndef score_email(subject, snippet, sender, age_hours, has_replied):\n    score = 50\n    text  = (subject + \" \" + snippet).lower()\n    if any(k in text for k in URGENT_KEYWORDS): score += 20\n    if \"?\" in text: score += 10\n    if age_hours > 48: score -= 10\n    if has_replied: score -= 15\n    return max(0, min(score, 100))\n\nconsole.print(Panel.fit(\n    f\"[bold cyan]ð Iris Lite â Quick Inbox Scan[/bold cyan]\\n\"\n    f\"Scanning last [yellow]{SCAN_LIMIT}[/yellow] emails from [green]{GMAIL_ADDR}[/green]\\n\"\n    f\"[dim]Lite: 25 emails, 2 drafts â upgrade to Pro for full inbox coverage[/dim]\",\n   "},{"language":"bash","snippet":"pip3 install rich --break-system-packages --quiet"},{"language":"python","snippet":"import os, imaplib, email, re\nfrom email.header import decode_header\nfrom email.utils import parsedate_to_datetime\nfrom datetime import datetime, timezone\nfrom rich.console import Console\nfrom rich.table import Table\nfrom rich.panel import Panel\nfrom rich import box\n\nconsole = Console()\n\nGMAIL_ADDR = os.environ.get(\"GMAIL_ADDRESS\", \"\").strip()\nGMAIL_PASS = os.environ.get(\"GMAIL_APP_PASSWORD\", \"\").strip()\nYOUR_NAME  = os.environ.get(\"YOUR_NAME\", \"\").strip()\n\n# Lite hard limits\nSCAN_LIMIT   = 25\nDRAFT_LIMIT  = 2\n\nif not GMAIL_ADDR or not GMAIL_PASS:\n    console.print(Panel(\n        \"[red]GMAIL_ADDRESS and GMAIL_APP_PASSWORD are required.[/red]\\n\\n\"\n        \"App password: myaccount.google.com/apppasswords\",\n        title=\"Setup Required\", border_style=\"red\"))\n    raise SystemExit(1)\n\nURGENT_KEYWORDS = [\"urgent\",\"asap\",\"deadline\",\"immediately\",\"overdue\",\"payment\",\"invoice\",\"emergency\"]\nNOISE_PATTERNS  = [r\"unsubscribe\",r\"newsletter\",r\"no-reply@\",r\"noreply@\",r\"marketing@\"]\n\ndef decode_str(s):\n    if not s: return \"\"\n    parts = decode_header(s)\n    result = []\n    for part, enc in parts:\n        if isinstance(part, bytes):\n            result.append(part.decode(enc or \"utf-8\", errors=\"replace\"))\n        else:\n            result.append(str(part))\n    return \" \".join(result)\n\ndef is_noise(sender, subject):\n    return any(re.search(p, (sender+subject).lower()) for p in NOISE_PATTERNS)\n\ndef score_email(subject, snippet, sender, age_hours, has_replied):\n    score = 50\n    text  = (subject + \" \" + snippet).lower()\n    if any(k in text for k in URGENT_KEYWORDS): score += 20\n    if \"?\" in text: score += 10\n    if age_hours > 48: score -= 10\n    if has_replied: score -= 15\n    return max(0, min(score, 100))\n\nconsole.print(Panel.fit(\n    f\"[bold cyan]ð Iris Lite â Quick Inbox Scan[/bold cyan]\\n\"\n    f\"Scanning last [yellow]{SCAN_LIMIT}[/yellow] emails from [green]{GMAIL_ADDR}[/green]\\n\"\n    f\"[dim]Lite: 25 emails, 2 drafts â upgrade to Pro for full inbox coverage[/dim]\",\n   "}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: iris-lite\ndescription: \"Iris Lite — Inbox Intelligence (Free). Scans your last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies. A free taste of what Iris Pro does for your full inbox every day.\"\nversion: \"1.0.4\"\nmetadata:\n  openclaw:\n    requires:\n      env: [GMAIL_ADDRESS, GMAIL_APP_PASSWORD]\n      bins: [python3, pip3]\n    primaryEnv: GMAIL_ADDRESS\n    emoji: \"🌈\"\n    homepage: https://clawhub.ai/occupythemilkyway/iris-lite\n    tags: [email, gmail, inbox, triage, productivity, free, lite, iris]\n    envVars:\n      - name: GMAIL_ADDRESS\n        required: true\n        description: Your Gmail address\n      - name: GMAIL_APP_PASSWORD\n        required: true\n        description: \"Gmail app password from myaccount.google.com/apppasswords\"\n      - name: YOUR_NAME\n        required: false\n        description: Your name for draft replies\n        default: \"\"\n---\n\n# Iris Lite — Free Inbox Triage\n\nScan your last 25 emails and get a quick priority list with 2 draft replies.\n\n## Free vs Pro\n\n| Feature | Iris Lite (Free) | Iris Pro |\n|---------|-----------------|----------|\n| Emails scanned | **25** | Up to 200 |\n| Draft replies | **2** | Every actionable email |\n| Email categories | ❌ | ✅ Sales, HR, Legal, Finance |\n| Reply tones | ❌ | Professional / Friendly / Brief |\n| Weekly analytics | ❌ | ✅ |\n| JSON export | ❌ | ✅ |\n\n👉 **Upgrade:** `openclaw skills install iris-pro` — get your key at **ko-fi.com/s/f75940a0ce**\n\n💰 **Bundle deal:** all 5 Pro skills for **$29** → **ko-fi.com/s/7625accf3f** (save $16)\n\n---\n\n## Step 1 — Install\n\n```bash\npip3 install rich --break-system-packages --quiet\n```\n\n---\n\n## Step 2 — Quick inbox scan (Lite)\n\n```python\nimport os, imaplib, email, re\nfrom email.header import decode_header\nfrom email.utils import parsedate_to_datetime\nfrom datetime import datetime, timezone\nfrom rich.console import Console\nfrom rich.table import Table\nfrom rich.panel import Panel\nfrom rich import box\n\nconsole = Console()\n\nGMAIL_ADDR = os.environ.get(\"GMAIL_ADDRESS\", \"\").strip()\nGMAIL_PASS = os.environ.get(\"GMAIL_APP_PASSWORD\", \"\").strip()\nYOUR_NAME  = os.environ.get(\"YOUR_NAME\", \"\").strip()\n\n# Lite hard limits\nSCAN_LIMIT   = 25\nDRAFT_LIMIT  = 2\n\nif not GMAIL_ADDR or not GMAIL_PASS:\n    console.print(Panel(\n        \"[red]GMAIL_ADDRESS and GMAIL_APP_PASSWORD are required.[/red]\\n\\n\"\n        \"App password: myaccount.google.com/apppasswords\",\n        title=\"Setup Required\", border_style=\"red\"))\n    raise SystemExit(1)\n\nURGENT_KEYWORDS = [\"urgent\",\"asap\",\"deadline\",\"immediately\",\"overdue\",\"payment\",\"invoice\",\"emergency\"]\nNOISE_PATTERNS  = [r\"unsubscribe\",r\"newsletter\",r\"no-reply@\",r\"noreply@\",r\"marketing@\"]\n\ndef decode_str(s):\n    if not s: return \"\"\n    parts = decode_header(s)\n    result = []\n    for part, enc in parts:\n        if isinstance(part, bytes):\n            result.append(part.decode(enc or \"utf-8\", errors=\"replace\"))\n        else:\n            result.append(str(part))\n    return \" \".join(result)\n\ndef is"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7474wrtsjp988869sw7mygz1860n2v\",\n  \"slug\": \"iris-lite\",\n  \"version\": \"1.0.4\",\n  \"publishedAt\": 1779648655243\n}"},{"path":"skill-card.md","content":"## Description:\n\nIris Lite scans the last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[occupythemilkyway](https://clawhub.ai/user/occupythemilkyway)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nExternal users can run this skill to triage a small Gmail inbox sample, prioritize recent messages, and generate quick reply drafts. It is suited for lightweight personal productivity workflows that can tolerate manual review before acting on results.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill requires sensitive Gmail mailbox credentials and reads message headers and snippets.\n\nMitigation: Use a dedicated Gmail app password that can be revoked, and review the mailbox access implications before running the skill.\n\nRisk: The documented dependency install targets the system Python environment.\n\nMitigation: Install and run the dependency in an isolated virtual environment instead of using the system Python environment.\n\nRisk: The release includes advertising links for a separate Pro component.\n\nMitigation: Treat the Pro install and purchase links as separate from the Lite inbox scan and review them independently.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/occupythemilkyway/skills/iris-lite)\n- [ClawHub metadata homepage](https://clawhub.ai/occupythemilkyway/iris-lite)\n- [Publisher profile](https://clawhub.ai/user/occupythemilkyway)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown with inline Python and shell code blocks]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [The skill produces a priority table, urgency scores, summary counts, and up to 2 draft replies for human review.]\n\n## Skill Version(s):\n\n1.0.4 (source: frontmatter and server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Iris Lite — Inbox Intelligence (Free). Scans your last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies. A free taste of... Skill: Iris Lite — Quick Inbox Scan Owner: occupythemilkyway Summary: Iris Lite — Inbox Intelligence (Free). Scans your last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies. A free taste of... Tags: email:1.0.0, free:1.0.0, gmail:1.0.0, inbox:1.0.0, iris:1.0.0, latest:1.0.4, lite:1.0.0, productivity:1.0.0, triage:1.0.0 Version history: v1.0.4 | 2026-05-24T18:50:55.243Z | user - F","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1116,"uniquenessScore":50,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-12T00:18:13.468Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-12T00:18:13.468Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-14T04:35:18.214Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}