Agent skill

Migrating To Modifier Node

by rosuH in rosuH/EasyWatermark

A skill your agent uses to author new custom Jetpack Compose modifiers and migrate legacy ones from Modifier.composed { } to Modifier.Node + ModifierNodeElement<T.

Apache-2.0Auto-check passedMobile

Install Migrating To Modifier Node

skills CLI
$ npx skills add rosuH/EasyWatermark --skill migrating-to-modifier-node -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install rosuH/EasyWatermark migrating-to-modifier-node --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/rosuH/EasyWatermark.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/migrating-to-modifier-node .claude/skills/migrating-to-modifier-node && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
migrating-to-modifier-node
GitHub stars
1.9k
Used in
1 other repo
Token cost
~5k tokens
SKILL.md length
1,355 words
Files
2 (incl. references)
Skills in repo
28
Repo updated
First seen
Licence
Apache-2.0

At a glance

A skill your agent uses to author new custom Jetpack Compose modifiers and migrate legacy ones from Modifier.composed { } to Modifier.Node + ModifierNodeElement<T.

  • Author new custom Jetpack Compose modifiers and migrate legacy ones from Modifier.composed { } to Modifier.Node + ModifierNodeElement<T
  • SKILL.md covers When to use this skill, When NOT to use this skill, Prerequisites and Workflow, plus 6 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md
  • The developer mentions Modifier.composed

What it does

Migrating To Modifier Node is an agent skill from rosuH/EasyWatermark. Use this skill to author new custom Jetpack Compose modifiers and migrate legacy ones from Modifier.composed { } to Modifier.Node + ModifierNodeElement<T. Covers the persistent-node lifecycle (onAttach, onDetach, onReset, coroutineScope), the specialized node interfaces (DrawModifierNode, LayoutModifierNode, SemanticsModifierNode, PointerInputModifierNode, CompositionLocalConsumerModifierNode, LayoutAwareModifierNode, GlobalPositionAwareModifierNode, ObserverModifierNode, DelegatingNode, TraversableNode), why…

Its SKILL.md is about 5k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files, including reference files (for example `references/modifier-node-anatomy.md`).

It sits in Mobile, covering Android development. It works with Jetpack Compose. The repository describes itself as: 🔒 🖼 Securely, easily add a watermark to your sensitive photos. 安全、简单地为你的敏感照片添加水印,防止被人泄露、利用. The licence is Apache-2.0.

When your agent uses it

  • Author new custom Jetpack Compose modifiers and migrate legacy ones from Modifier.composed { } to Modifier.Node + ModifierNodeElement<T
  • The developer mentions Modifier.composed
  • Custom modifier
  • ModifierNodeElement

Example prompts

  • “rewriting our drawBehind helper”
  • “/migrating-to-modifier-node”

What it can do on your machine

Read from SKILL.md and the folder at commit 61223db. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are kotlin).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Links to these hosts (documentation or services it may open):

    • developer.android.com
    • medium.com
    • chrisbanes.me
    • speakerdeck.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Migrating To Modifier Node loads about 5k tokens when it runs, and up to ~8.5k if it reads all its reference files. Until then it costs about 229 tokens; SKILL.md has 1,355 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~229
When it runs · the whole SKILL.md, loaded when a task matches
~5k
With references · SKILL.md plus every file in references/, read only if the agent opens them
~8.5k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from rosuH/EasyWatermark at commit 61223db, republished under its Apache-2.0 licence (© rosuH). 1,355 words, ~4,952 tokens.

