Build a CLI Tool
This tutorial walks you through building a complete command-line tool with Zerolang. You will create a package, parse arguments, read and write files, handle errors, and cross-compile to Linux.
Prerequisites: Zerolang installed (zero --version works).
Time: ~30 minutes
Step 1: Create a Package
zero new cli mytool
cd mytool
This creates:
mytool/
├── zero.json
└── src/
└── main.0
The zero.json manifest:
{
"package": { "name": "mytool", "version": "0.1.0" },
"targets": { "cli": { "kind": "exe", "main": "src/main.0" } }
}
Step 2: Read Command-Line Arguments
Edit src/main.0:
use std.args
pub fn main(world: World) -> Void raises {
let name: Maybe<String> = std.args.get(1)
if name.has {
check world.out.write("Hello, " + name.value + "!\n")
} else {
check world.out.write("Usage: mytool <name>\n")
}
}
std.args.get returns Maybe<String> because the argument may not exist. Use .has to check presence and .value to access it.
Run it:
zero run src/main.0 -- Alice
Output: Hello, Alice!
Without arguments:
zero run src/main.0
Output: Usage: mytool <name>
Step 3: Write to a File
use std.args
use std.fs
pub fn main(world: World) -> Void raises {
let name: Maybe<String> = std.args.get(1)
if name.has {
let output: String = "Hello, " + name.value + "!\n"
let written: usize = std.fs.write("output.txt", output)
if written > 0 {
check world.out.write("Wrote " + name.value + " to output.txt\n")
}
} else {
check world.out.write("Usage: mytool <name>\n")
}
}
std.fs.write is a hosted API — it works on the host target but reports TAR002 on non-host targets.
Step 4: Handle Errors Explicitly
Zero's error handling uses raises and check. Functions that can fail declare raises:
use std.args
use std.fs
fn writeGreeting(name: String) -> Void raises [Io] {
let output: String = "Hello, " + name + "!\n"
let written: usize = std.fs.write("output.txt", output)
if written == 0 {
raise Io
}
}
pub fn main(world: World) -> Void raises [Io] {
let name: Maybe<String> = std.args.get(1)
if name.has {
writeGreeting(name.value)
check world.out.write("Done\n")
} else {
check world.out.write("Usage: mytool <name>\n")
}
}
The raises [Io] declaration tells the compiler (and any agent reading the code) exactly which errors this function can produce.
Step 5: Add a Test
Add a test block to src/main.0:
test "greeting format" {
let greeting: String = "Hello, " + "Zero" + "!\n"
expect (greeting == "Hello, Zero!\n")
}
Run tests:
zero test src/main.0
Step 6: Build an Executable
zero build --emit exe --target host src/main.0 --out ./mytool
This produces a native executable. Run it:
./mytool Alice
Step 7: Cross-Compile to Linux
zero build --emit exe --target linux-musl-x64 src/main.0 --out ./mytool-linux
Check target readiness:
zero doctor --json
The output shows host checks plus targetToolchains and the per-target readiness matrix.
Step 8: Inspect the Binary Size
zero size --json src/main.0
The output includes:
graph— graph identity informationprofileSemantics— profile semanticsprofileCatalog— profile catalogprofileBudget— profile budgetsafetyFacts— safety factsbackendProfile— backend profilebackendComparison— backend comparisonsizeBreakdown— size per sectionretentionReasons— why each helper is retainedoptimizationHints— suggestions for reducing size
Step 9: Let an Agent Check Your Code
zero check --json src/main.0
If there are issues, the agent reads the structured diagnostics and applies fixes:
zero fix --plan --json src/main.0
The Complete Package
Your final src/main.0:
use std.args
use std.fs
fn writeGreeting(name: String) -> Void raises [Io] {
let output: String = "Hello, " + name + "!\n"
let written: usize = std.fs.write("output.txt", output)
if written == 0 {
raise Io
}
}
pub fn main(world: World) -> Void raises [Io] {
let name: Maybe<String> = std.args.get(1)
if name.has {
writeGreeting(name.value)
check world.out.write("Done\n")
} else {
check world.out.write("Usage: mytool <name>\n")
}
}
test "greeting format" {
let greeting: String = "Hello, " + "Zero" + "!\n"
expect (greeting == "Hello, Zero!\n")
}
What You Learned
- Packages:
zero newcreates a package withzero.jsonandsrc/ - Arguments:
std.args.getreturnsMaybe<String>for safe access - File I/O:
std.fs.writefor hosted file operations - Error handling:
raises,check, and named error sets - Testing:
testblocks withexpectassertions - Building:
zero build --emit exefor native executables - Cross-compilation:
--target linux-musl-x64for Linux binaries - Size inspection:
zero sizefor binary analysis - Agent workflow:
zero check --jsonandzero fix --plan --json
Next Steps
- Read CLI Reference for all commands
- Explore Standard Library for more modules
- Try Let Agent Edit Your Code for the agent workflow