Agent skill

Ply Engine

by TheRedDeveloper in TheRedDeveloper/ply-engine

Complete implementation and design guide for Ply. An agent skill from TheRedDeveloper/ply-engine.

ISCAuto-check passed

Install Ply Engine

skills CLI
$ npx skills add TheRedDeveloper/ply-engine --skill ply-engine -a claude-code

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

GitHub CLI
$ gh skill install TheRedDeveloper/ply-engine ply-engine --agent claude-code

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

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

Facts

Skill name
ply-engine
GitHub stars
506
Token cost
~10k tokens
SKILL.md length
3,626 words
Files
60
Skills in repo
1
Repo updated
First seen
Licence
ISC

At a glance

Complete implementation and design guide for Ply. An agent skill from TheRedDeveloper/ply-engine.

  • Works in 7 steps: Never hallucinate Ply APIs. If a method… → Always make sure you do → Respect feature gates. If an API… → …
  • A task involves ply-engine APIs
  • SKILL.md covers Part 1: Non-Negotiable Rules, Part 2: Feature Matrix, Part 3: App Skeleton and Part 4: Prelude, plus 4 more sections
  • Runs JavaScript, Java and Shell scripts from its folder; calls cursor

What it does

Ply Engine is an agent skill from TheRedDeveloper/ply-engine. Complete implementation and design guide for Ply. Use this skill whenever a task involves ply-engine APIs, plyx workflows, or building UI in Rust with Ply.

Its SKILL.md is about 10k tokens, which your agent loads only when the skill is triggered. The skill folder holds 63 other files (for example `.github/FUNDING.yml`, `.github/ISSUE_TEMPLATE/bug_report.md` and `.github/ISSUE_TEMPLATE/feature_request.md`).

It works with Rust. The repository describes itself as: Beautiful UIs in Rust. Cross-platform. Dead simple. The licence is ISC.

When your agent uses it

  • A task involves ply-engine APIs
  • Building UI in Rust with Ply

Example prompts

  • “/ply-engine”

Requirements

  • Node.js
  • A Bash shell

Workflow steps

7 steps, taken from the first numbered list in SKILL.md.

  1. Never hallucinate Ply APIs. If a method is not listed in this skill, do not invent it.
  2. Always make sure you do
  3. Respect feature gates. If an API requires a feature, make sure the feature is enabled before using it.
  4. Indentation policy for generated code
  5. Prefer explicit IDs for interactive elements and list items.
  6. Every element should have explicit sizing configured.
  7. Builder closures must use chain-return style by default.

What it can do on your machine

Read from SKILL.md and the folder at commit 52be132. 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

    Ships script files (JavaScript, Java and Shell, from the files we listed), which the agent can run.

    Shell commands in SKILL.md call:

    • cursor

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

  • Network

    No URLs in SKILL.md.

    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

Ply Engine loads about 10k tokens when it runs. Until then it costs about 42 tokens; SKILL.md has 3,626 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~42
When it runs · the whole SKILL.md, loaded when a task matches
~10k

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 TheRedDeveloper/ply-engine at commit 52be132, republished under its ISC licence (© TheRedDeveloper). 3,626 words, ~10,207 tokens.

