Skip to content
convert-haskell-roc logo

Convert Haskell to Roc

convert-haskell-roc

Convert Haskell code to idiomatic Roc. Use when migrating Haskell applications to Roc's platform model, translating lazy pure functional code to strict platform-based architecture, or refactoring type class based designs to ability-based patterns. Extends meta-convert-dev with Haskell-to-Roc spec...

SKILL.md

Full skill instructions

Convert Haskell to Roc

Convert Haskell code to idiomatic Roc. This skill extends meta-convert-dev with Haskell-to-Roc specific type mappings, idiom translations, and tooling for translating from lazy pure functional programming to strict platform-based architecture.

This Skill Extends

  • meta-convert-dev - Foundational conversion patterns (APTV workflow, testing strategies)

For general concepts like the Analyze → Plan → Transform → Validate workflow, testing strategies, and common pitfalls, see the meta-skill first.

This Skill Adds

  • Type mappings: Haskell's HM types → Roc's structural types
  • Idiom translations: Type classes → abilities, monads → platform effects
  • Error handling: Maybe/​Either → Result with tag unions
  • Evaluation strategy: Lazy → strict evaluation
  • Concurrency patterns: STM/​async → platform-managed tasks
  • Platform architecture: GHC runtime → platform/​application separation
  • Paradigm shift: Pure lazy functional → strict functional with platform effects

This Skill Does NOT Cover

  • General conversion methodology - see meta-convert-dev
  • Haskell language fundamentals - see lang-haskell-dev
  • Roc language fundamentals - see lang-roc-dev
  • Reverse conversion (Roc → Haskell) - see convert-roc-haskell
  • Advanced type system features (GADTs, Type Families, DataKinds)

Quick Reference

HaskellRocNotes
f :: a -> b<br>f x = ...f : a -> b<br>f = \x -> ...Function definition
StringStrString type
Int / IntegerI64 / I32Integer types (Roc fixed-size)
Double / FloatF64 / F32Floating point
BoolBoolBoolean type
Nothing / Just aNone / Some aOptional values via tag unions
[a]List aLists (Roc is strict, not lazy)
(a, b)(a, b)Tuples (same syntax)
dataRecord or tag unionDepends on usage
Maybe a[Some a, None]Optional pattern
Either e aResult a eError handling (note reversed order)
IO aTask a errEffects via platform
class C a whereAbility constraintType classes → abilities
case x ofwhen x isPattern matching
do notation! suffix for tasksMonadic sequencing

When Converting Code

  1. Analyze source thoroughly - understand lazy semantics before converting
  2. Map types first - convert type classes to ability constraints
  3. Identify strict vs lazy - translate infinite lists to finite or iterators
  4. Preserve semantics over syntax similarity
  5. Adopt platform model - separate pure logic from I/​O via platform boundary
  6. Handle monads explicitly - IO → Task, Maybe → tag union, Either → Result
  7. Test equivalence - same inputs → same outputs (watch for strictness differences)
  8. Leverage abilities - replace type class constraints with ability constraints

Type System Mapping

Primitive Types

HaskellRocNotes
IntI6464-bit signed (platform-dependent in Haskell)
IntegerN/​AArbitrary precision - use fixed size or external library
DoubleF6464-bit floating point
FloatF3232-bit floating point
BoolBoolDirect mapping
CharU32Unicode code point
(){}Unit type
StringStrString type

Important differences:

  • Haskell: Arbitrary precision Integer, lazy evaluation
  • Roc: Fixed-size integers, strict evaluation
  • Haskell: String is [Char] (linked list), lazy
  • Roc: Str is UTF-8 byte array, strict

Collection Types

HaskellRocNotes
[a]List aLAZY in Haskell, STRICT in Roc
(a, b)(a, b)Tuples (same syntax)
(a, b, c)(a, b, c)N-tuples
Map k vDict k vDictionaries (requires Hash + Eq abilities)
Set aSet aSets (requires Hash + Eq abilities)

Lazy → Strict Conversion:

