Skip to content
Back to skills

Resize

ASecurity

Use when resizing or reformatting a CoDesign / CE.SDK design to a new size or aspect ratio (ig-post ↔ ig-square ↔ ig-story ↔ widescreen, or a custom W×H); when building the size/format editions of a design from an existing design or from a brief; or when a reformatted edition has a dead void / letterboxed empty band, drifted or broken margins, off-canvas or clipped elements, mis-scaled decoration, or a stretched / squished layout. Also for animated / video designs: timed blocks, animations, c...

  • 39 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 29, 2026
ai-agentsgoapi

Works with

  • cli
  • api

Security analysis

A100/100

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

Scanned September 29, 2026

npx -y skills add imgly/codesign --skill resize --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Resize?

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

Security grade badge for Resize
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/imgly-resize/badge)](https://www.skillsdirectory.com/skills/imgly-resize)

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: resize
description: >-
  Use when resizing or reformatting a CoDesign / CE.SDK design to a new size or aspect ratio
  (ig-post ↔ ig-square ↔ ig-story ↔ widescreen, or a custom W×H); when building the size/format
  editions of a design from an existing design or from a brief; or when a reformatted
  edition has a dead void / letterboxed empty band, drifted or broken margins, off-canvas or
  clipped elements, mis-scaled decoration, or a stretched / squished layout. Also for animated /
  video designs: timed blocks, animations, captions, and audio carried into each edition.
---

# resize

Point this skill at the current design and one or more target formats, and it produces or repairs the size/format editions by **re-composing** each for its new aspect. It operates on the **latest revision of the current design** (re-ground via `list()` / `history()` if unsure — handbook §1); each produced format is a new revision (a non-destructive fork of the source). Single home for the resize workflow, the re-composition technique, and the reformat **no-goes**.

Builds on two things it does _not_ restate: handbook §6.3 Hierarchy + §6.4 Composition & grid (margins as % of the shortest dimension, the spacing ladder, balanced whitespace / no dead void, nothing off-canvas) and §6.1.2 type-scale bands; and the handbook's engine mechanics (the `text.font` declaration, auto-reshape, `loadResources`). For the _same-aspect, translate-the-strings_ case, use the `localize` skill instead.

**Scoring:** reformatting is judged with the `judge` skill — every applicable axis ≥ 8. A dead void, a stretched element, a broken margin, or anything off-canvas fails **composition**.

## Reference files (load on demand)

This `SKILL.md` holds the always-needed spine: inputs, the localize-vs-resize distinction, core
principle, workflow, the re-composition routine, the no-goes, and the **resize composition
checklist** (end of this file). Per-format data lives in the **`formats` skill** — exact W×H,
aspect, and platform safe zones, one file per format: `../formats/SKILL.md` for the index,
`../formats/<id>.md` for the format(s) you're building.

Building several editions of one design, or re-applying a fix to editions you already built: the
**master → editions** workflow, `editions.md`. For an animated or video design,
read **Video and animated designs** below as well.

## Localize vs resize — snap vs re-compose

**Format is the outer distinction: a different format is a different layout; a different language is the same layout with different strings.**

- **localize** — _same_ canvas: change words + the script's font, then **snap every block
  back to the reference geometry**. Any geometry deviation is drift.
- **resize** — _new_ canvas: the geometry **must** change. You **re-derive** the layout
  from the design system on the new canvas. Here "drift" is not "moved from the reference" — it's
  re-composition that **loses the design's identity** or leaves the canvas **unbalanced**.

Never confuse the two: don't snap a resize (that reproduces the source's proportions on the wrong
canvas → a dead void), and don't re-compose a localization (that's how localized sets drift).

## Inputs — what to resize, and into what

Two inputs, resolved before any work by the intake contract — derive, ask once, echo, invent
nothing: `../handbook/intake.md`. **R** = required, no default; **A** =
ask if underived; **D** = defaultable.

| Parameter      | Kind    | Chips / values                                                                                       | Default                               |
| -------------- | ------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------- |
| target formats | **R**   | `Instagram post` · `Instagram story` · `LinkedIn` · `Widescreen` · `A4 print` · `Custom W×H` · Other | none                                  |
| source design  | derived | —                                                                                                    | latest revision of the current design |

