prompts/skills/clojure-coffi/SKILL.md
Coffi FFI library for calling native C code from Clojure via Panama FFM API (JDK 22+). Use when: wrapping native C libraries, calling native functions, working with off-heap memory and arenas, etc.
npx skillsauth add ramblurr/nix-devenv clojure-coffiInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Coffi wraps the Panama Foreign Function & Memory API for calling native C code from Clojure.
Add to deps.edn:
;; use gh cli to check latest commit with `gh browse -c -n IGJoshua/coffi`
io.github.IGJoshua/coffi {:git/sha "ae3e38a449c88b998db98b0d4bffa9908dea1c79"}
JVM argument required:
--enable-native-access=ALL-UNNAMED
Or in deps.edn alias:
{:aliases {:dev {:jvm-opts ["--enable-native-access=ALL-UNNAMED"]}}}
Prep deps
clojure -Xdeps prep :aliases '[:dev :test]'
(require '[coffi.ffi :as ffi :refer [defcfn]]
'[coffi.mem :as mem])
;; Wrap a native function
(defcfn strlen
strlen [::mem/c-string] ::mem/long)
(strlen "hello") ;; => 5
;; Load a library
(ffi/load-system-library "z") ;; System library
(ffi/load-library "path/to/lib.so") ;; From path
(defcfn var-name
"docstring"
native_symbol_name [arg-types...] return-type)
;; With wrapper logic
(defcfn var-name
"native_symbol" [arg-types...] return-type
native-fn ;; Binds the raw native function
[clj-args...] ;; Clojure argument list
(body...)) ;; Wrapper body that calls native-fn
| Coffi Type | C Type | Notes |
|------------|--------|-------|
| ::mem/byte | int8_t | |
| ::mem/short | int16_t | |
| ::mem/int | int32_t | |
| ::mem/long | int64_t | |
| ::mem/float | float | |
| ::mem/double | double | |
| ::mem/pointer | void* | |
| ::mem/c-string | char* | Null-terminated |
| ::mem/void | void | Return only |
| [::mem/struct [...]] | struct | See below |
| [::mem/array type n] | type[n] | Fixed size |
| [::ffi/fn [args] ret] | function ptr | Callbacks |
For complete type reference: references/types.md
(require '[coffi.layout :as layout])
;; Always use layout/with-c-layout for FFI structs
(mem/defalias ::my-struct
(layout/with-c-layout
[::mem/struct
[[:name ::mem/c-string]
[:count ::mem/int]
[:value ::mem/double]]]))
;; Use in function
(defcfn process-data
process_data [::my-struct] ::mem/int)
(process-data {:name "test" :count 5 :value 3.14})
Always use confined-arena with with-open for temporary allocations:
(with-open [arena (mem/confined-arena)]
(let [ptr (mem/serialize data type arena)]
(native-fn ptr)))
;; Memory freed automatically
Arena types:
confined-arena - Thread-local, freed on close (most common)shared-arena - Multi-thread, freed on closeauto-arena - GC-managedglobal-arena - Never freedFor details: references/memory.md
(defcfn open-resource
"open_resource" [::mem/c-string ::mem/pointer] ::mem/int
native-open
[name]
(with-open [arena (mem/confined-arena)]
(let [out-ptr (mem/alloc-instance ::mem/pointer arena)
code (native-open name out-ptr)]
(if (zero? code)
(mem/deserialize-from out-ptr ::mem/pointer)
(throw (ex-info "Failed" {:code code}))))))
(defcfn bind-text
"bind_text" [::mem/pointer ::mem/c-string ::mem/int] ::mem/int
native-bind
[handle text]
(let [bytes (.getBytes text "UTF-8")]
(native-bind handle text (count bytes))))
;; Serialize array
(mem/serialize [1 2 3 4] [::mem/array ::mem/int 4] arena)
;; For raw Java arrays (better performance)
(mem/serialize (int-array [1 2 3 4]) [::mem/array ::mem/int 4 :raw? true] arena)
(defcfn set-callback
set_callback [[::ffi/fn [::mem/int] ::mem/int]] ::mem/void)
(set-callback (fn [x] (* x 2)))
For more examples: references/examples.md
Read these references depending on what you are doing. You should read at least one of the now, if not all of them
testing
Use this OCP when executing or preparing to execute commands that change a live or important system, service reloads/restarts, package changes, deployments, migrations, firewall/network/access changes, credential rotation, NixOS switch/test/boot/deploy, or incident mitigation. It guides safe operations with a persisted ledger for scope, preflight, baseline, rollback, validation, and evidence.
development
Create new agent skills with proper structure, progressive disclosure, and bundled resources. Use when user wants to create, write, or build a new skill.
documentation
Naming conventions for workflow documents in prompts/. Use when creating plans, PRDs, research reports, idea capture or other workflow documents. Triggers on (1) creating new planning documents, (2) naming PRDs or research reports, (3) questions about document organization in prompts/.
testing
Grilling session that challenges your plan against the existing domain model, sharpens terminology, and updates documentation (CONTEXT.md, ADRs) inline as decisions crystallise. Use when user wants to stress-test a plan against their project's language and documented decisions.