Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .cursor-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "iterable",
"owner": { "name": "Iterable" },
"metadata": {
"description": "Iterable's official skills for AI coding assistants — mobile SDK integration guidance.",
"version": "0.1.0"
},
"plugins": [
{
"name": "iterable-sdk",
"source": "./",
"description": "Iterable mobile SDK skill"
}
]
}
10 changes: 10 additions & 0 deletions .cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "iterable-sdk",
"version": "0.1.0",
"description": "Reliable Iterable mobile SDK integration guidance for AI coding assistants — version-pinned snippets + silent-failure pitfalls. Android today; iOS/RN/Web later.",
"author": { "name": "Iterable" },
"repository": "https://github.com/Iterable/iterable-sdk-skill",
"license": "MIT",
"keywords": ["iterable", "android", "sdk", "mobile", "push", "in-app"],
"skills": ["./iterable-android"]
}
57 changes: 57 additions & 0 deletions .github/workflows/validate-plugins.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Validate plugin manifests

# Lightweight gate for Cursor/Claude plugin packaging — no Java/Kotlin needed.

on:
push:
branches: [main]
paths:
- .cursor-plugin/**
- .claude-plugin/**
- mcp.json
- .mcp.json
- pipeline/src/validate-plugins.ts
- pipeline/package.json
- .github/workflows/validate-plugins.yml
pull_request:
branches: [main]
paths:
- .cursor-plugin/**
- .claude-plugin/**
- mcp.json
- .mcp.json
- pipeline/src/validate-plugins.ts
- pipeline/package.json
- .github/workflows/validate-plugins.yml
workflow_dispatch:

permissions:
contents: read

jobs:
validate-plugins:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
cache-dependency-path: pipeline/pnpm-lock.yaml

- name: Install pipeline deps
working-directory: pipeline
run: pnpm install --frozen-lockfile

- name: Validate plugin manifests
working-directory: pipeline
run: pnpm validate:plugins
5 changes: 4 additions & 1 deletion .mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
"mcpServers": {
"context7": {
"type": "http",
"url": "https://mcp.context7.com/mcp"
"url": "https://mcp.context7.com/mcp",
"headers": {
"CONTEXT7_API_KEY": "${env:CONTEXT7_API_KEY}"
}
}
}
}
43 changes: 32 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,30 @@ callback, and more).

This installs the skill and wires up its documentation source in one step.

**Cursor (or a manual install)** — symlink the skill directory into your
assistant's skills folder:
**Cursor** — install it as a plugin, which wires up the skill **and** its
documentation source (the Context7 MCP server) in one step. Cursor 3.9+ supports
Agent Skills and plugins natively.

Clone the repo and symlink it into Cursor's local-plugins folder, then restart
Cursor (or reload the window):

```bash
git clone --depth 1 git@github.com:Iterable/iterable-sdk-skill.git ~/iterable-skills
ln -s ~/iterable-skills/iterable-android ~/.cursor/skills/iterable-android
git clone --depth 1 https://github.com/Iterable/iterable-sdk-skill.git ~/iterable-skills
ln -s ~/iterable-skills ~/.cursor/plugins/local/iterable-sdk
```

A Cursor team admin can instead publish it to the whole org via **Dashboard →
Plugins → Add Marketplace → Import from Repo**, pointing at this repo.

> **Just the skill, no plugin?** Cursor 3.9+ auto-loads Agent Skills from
> `~/.cursor/skills/` — symlink only the skill directory:
> `ln -s ~/iterable-skills/iterable-android ~/.cursor/skills/iterable-android`.
> This skips the doc source; to add it, paste this into your browser to install
> the Context7 MCP server:
> ```
> cursor://anysphere.cursor-deeplink/mcp/install?name=context7&config=eyJ1cmwiOiJodHRwczovL21jcC5jb250ZXh0Ny5jb20vbWNwIn0=
> ```

Either way, the skill activates automatically whenever you mention Iterable: it
loads its always-on rules and `PITFALLS.md`, then pulls the documentation for
whatever feature you're working on (see [How it works](#how-it-works)).
Expand All @@ -39,15 +55,16 @@ The skill carries a copy of the Iterable documentation inside it
(`iterable-android/snapshot/`), so it always has the docs on hand — even offline.
This snapshot is the active source today.

The Claude Code plugin also connects to [Context7](https://context7.com), a
service that hosts docs for AI assistants to query on demand. That connection is
bundled and ready, but stays dormant until Iterable's curated library is
published there; once it is, the skill fetches the latest docs live, with the
snapshot as its fallback. No reinstall needed when that happens.
The plugin (both Claude Code and Cursor) also connects to
[Context7](https://context7.com), a service that hosts docs for AI assistants to
query on demand. That connection is bundled and ready, but stays dormant until
Iterable's curated library is published there; once it is, the skill fetches the
latest docs live, with the snapshot as its fallback. No reinstall needed when
that happens.

> Context7 works without an API key at a lower rate limit. To raise it, get a
> free key at [context7.com](https://context7.com) and add it as a
> `CONTEXT7_API_KEY` header on the `context7` MCP server.
> free key at [context7.com](https://context7.com) and set the `CONTEXT7_API_KEY`
> environment variable — the bundled MCP config forwards it automatically.

## What it covers

Expand All @@ -67,6 +84,10 @@ polished/ the docs in agent-ready form (what gets published to Context
pipeline/ tooling that builds polished/ from sources/, CI-gated
sources/ raw Iterable docs, fetched at pinned commits
context7.json Context7 manifest
.claude-plugin/ Claude Code plugin + marketplace manifests
.cursor-plugin/ Cursor plugin + marketplace manifests
mcp.json Context7 MCP server (Cursor plugin auto-discovery)
.mcp.json same config (Claude Code auto-discovery; kept in sync by CI)
```

## Staying current
Expand Down
34 changes: 19 additions & 15 deletions iterable-android/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ human readers but omit several silent-failure traps documented in
Do this **first**, before Preflight and before any edits. The SDK covers many
features — push, in-app, mobile inbox, embedded, deep links, event tracking,
user profiles. Do **not** assume the developer wants all of them, and do **not**
start implementing on a guessed scope. Ask which features they want via
`AskUserQuestion`. Set `multiSelect: true` so they can pick several, and offer
**at most 4 options** (the tool rejects more than 4 per question) — group the
long tail under one bucket, e.g.:
start implementing on a guessed scope. Ask which features they want. If your
host exposes an interactive multi-select question tool (in Claude Code,
`AskUserQuestion` with `multiSelect: true`; in Cursor, `AskQuestion` with
`allow_multiple: true`), use it and offer **at most 4 options** (those tools
reject more than 4 per question); otherwise ask in plain text. Either way, group
the long tail under one bucket, e.g.:

- Push notifications (FCM)
- In-app messages
Expand Down Expand Up @@ -88,17 +90,19 @@ pauses for a missing input is **always better** than one that compiles by
faking the input — the latter ships a broken or misleading state that looks
done.

> **Always ask via selectable options, not prose — every time.** For *any*
> question to the developer — the Step 0 scope question, the inputs below, the
> identity model, region, and "what would you like to do next?" — use the
> `AskUserQuestion` tool so they get interactive choices, not a plain-text list
> they must answer by typing. Offer realistic options with a short description
> each (e.g. identity: "Stable per-install UUID via `setUserId`" / "Account
> email via `setEmail`"; region: "US" / "EU"). **Offer at most 4 options per
> question** — the tool rejects more than 4 and the call fails with an
> "invalid parameters" error. If you have more than 4, group them or split into
> a second question. Only fall back to plain text if the question genuinely has
> no enumerable options.
> **Prefer selectable options over prose — every time.** For *any* question to
> the developer — the Step 0 scope question, the inputs below, the identity
> model, region, and "what would you like to do next?" — if your host exposes an
> interactive question tool (in Claude Code, `AskUserQuestion`; in Cursor,
> `AskQuestion`), use it so they get interactive choices instead of a plain-text
> list they must answer by typing. Offer realistic options with a short
> description each (e.g. identity: "Stable per-install UUID via `setUserId`" /
> "Account email via `setEmail`"; region: "US" / "EU"). With those tools,
> **offer at most 4 options per question** — they reject more than 4 and the
> call fails with an "invalid parameters" error; if you have more than 4, group
> them or split into a second question. If no such tool is available, ask in
> plain text with the options clearly enumerated. Only skip options entirely if
> the question genuinely has none.

| Input | Needed when | If missing |
|---|---|---|
Expand Down
11 changes: 11 additions & 0 deletions mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"mcpServers": {
"context7": {
"type": "http",
"url": "https://mcp.context7.com/mcp",
"headers": {
"CONTEXT7_API_KEY": "${env:CONTEXT7_API_KEY}"
}
}
}
}
3 changes: 2 additions & 1 deletion pipeline/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"fetch:sources": "tsx src/fetch.ts",
"polish:a": "tsx src/polish-layer-a.ts",
"validate:polished": "tsx src/validate-polished.ts",
"validate:plugins": "tsx src/validate-plugins.ts",
"check:snippets": "tsx src/check-snippets.ts",
"lint:chunking": "tsx src/lint-chunking.ts",
"recompute:manifest": "tsx src/recompute-manifest.ts",
Expand All @@ -20,7 +21,7 @@
"eval:run": "tsx src/eval-run.ts run",
"eval:report": "tsx src/eval-report.ts",
"typecheck": "tsc --noEmit",
"check:all": "pnpm typecheck && pnpm validate:polished && pnpm snapshot:verify && pnpm check:snippets && pnpm lint:chunking"
"check:all": "pnpm typecheck && pnpm validate:polished && pnpm validate:plugins && pnpm snapshot:verify && pnpm check:snippets && pnpm lint:chunking"
},
"devDependencies": {
"@types/node": "^22.7.0",
Expand Down
Loading
Loading