Download SKILL.mdSave it as .claude/skills/ply-engine/SKILL.md (or your agent's skills folder). This skill also uses 59 other files; get the full folder from GitHub.
name
ply-engine
description
Complete implementation and design guide for Ply. Use this skill whenever a task involves ply-engine APIs, plyx workflows, or building UI in Rust with Ply.

Ply Engine

This part is the ultimate guide for writing Ply applications.

Part 1: Non-Negotiable Rules

  1. Never hallucinate Ply APIs. If a method is not listed in this skill, do not invent it.
  2. Always make sure you do:
rust
use ply_engine::prelude::*;
  1. Respect feature gates. If an API requires a feature, make sure the feature is enabled before using it.
  2. Indentation policy for generated code:
  • First, detect the existing indentation style and preserve it.
  • If style is unclear or you are generating a fresh snippet, default to 2-space indentation.
  • Never mix tabs and spaces in the same block.
  1. Prefer explicit IDs for interactive elements and list items.
  2. Every element should have explicit sizing configured.
  3. Builder closures must use chain-return style by default.
  • DO THIS:
    rust
    .layout(|l| l
      .direction(TopToBottom)
      .align(CenterX, Top)
      .gap(12)
      .padding(14)
    )
  • DO NOT DO THIS:
    rust
    .layout(|l| {
      l.direction(TopToBottom)
        .align(CenterX, Top)
        .gap(12)
        .padding(14);
      l
    })
  • The same rule applies to other builder closures such as overflow, border, scrollbar, floating, effect, shader, text_input, and accessibility.

Part 2: Feature Matrix

Ply crate features:

  • a11y (default): accessibility support
  • text-styling: inline styling and animation tags
  • tinyvg: TinyVG vector rendering
  • shader-build: shader build pipeline utilities for build.rs
  • built-in-shaders: built-in shader assets
  • net: HTTP + WebSocket APIs
  • net-json: JSON deserialization helpers for net responses
  • audio: macroquad audio re-exports
  • storage: cross-platform persistent storage API

Part 3: App Skeleton

This is the default project structure generated by plyx init:

rust
use ply_engine::prelude::*;

fn window_conf() -> macroquad::conf::Conf {
  macroquad::conf::Conf {
    miniquad_conf: miniquad::conf::Conf {
      window_title: "Hello Ply!".to_owned(),
      window_width: 800,
      window_height: 600,
      high_dpi: true,
      sample_count: 4,
      platform: miniquad::conf::Platform {
        webgl_version: miniquad::conf::WebGLVersion::WebGL2,
        ..Default::default()
      },
      ..Default::default()
    },
    draw_call_vertex_capacity: 100000,
    draw_call_index_capacity: 100000,
    ..Default::default()
  }
}

#[macroquad::main(window_conf)]
async fn main() {
  static DEFAULT_FONT: FontAsset = font!("assets/fonts/MyFont.ttf");
  let mut ply = Ply::new(&DEFAULT_FONT).await;

  loop {
    clear_background(BLACK);

    let mut ui = ply.begin();

    ui.element()
      .width(grow!())
      .height(grow!())
      .layout(|l| l.align(CenterX, CenterY))
      .children(|ui| {
        ui.text("Hello, Ply!", |t| t.font_size(32).color(0xFFFFFF));
      });

    ui.show().await;
    next_frame().await;
  }
}

Of course this is often expanded with different files that contain different functionality and components.

Part 4: Prelude

Everything below is re-exported from ply_engine::prelude in this repository.

4.1 Core Types
  • Ply
  • Ui
  • ElementBuilder
  • NoId
  • WithId
  • Id
  • GraphicAsset
  • FontAsset
  • ShaderAsset
  • Lerp
  • PointerData
  • PointerState
  • all easing functions from crate::easing::*
4.2 Utilities
  • render_to_texture
  • set_shader_source
4.3 Sizing Macros
  • grow!
  • fit!
  • fixed!
  • percent!
  • ratio!
4.4 Globbed Enums
  • AlignX::{Left, CenterX, Right}
  • AlignY::{Top, CenterY, Bottom}
  • BorderPosition::{Outside, Middle, Inside}
  • LayoutDirection::{LeftToRight, TopToBottom}
4.5 Type-Only Re-exports
  • WrapMode
  • AccessibilityRole
4.6 Feature-Gated Re-exports
  • built-in-shaders: *
  • net: net, WsMessage
  • storage: Storage
  • text-styling: styling
  • audio: *
4.7 Always Re-exported Modules and Helpers
  • jobs module
  • full macroquad::prelude::*
  • Ply Color
  • MacroquadColor alias for macroquad color
  • macroquad crate
  • CursorIcon

Important: because full macroquad::prelude::* is re-exported, any macroquad-prelude function/type is also in scope when using Ply prelude.

Part 5: Full API Reference (Ply, Ui, Builders)

5.1 Ply Core Methods

Construction and lifecycle:

  • Ply::new(default_font).await
  • Ply::new_headless(dimensions)
  • begin() -> Ui
  • eval() -> Vec<RenderCommand>
  • show().await

Pointer, focus, and cursor:

  • pointer_over(id) -> bool
  • pointer_over_ids() -> Vec<Id>
  • focused_element() -> Option<Id>
  • set_focus(id)
  • clear_focus()
  • is_pressed(id) -> bool
  • is_just_pressed(id) -> bool
  • is_just_released(id) -> bool
  • set_cursor(CursorIcon)
  • get_cursor() -> CursorIcon

Text input state by ID:

  • get_text_value(id) -> &str
  • set_text_value(id, value)
  • get_cursor_pos(id) -> usize
  • set_cursor_pos(id, pos)
  • get_selection_range(id) -> Option<(usize, usize)>
  • set_selection(id, anchor, cursor)

Layout, bounds, scroll:

  • set_layout_dimensions(dimensions)
  • pointer_state(position, is_down)
  • update_scroll_containers(drag_scrolling_enabled, scroll_delta, delta_time)
  • bounding_box(id) -> Option<BoundingBox>
  • scroll_container_data(id) -> Option<ScrollContainerData>
  • set_scroll_position(id, position)

Debug and performance:

  • set_debug_mode(bool)
  • set_debug_view_width(f32)
  • is_debug_mode() -> bool
  • set_culling(bool)
  • max_element_count(u32)
  • max_measure_text_cache_word_count(u32)
  • set_measure_text_function(|text, config| -> Dimensions)
5.2 Ui Methods
  • element() -> ElementBuilder
  • text(text, |TextConfig| ... )
  • scroll_offset() -> Vector2
  • inline-state queries for current open element context:
    • hovered()
    • pressed()
    • just_pressed()
    • just_released()
    • focused()

Ui dereferences to Ply, so all Ply methods are callable from ui too.

5.3 ElementBuilder Methods (Complete)

Layout and sizing:

  • width(Sizing)
  • height(Sizing)
  • contain(f32)
  • cover(f32)
  • layout(|LayoutBuilder| ...)

Visuals:

  • background_color(color)
  • corner_radius(f32 | (f32, f32, f32, f32))
  • border(|BorderBuilder| ...) (multiple possible)
  • overflow(|OverflowBuilder| ...)
  • image(ImageSource)
  • effect(shader_asset, |ShaderBuilder| ...)
  • shader(shader_asset, |ShaderBuilder| ...)
  • rotate_visual(|VisualRotationBuilder| ...)
  • rotate_shape(|ShapeRotationBuilder| ...)

Structure and identity:

  • id(id_like) (NoId -> WithId)
  • get_id() -> &Id
  • floating(|FloatingBuilder| ...)
  • children(|ui| ...) -> Id
  • empty() -> Id

Interactivity:

  • with(|&mut Ui, ElementBuilder<WithId>| ...)
  • capture()
  • on_hover(|Id, PointerData| ...)
  • on_press(|Id, PointerData| ...)
  • on_release(|Id, PointerData, bool| ...)
  • on_focus(|Id| ...)
  • on_unfocus(|Id| ...)
  • preserve_focus()

Text input and accessibility:

  • text_input(|TextInputBuilder| ...)
  • accessibility(|AccessibilityBuilder| ...)

Part 6: Sizing and Layout APIs

6.1 Sizing and Macros
  • fit!() or fit!(min, max) or named args (min:, max:)
  • grow!() or grow!(min, max, weight) or named args (min:, max:, weight:)
  • fixed!(px)
  • percent!(fraction)
  • ratio!(aspect_ratio)

Notes:

  • grow!(weight: 0.0) behaves as fit internally.
  • negative grow weight is invalid.
  • ratio!(r) derives the dimension from the opposite axis (height = width / ratio on height, width = height * ratio on width). Setting ratio! on both axes panics during layout.
  • contain(ratio) and cover(ratio) adjust the element's resolved bounding box to fit or cover its allocated slot to respect the aspect ratio.
6.2 LayoutBuilder
  • gap(u16)
  • align(AlignX, AlignY)
  • direction(LayoutDirection)
  • wrap()
  • wrap_gap(u16)
  • padding(u16 | (top, right, bottom, left))
6.3 OverflowBuilder
  • clipping: clip_x(), clip_y(), clip()
  • scrolling: scroll_x(), scroll_y(), scroll()
  • no_drag_scroll()
  • scrollbar(|ScrollbarBuilder| ...)
6.4 ScrollbarBuilder
  • width(f32)
  • corner_radius(f32)
  • thumb_color(color)
  • track_color(color)
  • min_thumb_size(f32)
  • hide_after_frames(u32)
6.5 FloatingBuilder
  • offset(Vector2-like)
  • z_index(i16)
  • anchor((AlignX, AlignY), (AlignX, AlignY))
  • attach_parent()
  • attach_root()
  • attach_id(id)
  • clip_by_parent()
  • passthrough()
6.6 BorderBuilder
  • color(color)
  • all(u16)
  • left(u16)
  • right(u16)
  • top(u16)
  • bottom(u16)
  • between_children(u16)
  • position(BorderPosition)
6.7 Rotation Builders

Visual rotation (rotate_visual):

  • degrees(f32)
  • radians(f32)
  • pivot((x, y)) normalized in [0,1]
  • flip_x()
  • flip_y()

Shape rotation (rotate_shape):

  • degrees(f32)
  • radians(f32)
  • flip_x()
  • flip_y()

Part 7: Text APIs

7.1 ui.text + TextConfig

Text configuration methods:

  • color(color)
  • font(&'static FontAsset)
  • font_size(u16)
  • letter_spacing(u16)
  • line_height(u16)
  • wrap_mode(WrapMode)
  • alignment(AlignX)
  • effect(shader_asset, |ShaderBuilder| ...)
  • accessible()

WrapMode variants:

  • Words
  • Newline
  • None
7.2 Color Inputs

Ply Color accepts:

  • 0xRRGGBB integers
  • (u8, u8, u8)
  • (u8, u8, u8, u8)
  • (f32, f32, f32)
  • (f32, f32, f32, f32)

Important: float channels are in 0..255 space, not 0..1.

Part 8: Text Input APIs

8.1 TextInputBuilder
  • placeholder(&str)
  • max_length(usize)
  • password()
  • multiline()
  • drag_select()
  • font(&'static FontAsset)
  • font_size(u16)
  • text_color(color)
  • placeholder_color(color)
  • cursor_color(color)
  • selection_color(color)
  • line_height(u16)
  • scrollbar(|ScrollbarBuilder| ...)
  • no_styles_movement()
  • on_changed(|&str| ...)
  • on_submit(|&str| ...)
8.2 text_input::styling module (feature: text-styling)

Important public functions:

  • escape_str(s): Escapes all style delimiters in a string
  • strip_styling(s): Removes all style tags, returning plain content
  • cursor_to_content(s, pos): Converts cursor pos to content character index
  • content_to_cursor(s, pos, snap_to_content): Converts content character index to cursor pos. When snap_to_content is true, the cursor skips structural positions like } and lands on the next visible character.
  • cursor_to_raw(s, pos): Converts styled cursor pos to raw index in the string
  • raw_to_cursor(s, pos): Converts raw string index to cursor position in the styled string

Different types of positions:

  • raw position: index in the full styled string, including markup such as {color=...|...} and escape characters.
  • cursor position: visual cursor index used by text input editing. This includes visible characters and styling structure positions that the cursor can pass through.
  • content position: index in plain visible content after styling is stripped.

Part 9: IDs, Interactivity, and State

9.1 Id
  • Id::new(label)
  • Id::new_index(label, index)
  • Id::new_index_seed(label, index, seed)
  • From<&'static str> and From<(&str, u32)>
9.2 Inline State Queries

Inside the active .children(|ui| ...) scope:

  • ui.hovered()
  • ui.pressed()
  • ui.just_pressed()
  • ui.just_released()
  • ui.focused()
9.3 Callback Events
  • .on_hover(|id, pointer| ...)
  • .on_press(|id, pointer| ...)
  • .on_release(|id, pointer, hovered| ...)
  • .on_focus(|id| ...)
  • .on_unfocus(|id| ...)

PointerData:

  • pointer.position: Vector2
  • pointer.state: PointerState (Idle, PressedThisFrame, Pressed, ReleasedThisFrame)
  • pointer.pressed() -> bool: checks if pointer is currently pressed (Pressed or PressedThisFrame)
  • pointer.released() -> bool: checks if pointer is currently released (Idle or ReleasedThisFrame)
  • pointer.just_pressed() -> bool: checks if pointer was just pressed this frame (PressedThisFrame)
  • pointer.just_released() -> bool: checks if pointer was just released this frame (ReleasedThisFrame)

Use .capture() on interactive elements (e.g. buttons inside a larger clickable area) to capture pointer input and prevent it from propagating to parent elements. Use .preserve_focus() on toolbar-like controls that should not steal text-input focus.

9.4 State Queries by ID
  • is_pressed(id)
  • is_just_pressed(id)
  • is_just_released(id)
  • pointer_over(id)
  • pointer_over_ids()
  • bounding_box(id)
  • scroll_container_data(id)

Part 10: Rendering, Assets, and Shader APIs

10.1 Assets

GraphicAsset:

  • graphic!("assets/images/logo.png") (macro embeds bytes relative to crate manifest)
  • GraphicAsset { id, data }

FontAsset:

  • font!("assets/fonts/lexend.ttf") (macro embeds bytes relative to crate manifest)
  • FontAsset { id, data }

Image inputs accepted by .image(...):

  • &'static GraphicAsset
  • Texture2D
  • tinyvg::format::Image (feature: tinyvg)
10.2 Shader APIs

ShaderAsset variants:

  • shader!("assets/shaders/custom.frag.glsl") (macro embeds shader source)
  • Source { id, fragment }
  • Stored(&'static str)

ShaderBuilder:

  • .uniform(name, value) where value is one of:
    • f32
    • [f32; 2]
    • [f32; 3]
    • [f32; 4]
    • i32
    • [[f32; 4]; 4]

Runtime shader source updates:

  • set_shader_source(name, fragment_source)
  • then reference through ShaderAsset::Stored(name)
10.3 Render Utility
  • render_to_texture(width, height, || { draw calls }) -> Texture2D
10.4 Built-in Shader Constants (feature: built-in-shaders)
  • FOIL: u_time (required), u_speed (default 1.0), u_intensity (default 0.3)
  • HOLOGRAPHIC: u_time (required), u_speed (default 1.0), u_saturation (default 0.7)
  • DISSOLVE: u_threshold (required), u_edge_color (required), u_edge_width (default 0.05), u_seed (default 0.0)
  • GLOW: u_glow_color (required), u_glow_radius (default 0.05), u_glow_intensity (default 1.0)
  • CRT: u_line_count (default 100.0), u_intensity (default 0.3), u_time (recommended)
  • GRADIENT_LINEAR: u_color_a (required), u_color_b (required), u_angle (default 0.0, radians)
  • GRADIENT_RADIAL: u_color_a (required), u_color_b (required), u_center (default [0.5, 0.5]), u_radius (default 0.5)
  • GRADIENT_CONIC: u_color_a (required), u_color_b (required), u_center (default [0.5, 0.5]), u_offset (default 0.0), u_hardness (default 0.0)

Part 11: Accessibility APIs

11.1 AccessibilityRole variants
  • None
  • Button
  • Link
  • Heading { level }
  • Label
  • StaticText
  • TextInput
  • TextArea
  • Checkbox
  • RadioButton
  • Slider
  • Group
  • List
  • ListItem
  • Menu
  • MenuItem
  • MenuBar
  • Tab
  • TabList
  • TabPanel
  • Dialog
  • AlertDialog
  • Toolbar
  • Image
  • ProgressBar
11.2 AccessibilityBuilder methods
  • role shortcuts:
    • button(label: &str)
    • heading(label: &str, level: u8)
    • link(label: &str)
    • static_text(label: &str)
    • checkbox(label: &str)
    • slider(label: &str)
    • image(alt: &str)
  • generic fields:
    • role(role: AccessibilityRole)
    • label(text: &str)
    • description(text: &str)
    • value(text: &str)
    • value_min(min: f32)
    • value_max(max: f32)
    • checked(checked: bool)
  • focus and order:
    • focusable()
    • tab_index(index: i32)
  • directional focus:
    • focus_right(target: impl Into<Id>)
    • focus_left(target: impl Into<Id>)
    • focus_up(target: impl Into<Id>)
    • focus_down(target: impl Into<Id>)
  • focus ring:
    • disable_ring()
    • ring_color(color: impl Into<Color>)
    • ring_width(width: u16)
  • live regions:
    • live_region_polite()
    • live_region_assertive()

Part 12: Feature Modules (Net, Storage, Jobs, Audio)

12.1 Networking (net)

HTTP:

  • net::get(id, url, |HttpConfig| ...)
  • net::post(...)
  • net::put(...)
  • net::delete(...)
  • net::request(id) -> Option<Request>

HttpConfig methods:

  • header(key, value)
  • body(&str)
  • body_bytes(Vec<u8>)

Request methods:

  • response() -> Option<Result<Arc<Response>, String>>
  • cancel()

Response methods:

  • status() -> u16
  • text() -> &str
  • bytes() -> &[u8]
  • json<T>() (feature: net-json)

WebSocket:

  • net::ws_connect(id, url, |WsConfig| ...)
  • net::ws(id) -> Option<WebSocket>

WsConfig methods:

  • header(key, value)
  • insecure()

WebSocket methods:

  • send(&[u8])
  • send_text(&str)
  • recv() -> Option<WsMessage>
  • close()

WsMessage variants:

  • Connected
  • Text(String)
  • Binary(Vec<u8>)
  • Error(String)
  • Closed
12.2 Storage (storage)
  • Storage::new(path).await
  • save_string(path, data).await
  • save_bytes(path, data).await
  • load_string(path).await
  • load_bytes(path).await
  • remove(path).await
  • export(path).await
12.3 Jobs (jobs)
  • jobs::spawn(id, || async move { ... }, |result| { ... })
  • jobs::running(id)
  • jobs::is_running(id)
  • jobs::list()
12.4 Audio (audio)

When audio feature is enabled, macroquad audio APIs are in prelude. Typical calls:

  • load_sound(path).await
  • load_sound_from_bytes(bytes).await
  • play_sound_once(&sound)
  • play_sound(&sound, PlaySoundParams { .. })
  • stop_sound(&sound)
  • set_sound_volume(&sound, volume)

Part 13: Lerp and Easing APIs

13.1 Lerp trait

Implemented for:

  • f32
  • u16
  • Vector2
  • macroquad::prelude::Vec2
  • (f32, f32, f32, f32)
  • (u16, u16, u16, u16)
  • Color

Color-specific helpers:

  • Color::lerp_srgb(...)
  • Color::lerp_oklab(...)
13.2 Easing functions in prelude
  • ease_in_quad, ease_out_quad, ease_in_out_quad
  • ease_in_cubic, ease_out_cubic, ease_in_out_cubic
  • ease_in_quart, ease_out_quart, ease_in_out_quart
  • ease_in_sine, ease_out_sine, ease_in_out_sine
  • ease_in_expo, ease_out_expo, ease_in_out_expo
  • ease_in_back, ease_out_back, ease_in_out_back
  • ease_in_elastic, ease_out_elastic, ease_in_out_elastic
  • ease_out_bounce, ease_in_bounce, ease_in_out_bounce

Part 14: Shader Build Pipeline API

Use in build.rs with feature shader-build in build-dependencies.

Main API:

  • ShaderBuild::new()
  • .source_dir("...")
  • .output_dir("...")
  • .slangc_path("...")
  • .override_file_type_handler(ext, handler)
  • .build()

Defaults:

  • source: shaders/
  • output: assets/build/shaders/
  • SPIR-V temp: build/shaders/spirv/

Part 15: plyx CLI Reference for AI

If plyx is available, prefer it for setup and platform builds.

  • plyx init
  • plyx add [args]
  • plyx skill [--install]
  • plyx apk [--native] [--install] [--auto]
  • plyx web
  • plyx ios [--device] [--actions] [--auto]
  • plyx completions <shell> [--install]
15.1 plyx init

Creates a project, selects a font from Google Fonts, and selects feature flags.

Generates:

  • Cargo.toml
  • src/main.rs
  • assets/fonts/<font>.ttf
  • optional build.rs and shaders/ if shader pipeline is selected
  • optional .claude/skills/ply-engine/SKILL.md
15.2 plyx add

Interactive mode (no args) or non-interactive forms:

  • plyx add <feature-key>
  • plyx add font <font name>

Feature keys recognized by plyx templates:

  • tinyvg
  • built-in-shaders
  • shader-pipeline
  • text-styling
  • net
  • net-json (depends on net)
  • audio
  • storage
  • skill

skill and shader-pipeline are plyx concepts, not feature keys in [dependencies]. shader-pipeline adds build-dependencies and build.rs. skill installs .claude/skills/ply-engine/SKILL.md into the project.

15.3 plyx web

Builds release wasm and outputs to build/web/:

  • build/web/app.wasm
  • build/web/index.html
  • build/web/ply_bundle.js
  • copies assets/ if present
15.4 plyx apk

Modes:

  • Docker (default)
  • native (--native)

Useful flags:

  • --install to install with adb
  • --auto non-interactive mode

APK output location:

  • target/android-artifacts/release/apk/<crate>.apk
15.5 plyx ios

Modes:

  • simulator build by default
  • real device with --device
  • workflow generation with --actions

Requirements:

  • macOS + Xcode tools (xcrun)
  • proper Rust iOS target
  • for --device: signing identity, provisioning profile, and entitlements
15.6 plyx skill
  • plyx skill prints the bundled skill text to stdout.
  • plyx skill --install installs to ~/.claude/skills/ply-engine/SKILL.md.

Part 16: Common Patterns

This section shows some common patterns that emerge when building with Ply. These are just examples, not requirements. You should feel free to deviate from these patterns when the situation calls for it and use appropriate values and variations. We show this with a lot of constants and default values like 24 that you would replace in a real app.

16.1 Cursor

Ply provides engine-managed cursor handling via ui.set_cursor(CursorIcon) and ui.get_cursor() -> CursorIcon:

rust
ui.set_cursor(CursorIcon::Pointer);
let current: CursorIcon = ui.get_cursor();
  • The default cursor is CursorIcon::Pointer.
  • ui.set_cursor(...) persists across frames until explicitly changed, and overwrites ui.get_cursor() immediately.
  • The cursor is automatically applied to the window during ui.eval() / ui.show() (when not headless).
16.2 Styling Function Pattern

Reusable styling should be plain Rust functions that take and return ElementBuilder.

rust
fn rounded<'ply, S>(el: ElementBuilder<'ply, S>) -> ElementBuilder<'ply, S> {
  el.corner_radius(12.0)
}

fn dark_bg<'ply, S>(el: ElementBuilder<'ply, S>) -> ElementBuilder<'ply, S> {
  el.background_color(0x2E2A28)
}

