Claude-code-plugins-plus-skills grammarly-upgrade-migration
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/grammarly-pack/skills/grammarly-upgrade-migration" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-grammarly-upgrade-migration && rm -rf "$T"
manifest:
plugins/saas-packs/grammarly-pack/skills/grammarly-upgrade-migration/SKILL.mdsource content
Grammarly Upgrade & Migration
Overview
Grammarly exposes versioned APIs for writing analysis, AI detection, and plagiarism checking. Each API family versions independently — the Writing Score API may be on v2 while AI Detection remains on v1. Tracking these versions matters because Grammarly deprecates older API versions on a fixed timeline, and the response schemas differ significantly between versions (score breakdowns, suggestion categories, and confidence thresholds all change). Missing a version cutover means silent failures or degraded writing feedback quality.
Version Detection
const GRAMMARLY_BASE = "https://api.grammarly.com/ecosystem/api"; interface GrammarlyVersionMap { scores: string; aiDetection: string; plagiarism: string; latestAvailable: Record<string, string>; } async function detectGrammarlyVersions(apiKey: string): Promise<GrammarlyVersionMap> { const endpoints = { scores: `${GRAMMARLY_BASE}/v2/scores`, aiDetection: `${GRAMMARLY_BASE}/v1/ai-detection`, plagiarism: `${GRAMMARLY_BASE}/v1/plagiarism`, }; const versions: Record<string, string> = {}; for (const [api, url] of Object.entries(endpoints)) { const res = await fetch(url, { method: "HEAD", headers: { Authorization: `Bearer ${apiKey}` }, }); versions[api] = res.headers.get("x-grammarly-api-version") ?? "unknown"; const deprecated = res.headers.get("x-grammarly-deprecated"); if (deprecated) console.warn(`${api} endpoint deprecated: ${deprecated}`); } return { scores: "v2", aiDetection: "v1", plagiarism: "v1", latestAvailable: versions }; }
Migration Checklist
- Audit codebase for all
endpoint referencesapi.grammarly.com - Map each endpoint to its current API version (v1 vs. v2)
- Check if Writing Score v2 response includes new sub-score categories
- Verify AI Detection confidence threshold changes between versions
- Update plagiarism check response parser if source attribution format changed
- Test OAuth token flow — scope names may differ between API versions
- Validate suggestion category enums (clarity, engagement, delivery, correctness)
- Update error handling for new rate limit headers in v2
- Check if text submission size limits changed between versions
- Run A/B comparison of v1 vs. v2 scores on sample corpus to verify parity
Schema Migration
// Grammarly scores response changed from flat scores to structured breakdown interface OldScoreResponse { overall: number; correctness: number; clarity: number; engagement: number; delivery: number; } interface NewScoreResponse { overall: { score: number; label: string }; dimensions: Array<{ name: "correctness" | "clarity" | "engagement" | "delivery"; score: number; label: string; suggestions: Array<{ category: string; message: string; severity: "critical" | "warning" | "info" }>; }>; metadata: { wordCount: number; readingTime: number; apiVersion: string }; } function migrateScoreResponse(old: OldScoreResponse): NewScoreResponse { const toLabel = (s: number) => (s >= 80 ? "Strong" : s >= 60 ? "Good" : "Needs work"); return { overall: { score: old.overall, label: toLabel(old.overall) }, dimensions: (["correctness", "clarity", "engagement", "delivery"] as const).map((name) => ({ name, score: old[name], label: toLabel(old[name]), suggestions: [], })), metadata: { wordCount: 0, readingTime: 0, apiVersion: "v1-migrated" }, }; }
Rollback Strategy
class GrammarlyClient { private versionMap: Record<string, string> = { scores: "v2", aiDetection: "v1", plagiarism: "v1" }; private fallbackMap: Record<string, string> = { scores: "v1", aiDetection: "v1", plagiarism: "v1" }; constructor(private apiKey: string) {} async checkText(text: string, api: "scores" | "aiDetection" | "plagiarism"): Promise<any> { const version = this.versionMap[api]; try { const res = await fetch(`https://api.grammarly.com/ecosystem/api/${version}/${api}`, { method: "POST", headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json" }, body: JSON.stringify({ text }), }); if (!res.ok) throw new Error(`Grammarly ${api} ${res.status}`); return await res.json(); } catch (err) { const fallback = this.fallbackMap[api]; if (fallback !== version) { console.warn(`Falling back ${api} from ${version} to ${fallback}`); this.versionMap[api] = fallback; return this.checkText(text, api); } throw err; } } }
Error Handling
| Migration Issue | Symptom | Fix |
|---|---|---|
| Score dimension renamed | Response missing , has instead | Update parser to map new dimension names to internal schema |
| API version sunset | on v1 scores endpoint | Migrate all calls to v2 and update response parsing |
| OAuth scope mismatch | after upgrading API version | Re-authorize with scopes matching new version requirements |
| Text length limit changed | on previously working requests | Chunk text into smaller segments per new version limits |
| Rate limit structure changed | without header | Switch to header or implement exponential backoff |
Resources
Next Steps
For CI pipeline integration, see
grammarly-ci-integration.