magpie-lang/magpie/magpie

Claude Code and agent skill by magpie-lang/magpie

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Comprehensive guide for writing Magpie (.mp) programs, debugging compiler errors, and using the Magpie CLI. Designed for agents who have never seen Magpie before.

Install

npx vskill@latest install magpie-lang/magpie/magpie

SKILL.md

Magpie Language Programming Guide

Use this skill whenever you write, debug, or review Magpie .mp programs.


0) Non-negotiable Rules

  1. Binary-first: treat compiler diagnostics as ground truth.
  • Run magpie explain <CODE> for any error code you don't recognize.
  • Use --output json for machine-readable diagnostics.
  1. Diagnose by error code first, then apply the smallest fix.
  2. Smallest reproducer first -- one tiny .mp file, then grow.
  3. Change one dimension at a time (syntax, then types, then ownership, then backend).
  4. Prove fixes with commands and exit codes.

1) What is Magpie

Magpie is a compiled, SSA-based language with explicit ownership, ARC memory management, and first-class GPU support. Programs are written in a low-level IR-like textual format (CSNF).

Key characteristics:

  • SSA form -- every value is defined once, used by name (%x, %y)
  • Explicit ownership -- you manually borrow.shared, borrow.mut, share, clone.shared
  • ARC-managed heap -- the compiler inserts reference counting; you manage ownership transitions
  • Block-structured -- functions contain labeled basic blocks (bb0:, bb1:) with explicit terminators
  • No implicit conversions -- types must match exactly; const suffixes must match declared types
  • 5 GPU backends -- SPIR-V, Metal (MSL), PTX (CUDA), HIP (AMD), WGSL (WebGPU)

How Magpie differs from familiar languages:

  • There are no expressions or statements -- only SSA instructions and terminators
  • Control flow uses br, cbr, switch -- not if/else/for/while
  • Variables are SSA locals (%name) -- assigned exactly once
  • Functions are @name -- always prefixed with @
  • Types are TName -- user-defined types start with T
  • Comments use ; (not // or #); doc comments use ;;

2) Module Structure

Every .mp file must have a strict header in exactly this order:

mp
module <module.path>
exports { <exported symbols> }
imports { <import groups> }
digest "<hex string>"

The order is mandatory. Swapping any header line causes a parse error (MPP0002).

Minimal template (copy-paste this to start any program)
mp
module demo.main
exports { @main }
imports { }
digest "0000000000000000"
fn @main() -> i32 {
bb0:
ret const.i32 0
}
Exports

List functions (@name) and types (TName) the module makes visible:

mp
exports { @main, @helper, TPoint }
Imports

Group imports by source module:

mp
imports { std.io::{@println}, util.math::{@sum, TVector} }

Use imported functions with their full qualified path in calls:

mp
call_void std.io.@println { args=[%msg] }
Digest

A hex string for content hashing. Use "0000000000000000" as a placeholder during development.


3) Functions

Function kinds
mp
fn @name(%param: Type) -> ReturnType { ... }
async fn @name(%param: Type) -> ReturnType { ... }
unsafe fn @name(%param: Type) -> ReturnType { ... }
gpu fn @name(%param: Type) -> unit target(msl) { ... }
Parameters and return types
mp
fn @add(%a: i64, %b: i64) -> i64 {
bb0:
%r: i64 = i.add { lhs=%a, rhs=%b }
ret %r
}

Parameters are SSA names (%name: Type). Return type follows ->.

Meta blocks (optional)
mp
fn @compute(%x: i64) -> i64 meta {
uses { @helper, @util }
effects { io, alloc }
cost { time=5, space=2 }
} {
bb0:
%r: i64 = call @helper { x=%x }
ret %r
}
Basic blocks and terminators

Every function body contains one or more blocks. Each block has a label (bbN:) and ends with exactly one terminator.