dark_bg(rounded(ui.element()))
  .width(grow!())
  .height(fixed!(60.0))
  .children(|ui| {
    ui.text("Styled with functions", |t| t.font_size(20).color(0xFFFFFF));
  });

For parameterized styles:

rust
fn my_style<'ply, S>(el: ElementBuilder<'ply, S>, bg: u32, radius: f32) -> ElementBuilder<'ply, S> {
  el.background_color(bg).corner_radius(radius)
}

my_style(ui.element(), 0x181515, 10.0)
  .width(fit!())
  .height(fit!())
  .empty();
16.3 Button Example

You might want your buttons to use something like this with .with():

rust
fn button(ui: &mut Ui, label: &str, mut on_click: impl FnMut() + 'static) {
  ui.element().width(fit!()).height(fit!())
    .corner_radius(SOME_RADIUS)
    .layout(|l| l.padding(SOME_PADDING).align(CenterX, CenterY))
    .accessibility(|a| a.button(label))
    .on_press(move |_, _| on_click())
    .with(|ui, el| {
      let bg = if ui.pressed() {
        LIGHT_PRIMARY_COLOR
      } else if ui.hovered() || ui.focused() {
        DARK_PRIMARY_COLOR
      } else {
        PRIMARY_COLOR
      };
      el.background_color(bg)
    })
    .children(|ui| {
      ui.text(label, |t| t.font_size(24).color(0xFFFFFF));
    });
}

