| name | kukicha |
| description | Help write, debug, and understand Kukicha code - a near-superset of Go with pipes, onerr, enums, and readable operators. Use when working with .kuki files, discussing Kukicha syntax, error handling with onerr, pipe operators, or the Kukicha compiler/transpiler. |
Kukicha Language Skill
Kukicha (茎) is a near-superset of Go — most Go compiles as-is, with a few exceptions (range, case/default, struct {}, chan T, goto, parenthesized const ( ... )) that have Kukicha replacements. Kukicha adds pipes, onerr, enums, if-expressions, and readable operators on top. Full language reference is in docs/SKILL.md.
For compiler errors and diagnostics, read .agent/skills/kukicha/troubleshooting.md.
Gotchas
{error} vs {err} in onerr blocks
Inside any onerr handler, the caught error is always named error, never err. Using {err} is a compile-time error. To use a custom name, write onerr as <ident> — then both {error} and {<ident>} are valid inside that block.
# CORRECT — canonical name
result := fetch.Get(url) onerr
print("failed: {error}")
return
# CORRECT — named alias (onerr as e)
result := fetch.Get(url) onerr as e
print("failed: {e}") # {e} and {error} both work here
return
# WRONG — compiler rejects {err} inside onerr
result := fetch.Get(url) onerr
print("failed: {err}") # error: use {error} not {err} inside onerr
return
kukicha init required before stdlib imports
kukicha init
Auto-imports for interpolated strings
The compiler auto-imports fmt when any string interpolation is used, including error "" literals with {expr}. No manual import "fmt" is needed.
# fmt is auto-imported — no manual import needed
func doThing(name: string) error
return error "failed for {name}"
in / not in — membership over lists, maps, and strings
# Lists — needle == element
if item in items
...
# Maps — key lookup
if "alice" in users
...
# Strings — substring search
if "lo" in "hello"
...
# Negate with 'not in'
if user not in banned
...
# 'in' also drives for loops
for item in items
process(item)
For lists, the element type must be comparable (==-able). Slices, maps,
and functions as element types are rejected — reach for slice.Contains
with a custom predicate in those cases.
fetch.Json / fetch.GetJson — explicit type argument
Use of T from x to pass the decode target as a compile-time type argument.
| Form | Decodes |
|---|
fetch.Json of list of Repo from resp | JSON array → []Repo |
fetch.Json of Repo from resp | JSON object → Repo |
fetch.Json of map of string to string from resp | JSON object → map[string]string |
fetch.GetJson of list of Repo from url | GET + CheckStatus + decode |
Wrong shape = runtime decode error with no compile-time warning.
Struct literals support brace and indentation forms
# Braced multiline
todo := Todo{
id: 1,
title: "Learn Kukicha",
}
# Indented multiline (no braces or commas required)
todo := Todo
id: 1
title: "Learn Kukicha"
Piped switch — pipe a value into a switch
user.Role |> switch
when "admin"
grantAccess()
when "guest"
denyAccess()
otherwise
checkPermissions()
The compiler wraps the switch in an IIFE: func() { switch role { ... } }().
Pipeline-level onerr — onerr at end of pipe chains
processed := data
|> parse.CSVRecords()
|> fetch.EnrichWithDB()
|> validate.Safe()
onerr panic "pipeline failed: {error}"
If any function in the pipe returns a Go error, the pipeline short-circuits to the onerr block. The compiler generates if err != nil checks between each stage.
stdlib/iterator — lazy iteration via Go 1.23 iter.Seq
import "stdlib/iterator"
names := repos
|> iterator.Values()
|> iterator.Filter((r: Repo) => r.Stars > 100)
|> iterator.Map((r: Repo) => r.Name)
|> iterator.Take(5)
|> iterator.Collect()
Functions: Values, Filter, Map, FlatMap, Take, Skip, Enumerate, Chunk, Zip, Reduce, Collect, Any, All, Find.
Enums — declaration, dot access, and exhaustiveness
enum Status
OK = 200
NotFound = 404
Error = 500
status := Status.OK # Dot access → transpiles to StatusOK in Go
switch status
when Status.OK
print("ok")
when Status.NotFound
print("not found")
when Status.Error
print("error")
# Omitting a case without 'otherwise' → compiler warning
- Underlying type (int or string) inferred from values — all cases must match
- Integer enums warn if no case has value 0 (zero-value safety)
- Auto-generated
String() method (skipped if user defines one)
- Cross-package enums from stdlib are resolved automatically
Shorthand .Field / .Method() — pipe context only
# CORRECT — shorthand in a pipe
name := user |> .Name
result := data |> .Process()
# WRONG — shorthand outside a pipe (compile error)
name := .Name
result := .Process()
Shorthand dot syntax is syntactic sugar for pipe receivers. Using it outside a pipe expression is a compile error: shorthand .X can only be used in a pipe expression.
key in stdlib source is a compiler placeholder — not user syntax
When reading stdlib .kuki files you will see key in function signatures. Do not use it in application code — it is a compiler-reserved name for a second generic type parameter (K comparable in Go).