Download SKILL.mdSave it as .claude/skills/migrating-to-modifier-node/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.
name
migrating-to-modifier-node
description
Use this skill to author new custom Jetpack Compose modifiers and migrate legacy ones from Modifier.composed { } to Modifier.Node + ModifierNodeElement<T>. Covers the persistent-node lifecycle (onAttach, onDetach, onReset, coroutineScope), the specialized node interfaces (DrawModifierNode, LayoutModifierNode, SemanticsModifierNode, PointerInputModifierNode, CompositionLocalConsumerModifierNode, LayoutAwareModifierNode, GlobalPositionAwareModifierNode, ObserverModifierNode, DelegatingNode, TraversableNode), why ModifierNodeElement MUST be a data class for diffing, and the manual-invalidation knobs (invalidateDraw, invalidateMeasurement, invalidatePlacement, shouldAutoInvalidate). Use when the developer mentions Modifier.composed, custom modifier, ModifierNodeElement, Modifier.Node, "rewriting our drawBehind helper", node lifecycle, or sees Modifier.composed { } in a code review.
license
Apache-2.0. See LICENSE for complete terms.
metadata.author
Jaewoong Eum (skydoves)
metadata.keywords
jetpack-compose, performance, modifier-node, modifier-composed, custom-modifier, draw-modifier-node, layout-modifier-node, composition-local-consumer

Migrating to Modifier.Node — Persistent Nodes Over composed { }

Modifier.composed { } allocates a fresh composable scope per modifier per composition: it cannot be skipped, cannot be hoisted, and forces the parent to run on every recomposition. Modifier.Node is a persistent node diffed by ModifierNodeElement.equals() — created once on first apply, updated in place on subsequent applies. There is no per-recomposition allocation, no fresh composable scope, and no parent invalidation chain, which is why Android Developers describes the system as "designed from the ground up to be far more performant" than the legacy composed { } factory (see developer.android.com/develop/ui/compose/custom-modifiers). This skill teaches Claude how to author new modifiers as Modifier.Node and migrate legacy composed { } factories.

When to use this skill

  • A custom modifier currently uses Modifier.composed { } (search the module for Modifier.composed).
  • Authoring a new custom modifier from scratch — never start with composed { }.
  • A code review surfaces a composed { } factory.
  • The custom modifier needs a CoroutineScope (animation loop, debouncer), reads a CompositionLocal, participates in layout / drawing / pointer input, or tracks layout coordinates.
  • A @TraceRecomposition log shows the parent composable recomposing on every frame because a composed { } modifier is in the chain.

When NOT to use this skill

  • The "modifier" is actually a one-line composable wrapper that can stay a @Composable function — leave it alone.
  • The built-in modifier composition (Modifier.padding(...).clickable(...)) is sufficient, no custom node behavior needed.
  • The fix the developer needs is reordering an existing chain, not authoring a new node — see ../ordering-modifier-chains/SKILL.md.
  • The custom modifier reads a hot animation value via Modifier.composed { } only to feed a value-form modifier underneath — the underlying issue is a wrong-phase state read; see ../../recomposition/deferring-state-reads/SKILL.md.

