Output and logging
Write lines through the shared output system and reuse styles where needed.
oscli routes user-facing output through one output layer so logs, prompts,
boxes, progress, and links all stay visually aligned.
Logging
Use cli.log() when you want one line in the normal output flow.
If you call cli.log(level, message), the returned chain lets you apply text
modifiers before the message is written.
await cli.run(async () => {
cli.log("ready");
cli.log("warn", "review this before production").bold().underline().flush();
cli.success("Done.");
}); ready
⚠ review this before production
✓ Done.Prop
Type
Steps
Use cli.step() to render a step with a status icon. Compose multiple steps
for step lists and rail patterns.
await cli.run(async () => {
cli.step("Creating project...", "active");
cli.step("Installing dependencies...", "active");
cli.step("Build complete", "done");
});◆ Creating project...
◆ Installing dependencies...
◇ Build completeStatus options: "active" (◆), "done" (◇), "error" (✗), "warning" (⚠), "pending" (○).
Badge logging
Use cli.badge() for [TAG] prefixed log lines — common in build tools and CI output.
await cli.run(async () => {
cli.badge("info", "Pulling image node:20-alpine");
cli.badge("warn", "Layer cache expired");
cli.badge("ok", "Build finished in 4.2s");
});[INFO] Pulling image node:20-alpine
[WARN] Layer cache expired
[OK] Build finished in 4.2sTimestamped logging
Use cli.timestamp() for log stream / tail style output with real-time timestamps.
await cli.run(async () => {
cli.timestamp("info", "Server started on port 3000");
cli.timestamp("warn", "Rate limit approaching: 90/100");
cli.timestamp("error", "Database connection dropped");
});12:04:01 INFO Server started on port 3000
12:04:05 WARN Rate limit approaching: 90/100
12:04:07 ERROR Database connection droppedInline output
Use cli.raw(), cli.lines(), and cli.write() for tight output without
the section spacing that cli.log() adds.
cli.raw(line)— one line, no gapcli.lines(lines)— multiple lines, no gaps between themcli.write(content)— writes pre-formatted content (e.g. fromcli.table()) without extra indent/color
await cli.run(async () => {
cli.lines([
"┌ Connecting",
"│",
"├─┬ Auth",
"│ ├── Checking token... ok",
"│ └── Resolved as user@app",
"│",
"└ Ready",
]);
});┌ Connecting
│
├─┬ Auth
│ ├── Checking token... ok
│ └── Resolved as user@app
│
└ ReadyReusable styles
Use cli.style() when you want to build a style once and reuse it across
multiple writes.
await cli.run(async () => {
const callout = cli.style().color("cyan").bold();
cli.log("info", callout.render("important"));
cli.log(callout.render("plain line with shared styling"));
}); ℹ important
plain line with shared stylingProp
Type
Exit with hints
Use cli.exit() when the command cannot continue and you want a concrete next
step in the error output.
cli.exit("package.json not found.", {
hint: "Run this command from the project root.",
code: "not_found",
}); ✗ package.json not found.
→ Run this command from the project root.Inline confirm
Use cli.confirm() when you need one simple confirmation in the middle of a
flow without defining a named prompt.
await cli.run(async () => {
const approved = await cli.confirm("Deploy now?", false);
if (!approved) {
cli.exit("Cancelled.");
}
}); Deploy now?
› (y/N) _cli.exit() writes to stderr. cli.log("error", ...) also writes to stderr.
Other output stays on stdout.