.with() executes after the element's ID has been established in .empty() or .children(), so queries like ui.pressed(), ui.hovered(), and ui.focused() accurately refer to this element and el.get_id() is available without needing an extra wrapper element.

16.4 Polling HTTP Example
rust
if user_data.is_none() {
  net::get("user_data", "https://example.com/", |r| r
    .header("Authorization", "Bearer token")
  );
}
// ...
if user_data.is_none() {
  if let Some(req) = net::request("user_data") {
    match req.response() {
      None => {
        ui.text("{swing|Loading...}", |t| t.font_size(24).color(0xFFFFFF));
      }
      Some(Ok(resp)) => {
        if resp.status() == 200 {
          let data: UserData = resp.json().unwrap_or_else(|e| {
            eprintln!("failed to parse user data: {e}");
            UserData::default()
          });
          user_data = Some(data);
        }
      }
      Some(Err(err)) => {
        eprintln!("request failed: {err}");
      }
    }
  }
}
16.5 Storage + Jobs Example
rust
jobs::spawn(
  "save_game",
  || async move {
    Storage::new("my_app").await?
      .save_string("save.json",
        serde_json::to_string(&game_state).map_err(|e| e.to_string())?
      ).await
  },
  |result| {
    if let Err(e) = result {
      eprintln!("Failed to save game: {e}");
    }
  },
)?;