Prerequisites

  • Compose UI 1.5+ — Modifier.Node and the specialized node interfaces are stable here.
  • Kotlin 2.0+ with org.jetbrains.kotlin.plugin.compose applied. Strong Skipping is on by default; non-skippable modifiers compound at scroll velocity.
  • A release build for any final measurement (skydoves hot take #5: debug builds run interpreted and lie).
  • For the full per-interface override surface and lifecycle diagram, read references/modifier-node-anatomy.md before authoring anything beyond a DrawModifierNode.

Workflow

  • 1. Identify every Modifier.composed { } factory in the module. Grep for Modifier.composed. Each match is one migration target. Note what the body does — remember, drawBehind, LaunchedEffect, a CompositionLocal read, a pointer handler — because that decides which specialized node interface(s) you need.

  • 2. Sketch the three pieces every Modifier.Node migration produces.

    • (a) The public extension — fun Modifier.foo(...): Modifier = this then FooElement(...). Same name and signature as the old composed { } factory.
    • (b) The ModifierNodeElement<T> data class — holds the parameters; implements create() (called once on first apply) and update(node: T) (called on subsequent applies).
    • (c) The Modifier.Node subclass — holds mutable state (var fields), implements one or more specialized node interfaces, and runs lifecycle hooks (onAttach, onDetach, onReset).
  • 3. Make the Element a data class. The compiler-synthesized equals()/hashCode() are how Compose decides whether to call update() vs leave the node alone. MUST be data class. A plain class falls back to referential equality, the diff thinks every apply is a new modifier, and update() is never called — your node holds stale parameters silently.

  • 4. Pick the right specialized node interface(s). A Modifier.Node is empty by itself; behavior comes from interfaces it implements. The common ones:

InterfaceUse when the modifier needs to …
DrawModifierNodedraw (replaces drawBehind/drawWithCache)
LayoutModifierNodemeasure/place (replaces layout { } and custom Layout)
SemanticsModifierNodecontribute to accessibility
PointerInputModifierNodehandle pointer / gesture input (replaces pointerInput)
CompositionLocalConsumerModifierNoderead a CompositionLocal from inside the node
LayoutAwareModifierNodeget notified when this node's size/coordinates change
GlobalPositionAwareModifierNodeget notified about position in the window/root
ObserverModifierNodeobserve arbitrary state reads with a custom observer (observeReads { ... })
DelegatingNodecompose multiple node behaviors by delegating to child nodes
TraversableNodewalk the modifier chain (parent/child traversal)

A node MAY implement several interfaces at once — e.g. DrawModifierNode + CompositionLocalConsumerModifierNode + LayoutAwareModifierNode. For complex multi-behavior modifiers, PREFERRED: compose smaller DelegatingNode children rather than one mega-node implementing five interfaces.

  • 5. Use the built-in coroutineScope for async work. Every Modifier.Node exposes a coroutineScope: CoroutineScope lazily tied to the node's attach/detach lifecycle. Launch animations, observers, debouncers there from onAttach(). MUST NOT create your own CoroutineScope inside onAttach — you will leak it past onDetach.

  • 6. Trigger re-runs explicitly when needed. When you mutate node state from inside update() or a coroutine and need a redraw / re-measure / re-place, call invalidateDraw(), invalidateMeasurement(), or invalidatePlacement(). By default, update() triggers an auto-invalidation; for fine control, override shouldAutoInvalidate = false and invalidate manually.

  • 7. Implement onAttach/onDetach/onReset for resource lifecycle. onAttach runs when the node joins the tree; onDetach when it leaves; onReset when the node is reused (only relevant inside lazy layouts). MUST release listeners, observers, and external subscriptions in onDetach.

  • 8. Verify migration: no Modifier.composed { } remains. Re-grep the module. If any composed { } calls survive, list them with rationale; otherwise the migration is complete.

Patterns

Pattern: migrate composed { drawBehind } to DrawModifierNode
kotlin
// WRONG (legacy)
fun Modifier.circle(color: Color): Modifier = composed {
    val computed = remember(color) { color.copy(alpha = 0.5f) }
    drawBehind { drawCircle(computed) }
}
// WRONG because: composed { } opens a fresh composable scope per parent recomposition; the modifier can never be skipped and forces the parent to recompose on every read it does inside.
kotlin
// RIGHT
private data class CircleElement(val color: Color) : ModifierNodeElement<CircleNode>() {
    override fun create(): CircleNode = CircleNode(color)
    override fun update(node: CircleNode) { node.color = color }
}

private class CircleNode(var color: Color) : Modifier.Node(), DrawModifierNode {
    override fun ContentDrawScope.draw() {
        drawCircle(color.copy(alpha = 0.5f))
        drawContent()
    }
}

fun Modifier.circle(color: Color): Modifier = this then CircleElement(color)

The data class Element gives equals() / hashCode() for free. When the caller passes the same color, equals() returns true and the node is left alone. When the color changes, update() mutates node.color in place — no allocation, no Composition invalidation in the parent.

Show full SKILL.md (535 more words)Show less
Pattern: coroutine work in a node (replace composed { LaunchedEffect })
kotlin
// WRONG (legacy)
fun Modifier.pulse(period: Long): Modifier = composed {
    val alpha = remember { Animatable(1f) }
    LaunchedEffect(period) {
        while (true) { alpha.animateTo(0.3f); alpha.animateTo(1f); delay(period) }
    }
    graphicsLayer { this.alpha = alpha.value }
}
// WRONG because: every parent recomposition allocates a new composable scope, and the LaunchedEffect's keying logic re-evaluates inside that scope.
kotlin
// RIGHT
private data class PulseElement(val period: Long) : ModifierNodeElement<PulseNode>() {
    override fun create(): PulseNode = PulseNode(period)
    override fun update(node: PulseNode) { node.period = period }
}

private class PulseNode(var period: Long) : Modifier.Node(), DrawModifierNode {
    private var alpha by mutableFloatStateOf(1f)

    override fun onAttach() {
        coroutineScope.launch {
            while (true) {
                animate(1f, 0.3f) { value, _ -> alpha = value; invalidateDraw() }
                animate(0.3f, 1f) { value, _ -> alpha = value; invalidateDraw() }
                delay(period)
            }
        }
    }

    override fun ContentDrawScope.draw() {
        drawContent()
        drawRect(Color.Black.copy(alpha = 1f - alpha), blendMode = BlendMode.DstIn)
    }
}

fun Modifier.pulse(period: Long): Modifier = this then PulseElement(period)

The node's built-in coroutineScope is cancelled automatically on onDetach. No leak, no manual DisposableEffect.

Pattern: read a CompositionLocal inside a node
kotlin
// WRONG (legacy)
fun Modifier.themedBorder(width: Dp): Modifier = composed {
    val tokens = LocalThemeTokens.current
    drawBehind { drawRect(tokens.outline, style = Stroke(width.toPx())) }
}
// WRONG because: every CompositionLocal read inside composed { } pins the modifier to a fresh scope per parent recomposition.
kotlin
// RIGHT
private data class ThemedBorderElement(val width: Dp) : ModifierNodeElement<ThemedBorderNode>() {
    override fun create(): ThemedBorderNode = ThemedBorderNode(width)
    override fun update(node: ThemedBorderNode) { node.width = width }
}

private class ThemedBorderNode(
    var width: Dp,
) : Modifier.Node(), DrawModifierNode, CompositionLocalConsumerModifierNode {
    override fun ContentDrawScope.draw() {
        val tokens = currentValueOf(LocalThemeTokens)
        drawContent()
        drawRect(tokens.outline, style = Stroke(width.toPx()))
    }
}

fun Modifier.themedBorder(width: Dp): Modifier = this then ThemedBorderElement(width)

CompositionLocalConsumerModifierNode exposes currentValueOf(local) from inside any node callback. Reads are tracked by the Draw invalidation list, not by a Composition restart scope, so changing LocalThemeTokens redraws the node without recomposing the parent.

Pattern: forgetting data class (the silent-stale-node bug)
kotlin
// WRONG
private class CircleElement(val color: Color) : ModifierNodeElement<CircleNode>() {
    override fun create() = CircleNode(color)
    override fun update(node: CircleNode) { node.color = color }
}
// WRONG because: not a data class -> equals() is referential -> every apply looks like a different element -> Compose tears down and recreates the node every time, OR the diff fails and update() is never called, leaving the node with the original color forever.
kotlin
// RIGHT
private data class CircleElement(val color: Color) : ModifierNodeElement<CircleNode>() {
    override fun create() = CircleNode(color)
    override fun update(node: CircleNode) { node.color = color }
}
Pattern: holding a reference to the calling composable
kotlin
// WRONG
private class HostingNode(val composer: Composer) : Modifier.Node() { /* ... */ }
// WRONG because: a Modifier.Node outlives any single composition pass; holding a Composer/composition-scoped object leaks it and invokes undefined behavior.
kotlin
// RIGHT — accept primitive/stable parameters; read CompositionLocals via CompositionLocalConsumerModifierNode if you need composition context.
private data class HostingElement(val tag: String) : ModifierNodeElement<HostingNode>() {
    override fun create() = HostingNode(tag)
    override fun update(node: HostingNode) { node.tag = tag }
}
private class HostingNode(var tag: String) : Modifier.Node() { /* ... */ }
Pattern: composing multiple behaviors with DelegatingNode
kotlin
// RIGHT — one public modifier, three small nodes delegated under one element
private data class CardEffectsElement(
    val color: Color,
    val onClick: () -> Unit,
) : ModifierNodeElement<CardEffectsNode>() {
    override fun create() = CardEffectsNode(color, onClick)
    override fun update(node: CardEffectsNode) {
        node.update(color, onClick)
    }
}

private class CardEffectsNode(
    color: Color,
    onClick: () -> Unit,
) : DelegatingNode() {
    private val background = delegate(BackgroundNode(color))
    private val click = delegate(ClickNode(onClick))

    fun update(color: Color, onClick: () -> Unit) {
        background.color = color
        click.onClick = onClick
    }
}

DelegatingNode is the canonical way to assemble multi-behavior modifiers without one node implementing every interface. The delegated children share the host's lifecycle.

Specialized node interfaces

Cheat sheet — full override surface and "use when" guidance lives in references/modifier-node-anatomy.md:

  • DrawModifierNode — implement ContentDrawScope.draw(). Replaces drawBehind/drawWithCache for custom modifiers.
  • LayoutModifierNode — implement MeasureScope.measure(...). Replaces Modifier.layout { }.
  • SemanticsModifierNode — implement SemanticsPropertyReceiver.applySemantics().
  • PointerInputModifierNode — implement onPointerEvent(...) and onCancelPointerInput().
  • CompositionLocalConsumerModifierNode — exposes currentValueOf(local) inside any node callback.
  • LayoutAwareModifierNode — onPlaced(coordinates) / onRemeasured(size).
  • GlobalPositionAwareModifierNode — onGloballyPositioned(coordinates).
  • ObserverModifierNode — wrap state reads with observeReads { ... } and react in onObservedReadsChanged().
  • DelegatingNode — delegate(otherNode) to compose behaviors.
  • TraversableNode — walk parents/children/descendants via the top-level extension functions on DelegatableNode: traverseAncestors(key, block), traverseChildren(key, block), traverseDescendants(key, block). The key parameter selects which traversable nodes participate; the descendants overload's block returns a TraverseDescendantsAction (continue / skip / cancel).

Lifecycle (short form — full diagram in references)

ModifierNodeElement.create()         // first apply only
        ↓
Modifier.Node.onAttach()             // node joins the tree; coroutineScope becomes valid
        ↓                            // (lives here across many parent recompositions)
ModifierNodeElement.update(node)     // each subsequent apply with !equals previous
        ↓                            // mutate node.var fields; auto-invalidates by default
Modifier.Node.onReset()              // optional: lazy-layout reuse
        ↓
Modifier.Node.onDetach()             // node leaves the tree; coroutineScope is cancelled

Mandatory rules

  • MUST prefer Modifier.Node for any new custom modifier — Modifier.composed { } is legacy.
  • MUST make ModifierNodeElement<T> a data class so the synthesized equals()/hashCode() drive the diff. Plain class silently breaks update().
  • MUST override update(node: T) to mutate node state in place. MUST NOT recreate the node from update().
  • MUST release subscriptions, listeners, and external resources in onDetach(). coroutineScope is cancelled for you; manual resources are not.
  • MUST NOT hold a reference to the Composer, the calling composable, the parent composition, or any composition-scoped object inside a Modifier.Node.
  • MUST NOT allocate a new CoroutineScope in onAttach — use the built-in coroutineScope property.
  • MUST NOT recommend Modifier.composed { } for new code. (Repo-wide rule from SPEC §8.)
  • PREFERRED: specialized node interfaces (DrawModifierNode, LayoutModifierNode, …) over a bare Modifier.Node.
  • PREFERRED: DelegatingNode over implementing more than ~3 specialized interfaces on a single node.
  • PREFERRED: override shouldAutoInvalidate = false and call invalidateDraw() / invalidateMeasurement() / invalidatePlacement() explicitly when fine-grained control matters; otherwise rely on the auto-invalidation triggered by update().

Verification

  • grep -R "Modifier.composed" <module>/src returns zero matches in the migrated module.
  • Every migrated modifier exposes a data class Element (search: class .*Element : ModifierNodeElement should be data class).
  • The compiler report (composables.txt) shows the parent composables that consume the migrated modifier are now restartable skippable (no composed-induced non-skip).
  • Layout Inspector → Recomposition Counts on the parent composable plateaus across animation frames driven by the modifier; only the node's draw/layout invalidation list ticks.
  • @TraceRecomposition (skydoves/compose-stability-analyzer) on the parent confirms in release + R8 + real device that the parent is not recomposed by the modifier's internal animation.
  • Resources allocated in onAttach (listeners, observers) are released in onDetach — verify with a leak canary pass.

References

© rosuH, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 1 other file (references) in .agents/skills/migrating-to-modifier-node of rosuH/EasyWatermark.

  • SKILL.md
  • references/modifier-node-anatomy.md

Open the folder on GitHubat commit 61223db

Used in 1 other repository

We found 1 copy of this SKILL.md (exact, near-identical or edited) in other folders, from 1 other GitHub owner. This page covers the copy in rosuH/EasyWatermark, which our catalogue first saw on October 7, 2026.

Compare with similar skills

Migrating To Modifier Node next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Migrating To Modifier Node compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Migrating To Modifier Node this skillrosuH/EasyWatermark1.9k1 repos~5kAutomated safety check: PassApache-2.0
Compose Performance AuditMoustachauve/WLED-Android1693 repos~1.7kAutomated safety check: PassApache-2.0
Compose UIMoustachauve/WLED-Android1693 repos~676Automated safety check: PassApache-2.0
Publish ReleaseAyuilos/Miffan182—~635Automated safety check: PassAGPL-3.0
Composewebview Documentationparkwoocheol/compose-webview103—~1.5kAutomated safety check: PassMIT
Compose Multiplatform Patternsmonta-app/ocpp-emulator1795 repos~2kAutomated safety check: PassApache-2.0

Similar skills

  • Compose Performance Audit

    Moustachauve/WLED-Android

    Audit and improve Jetpack Compose runtime performance from code review and architecture.

    169 GitHub starsUsed in 3 repos~1.7k tokens
    MobileAuto-check passed
  • Compose UI

    Moustachauve/WLED-Android

    Best practices for building UI with Jetpack Compose, focusing on state hoisting, detailed performance optimizations, and theming.

    169 GitHub starsUsed in 3 repos~676 tokens
    MobileAuto-check passed
  • Publish Release

    Ayuilos/Miffan

    Publish a GitHub release for this fork, with a bilingual changelog that separates fork-owned changes from changes introduced by upstream merges.

    182 GitHub stars~635 tokensUpdated yesterday
    MobileAuto-check passed
  • Composewebview Documentation

    parkwoocheol/compose-webview

    Manages MkDocs documentation site and API references for ComposeWebView.

    103 GitHub stars~1.5k tokensUpdated 1 mo ago
    MobileAuto-check passed
  • Compose Multiplatform Patterns

    monta-app/ocpp-emulator

    Compose Multiplatform and Jetpack Compose patterns for KMP projects — state management, navigation, theming, performance, and platform-specific UI.

    179 GitHub starsUsed in 5 repos~2k tokens
    MobileAuto-check passed
  • Android Development

    dpconde/claude-android-skill

    Create production-quality Android applications following Google's official architecture guidance and NowInAndroid best practices.

    336 GitHub stars~1.7k tokensUpdated 10 mo ago
    MobileAuto-check passed

More from rosuH/EasyWatermark

All 28 skills in this repo
  • Deferring State Reads

    rosuH/EasyWatermark

    A skill your agent uses to push frequently-changing Jetpack Compose state reads (scroll position, animation values, drag offsets) out of the Composition phase and down into Layout or Draw using…

    1.9k GitHub starsUsed in 1 repo~3.8k tokens
    Auto-check passed
  • Diagnosing Compose Stability

    rosuH/EasyWatermark

    A skill your agent uses to diagnose Jetpack Compose stability problems by enabling and reading the Compose Compiler Reports (classes.txt, composables.txt, composables.csv, module.json).

    1.9k GitHub starsUsed in 1 repo~3.3k tokens
    Auto-check passed
  • Generating Baseline Profiles

    rosuH/EasyWatermark

    A skill your agent uses to generate and measure Jetpack Compose Baseline Profiles end-to-end with the AGP 8.2+ Baseline Profile Generator module and the Macrobenchmark harness.

    1.9k GitHub starsUsed in 1 repo~5k tokens
    Auto-check passed
  • ML Kit Genai Prompt API

    rosuH/EasyWatermark

    Analyzes Android codebases to implement ML Kit GenAI Prompt API.

    1.9k GitHub starsUsed in 1 repo~1k tokens
    Auto-check passed
  • Stabilizing Compose Types

    rosuH/EasyWatermark

    A skill your agent uses to fix unstable Jetpack Compose types once a stability diagnosis has identified them.

    1.9k GitHub starsUsed in 1 repo~4.4k tokens
    Auto-check passed
  • A skill your agent uses to explain why the Compose compiler classified a class or composable parameter as stable, runtime, unknown, or unstable.

    1.9k GitHub starsUsed in 1 repo~4.3k tokens
    Auto-check passed

Works with

Questions about Migrating To Modifier Node

What does Migrating To Modifier Node do?

A skill your agent uses to author new custom Jetpack Compose modifiers and migrate legacy ones from Modifier.composed { } to Modifier.Node + ModifierNodeElement<T. Migrating To Modifier Node is an agent skill from rosuH/EasyWatermark.Node + ModifierNodeElement<T.

When should I use Migrating To Modifier Node?

Migrating To Modifier Node fits situations like: author new custom Jetpack Compose modifiers and migrate legacy ones from Modifier.composed { } to Modifier.Node + ModifierNodeElement<T; the developer mentions Modifier.composed; custom modifier; modifierNodeElement.

How do I install Migrating To Modifier Node in Claude Code?

Run `npx skills add rosuH/EasyWatermark --skill migrating-to-modifier-node -a claude-code`. Or copy the skill folder (.agents/skills/migrating-to-modifier-node in rosuH/EasyWatermark) into .claude/skills/migrating-to-modifier-node in your project. Claude Code loads it when a task matches its description.

How do I install Migrating To Modifier Node in Codex?

Run `npx skills add rosuH/EasyWatermark --skill migrating-to-modifier-node -a codex`. Or copy the skill folder (.agents/skills/migrating-to-modifier-node in rosuH/EasyWatermark) into .agents/skills/migrating-to-modifier-node in your project. Codex loads it when a task matches its description.

Can I use Migrating To Modifier Node in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add rosuH/EasyWatermark --skill migrating-to-modifier-node -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/migrating-to-modifier-node, .gemini/skills/migrating-to-modifier-node, .github/skills/migrating-to-modifier-node and .opencode/skills/migrating-to-modifier-node in your project.

What does Migrating To Modifier Node need to run?

SKILL.md names no scripts, command-line tools or credentials: Migrating To Modifier Node is instructions for the agent only.

Does Migrating To Modifier Node access the network?

SKILL.md names 4 domains. As links in the text: developer.android.com, medium.com, chrisbanes.me and speakerdeck.com. This is read from the text; nothing was executed.

Is Migrating To Modifier Node safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Migrating To Modifier Node use?

Migrating To Modifier Node is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Migrating To Modifier Node use?

About 5k tokens (SKILL.md is roughly 20k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 3.5k tokens, read only when the agent opens those files.

What are the alternatives to Migrating To Modifier Node?

Skills that share tags, products or a category with Migrating To Modifier Node: Compose Performance Audit (Moustachauve/WLED-Android, 169 stars), Compose UI (Moustachauve/WLED-Android, 169 stars), Publish Release (Ayuilos/Miffan, 182 stars) and Composewebview Documentation (parkwoocheol/compose-webview, 103 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Migrating To Modifier Node?

rosuH (a GitHub user) maintains it in rosuH/EasyWatermark, which has 1,894 GitHub stars. The repository holds 28 skills in this directory. The repository was last updated on October 6, 2026.

Source: rosuH/EasyWatermark on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.