**1. The source** — the latest revision of the current design. Every target format is re-composed _from_ that source — **never resize a resize** (don't chain: fork each format from the native source revision). If the user asks to resize an earlier design, find it via `list()` and its latest revision via `history()` first. If the request is a brief (no design exists yet), **build the native base first** at the brief's canvas per handbook §6, preview and sanity-check it, then treat that revision as the source.

**2. The formats** — named formats (`ig-post`, `ig-square`, `ig-story`, `widescreen`, and the
rest of the `formats` skill's index) or a custom `W×H`. Resolve each named format's exact size,
aspect, and safe zones from the **`formats` skill** (`../formats/<id>.md`).

If the format set is not stated, **ask** — _**Which sizes do you need?**_ — with the chips above;
a format set has no sensible default, so never pick one for the user. Then echo the resolved set
("1 design → 3 formats") before building.

## Core principle — re-compose for the aspect, preserve the system

A new format is a **new layout**. Re-lay it out for the new canvas while holding the **design
system** constant:

- **Invariant (carry to every format):** palette + the single accent; type-scale _bands_ (headline
  / body / caption as % of page height, §6.1.2); margin ratio (§6.4: 8–12% of the shortest side);
  spacing scale (§6.4 ladder); hierarchy order (§6.3); the **group hierarchy** (carry the source's
  grouping — re-group each cluster after re-composition, don't ship a flattened tree); the **region
  of interest** of every image/video fill (the subject stays framed — see the ROI section below);
  each decoration's _role_ (a ghost quote stays a ghost quote); the **element inventory** (every
  content cluster on the source has a counterpart on the new canvas — see below); and the
  **appearance** of every block (fill/gradient, effect/blur stack, opacity, corner radius, stroke,
  shadow, and font weight — carried by forking, not re-created by eye).
- **Element inventory — nothing silently dropped.** A reformat re-composes; a tight format tempts
  you to drop elements, and the result reads "way too simplistic / missing elements from the
  original." Before finishing, **count**: every text cluster, image, chip, icon, and decoration on
  the source must have a counterpart on the new canvas. An element may be **omitted only
  deliberately** (it genuinely can't earn its place on a smaller canvas) — never by oversight, and
  the omission is a judgment you can name. Re-composing from an _impression_ of the source instead
  of its actual block list is how elements vanish.
- **Variable (re-derive per canvas):** absolute positions, absolute sizes, wrap width, line count,
  and how the content group is distributed vertically.

Never **stretch or squish** the whole design to force-fit (that distorts type and shapes — resize
by re-flow, not by scaling the canvas contents). Never leave a **lopsided void** (§6.4).

## Workflow

1. **Read the source's design system** (palette, accent, type bands, margin %, spacing rhythm,
   hierarchy) and **capture its layout as fractions** of the source canvas (positions/sizes as % of
   source W/H — see routine).
2. **Fork the source** per target format; **capture the region of interest** of every image/video
   fill first (ROI section below); **set the page** to the new W×H and resize the full-bleed
   background to cover it; then **restore each fill's ROI** so the same region of the image stays
   framed — never let a `Cover` fill silently re-center the subject on the new aspect.
3. **Re-derive from the new canvas:** margins = 8–12% of the new **shortest** dimension; type sizes
   from the new page **height** within their bands (§6.1.2); gaps from the spacing scale (§6.4).
4. **Proportional re-flow to fill, then balance.** Place blocks by their captured fractions × the
   new canvas (this fills a taller/wider canvas instead of letterboxing it), then **center or
   distribute the content group within the platform safe band** — no top-anchored void. (Safe-zone
   bounds and per-format guidance: the `formats` skill, `<id>.md`.)
5. **Scale decoration proportionally** to the canvas (a ghost glyph sized as % of page height grows
   on a taller format, shrinks on a shorter one) — keep its role and framing.
6. **Re-wrap & re-fit:** text re-wraps at the new column width; check **overflow / clipping /
   off-canvas**; adjust size _within its band_ only.
7. **Let the size/font write carry the rich text.** A `text.fontSize` / `text.font` /
   `text.lineHeight` write reshapes on its own and **preserves every per-character style range** —
   color, weight, style, case, decoration (handbook §6.1.3). A resize keeps the **same string**, so
   there is nothing to capture and replay. What still flattens the accent is a **whole-block**
   styling write: `getProps(id, ['text.color']).text.color` reads run 0 only, and writing it back as
   `text: { color }` collapses the whole block to run 0's formatting. Don't re-apply whole-block
   styling in a resize; if you must, pass the runs from `getProps(id, ['text.ranges'])` back as
   `text: { ranges: [...] }` in the same `setProps` — **every** run, not just the accent.
8. **`loadResources` → `preview` each format** (dead void / crop / tofu only show in the
   render; for a video design, at several `time`s — see below). Each format lands as its own revision — give each a `note` naming the format (e.g.
   "ig-story 1080×1920").

## Proportional re-composition routine

The resize analog of localize's snap-to-reference — but a **first pass, not the finish**. It fills
the new canvas proportionally (killing the letterbox void); you then clamp margins/bands/safe-zones
and **judge the balance in `preview`**. Resize is judgment-driven; there is no single deterministic
answer.

**Capture the source as fractions** (run on the source page):

```js
const page = engine.scene.getPages()[0];
const pg = await engine.design.getProps(page, ['width', 'height']);
const PW = pg.width,
  PH = pg.height;
const out = [];
async function walk(id) {
  const p = await engine.design.getProps(id, [
    'type',
    'width',
    'height',
    'position'
  ]);
  const r = {
    type: p.type, // the SHORT name: 'text', 'graphic', 'page'
    fx: p.position.x / PW,
    fy: p.position.y / PH,
    fw: p.width / PW,
    fh: p.height / PH
  };
  if (p.type === 'text') {
    const t = (await engine.design.getProps(id, ['text'])).text;
    r.s = t.string;
    // the size read carries its unit ('32px', always pixels) — parse for math
    r.ffs = parseFloat(t.fontSize) / PH; // fraction of page height
    r.lh = t.lineHeight;
    r.ls = t.letterSpacing;
    r.align = t.horizontalAlignment;
  }
  if (id !== page) out.push(r);
  for (const c of await engine.design.getChildren(id)) await walk(c);
}
await walk(page);
return { type: 'text', text: JSON.stringify({ PW, PH, blocks: out }, null, 1) };
```

**Apply to a forked source at the new size** (first pass, then clamp):

```js
const NW = 1080,
  NH = 1920; // target format
const SAFE_TOP = 250,
  SAFE_BOT = 320; // ig-story/reel UI chrome (authoritative per-format values: formats skill, <id>.md); 0 for post/square/widescreen
const page = engine.scene.getPages()[0];
await engine.design.setProps(page, { width: NW, height: NH });

// full-bleed background → cover the new page (don't reposition it proportionally)
let bg;
for (const id of await engine.design.getChildren(page)) {
  const p = await engine.design.getProps(id, ['type', 'width']);
  if (p.type === 'graphic' && p.width >= NW * 0.9) {
    bg = id;
    break;
  }
}
if (bg !== undefined) {
  const roi = await captureFill(bg); // BEFORE the aspect change — ROI section
  await engine.design.setProps(bg, {
    position: { x: 0, y: 0 },
    width: NW,
    height: NH
  });
  await reapplyFill(bg, roi); // keep the same image region framed on the new aspect
}
// ...and captureFill/reapplyFill around EVERY other image/video-fill block you resize.

// content blocks → position/size by captured fractions (fill), then you clamp + balance below
// for each captured fraction f matched to its block id:
//   await engine.design.setProps(id, { position: { x: f.fx*NW, y: f.fy*NH } }); // fills the taller canvas
//   if (f.fw) await engine.design.setProps(id, { width: f.fw*NW });
//   if (f.ffs) await engine.design.setProps(id, { text: { fontSize: clampToBand(f.ffs*NH) + 'pt' } });
//              // the unit is required; the write auto-reshapes, ranges preserved
//
// THEN, not optional:
//   • margins: ensure left/right within 8–12% of min(NW,NH); re-center centered blocks on NW.
//   • safe band: shift the whole content group so nothing vital sits above SAFE_TOP or below NH-SAFE_BOT.
//   • balance: if proportional spread looks airy/crowded, distribute the group evenly or center it —
//     preview and adjust. Scale decoration with the canvas; keep type within §6.1.2 bands.
return {
  type: 'text',
  text: 'first pass placed — now clamp margins/safe-zone/balance and preview'
};
```

Platform safe zones and per-format notes are in the `formats` skill — read `<id>.md` for each
target format.

## Preserve the region of interest (image / video / pattern fills)

Re-flowing positions is only half the job. A block backed by an **image or video fill** shows a
_window_ onto its source, and that window is recomputed when the block's frame **aspect** changes:
a `Cover` fill re-centers, a `Crop` fill exposes gaps. Left alone, the resize crops the **subject**
out of frame — the face, product, logo, or focal point that made the asset worth using. That is a
composition **and** craft failure, and it only shows in `preview`.

**The ROI is defined by** (all on the block, via the `api` skill): `contentFill.mode`
(`Crop | Cover | Contain`); for the auto modes, `contentFill.horizontalAlignment` +
`contentFill.verticalAlignment` (`Left|Center|Right`, `Top|Center|Bottom`) — this is the focal side
a `Cover` fill keeps; and for `Crop`, `crop.scaleRatio` + `crop.translationX/Y` (translation in
**% of the crop frame**) + `crop.rotation`, which pin an explicit window.

**Capture before the resize, re-apply after** — around every fill-bearing block, not just the
background:

```js
async function captureFill(id) {
  // image/video/pixel fills only
  const caps = await engine.design.getCapabilities(id);
  if (!caps.includes('contentFillMode')) return null;
  const cf = (await engine.design.getProps(id, ['contentFill'])).contentFill;
  const cap = {
    mode: cf.mode,
    hAlign: cf.horizontalAlignment,
    vAlign: cf.verticalAlignment
  };
  if (cap.mode === 'Crop') {
    const c = (await engine.design.getProps(id, ['crop'])).crop;
    cap.crop = {
      sr: c.scaleRatio,
      tx: c.translationX,
      ty: c.translationY,
      rot: c.rotation
    };
  }
  return cap;
}

async function reapplyFill(id, cap) {
  if (!cap) return;
  await engine.design.setProps(id, {
    contentFill: {
      mode: cap.mode,
      horizontalAlignment: cap.hAlign, // ROI focus for Cover/Contain
      verticalAlignment: cap.vAlign
    }
  });
  if (cap.mode === 'Crop' && cap.crop) {
    const c = cap.crop;
    await engine.design.setProps(id, {
      crop: { rotation: c.rot, translationX: c.tx, translationY: c.ty }
    });
    await engine.design.adjustCropToFillFrame(id, c.sr); // refill gaps, keep scale ≥ source
  }
}
```

**Order matters:** `captureFill` **before** you write the block's `width`/`height`, `reapplyFill`
**after**. `setProps(id, { crop: null })` forces the mode back to `Cover`, so never call it in a resize
path. Default to keeping the ROI **centered**; when the brief or the asset's metadata names a focal point, bias
`alignment` (Cover) or `translation` (Crop) toward it. `preview` each format and confirm the subject
is still framed — the render is the only place ROI drift shows.

**Crop invariants — the three properties every fill-in-a-frame must hold** (checked in `preview`;
they fail independently of ROI focus, and a shape frame — circle/rounded card — makes them easy to
miss):

1. **Full coverage, zero gap.** The fill covers its frame completely — no sliver of empty frame at
   any edge (the "gap at the top of the circle" defect). If the aspect change exposed a gap,
   `adjustCropToFillFrame` (Crop) or the Cover mode refills it; never leave the frame partly unfilled.
2. **Uniform scale — no stretch.** The image scales the **same on both axes**; a fill stretched to
   fit a new aspect (`Contain` into a non-matching frame, or a hand-set non-uniform crop) distorts
   the subject. Scale is one ratio, not two.
3. **Subject inside the window.** After 1–2, the focal point is still within the visible crop, not
   pushed out by the refill. This is the ROI check above — do it last, after coverage is guaranteed.

**Re-group after re-composition.** Resize moves and resizes loose blocks; before you finish, restore
the source's **group hierarchy** on the new canvas (handbook §4 "Group blocks & hierarchy") so each
format ships the same grouped, nested structure — not a flattened tree.

## Video and animated designs

Applies when the page has a `playback.duration`, blocks carry `playback.timeOffset` /
`playback.duration`, `animations`, an `audio` block, or a `captionTrack`. A resize re-composes the
**frame**; the **timeline** is invariant.

- **Fork, move, resize — never recreate a timed block.** A page resize keeps every block's
  `timeOffset`, `duration`, animations, audio and video trims; a re-created block loses all of them.
  Walk the whole tree, not just the page's children: timed blocks can sit under `track`s and
  captions under a `captionTrack` (the capture `walk` above already recurses).
- **Re-aim every entrance for the new layout.** `slide` / `pan` `direction` is the **direction of
  travel** in radians, y pointing down: `0` enters from the left moving right, `π/2` from the top
  moving down, `π` from the right, `3π/2` from the bottom moving up. A slide that entered from the
  photo's side on 16:9 crosses the photo once 9:16 stacks text under it — aim it along the new
  reading axis. Patch in place with the **same type**, which keeps the animation's duration:
  `setProps(id, { animations: { in: { type: 'slide', slide: { direction: (3 * Math.PI) / 2 } } } })`
  (a different type replaces the animation and resets its duration). The duration itself is not in
  the spec: `engine.block.setDuration(engine.block.getInAnimation(id), s) // engine.block: animation duration`.
- **Scaling entrances overdraw.** `zoom` / `grow` scale around the block's centre, so a
  full-height photo spills over its neighbours during the entrance — check the first frames after
  every re-layout.
- **Vertical safe zones hold for the whole timeline.** Story / reel / TikTok / Shorts UI covers the
  top and bottom bands (and the right edge on TikTok / Shorts) — `../formats/ig-story.md`,
  `../formats/tiktok.md`, `../formats/yt-shorts.md`. Captions and CTAs belong inside
  the safe band, and so does every block's position _mid-entrance_.
- **Captions carry their own frame, per caption.** Writing the first caption's position or size
  does not reach the others — set `position`, `width`, `height`, `caption/fontSize` on **every**
  caption, with `caption/automaticFontSizeEnabled` off (otherwise the text grows past its frame
  and over the layout). Keep the frame's bottom at or above `H − bottom safe band`.
- **A fill's region of interest moves over time.** Capture / re-apply the ROI as above, then check
  it at the start, middle and end of every video clip and of every `ken_burns` / `pan` move — a
  crop that frames the subject at 0 s can cut it at 3 s.
- **Preview at several `time`s, per edition.** One frame of a video proves nothing about the
  rest: `preview({ revision, time })` at the first frame, mid-entrance of each timed block, each
  hold, each caption change and the last second. Then `export({ format: 'mp4', blockId: page })`
  for the real motion. `preview` accepts `time` on a Video-mode scene only; if it answers "not a
  video scene", switch it once — `engine.scene.setMode('Video')`, move the page under the scene
  (`engine.design.appendChild(engine.scene.get(), page)`) and destroy the now-empty `stack`.

## No-goes / red flags

- **Dead void / letterboxing** — the headline failure: content clustered on one side of a
  taller/wider canvas, an empty band elsewhere. Fill or balance the group; respect safe zones.
- **Design drift** — re-composition that loses the design's identity: margins wander, the type
  scale or hierarchy shifts, the accent is misused, or decoration loses its role. Re-compose
  _within_ the system, don't redesign.
- **Stretch / squish** — scaling the canvas contents non-proportionally to force-fit. Never distort
  type or shapes; re-flow instead.
- **Broken margin rhythm** — margins not re-derived from the new shortest dimension, or asymmetric.
- **Off-canvas / clipping / overflow** — elements pushed past the new edges; decoration over-cropped;
  text overflowing the new column.
- **Decoration mis-scale** — a ghost glyph or accent kept at the source's absolute size instead of
  scaling with the canvas.
- **ROI drift** — a resized image/video fill re-centered and cropped the subject out of frame
  (face, product, logo, focal point). Capture and re-apply the fill's region of interest.
- **Missing elements** — the reformat re-composed from an impression and silently dropped source
  content ("too simplistic / elements missing from the original"). Inventory every source cluster
  against the new canvas; omit only deliberately, never by oversight.
- **Appearance drift** — a block's fill/gradient/effect/corner-radius/overlay differs from the
  source, or an overlay was added/removed the source didn't have (a cover that "became darker").
  Appearance is inherited by forking; don't re-create or restyle it by eye.
- **Crop broken** — a fill leaves a gap inside its frame, or was stretched non-uniformly. See the
  three crop invariants (coverage, uniform scale, subject-in-window).
- **Flattened hierarchy** — the resize dropped the source's group blocks and shipped a flat list of
  siblings. Re-group each cluster (nested where nested) on the new canvas.
- **Ignoring platform safe zones** — vital content under story/reel UI chrome.
- **Timeline drift (video)** — a timed block re-created instead of moved, an entrance still aimed
  across the old layout, or a caption left in the platform's UI band.
- **tofu / overflow at new wrap widths** — a new measure can push a glyph the font lacks, or wrap to
  a line count that overflows; only `preview` catches it.

## Before reporting done

Run the **resize composition checklist** below against each format's render, then close with a `judge` run per format (the `judge` skill; composition and hierarchy are the axes reformatting most often breaks). Score honestly — a residual void is a composition fail, say so.

### Resize composition checklist

Any "no" on an applicable line drops the axis below 8.

**Composition (§6.4)**

- [ ] Margins 8–12% of the new shortest dimension; on the shared grid / one alignment
- [ ] Whitespace balanced — **no lopsided dead void / letterbox**; content occupies or balances the canvas
- [ ] Consistent spacing scale (§6.4 ladder); nothing off-canvas, clipped, or overlapping
- [ ] Primary content within the format's safe band (per its `formats` file; story/reel: clear of top ~250px / bottom ~320px)

**Hierarchy (§6.3) & identity**

- [ ] One dominant element preserved; reading order intact; ≈3 levels
- [ ] Palette + single accent, type-scale bands (§6.1.2), and each decoration's role carried over
- [ ] Decoration scaled proportionally to the canvas (not the source's absolute size)
- [ ] Every image/video fill keeps its **region of interest** — subject/focal point still framed, nothing important cropped out or newly empty (checked in `preview`)
- [ ] Source's **group hierarchy** restored on the new canvas (grouped clusters, nested where nested — not a flat list)
- [ ] **Element inventory complete** — every source content cluster (text, image, chip, icon, decoration) has a counterpart on the new canvas; any omission is deliberate and nameable, not an oversight
- [ ] **Appearance parity** — every block's fill/gradient, effect/blur, opacity, corner radius, stroke, shadow, and font weight match the source; no overlay added or removed the source didn't have (checked against the source render)

**Craft / baseline**

- [ ] No stretch/squish; type re-fit within its band, not distorted
- [ ] **Crop invariants** hold on every fill-in-frame — full coverage (no gap), uniform scale (no stretch), subject inside the window (checked in `preview`)
- [ ] Zero tofu / overflow at the new wrap width, checked in `preview`
- [ ] Native export dimensions (the page's own pixel size)

**Video / animated (when the design has a timeline)**

- [ ] Every block's `timeOffset` / `duration`, animations and audio carried over — moved, not re-created
- [ ] Every entrance re-aimed for the new layout (`direction` = direction of travel); no block crosses the photo or leaves the safe band mid-entrance
- [ ] Every caption framed and sized on the new canvas, inside the safe band, auto font size off
- [ ] Checked at several `time`s (entrances, holds, caption changes, last second), not one frame

## Cross-references

- **`formats` skill** — per-format W×H, aspect, and platform safe zones (one file per format id).
- handbook §6.3 Hierarchy, §6.4 Composition & grid, §6.1.2 type-scale bands — the general composition criteria this skill operationalizes; §6.1.2's canvas table for type sizing by % of page height; and the handbook's engine mechanics (the `text.font` declaration, auto-reshape, `loadResources`, dirty-on-throw).
- **`judge`** — the acceptance gate every produced format must pass.
- **`localize`** — the sibling for _translating_ a design at the _same_ size (snap, don't re-compose).

Files in this skill

  • SKILL.md25.8 KB
  • editions.md2.4 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.