Using Storage on the main thread will cause frame skips on the web. It's best to use it inside a job like this to avoid blocking the main thread.

16.6 Render To Texture + Shader Example

Use a static shader asset for normal rendering flows.

rust
static TINT_SHADER: ShaderAsset = ShaderAsset::Source {
  id: "tint.frag.glsl",
  fragment: r#"#version 100
precision mediump float;
varying vec2 uv;
uniform sampler2D Texture;
uniform vec4 u_tint_color;
uniform float u_tint_strength;

void main() {
  vec4 base = texture2D(Texture, uv);
  vec3 mixed_rgb = mix(base.rgb, u_tint_color.rgb, clamp(u_tint_strength, 0.0, 1.0));
  gl_FragColor = vec4(mixed_rgb, base.a);
}"#,
};

let chart = render_to_texture(400.0, 200.0, || {
  clear_background(BLANK);
  draw_line(0.0, 180.0, 400.0, 40.0, 3.0, GREEN);
  draw_circle(200.0, 110.0, 8.0, RED);
});

ui.element()
  .width(fixed!(400.0))
  .height(fixed!(200.0))
  .image(chart)
  .effect(&TINT_SHADER, |s| s
    .uniform("u_tint_color", [1.0, 0.85, 0.75, 1.0])
    .uniform("u_tint_strength", 0.25f32)
  )
  .empty();
16.7 Accessibility Example
rust
ui.element().id("first").width(fit!()).height(fit!())
  .layout(|l| l.padding(SOME_PADDING))
  .background_color(FIRST_BUTTON_COLOR)
  .accessibility(|a| a.button("First").tab_index(1).focus_right("second"))
  .on_press(|_, _| println!("first pressed"); )
  .children(|ui| {
    ui.text("First", |t| t.font_size(24).color(0xFFFFFF).accessible());
  });