bb0: ; block label (bb followed by a number)
<instructions> ; zero or more SSA assignments or void ops
<terminator> ; exactly one: ret, br, cbr, switch, or unreachable
Terminators
TerminatorSyntaxDescription
retret %value or ret const.i32 0Return a value
ret (void)retReturn unit (for -> unit functions)
brbr bb1Unconditional branch
cbrcbr %cond bb_true bb_falseConditional branch (%cond must be bool)
switchswitch %val { case 0 -> bb1 case 1 -> bb2 } else bb3Multi-way branch
unreachableunreachableMark unreachable code path
Branching example
mp
fn @abs(%x: i64) -> i64 {
bb0:
%neg: bool = icmp.slt { lhs=%x, rhs=const.i64 0 }
cbr %neg bb1 bb2
bb1:
%zero: i64 = const.i64 0
%r: i64 = i.sub { lhs=%zero, rhs=%x }
ret %r
bb2:
ret %x
}
Phi nodes (merging values from multiple predecessors)
mp
fn @max(%a: i64, %b: i64) -> i64 {
bb0:
%cond: bool = icmp.sgt { lhs=%a, rhs=%b }
cbr %cond bb1 bb2
bb1:
br bb3
bb2:
br bb3
bb3:
%result: i64 = phi i64 { [bb1:%a], [bb2:%b] }
ret %result
}

phi selects a value based on which predecessor block was taken. Borrow values cannot appear in phi nodes (MPO0102).


4) Type System

4.1 Primitive types
CategoryTypes
Signed integersi1 i8 i16 i32 i64 i128
Unsigned integersu1 u8 u16 u32 u64 u128
Floatsf16 f32 f64 bf16
Booleanbool
Unitunit
4.2 Builtin heap types
TypeDescription
StrUTF-8 string (built-in hash, eq, ord -- no impl needed)
Array<T>Dynamic array
Map<K, V>Hash map (K must have hash + eq impls)
TOption<T>Optional value -- variants: Some { v: T }, None { }
TResult<Ok, Err>Result value -- variants: Ok { v: Ok }, Err { e: Err }
TStrBuilderMutable string builder
TMutex<T>Mutual exclusion lock
TRwLock<T>Reader-writer lock
TCell<T>Interior mutability cell
TFuture<T>Async future
TChannelSend<T>Channel send endpoint
TChannelRecv<T>Channel receive endpoint
TCallable<TSigName>First-class callable (closure/function pointer)
4.3 Ownership modifiers

Place these before a type to change ownership mode:

ModifierMeaningExample
(none)Unique owner (default)%p: TPoint
sharedReference-counted shared%s: shared TPoint
borrowShared read-only borrow%b: borrow TPoint
mutborrowExclusive mutable borrow%m: mutborrow TPoint
weakWeak reference (non-owning)%w: weak TPoint

Important restrictions:

  • TOption and TResult reject shared and weak prefixes (MPT0002, MPT0003)
  • Borrows cannot cross block boundaries, appear in phi, or be returned from functions
4.4 User-defined types
Heap struct (reference-counted, can contain any types)
mp
heap struct TPoint {
field x: i64
field y: i64
}
Value struct (stack-allocated, cannot contain heap handles)
mp
value struct TColor {
field r: u8
field g: u8
field b: u8
}

If a value struct field contains a heap type (like Str), you get MPT1005. Use heap struct instead.

Heap enum
mp
heap enum TShape {
variant Circle { field radius: f64 }
variant Rect { field w: f64, field h: f64 }
}
Value enum (deferred in v0.1 -- use heap enum instead)

value enum will produce MPT1020. Use heap enum for now.

4.5 Signatures and callables
mp
sig TBinaryOp(i64, i64) -> i64

Then use TCallable<TBinaryOp> as the type for first-class function values.

4.6 Raw pointers
mp
rawptr<T> ; e.g., rawptr<i64>, rawptr<u8>

Raw pointer ops require unsafe context.