-- Haskell: Infinite list (lazy)
naturals :: [Integer]
naturals = [0..]

take 10 naturals  -- [0,1,2,3,4,5,6,7,8,9]
# Roc: Must be finite or use generator pattern
naturals : List I64
naturals = List.range { start: At 0, end: At 1000000 }

List.take naturals 10  # [0,1,2,3,4,5,6,7,8,9]

# Alternative: Iterator/​Stream pattern (platform-provided)
# naturalsStream = Stream.iterate 0 (\n -> n + 1)
# Stream.take naturalsStream 10

Composite Types

HaskellRocNotes
data Point = Point Int IntPoint : { x : I64, y : I64 }Product type → record
data Shape = Circle Float | Rect Float FloatShape : [Circle F64, Rect F64 F64]Sum type → tag union
newtype Age = Age IntAge := I64Newtype → opaque type
type Name = StringName : StrType alias

Idiom Translation

Pattern: Maybe/​Optional Values

Haskell:

findUser :: Int -> Maybe User
findUser 1 = Just (User "Alice" 30)
findUser _ = Nothing

-- Using Maybe
getUserName :: Int -> String
getUserName uid = case findUser uid of
    Just user -> name user
    Nothing -> "Unknown"

-- With do notation
getOlderUser :: Int -> Maybe User
getOlderUser uid = do
    user <- findUser uid
    return $ user { age = age user + 1 }

Roc:

findUser : I64 -> [Some User, None]
findUser = \uid ->
    if uid == 1 then
        Some { name: "Alice", age: 30 }
    else
        None

# Using pattern matching
getUserName : I64 -> Str
getUserName = \uid ->
    when findUser uid is
        Some user -> user.name
        None -> "Unknown"

# No monadic do - use direct manipulation
getOlderUser : I64 -> [Some User, None]
getOlderUser = \uid ->
    when findUser uid is
        Some user -> Some { user & age: user.age + 1 }
        None -> None

Why this translation:

  • Roc uses structural tag unions instead of Maybe type constructor
  • No monadic bind for optional values - use explicit pattern matching
  • More verbose but clearer control flow

Pattern: Either/​Error Handling

Haskell:

divide :: Float -> Float -> Either String Float
divide _ 0 = Left "Division by zero"
divide x y = Right (x / y)

-- Chaining with do notation
calculate :: Float -> Float -> Float -> Either String Float
calculate a b c = do
    x <- divide a b
    y <- divide x c
    return y

-- With error mapping
parseAge :: String -> Either String Int
parseAge str = case reads str of
    [(n, "")] -> if n >= 0
                 then Right n
                 else Left "Age must be non-negative"
    _ -> Left "Not a valid number"

Roc:

divide : F64, F64 -> Result F64 [DivByZero]
divide = \x, y ->
    if y == 0 then
        Err DivByZero
    else
        Ok (x / y)

# Chaining with try operator (!)
calculate : F64, F64, F64 -> Result F64 [DivByZero]
calculate = \a, b, c ->
    x = divide! a b  # Early return on Err
    y = divide! x c
    Ok y

# With error mapping
parseAge : Str -> Result I64 [ParseError Str, InvalidAge]
parseAge = \str ->
    n = Str.toI64! str |> Result.mapErr \_ -> ParseError "Not a number"

    if n >= 0 then
        Ok n
    else
        Err InvalidAge

Why this translation:

  • Haskell Either e a maps to Roc Result a e (note reversed order!)
  • Haskell's do notation maps to Roc's ! try operator
  • Tag unions allow more expressive error types than String

Pattern: IO Monad → Task

Haskell:

main :: IO ()
main = do
    putStrLn "What is your name?"
    name <- getLine
    putStrLn $ "Hello, " ++ name

-- Reading files
readConfig :: FilePath -> IO String
readConfig path = do
    content <- readFile path
    return content

Roc:

import pf.Stdout
import pf.Stdin
import pf.Task exposing [Task]

main : Task {} []
main =
    Stdout.line! "What is your name?"
    name = Stdin.line!
    Stdout.line! "Hello, \(name)"