ui.element().id("second").width(fit!()).height(fit!())
  .layout(|l| l.padding(SOME_PADDING))
  .background_color(SECOND_BUTTON_COLOR)
  .accessibility(|a| a.button("Second").tab_index(2).focus_left("first"))
  .on_press(|_, _| println!("second pressed"); )
  .children(|ui| {
    ui.text("Second", |t| t.font_size(24).color(0xFFFFFF));
  });
16.8 Text Input + Text Styling Cursor Helpers
rust
ui.element().id("editor").width(fixed!(400.0)).height(fixed!(120.0))
  .layout(|l| l.padding(BACKGROUND_PADDING))
  .background_color(BACKGROUND_COLOR)
  .text_input(|t| t
    .multiline()
    .font_size(24)
    .drag_select()
    .no_styles_movement()
    .on_changed(|text| println!("changed: {text}"))
  );

let raw = ui.get_text_value("editor").to_string();
let plain = styling::strip_styling(&raw);
let highlighted = plain.replace("TODO", "{color=#FFC32C|TODO}");

if raw != highlighted {
  let cursor = ui.get_cursor_pos("editor");
  let content_pos = styling::cursor_to_content(&raw, cursor);
  ui.set_text_value("editor", &highlighted);
  let new_cursor = styling::content_to_cursor(&highlighted, content_pos, true);
  ui.set_cursor_pos("editor", new_cursor);
}

.text_input() is a terminal method on ElementBuilder (returning Id), allowing developers to configure layout modifiers like .padding() directly on the housing element itself. The scrollable area includes the padding and scrollbars extend to include the padded perimeter. Also note how the .text_input closure is styled with the letter t on the first line and then the .arguments() on the following lines. This is how you should style builders that stretch across multiple lines.

16.9 Floating Tooltip
rust
ui.element().id("tooltip_target").width(fit!()).height(fit!())
  .layout(|l| l.padding(5))
  .corner_radius(6.0)
  .background_color(BUTTON_COLOR)
  .children(|ui| {
    ui.text("Hover me", |t| t.font_size(24).color(0xE8E0DC));

    if ui.hovered() {
      ui.element().width(fit!()).height(fit!())
        .floating(|f| f
          .attach_parent()
          .anchor((CenterX, Top), (CenterX, Bottom))
          .offset((0.0, -4.0))
        )
        .background_color(TOOLTIP_COLOR)
        .corner_radius(6.0)
        .layout(|l| l.padding(5))
        .children(|ui| {
          ui.text("Tooltip text", |t| t.font_size(16).color(0xFFFFFF));
        });
    }
  });
16.10 Wrap Layout and Scrollbar
rust
ui.element().width(grow!()).height(grow!())
  .layout(|l| l.wrap().wrap_gap(8).gap(8).padding(8))
  .overflow(|o| o
    .scroll_y()
    .scrollbar(|s| s
      .width(4.0)
      .thumb_color(0xFFFFFF)
      .track_color(0x222222)
      .hide_after_frames(60)
    )
  )
  .children(|ui| {
    for i in 0..200 {
      ui.element().id(("chip", i)).width(fit!()).height(fit!())
        .layout(|l| l.padding(6))
        .background_color(0x333333)
        .corner_radius(6.0)
        .children(|ui| {
          ui.text(&format!("Chip {i}"), |t| t.font_size(16).color(0xFFFFFF));
        });
    }
  });
16.11 GraphicAsset and FontAsset Pattern

Declare graphic and font assets as static using graphic! and font! macros.

rust
static LOGO: GraphicAsset = graphic!("assets/images/logo.png");
static ICON_TVG: GraphicAsset = graphic!("assets/images/icon.tvg");
static BODY_FONT: FontAsset = font!("assets/fonts/lexend.ttf");

// ...

ui.element().width(fixed!(96.0)).height(fixed!(96.0)).image(&LOGO).empty();
ui.element().width(fixed!(96.0)).height(fixed!(96.0)).image(&ICON_TVG).empty();

ui.text("Body", |t| t.font(&BODY_FONT).font_size(24).color(0xFFFFFF));

UI/UX Playbook

This part is the definitive reference for producing interfaces and visuals that are both rigorously functional and genuinely memorable. It covers design thinking, aesthetic direction, foundational principles, common failure modes, advanced UX strategy, motion design, and game UI.

Show full SKILL.md (1,458 more words)Show less

Part 1: Design Thinking

Before touching anything visual, understand the context and commit to a direction.

1.1 Understand the Brief
  • Purpose: What problem does this interface solve? Who uses it, and when?
  • Tone: Pick a clear aesthetic extreme and commit to it. Options include: brutally minimal, maximalist, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian. These are starting points, design something true to the context, not a template.
  • Constraints: Platform, performance needs, accessibility requirements.
  • Differentiation: What is the one thing someone will remember about this interface? Name it before you build it.
1.2 Commit, Don't Hedge

The single biggest failure mode in design is timidity, making choices that offend no one and inspire no one. Bold maximalism and refined minimalism are both valid. What fails is the middle: generic layouts, safe fonts, and evenly-distributed palettes that look like every other product.

Match execution depth to aesthetic vision. A maximalist design needs elaborate layering, rich animation, and complex composition. A minimalist design needs razor-sharp spacing, perfect type, and restraint exercised with intention.

Part 2: Foundations

2.1 Signifiers & Affordances
  • Self-Explanatory UI: Use containers to signal relationships. If items share a container, they are perceived as related.
  • Visual States: Always define all four interaction states if they exist: Hover, Active, Disabled, Loading. An incomplete state system is an incomplete design.
  • Cursor Icons: Explicitly define cursor changes. A static cursor is a broken signifier.
  • Show, Don't Tell: The UI should communicate how it works through visual cues alone, not instructional text.
2.2 Visual Hierarchy & Contrast
  • Scanning Priority: Place high-priority information at the top. Use size, weight, and color to direct the eye.
  • The Contrast Engine: Contrast creates hierarchy. Juxtapose big/small, colorful/monochrome, heavy/light. Everything cannot be equally important.
  • Flow Indicators: Use visual indicators to demonstrate relationships and flow without requiring text.