4.7 Type hierarchy diagram
Types
┌───────────────┼───────────────┐
Primitive Builtin User-defined
i8..i128 Str, Array<T> heap struct
u8..u128 Map<K,V> value struct
f16 f32 f64 TOption<T> heap enum
bf16 bool TResult<Ok,Err> (value enum: v0.1 deferred)
unit TStrBuilder
TMutex<T> ... rawptr<T>
TCallable<TSig>
4.8 Trait requirements for collections
TypehasheqordNotes
All primitivesyesyesyesBuilt-in
boolyesyesyesBuilt-in
StryesyesyesBuilt-in -- no explicit `impl` needed
User heap structnononoMust provide explicit impl declarations
User heap enumnononoMust provide explicit impl declarations

Map<Str, V> works with no extra code. Map<TMyStruct, V> requires:

mp
fn @hash_my(%v: borrow TMyStruct) -> u64 { bb0: ret const.u64 0 }
impl hash for TMyStruct = @hash_my
fn @eq_my(%a: borrow TMyStruct, %b: borrow TMyStruct) -> bool { bb0: ret const.bool true }
impl eq for TMyStruct = @eq_my

Trait impl signature requirements:

  • hash: (%v: borrow T) -> u64
  • eq: (%a: borrow T, %b: borrow T) -> bool
  • ord: (%a: borrow T, %b: borrow T) -> i32
4.9 Extern declarations (FFI)
mp
extern "c" module libc {
fn @malloc(%size: u64) -> rawptr<u8> attrs { returns="owned" }
fn @free(%ptr: rawptr<u8>) -> unit
}

Extern functions returning rawptr must have attrs { returns="owned" } or attrs { returns="borrowed" } (MPF0001).

4.10 Globals
mp
global @MAX_SIZE: i64 = const.i64 1024

5) Ownership & Borrowing Model

5.1 Ownership state machine
┌──────────────────────┐
│ Unique │ <── initial state after `new`
│ (sole owner) │
└───┬────────┬─────┬──┘
│ │ │
share{} │ │ │ borrow.mut{}
(consumes │ │ │
original) │ │ v
v │ ┌──────────────────┐
┌──────────┐ │ │ MutBorrow │
│ Shared │ │ │ (exclusive r/w) │
│(ref-cnt) │ │ │ block-scoped only │
└────┬─────┘ │ └──────────────────┘
│ │
clone │ │ borrow.shared{}
.shared{} │ │
v v
┌──────────┐ ┌──────────────────┐
│ Shared │ │ Borrow │
│ (copy) │ │ (shared r/o) │
└──────────┘ │ block-scoped │
└──────────────────┘
5.2 Key rules
RuleConsequence
share { v=%x }Consumes %x. %x is dead after this.
borrow.mut { v=%x }Creates exclusive mutable access, scoped to current block only
borrow.shared { v=%x }Creates shared read access, scoped to current block only
clone.shared { v=%x }Creates a new shared ref. Original survives.
Borrows cannot cross block boundariesMPO0101
Borrows cannot appear in phi nodesMPO0102
Borrows cannot be returned from functionsMPHIR03
Cannot use a value after it has been moved/consumedMPO0007
Cannot move a value while a borrow is activeMPO0011
5.3 Borrow lifecycle per block
Block entry
│
│ %x: TPoint = ... (unique value)
│
│ %xb: borrow TPoint = (borrow starts here ─┐
│ borrow.shared { v=%x } │ valid scope
│ │
│ getfield { obj=%xb, field=y } (OK: same block) │
│ │
│ br bb1 (block exit ──────────┘ borrow dies)
│
v
bb1:
│ %xb is DEAD here. Cannot reference it.
│ To borrow again: %xb2 = borrow.shared { v=%x }
5.4 Correct multi-block borrow pattern

Wrong -- borrow crosses block boundary (MPO0101):

mp
bb0:
%pb: borrow TPoint = borrow.shared { v=%p }
br bb1
bb1:
%x: i64 = getfield { obj=%pb, field=x } ; ERROR: %pb from bb0

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

  • SKILL.md in the repositoryCheckingSKILL.md

Popularity

Repository stars
49
Repository forks
3
7-day trend score
20

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 18 Mar 2026, 7 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/magpie-lang/magpie/magpie/badge)](https://verified-skill.com/skills/magpie-lang/magpie/magpie)

Something wrong with this skill or its page? Tell us.