# Reading files
import pf.File

readConfig : Str -> Task Str [FileReadErr]
readConfig = \path ->
    content = File.readUtf8! path
    Task.ok content

Why this translation:

  • Haskell's IO monad maps to Roc's Task type
  • Platform provides I/​O primitives (Stdout, File, etc.)
  • No explicit return - use Task.ok for wrapping pure values
  • ! suffix for task sequencing (like Haskell's <-)

Pattern: Type Classes → Abilities

Haskell:

-- Type class definition
class Eq a where
    (==) :: a -> a -> Bool

class Show a where
    show :: a -> String

-- Using type class constraints
printEqual :: (Eq a, Show a) => a -> a -> IO ()
printEqual x y = putStrLn $ if x == y
    then show x ++ " equals " ++ show y
    else show x ++ " not equals " ++ show y

-- Deriving instances
data Color = Red | Green | Blue
    deriving (Eq, Show)

Roc:

# Abilities are automatically derived for records and tags
Color : [Red, Green, Blue]

# Ability constraints in function signatures
printEqual : a, a -> Task {} [] where a implements Eq & Inspect
printEqual = \x, y ->
    msg = if x == y then
        "\(Inspect.toStr x) equals \(Inspect.toStr y)"
    else
        "\(Inspect.toStr x) not equals \(Inspect.toStr y)"
    Stdout.line! msg

# Automatic derivation
User : {
    name : Str,
    age : U32,
}
# User automatically has: Eq, Hash, Inspect, Encode, Decode

user1 = { name: "Alice", age: 30 }
user2 = { name: "Alice", age: 30 }
user1 == user2  # Works automatically

Why this translation:

  • Haskell type classes map to Roc abilities
  • Haskell Show maps to Roc Inspect
  • Roc derives abilities automatically for records/​tags
  • No manual instance definitions needed for common abilities

Pattern: Functor/​Applicative/​Monad → Direct Operations

Haskell:

-- Functor: fmap
doubled :: Maybe Int -> Maybe Int
doubled = fmap (*2)

-- Applicative
createUser :: Maybe String -> Maybe Int -> Maybe User
createUser mName mAge = User <$> mName <*> mAge

-- Monad: bind
chain :: Maybe Int -> Maybe Int
chain mx = mx >>= \x -> return (x * 2)

Roc:

# No Functor/​Applicative/​Monad abstractions
# Use explicit pattern matching or helper functions

doubled : [Some I64, None] -> [Some I64, None]
doubled = \m ->
    when m is
        Some x -> Some (x * 2)
        None -> None

# Or use Result.map for Result type
doubled = \m ->
    Result.map m \x -> x * 2

# No applicative - construct directly
createUser : [Some Str, None], [Some U32, None] -> [Some User, None]
createUser = \mName, mAge ->
    when (mName, mAge) is
        (Some name, Some age) -> Some { name, age }
        _ -> None

# Chaining
chain : [Some I64, None] -> [Some I64, None]
chain = \mx ->
    when mx is
        Some x -> Some (x * 2)
        None -> None

Why this translation:

  • Roc doesn't have Functor/​Applicative/​Monad abstractions
  • Use explicit pattern matching for clarity
  • Platform-specific types (Task, Result) may have helper functions
  • Simpler mental model at the cost of some verbosity

Evaluation Strategy

Lazy → Strict Translation

Haskell (Lazy):

-- Infinite Fibonacci
fibs :: [Integer]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

take 10 fibs  -- Only computes first 10

-- Lazy evaluation allows cycles
ones :: [Int]
ones = 1 : ones

Roc (Strict):

# Must generate finite list or use explicit generator
fibList : I64 -> List I64
fibList = \n ->
    List.walk (List.range { start: At 0, end: Before n })
        [0, 1]
        \fibs, _ ->
            a = List.get fibs (List.len fibs - 2)
                |> Result.withDefault 0
            b = List.get fibs (List.len fibs - 1)
                |> Result.withDefault 0
            List.append fibs (a + b)

fibList 10  # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

# Alternative: Iterator pattern (if platform provides)
# fibStream = Stream.iterate (0, 1) \(a, b) -> (b, a + b)
#             |> Stream.map \(a, _) -> a
# Stream.take fibStream 10

Key differences:

  • Haskell: Infinite structures work naturally (lazy)
  • Roc: Must use finite structures or explicit generators
  • Haskell: Evaluation on demand
  • Roc: Immediate evaluation

Concurrency Patterns

STM → Platform Tasks

Haskell:

import Control.Concurrent.STM

type Account = TVar Int

transfer :: Account -> Account -> Int -> STM ()
transfer from to amount = do
    fromBal <- readTVar from
    when (fromBal >= amount) $ do
        modifyTVar from (subtract amount)
        modifyTVar to (+ amount)

-- Run transaction
main = do
    acc1 <- newTVarIO 1000
    acc2 <- newTVarIO 0
    atomically $ transfer acc1 acc2 500

Roc:

# No built-in STM - platform manages state
# Pattern: Use platform-provided state management

import pf.Task exposing [Task]

# Platform-specific state API (example)
# This depends on your platform implementation

Account : { balance : I64 }

transfer : Account, Account, I64 -> Task {} [InsufficientFunds]
transfer = \from, to, amount ->
    if from.balance >= amount then
        # Platform handles atomicity
        newFrom = { from & balance: from.balance - amount }
        newTo = { to & balance: to.balance + amount }
        Task.ok {}
    else
        Task.err InsufficientFunds

# Usage
main : Task {} []
main =
    acc1 = { balance: 1000 }
    acc2 = { balance: 0 }
    transfer! acc1 acc2 500
    Task.ok {}

Why this translation:

  • Haskell: Built-in STM for transactional memory
  • Roc: Platform manages concurrency and state
  • Application code stays pure; platform handles atomicity
  • Platform-specific APIs vary

Async → Task-Based

Haskell:

import Control.Concurrent.Async

main :: IO ()
main = do
    (res1, res2) <- concurrently
        (fetchUrl "http://example.com/​1")
        (fetchUrl "http://example.com/​2")
    print (res1, res2)

Roc:

import pf.Task exposing [Task]
import pf.Http

# Platform may provide concurrent execution
main : Task {} []
main =
    # Sequential by default
    res1 = Http.get! "http://example.com/​1"
    res2 = Http.get! "http://example.com/​2"

    # Or platform-provided parallel execution (if available)
    # (res1, res2) = Task.parallel2!(
    #     Http.get "http://example.com/​1",
    #     Http.get "http://example.com/​2"
    # )

    Stdout.line! (Inspect.toStr (res1, res2))

Why this translation:

  • Haskell: Explicit async library
  • Roc: Platform controls concurrency
  • Application code composes tasks; platform decides execution strategy

Common Pitfalls

1. Lazy vs Strict - Infinite Lists

Problem: Direct translation of lazy infinite structures

-- Haskell: Works fine
naturals = [0..]
evens = filter even naturals
# Roc: Would hang forever!
# naturals = List.range { start: At 0, end: At maxI64 }  # Too large
# evens = List.keepIf naturals Num.isEven  # Never completes

Fix: Use finite ranges or iterators

# Generate finite range
naturals = List.range { start: At 0, end: Before 1000 }
evens = List.keepIf naturals Num.isEven

# Or use stream/​iterator pattern (if platform provides)

2. Type Class Constraints → Ability Constraints

Problem: Assuming type class polymorphism works the same

-- Haskell: Polymorphic function
sort :: Ord a => [a] -> [a]
sort = ...
# Roc: Ability constraint
sort : List a -> List a where a implements Ord
sort = \list -> ...

# BUT: Roc doesn't have Ord ability built-in!
# Must use specific types or platform-provided sorting

Fix: Use concrete types or platform functions

# Concrete type
sortInts : List I64 -> List I64
sortInts = List.sortAsc

# Or use platform's polymorphic sort (if available)

3. IO Monad → Task Platform Boundary

Problem: Mixing pure and impure code

-- Haskell: IO monad isolates effects
main :: IO ()
main = do
    content <- readFile "config.txt"  -- IO
    let result = process content       -- Pure
    print result                        -- IO
# Roc: Clear platform boundary
main : Task {} []
main =
    content = File.readUtf8! "config.txt"  # Task (platform)
    result = process content                # Pure function
    Stdout.line! (Inspect.toStr result)     # Task (platform)

# Pure function (no Task)
process : Str -> Str
process = \text ->
    Str.toUpper text

Key difference:

  • Haskell: IO type tracks effects
  • Roc: Platform boundary separates pure from effectful
  • Pure functions in Roc have no Task type

4. Monadic Do Notation → Try Operator

Problem: Expecting do-notation to work

-- Haskell
parseUser :: String -> Either String User
parseUser str = do
    age <- parseAge str
    email <- parseEmail str
    return $ User email age
# Roc: Use try operator (!)
parseUser : Str -> Result User [ParseErr Str]
parseUser = \str ->
    age = parseAge! str      # Early return on Err
    email = parseEmail! str  # Early return on Err
    Ok { email, age }

Key difference:

  • Haskell: do notation for any monad
  • Roc: ! operator only for Result and Task

5. Type Inference Differences

Problem: Expecting Haskell-level inference

-- Haskell: Polymorphic
id x = x  -- Inferred: a -> a
# Roc: Usually needs annotation for polymorphic functions
identity : a -> a
identity = \x -> x

# Or will infer concrete type from usage
id = \x -> x  # Type depends on how it's used

Fix: Add type signatures for polymorphic functions


Testing Strategy

Property Testing: QuickCheck → Roc Expect

Haskell (QuickCheck):

import Test.QuickCheck

prop_reverse :: [Int] -> Bool
prop_reverse xs = reverse (reverse xs) == xs

prop_sortLength :: [Int] -> Bool
prop_sortLength xs = length (sort xs) == length xs

Roc (Expect):

# Inline property-style tests
expect
    xs = [1, 2, 3, 4, 5]
    List.reverse (List.reverse xs) == xs

expect
    xs = [3, 1, 4, 1, 5, 9]
    List.len (List.sortAsc xs) == List.len xs

# For comprehensive property testing, use external fuzzer
# or generate test cases

Limitations:

  • Roc: No built-in property testing framework
  • Use expect for inline tests
  • Generate test cases externally or use platform-provided fuzzing

Tooling

Haskell ToolRoc EquivalentNotes
GHCroc compilerCompiles to native or LLVM IR
GHCi (REPL)roc replInteractive REPL
Stack / CabalPlatformsDependency management via platforms
HSpec / Tastyroc testBuilt-in testing with expect
QuickCheckN/​ANo built-in property testing
hlintN/​ANo Roc linter yet
Hoogleroc docsGenerate docs from code

Examples

Example 1: Simple - Maybe to Tag Union

Before (Haskell):

data User = User { name :: String, age :: Int }

findUser :: Int -> Maybe User
findUser 1 = Just (User "Alice" 30)
findUser _ = Nothing

displayUser :: Int -> String
displayUser uid = case findUser uid of
    Just user -> "Found: " ++ name user
    Nothing -> "Not found"

After (Roc):

User : {
    name : Str,
    age : I64,
}

findUser : I64 -> [Some User, None]
findUser = \uid ->
    if uid == 1 then
        Some { name: "Alice", age: 30 }
    else
        None

displayUser : I64 -> Str
displayUser = \uid ->
    when findUser uid is
        Some user -> "Found: \(user.name)"
        None -> "Not found"

Example 2: Medium - Either Error Handling

Before (Haskell):

divide :: Double -> Double -> Either String Double
divide _ 0 = Left "Division by zero"
divide x y = Right (x / y)

validateAge :: Int -> Either String Int
validateAge age
    | age < 0 = Left "Age cannot be negative"
    | age > 150 = Left "Age too high"
    | otherwise = Right age

createUser :: String -> Int -> Either String User
createUser email age = do
    validAge <- validateAge age
    return $ User email validAge

After (Roc):

divide : F64, F64 -> Result F64 [DivByZero]
divide = \x, y ->
    if y == 0 then
        Err DivByZero
    else
        Ok (x / y)

validateAge : I64 -> Result I64 [NegativeAge, AgeTooHigh]
validateAge = \age ->
    if age < 0 then
        Err NegativeAge
    else if age > 150 then
        Err AgeTooHigh
    else
        Ok age

createUser : Str, I64 -> Result User [NegativeAge, AgeTooHigh]
createUser = \email, age ->
    validAge = validateAge! age
    Ok { email, age: validAge }

Example 3: Complex - IO Monad to Platform Task

Before (Haskell):

import System.IO
import Control.Exception

data Config = Config { port :: Int, host :: String }
    deriving (Show, Read)

readConfig :: FilePath -> IO (Either String Config)
readConfig path = catch
    (do
        content <- readFile path
        case reads content of
            [(config, "")] -> return $ Right config
            _ -> return $ Left "Invalid config format"
    )
    (\(e :: IOException) -> return $ Left $ show e)

runApp :: Config -> IO ()
runApp config = do
    putStrLn $ "Starting server on " ++ host config
    putStrLn $ "Port: " ++ show (port config)
    -- Actual server logic here

main :: IO ()
main = do
    result <- readConfig "config.txt"
    case result of
        Right config -> runApp config
        Left err -> putStrLn $ "Error: " ++ err

After (Roc):

import pf.Stdout
import pf.File
import pf.Task exposing [Task]

Config : {
    port : I64,
    host : Str,
}

readConfig : Str -> Task Config [FileReadErr, InvalidFormat Str]
readConfig = \path ->
    content = File.readUtf8! path
        |> Task.mapErr \_ -> FileReadErr

    # Parse JSON or custom format
    # For simplicity, assume JSON parsing available via platform
    config = parseConfig! content
        |> Task.mapErr \_ -> InvalidFormat "Invalid config format"

    Task.ok config

parseConfig : Str -> Result Config [ParseErr]
parseConfig = \content ->
    # Parsing logic (simplified)
    # In real code, use JSON parser
    Ok { port: 8080, host: "localhost" }

runApp : Config -> Task {} []
runApp = \config ->
    Stdout.line! "Starting server on \(config.host)"
    Stdout.line! "Port: \(Num.toStr config.port)"
    # Actual server logic here
    Task.ok {}

main : Task {} []
main =
    when readConfig "config.txt" is
        Ok config -> runApp! config
        Err FileReadErr -> Stdout.line! "Error: Could not read config file"
        Err (InvalidFormat msg) -> Stdout.line! "Error: \(msg)"

Limitations (lang-roc-dev gaps)

The following areas required external research due to incomplete coverage in lang-roc-dev:

  1. Zero/​Default Values: Roc has optional fields via tag unions, but no comprehensive Default trait equivalent
  2. Serialization Idioms: Encode/​Decode abilities mentioned but lacks practical examples (JSON, YAML)
  3. Build/​Deps: Package structure shown but no roc build, roc test, roc run command documentation

These gaps have been addressed in this skill through:

  • External Roc documentation research
  • Inference from Roc design philosophy
  • Comparison with similar languages

See issues #XXX, #YYY, #ZZZ for tracking improvements to lang-roc-dev.


See Also

For more examples and patterns, see:

  • meta-convert-dev - Foundational patterns with cross-language examples
  • convert-clojure-roc - Dynamic FP to static FP (similar paradigm shift)
  • lang-haskell-dev - Haskell development patterns
  • lang-roc-dev - Roc development patterns

Cross-cutting pattern skills:

  • patterns-concurrency-dev - STM vs Task models across languages
  • patterns-serialization-dev - JSON, YAML serialization patterns
  • patterns-metaprogramming-dev - Type classes vs abilities comparison

References

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View