2.3 The 4-Point Grid
  • Standard Spacing: Use multiples of 4 for all spacing: 4, 8, 16, 24, 32, 48, 64, unless when your UI should stay the same when resizing.
  • Section Breathing Room: Usually use at minimum 32px between visually distinct sections.
  • Proximity as Meaning: Group related elements tightly. Separate unrelated ones clearly. If a label and its input are far apart, the relationship breaks.
2.4 Color & Depth
  • Semantic Color: Assign meaning consistently: Blue (action/interactive), Red (error/danger), Green (success/confirmation), Yellow (warning/caution). Never reverse these expectations.
  • Dark Mode Depth: Represent depth by making foreground surfaces lighter than the background, not darker. This mirrors how light physically works.
  • Dominant + Accent: Dominant colors with sharp accent colors outperform evenly-distributed palettes. Pick a position and commit to it.
2.5 Typography

Typography is communication architecture.

Font Personality by Category:

  • Serif: Traditional, classic, editorial. Best for print contexts or brands that lean into heritage.
  • Sans-Serif: Modern, legible, digital-native. The standard for screens.
  • Display/Decorative: Expressive, high-impact. Use only for headlines or hero moments.
  • Outdated Fonts: Avoid fonts with strong cultural baggage (Comic Sans, Papyrus, Courier). They undermine credibility regardless of context.
  • Availibility: Usually use fonts that the user already has in their assets. If the user has no fitting fonts use plyx add font NAME to download from Google Fonts.

Pairing & Palette:

  • Usually use no more than three typefaces per project. One is often enough.
  • Pair by contrast: a bold, characterful display face with a quiet, readable body face.
  • If using a single typeface, create hierarchy through weight, size, and style variation.
  • Avoid generic defaults. Reach for typefaces with genuine personality. The choice should feel considered, not defaulted to.

Part 3: Aesthetic Execution

3.1 Spatial Composition

Move beyond standard grid layouts. Explore:

  • Asymmetry and overlap: Elements that cross boundaries or break the grid create dynamism.
  • Diagonal flow: Leading the eye along a diagonal rather than straight down creates energy.
  • Generous negative space OR controlled density: Both work. What fails is accidental clutter or accidental emptiness.
  • Rule of Thirds: It's often good to divide the composition into a 3×3 grid. Place focal points near intersections. The eye naturally follows this path.
3.2 Shape Language
  • Geometric: Circles and squares are dependable, structured and universal for components and UI containers.
  • Organic: Free-form shapes are best for illustration, branding moments, or softening otherwise rigid layouts.
  • Shapes organize, separate, and give content visual "weight." Use them intentionally.
3.3 Texture & Form
  • Texture adds tactility and depth to flat designs. Use for backgrounds, icons, and accent areas - never everywhere at once.
  • Form implies three-dimensionality through light, shadow, and perspective.
3.4 Repetition & Consistency

Repeat visual elements (color palette, header styles, icon library, corner radii, spacing patterns) throughout a design. Repetition is not laziness - it is branding. It lets users relax and engage with content rather than decode the interface.

Part 4: Anti-Patterns to Avoid

These are common failure modes that immediately mark a design as amateur.

4.1 Visual Noise
  • Rainbow gradients: Multi-hue gradients almost always look unintentional. Limit gradients to tonal variations of a single color family.
  • Unnecessary strokes/borders: Remove borders that don't serve a specific contrast or containment purpose. When in doubt, remove it.
  • Effect overload: Each visual effect competes for attention. Use effects sparingly so meaningful ones land.
4.2 Inconsistency
  • Corner radius chaos: Mixing 4px, 8px, 16px, and 24px radii across components without logic looks broken, not dynamic.
  • Mismatched assets: Icons from different libraries, or illustrations with conflicting lighting and stroke styles, destroy visual coherence.
  • Redundant affordances: Don't show swipe arrows on touch interfaces or label buttons that already have universal icons.
4.3 User Flow Gaps
  • Missing escape routes: Every optional flow needs a "Skip" or "Not now" path. Trapping users is a trust violation.
  • Blank search states: Never show an empty screen when a user opens search. Provide recent searches, suggestions, or categories.
4.4 Generic Aesthetics

Avoid the convergent defaults that make designs indistinguishable from each other:

  • Overused typefaces that have become visual wallpaper
  • Purple gradients on white backgrounds
  • Predictable card-based layouts with no compositional tension
  • Safe, forgettable color palettes that feel chosen by elimination rather than intention

Every design should feel like it was made for this specific context.

Part 5: Advanced UX Strategy

5.1 User Bases

Design for different user bases:

  • New users: Prioritize simplicity and clear "getting started" pathways. Reduce cognitive load.
  • Returning users: Surface current progress, quick-access features, and continuations of prior sessions.
  • Power users: Expose advanced stats, shortcuts, and optimization tools. Don't hide capability behind beginner guardrails.
5.2 Progress & Trust
  • Visual timelines: Use staged progress indicators (Processing → Shipped → Delivered) rather than raw dates or text status updates. People understand journey better than timestamp.
  • Humanizing data: Include names, photos, or personal details for service providers or counterparties. It transforms transactions into interactions.
5.3 Input Method Selection
  • Sliders: Use for casual, range-based, or exploratory input (age ranges, filters, approximate values).
  • Text fields: Use for precise, repetitive, or high-accuracy input (weights, quantities, identifiers).
5.4 Alignment Discipline

Elements that are visually "almost" aligned create tension that reads as disorder.

Part 6: Motion Design

Motion is communication. All UI movement must justify itself against these five criteria:

  1. Responsiveness: Interactions must feel immediate and fluid. Latency in motion reads as lag even when performance is fine.
  2. Intention: Motion should guide focus; toward a key action, toward new content, away from what just resolved.
  3. Awareness: Elements should behave differently based on their position and context on screen. Motion is spatial.
  4. Consistency: Repeated motion patterns build familiarity. Users learn interface "physics".
  5. Physical Intuition: Movement should feel grounded. Paper-like physics for document metaphors. Elastic snap for playful UI. Parallax depth for epic scale. Match the motion vocabulary to the world of the product.

Practical priorities:

  • One well-orchestrated entrance with staggered reveals creates more delight than dozens of scattered micro-interactions.
  • Hover states and scroll-triggered reveals should surprise.
  • Animate meaning, not decoration. If removing the animation loses nothing, remove it.

Part 7: Game UI & Immersive Interfaces

