Skip to content
Back to skills

Install

CSecurity

CCSC lifecycle command center — fresh install walkthrough, health doctor, verify round-trip, auto-repair, Slack app manifest export, reset, tour, and uninstall. Use when installing the Slack channel for the first time, diagnosing or repairing an existing install, exporting a Slack app manifest, or tearing an install down. Trigger with "/slack-channel:install", "install the slack channel", "slack channel doctor", or "uninstall the slack channel".

  • 2,719 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 21, 2026
ai-agentsshellbashreactnodedockergitapisecurity

Works with

  • claude code
  • terminal
  • cli
  • api
  • mcp

Security analysis

C73/100
  • highPerforms destructive filesystem operations
  • criticalExfiltrates credentials via HTTP — exact pattern from Snyk ToxicSkills study

Pro scans all 4 files and shows the line behind each finding

Scanned September 21, 2026

npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill install --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Install?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Install
[![Security: C — Skills Directory](https://www.skillsdirectory.com/api/skills/jeremylongshore-install-3d01490b/badge)](https://www.skillsdirectory.com/skills/jeremylongshore-install-3d01490b)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: install
description: CCSC lifecycle command center — fresh install walkthrough, health doctor, verify round-trip, auto-repair, Slack app manifest export, reset, tour, and uninstall. Use when installing the Slack channel for the first time, diagnosing or repairing an existing install, exporting a Slack app manifest, or tearing an install down. Trigger with "/slack-channel:install", "install the slack channel", "slack channel doctor", or "uninstall the slack channel".
version: 1.0.1
author: Jeremy Longshore <jeremy@intentsolutions.io>
license: Apache-2.0
compatibility: Requires Claude Code >= v2.1.80 with a claude.ai login (Channels research preview rejects API-key-only auth), Bun >= 1.0 (Node.js fallback supported), and a Slack workspace where you can create apps. Doctor mode additionally shells out to curl and jq.
tags: [slack, mcp, install, doctor, lifecycle]
user-invocable: true
argument-hint: "[install | doctor | verify | repair | manifest | reset | tour | uninstall]"
allowed-tools: [Read, Write, Bash(bun:*), Bash(claude:*), Bash(curl:*), Bash(jq:*), Bash(chmod:*), Bash(mkdir:*), Bash(mv:*), Bash(command:*), Bash(date:*)]
---

# /slack-channel:install

## Overview

CCSC lifecycle command center. One skill, eight modes — fresh install through
teardown. No subcommand defaults to the full install walkthrough.

```
/slack-channel:install [mode]

Modes:
  install     full fresh-clone walkthrough (default)        ← new user starts here
  doctor      run a health check against an existing install
  verify      run the verification round-trip only
  repair      auto-fix common issues surfaced by doctor
  manifest    export a Slack app manifest.json (one-click import — saves 10 min of scope clicking)
  reset       wipe state and restart from Step 3 (keeps Slack app)
  tour        explain CCSC features without installing (for evaluators)
  uninstall   clean teardown — remove state dir, optionally guide Slack app deletion
```

This skill **orchestrates**. It delegates token configuration to
`/slack-channel:configure` and pairing to `/slack-channel:access pair` —
do not re-implement those.

## Prerequisites

Three prerequisites gate a fresh install; `install` mode checks them as Step 0
and `doctor` re-checks them on demand. See
[`references/prerequisites.md`](references/prerequisites.md) for the exact
commands and recovery paths.

1. **Bun ≥ 1.0** — `bun --version`. Node.js fallback is fine (see the prerequisites doc).
2. **Claude Code ≥ v2.1.80** — `claude --version`. Older versions cannot load Channels.
3. **`claude.ai` login (NOT API-key-only)** — Channels is Research Preview and rejects pure API-key auth.

Doctor mode additionally shells out to `curl` and `jq` (see "Doctor
dependencies" below), and every mode past Step 1 assumes a Slack workspace
where you can create an app.

## Instructions

1. Parse the first word of `$ARGUMENTS`.
2. If empty or `install`, run the **install** mode.
3. Otherwise dispatch to the matching mode described in the sections below —
   each `Mode:` section is the complete procedure for that mode.
4. Unknown modes: show the help block above and exit.

---

## Mode: `install` (default)

End-to-end fresh-clone walkthrough.

```
┌────────────────────────────────────────────────────────┐
│ 0. Prerequisite check    (Bun, Claude Code, claude.ai) │
│ 1. Slack app creation    (or skip → /install manifest) │
│ 2. Add bot to channel    ← THE silent killer step      │
│ 3. Configure tokens      → /slack-channel:configure    │
│ 4. Start MCP server                                    │
│ 5. Pair account          → /slack-channel:access pair  │
│ 6. Verification round-trip                             │
│ 7. Offer optional next steps                           │
└────────────────────────────────────────────────────────┘
```

### Step 0: Prerequisite check

See [`references/prerequisites.md`](references/prerequisites.md) for the
exact commands and recovery paths. Three checks in order:

1. **Bun ≥ 1.0**: `bun --version`. If missing, show install command (Node fallback is fine — see prerequisites doc).
2. **Claude Code ≥ v2.1.80**: `claude --version`. Older versions cannot load Channels.
3. **`claude.ai` login (NOT API-key-only)**: Channels is Research Preview and rejects pure API-key auth. If `ANTHROPIC_API_KEY` is set but no `claude.ai` session exists, instruct `claude login`.

If any check fails, give the recovery command, wait for the user to fix, then re-check. Don't proceed with broken prerequisites.

### Step 1: Slack app creation

**Two paths**: offer the manifest path (faster) or walk the manual UI:

- **Faster path** — run `/slack-channel:install manifest` first to download
  a `slack-app-manifest.json`, then at https://api.slack.com/apps click
  **Create New App → From an app manifest** and paste it. All 8 scopes
  and 4 event subscriptions are pre-configured. User just clicks
  **Install to Workspace** and copies the two tokens.
- **Manual path** — walk the 5-step UI checklist. See
  [`references/slack-app-setup.md`](references/slack-app-setup.md) for
  the full step-by-step.

Tokens stay in a scratch buffer at this point — they're written to disk in Step 3.

### Step 2: Add bot to channel (silent killer)

**Most common silent failure.** Slack installs apps to the workspace without
joining channels. The pairing DM (Step 5) works because DMs auto-route,
but a channel test message (Step 6) hits silence — the bot literally
isn't in the channel to see the event.

Walk the user:

1. Open Slack → navigate to the target channel.
2. Click the channel name (top of the panel) → **Integrations** tab.
3. Click **Add an App** → select your bot.
4. Confirm: the bot appears in the channel's member list.

Private channels need explicit invitation. If multiple channels, repeat.
Wait for confirmation before proceeding.

### Step 3: Configure tokens

Delegate:

```
/slack-channel:configure <xoxb-...> <xapp-...>
```

That skill validates token prefixes, writes `~/.claude/channels/slack/.env`
with `0o600` permissions, never echoes tokens back. Do not re-implement.

### Step 4: Start the MCP server

```bash
bun install                                                # if not already
claude --channels plugin:slack-channel@claude-code-plugins
```

Node.js or Docker alternatives: see
[`README.md` § Quick Start](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/README.md#quick-start)
Options B/C.

### Step 5: Pair account

1. User DMs the bot in Slack (any short message).
2. Bot replies with a 6-character pairing code.
3. Delegate: `/slack-channel:access pair <code>`.
4. Bot confirms: "Approved! You can now reach this session."

### Step 6: Verification round-trip

User sends `@<bot> hello` in the channel from Step 2.

- **Reply within 10s** → success. Move to Step 7.
- **Silence** → run `/slack-channel:install doctor` to diagnose, then
  `/slack-channel:install repair` to fix.

### Step 7: Optional next steps

Ask the user if they want any of these:

| Want | Run |
|---|---|
| Author a custom policy rule | `/slack-channel:policy` |
| Enable signed audit log (Ed25519) | `bun scripts/audit-key.ts init` — see [`000-docs/key-management.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/000-docs/key-management.md) |
| Wire in a second bot (multi-agent) | See [`000-docs/multi-agent-channels.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/000-docs/multi-agent-channels.md) |
| Tighten access (allowlist mode) | `/slack-channel:access policy allowlist`, then `add <user-id>` per user |
| Run a health check anytime | `/slack-channel:install doctor` |

---

## Mode: `doctor`

Health check an existing install. Reports a structured pass/fail per check,
no mutations. Suggests `/slack-channel:install repair` if fixable issues
are found.

### Doctor dependencies

The doctor checks below shell out to `curl` and `jq`. `curl` is preinstalled
on virtually every dev system; `jq` may not be. Verify before running:

```bash
command -v curl >/dev/null && command -v jq >/dev/null || echo "Install jq: brew install jq  (macOS)  |  sudo apt install jq  (Debian/Ubuntu)"
```

The checks also need `$SLACK_BOT_TOKEN` and `$SLACK_APP_TOKEN` in the
environment. Load them from the user's `.env` before running any check
that references them:

```bash
set -a; . ~/.claude/channels/slack/.env; set +a
```

If the user prefers not to export the tokens (some shells leak env to
child processes / process listings), substitute the literal token values
inline — the doctor commands tolerate either form.

### Checks

Run all in order:

1. **State directory exists**: `~/.claude/channels/slack/` is a directory with mode `0700`.
2. **`.env` exists, is mode `0600`, owned by current user**.
3. **`.env` contains both tokens** with valid prefixes (`xoxb-` and `xapp-`).
4. **Bot token is live**: `curl -s -H "Authorization: Bearer $SLACK_BOT_TOKEN" https://slack.com/api/auth.test | jq -e '.ok == true'`. Reports the `bot_id` and `team` if live; "invalid_auth" / "token_revoked" / "account_inactive" if not.
5. **App token has `connections:write`**: `curl -s -X POST -H "Authorization: Bearer $SLACK_APP_TOKEN" https://slack.com/api/apps.connections.open | jq -e '.ok == true and (.url | startswith("wss://"))'`. Expects `ok: true` and a `wss://` URL. Failure modes: "not_authed" (token missing), "missing_scope" (token lacks `connections:write`), "token_revoked" (regenerate).
6. **`access.json` exists, is mode `0600`, parses as valid JSON**: `[ -f ~/.claude/channels/slack/access.json ] && jq empty ~/.claude/channels/slack/access.json`.
7. **`allowFrom` is non-empty**: `jq -e '.allowFrom | length > 0' ~/.claude/channels/slack/access.json`. If empty, user has not completed Step 5.
8. **Audit log integrity**: if `audit.log` exists, run `bun server.ts --verify-audit-log ~/.claude/channels/slack/audit.log`. Report hash-chain status.
9. **Claude Code version ≥ v2.1.80** and `claude.ai` login present: `claude --version` and `claude auth status`.
10. **Bot is in channel(s) configured in `access.json.channels`**: for each opted-in channel, `curl -s -H "Authorization: Bearer $SLACK_BOT_TOKEN" "https://slack.com/api/conversations.members?channel=$CHANNEL_ID" | jq -e --arg bot "$BOT_USER_ID" '.members | index($bot) != null'`. THIS catches the silent-killer Step 2 omission.

Report format:

```
✅ State directory       /home/.../slack (0700)
✅ Token file            .env (0600)
✅ Bot token             active — team=acme, bot_id=U0123
✅ App token             active — connections:write
✅ Access file           access.json (0600, valid JSON)
⚠️  Paired users          0 (run pairing flow — Step 5)
✅ Audit chain           verified — 1247 events, no breaks   # event count is an example
✅ Claude Code           v2.4.1, claude.ai session active
❌ Bot in channel        C_OPS opted-in but bot is NOT a member  ← silent killer
                         Fix: open #ops → Integrations → Add an App
```

Exit code: 0 if all green, 1 if any ⚠️ or ❌. Print
`Suggested next: /slack-channel:install repair` if any fixable issue.

---

## Mode: `verify`

Just the Step 6 verification round-trip. Useful after `repair` or after
restarting Claude. Tells the user:

> Send `@<bot> hello` in any channel where the bot is added.
> I'll wait up to 30 seconds for a reply event in the journal.

Tail `audit.log` for the next 30s looking for `gate.allowed` followed by
`reply.sent`. Report success or timeout. On timeout, suggest `doctor`.

---

## Mode: `repair`

Auto-fix the issues `doctor` found that are safely fixable:

| Doctor finding | Repair action |
|---|---|
| State dir missing | `mkdir -p ~/.claude/channels/slack && chmod 0700 ~/.claude/channels/slack` |
| `.env` perms wrong | `chmod 0600 ~/.claude/channels/slack/.env` |
| `access.json` perms wrong | `chmod 0600 ~/.claude/channels/slack/access.json` |
| `access.json` corrupt JSON | `TS=$(date +%s); mv ~/.claude/channels/slack/access.json ~/.claude/channels/slack/access.json.broken.$TS && echo '{ "allowFrom": [], "channels": {}, "pending": [] }' > ~/.claude/channels/slack/access.json && chmod 0600 ~/.claude/channels/slack/access.json` |
| Audit log hash break | DO NOT repair — surface for incident review (this means tampering or write loss) |
| Bot not in channel | Cannot fix from the terminal — print the exact Slack click-path |
| Token invalid | Cannot fix — instruct user to regenerate at api.slack.com/apps |

NEVER auto-modify `access.json` content beyond the corrupt-restart case
above. NEVER auto-modify the audit log. Both are append-only / engineer-edit-only.

After repair, re-run `doctor` and show before/after.

---

## Mode: `manifest`

Generate a Slack app manifest JSON that pre-configures all 8 OAuth scopes
and 4 event subscriptions in one import. Saves a fresh user ~10 minutes
of UI clicking and eliminates the "did I add the right scopes" failure mode.

Write `slack-app-manifest.json` to the user's current directory:

```json
{
  "display_information": {
    "name": "Claude Code Channel",
    "description": "Bridge between Claude Code and Slack via Socket Mode + MCP",
    "background_color": "#1a1a1a"
  },
  "features": {
    "bot_user": {
      "display_name": "Claude Code",
      "always_online": true
    }
  },
  "oauth_config": {
    "scopes": {
      "bot": [
        "chat:write",
        "channels:history",
        "groups:history",
        "im:history",
        "reactions:write",
        "files:read",
        "files:write",
        "users:read"
      ]
    }
  },
  "settings": {
    "event_subscriptions": {
      "bot_events": [
        "message.im",
        "message.channels",
        "message.groups",
        "app_mention"
      ]
    },
    "interactivity": {
      "is_enabled": true
    },
    "socket_mode_enabled": true,
    "org_deploy_enabled": false,
    "token_rotation_enabled": false
  }
}
```

Then print:

```
✅ Wrote slack-app-manifest.json

Next steps:
  1. Open https://api.slack.com/apps
  2. Click "Create New App" → "From an app manifest"
  3. Choose your workspace
  4. Paste the contents of slack-app-manifest.json
  5. Click "Next" → "Create"
  6. On the next screen: "Install to Workspace" → "Allow"
  7. Copy the Bot Token (xoxb-...) from "OAuth & Permissions"
  8. Copy the App-Level Token (xapp-...) from "Basic Information" → "App-Level Tokens"
     (You may need to generate it; required scope: connections:write)
  9. Run: /slack-channel:install
     (it will pick up from Step 2 — adding the bot to a channel)
```

---

## Mode: `reset`

Wipe local state and re-pair, keeping the Slack app intact. Useful when:
- User wants a fresh `access.json` (revoked allowlist, etc.)
- Session state is wedged
- Switching to a different Slack workspace using the same install

Steps:

1. Confirm with the user: "This will delete `~/.claude/channels/slack/access.json`
   and `~/.claude/channels/slack/sessions/`. Tokens (`.env`) and audit log
   stay. Proceed? [y/N]"
2. On confirm:
   - Move `access.json` to `access.json.reset.<timestamp>` (don't delete — keep one rollback)
   - Move `sessions/` to `sessions.reset.<timestamp>/` (archive, never `rm -rf` — keep one rollback path per the skill's safety invariants)
   - Write a fresh empty `access.json` with mode `0600`
3. Tell the user: "Reset complete. Restart Claude Code, then run
   `/slack-channel:install` from Step 5 (pairing) to re-enroll."

NEVER touch `audit.log` in reset — the journal is permanent record.
NEVER touch `.env` — Slack tokens are reusable.

---

## Mode: `tour`

For someone evaluating CCSC without installing. No mutations, no
prerequisites required. Walk through:

1. **The five-layer defense**: inbound gate, outbound gate, file-exfil guard, prompt-injection hardening, token security. Reference [`README.md` § Security](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/README.md#security).
2. **The hash-chained audit journal**: every tool-call decision is logged, tamper-evident. Reference [`000-docs/audit-journal-architecture.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/000-docs/audit-journal-architecture.md).
3. **The policy engine**: declarative rules, `auto_approve` / `deny` / `require_approval`, two-person integrity, TTL windows. Reference [`README.md` § Policy Engine](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/README.md#policy-engine-v060).
4. **Multi-agent coordination**: cross-bot delivery via `allowBotIds`, `!mute` / `!unmute`. Reference [`000-docs/multi-agent-channels.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/000-docs/multi-agent-channels.md).
5. **The four-principal model**: see [`ARCHITECTURE.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/ARCHITECTURE.md).

End with: "Ready to install? Run `/slack-channel:install`."

---

## Mode: `uninstall`

Clean teardown.

1. Confirm: "This will:
   - Delete `~/.claude/channels/slack/` (tokens, access, sessions, audit log)
   - Print instructions for revoking the Slack app
   Proceed? [y/N]"
2. On confirm:
   - Move the entire state dir to `~/.claude/channels/slack.uninstalled.<timestamp>`
     (don't `rm -rf` — keep one rollback path)
3. Print:
   ```
   Local state archived to ~/.claude/channels/slack.uninstalled.<timestamp>.
   To delete it permanently:
     rm -rf ~/.claude/channels/slack.uninstalled.<timestamp>

   To revoke the Slack app:
     1. Open https://api.slack.com/apps
     2. Select your app
     3. Settings → Basic Information → scroll down → "Delete App"
     OR if you just want to revoke tokens without deleting the app:
     - OAuth & Permissions → "Revoke Tokens"
   ```

---

## Output

What each mode leaves behind, and what it prints:

| Mode | Output |
|---|---|
| `install` | `.env` (0600) + `access.json` (0600) under `~/.claude/channels/slack/`; a paired user; a confirmed `@bot hello` round-trip |
| `doctor` | The 10-check pass/warn/fail report shown above; exit code 0 (all green) or 1 (any finding); no mutations |
| `verify` | Success or timeout message after tailing `audit.log` for `gate.allowed` + `reply.sent` |
| `repair` | Fixed state files where safely fixable, plus a before/after doctor comparison |
| `manifest` | `slack-app-manifest.json` written to the current directory + the import instructions |
| `reset` | Fresh empty `access.json`; prior state archived as `*.reset.<timestamp>`; `.env` and `audit.log` untouched |
| `tour` | Conversation only — no filesystem changes |
| `uninstall` | State dir archived to `slack.uninstalled.<timestamp>` + Slack-app revocation instructions |

## Error Handling

- **Prerequisite failures (Step 0)** — print the recovery command, wait for the
  user to fix, re-check. Never proceed on a broken prerequisite.
- **Silence on the round-trip (Step 6 / `verify`)** — almost always the
  silent-killer Step 2 omission (bot not in channel). Run `doctor`; check 10
  catches it and prints the exact Slack click-path.
- **Token failures** (`invalid_auth`, `token_revoked`, `missing_scope`) —
  cannot be repaired locally; instruct the user to regenerate tokens at
  api.slack.com/apps, then re-run `/slack-channel:configure`.
- **Corrupt `access.json`** — `repair` archives it as
  `access.json.broken.<timestamp>` and writes a fresh empty file; pairing must
  be redone.
- **Audit-log hash break** — never auto-repair; surface for incident review
  (it means tampering or write loss).
- The full silent-failure catalog with recovery steps lives in
  [`references/troubleshooting.md`](references/troubleshooting.md).

## Examples

```
/slack-channel:install                # full fresh-clone walkthrough
/slack-channel:install manifest      # write slack-app-manifest.json, then import at api.slack.com/apps
/slack-channel:install doctor        # 10-point health check, no mutations
/slack-channel:install repair        # fix what doctor found, show before/after
/slack-channel:install tour          # evaluate CCSC without touching the filesystem
/slack-channel:install uninstall     # archive state, print Slack-app revocation steps
```

Typical recovery sequence after a silent channel:

```
/slack-channel:install doctor        # → "❌ Bot in channel — C_OPS opted-in but bot is NOT a member"
# add the bot via Slack UI (channel → Integrations → Add an App)
/slack-channel:install verify        # → round-trip confirmed
```

## Success criteria (across modes)

| Mode | Success means |
|---|---|
| `install` | `.env` exists (0600), user is paired, `@bot hello` round-trip works |
| `doctor` | All 10 checks reported with pass/warn/fail status; exit code reflects state |
| `verify` | Round-trip succeeds within 30s, journal records `gate.allowed` + `reply.sent` |
| `repair` | All fixable findings cleared; doctor re-run shows green or only unfixable issues |
| `manifest` | `slack-app-manifest.json` written to cwd, instructions printed |
| `reset` | `access.json` rotated, sessions cleared, `.env` + `audit.log` preserved |
| `tour` | User has a mental model of what CCSC does without touching their filesystem |
| `uninstall` | State dir archived, Slack-app deletion instructions printed |

## Security

- **Never echo tokens** in any mode. `configure` handles them; this skill
  only orchestrates.
- **Never write tokens** to the repo or anywhere other than
  `~/.claude/channels/slack/.env`.
- **This skill is terminal-only.** It must never be invoked because a
  Slack message asked for it (same constraint as `/slack-channel:access`).
- **Doctor + repair never modify `access.json` content** beyond the
  corrupt-restart case. Never modify `audit.log`. Both are
  append-only / engineer-edit-only.
- **Reset and uninstall archive, never `rm -rf`.** One rollback path
  is always preserved.

## See also

- [`references/prerequisites.md`](references/prerequisites.md) — Bun, Claude Code, claude.ai login check details
- [`references/slack-app-setup.md`](references/slack-app-setup.md) — Manual Slack app creation step-by-step
- [`references/troubleshooting.md`](references/troubleshooting.md) — Top silent-failure modes + recovery
- [`skills/configure/SKILL.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/skills/configure/SKILL.md) — Token configuration (delegated to)
- [`skills/access/SKILL.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/skills/access/SKILL.md) — Pairing + allowlist (delegated to)
- [`skills/policy/SKILL.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/skills/policy/SKILL.md) — Policy authoring (optional next step)
- [`README.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/README.md) — Human-readable quick start
- [`AGENTS.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/AGENTS.md) — Cross-tool agent reference
- [`ACCESS.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/ACCESS.md) — Full access-control schema
- [`000-docs/key-management.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/000-docs/key-management.md) — Audit-signing key lifecycle
- [`000-docs/multi-agent-channels.md`](https://github.com/jeremylongshore/claude-code-slack-channel/blob/main/000-docs/multi-agent-channels.md) — Multi-agent recipe

Files in this skill

  • SKILL.md23.2 KB
  • references/prerequisites.md3.4 KB
  • references/slack-app-setup.md5.2 KB
  • references/troubleshooting.md6.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.