7.1 Diegetic vs. Non-Diegetic UI
  • Diegetic: UI that exists within the game world (a holographic HUD inside a helmet visor, a health bar on a character). Creates immersion.
  • Non-diegetic: Traditional overlay UI that floats above the world. Familiar and legible, but breaks fiction.

Choose deliberately. Mixed approaches require strong thematic justification.

7.2 Unified Thematics

Define a visual "world", a set of recurring motifs (shape language, texture, color temperature, typographic character), and enforce it across every element: icons, typography, backgrounds, particle effects, and transitions. If the UI could be lifted out and placed in a different product without looking wrong, it isn't themed.

7.3 Platform Adaptation
  • Pointer interfaces: Support precision, density, and keyboard shortcuts. Small targets are acceptable.
  • Touch interfaces: Minimum 44×44pt touch targets. Simplified layouts. Remove affordances that assume hover states.
7.4 Information Speed

In high-intensity contexts, layout must allow the user to extract critical information in milliseconds without conscious eye movement. Proximity encodes meaning: a bar next to a name reads as "health"; a counter next to a bar reads as "progress." Design these relationships deliberately.

© TheRedDeveloper, ISC. 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 59 other files in the repository root of TheRedDeveloper/ply-engine.

  • SKILL.md
  • .github/FUNDING.yml
  • .github/ISSUE_TEMPLATE/bug_report.md
  • .github/ISSUE_TEMPLATE/feature_request.md
  • .github/ISSUE_TEMPLATE/idea.md
  • .github/ISSUE_TEMPLATE/question.md
  • .gitignore
  • CONTRIBUTING.md
  • Cargo.toml
  • LICENSE.md
  • README.md
  • android/main_activity_inject.java
  • changelog.md
  • js/buildbundle.sh
  • js/net.js
  • js/ply_accessibility.js
  • js/ply_bundle.js
  • … and 43 more

Open the folder on GitHubat commit 52be132

Compare with similar skills

Ply Engine 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.

Ply Engine compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Ply Engine this skillTheRedDeveloper/ply-engine506—~10kAutomated safety check: PassISC
Update V8 Versionopeninterpreter/openinterpreter69k2 repos~845Automated safety check: PassApache-2.0
Firecrawl Page Scrape Integrationfirecrawl/firecrawl190k1 repos~944Automated safety check: PassISC
Migrate Core Code to Submodulestinyhumansai/openhuman42k—~2.6kAutomated safety check: PassGPL-3.0
Rust TDD Workflowrtk-ai/rtk83k—~753Automated safety check: NotesApache-2.0
OpenLogi macOS Permissions TriageAprilNEA/OpenLogi23k—~2.5kAutomated safety check: NotesApache-2.0

Similar skills

  • Update V8 Version

    openinterpreter/openinterpreter

    Bumps the pinned v8 and rusty_v8 versions in Codex, validates the release-candidate path with the v8-canary check, and traces failures to upstream build changes.

    69k GitHub starsUsed in 2 repos~845 tokens
    DevOps & CloudAuto-check passed
  • Adds Firecrawl's /scrape endpoint to application code to pull markdown, HTML, links, screenshots or structured data from a single known URL.

    190k GitHub starsUsed in 1 repo~944 tokens
    Data & AnalyticsAuto-check passed
  • Migrate Core Code to Submodules

    tinyhumansai/openhuman

    Plans and carries out moving non-host-specific code and its tests from the OpenHuman core into vendored tiny submodule libraries, then releases the submodule and re-pins the host.

    42k GitHub stars~2.6k tokensUpdated today
    DevelopmentAuto-check passed
  • Enforces red-green-refactor for Rust work, with idiomatic test patterns, a naming convention and a pre-commit gate of cargo fmt, clippy and test.

    83k GitHub stars~753 tokensUpdated yesterday
    Testing & QAAuto-check: notes
  • Decides whether an OpenLogi device problem on macOS is a privacy-permission (TCC) problem, using agent log lines, and says which identity needs which grant.

    23k GitHub stars~2.5k tokensUpdated today
    DevelopmentAuto-check: notes
  • Guide for writing idiomatic Rust code based on Apollo GraphQL's best practices handbook.

    5.6k GitHub starsUsed in 3 repos~1.1k tokens
    DevelopmentAuto-check passed

Works with

Questions about Ply Engine

What does Ply Engine do?

Complete implementation and design guide for Ply. An agent skill from TheRedDeveloper/ply-engine. Ply Engine is an agent skill from TheRedDeveloper/ply-engine. Complete implementation and design guide for Ply.

When should I use Ply Engine?

Ply Engine fits situations like: A task involves ply-engine APIs; building UI in Rust with Ply.

How do I install Ply Engine in Claude Code?

Run `npx skills add TheRedDeveloper/ply-engine --skill ply-engine -a claude-code`. Or copy the skill folder (the TheRedDeveloper/ply-engine repository) into .claude/skills/ply-engine in your project. Claude Code loads it when a task matches its description.

How do I install Ply Engine in Codex?

Run `npx skills add TheRedDeveloper/ply-engine --skill ply-engine -a codex`. Or copy the skill folder (the TheRedDeveloper/ply-engine repository) into .agents/skills/ply-engine in your project. Codex loads it when a task matches its description.

Can I use Ply Engine 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 TheRedDeveloper/ply-engine --skill ply-engine -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/ply-engine, .gemini/skills/ply-engine, .github/skills/ply-engine and .opencode/skills/ply-engine in your project.

What does Ply Engine need to run?

Going by SKILL.md and its folder, Ply Engine needs JavaScript, Java and a shell for the scripts in its folder and the command-line tools its instructions call (cursor). Our summary lists: Node.js; A Bash shell.

Does Ply Engine access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Ply Engine 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 Ply Engine use?

Ply Engine is published under the ISC licence (from the LICENSE file in the skill folder). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Ply Engine use?

About 10k tokens (SKILL.md is roughly 41k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Ply Engine?

Skills that share tags, products or a category with Ply Engine: Update V8 Version (openinterpreter/openinterpreter, 69k stars), Firecrawl Page Scrape Integration (firecrawl/firecrawl, 190k stars), Migrate Core Code to Submodules (tinyhumansai/openhuman, 42k stars) and Rust TDD Workflow (rtk-ai/rtk, 83k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Ply Engine?

TheRedDeveloper (a GitHub user) maintains it in TheRedDeveloper/ply-engine, which has 506 GitHub stars. The repository was last updated on August 31, 2026.

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