npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

kokoirc

v0.2.8

Published

Modern terminal IRC client with inline image preview, SASL, scripting, encrypted logging, and theming — built with OpenTUI, React, and Bun

Readme

kokoIRC

A modern terminal IRC client built with OpenTUI, React, and Bun. Inspired by irssi, designed for the future.

Documentation | GitHub

Splash screen

Chat view with multiple networks

Help command list

Configuration and settings

Features

  • Full IRC protocol — channels, queries, CTCP, SASL, TLS, channel modes, ban lists
  • Inline image preview — kitty, iTerm2, sixel, and Unicode fallback with tmux support
  • irssi-style navigationEsc+1-9 window switching, /commands, aliases
  • Mouse support — click buffers/nicks, drag to resize sidepanels
  • Netsplit detection — batches join/part floods into single events
  • Flood protection — blocks CTCP and nick-change spam from botnets
  • Persistent logging — SQLite with optional AES-256-GCM encryption, FTS5 full-text search
  • Theming — irssi-compatible format strings with 24-bit color, custom abstracts
  • Scripting — TypeScript scripts with event bus, custom commands, IRC access
  • Single binary — compiles to a standalone executable with bun build --compile

Requirements

  • Bun v1.2+
  • A terminal with 256-color or truecolor support

Install

From npm

# Global install (adds `kokoirc` to your PATH)
bun install -g kokoirc

# Then run from anywhere
kokoirc
# Local install (in a project)
bun add kokoirc
bunx kokoirc

From source

git clone https://github.com/kofany/kokoIRC.git
cd kokoIRC
bun install

# Run directly
bun run start

# Or build a standalone binary
bun run build
./kokoirc

Configuration

Config lives at ~/.kokoirc/config.toml. Created on first run with defaults.

[general]
nick = "mynick"
username = "mynick"
realname = "kokoIRC user"
theme = "default"
flood_protection = true

[display]
nick_column_width = 8
scrollback_lines = 2000

[servers.libera]
label = "Libera"
address = "irc.libera.chat"
port = 6697
tls = true
autoconnect = true
channels = ["#kokoirc", "#secret mykey"]   # "channel key" syntax for keyed channels
autosendcmd = "MSG NickServ identify pass; WAIT 2000; MODE $N +i"
# sasl_user = "mynick"
# sasl_pass = "hunter2"

[image_preview]
enabled = true
max_width = 800
max_height = 400
protocol = "auto"     # "auto", "kitty", "iterm2", "sixel", "unicode"

[logging]
enabled = true
encrypt = false       # AES-256-GCM (key auto-generated in ~/.kokoirc/.env)
retention_days = 0    # 0 = keep forever
exclude_types = []    # filter: "event", "notice", "ctcp", etc.

[aliases]
wc = "/close"
j = "/join"

Commands

44 built-in commands. Type /help for the full list, /help <command> for details.

| Category | Commands | |----------|----------| | Connection | /connect, /disconnect, /quit, /server | | Channel | /join, /part, /close, /clear, /topic, /names, /invite, /list | | Messaging | /msg, /me, /notice, /action, /slap, /wallops | | Moderation | /kick, /ban, /unban, /kb, /kill, /mode | | Nick/Ops | /nick, /op, /deop, /voice, /devoice | | Media | /image, /preview | | User | /whois, /wii, /ignore, /unignore | | Info | /version, /stats | | Server | /quote (/raw), /oper | | Config | /set, /alias, /unalias, /reload | | Logging | /log status, /log search <query> | | Scripts | /script load, /script unload, /script list |

Keyboard Shortcuts

| Key | Action | |-----|--------| | Esc+1-9 | Switch to window 1-9 | | Esc+0 | Switch to window 10 | | Esc+Left/Right | Previous/next window | | Page Up/Down | Scroll chat history | | Tab | Nick completion | | Ctrl+Q | Quit |

Theming

Themes use TOML with an irssi-inspired format string language. Place custom themes in ~/.kokoirc/themes/ and set theme = "mytheme" in config.

[meta]
name = "Nightfall"
description = "Modern dark theme with subtle accents"

[colors]
accent = "#7aa2f7"
fg = "#a9b1d6"

[abstracts]
timestamp = "%Z6e738d$*%N"
msgnick = "%Z565f89$0$1%Z7aa2f7>%N%| "
action = "%Ze0af68* $*%N"

[formats.messages]
pubmsg = "{msgnick $2 $0}$1"
action = "{action $0} $1"

Format codes: %r red, %g green, %ZRRGGBB hex color, %_ bold, %u underline, $0 $1 $2 positional args, {abstract $args} template references.

Scripting

Scripts are TypeScript files loaded from ~/.kokoirc/scripts/. See examples/scripts/ for samples.

import type { KokoAPI } from "kokoirc/api"

export default function slap(api: KokoAPI) {
  api.command("slap", ({ args, buffer }) => {
    const target = args[0] ?? "everyone"
    api.irc.action(buffer.connectionId, buffer.name,
      `slaps ${target} around a bit with a large trout`)
  })
}

Scripts can register commands, listen to IRC events, send messages, and interact with the store:

export default function highlights(api: KokoAPI) {
  api.on("irc.privmsg", (event) => {
    if (event.message.highlight) {
      api.ui.addLocalEvent(event.bufferId,
        `Highlight from ${event.nick}: ${event.text}`)
    }
  })
}

Configure autoloading in config.toml:

[scripts]
autoload = ["slap", "spam-filter"]

Logging

Messages are stored in ~/.kokoirc/logs.db (SQLite WAL mode). The log system supports:

  • Batched writes — flushes at 50 messages or every second
  • Full-text search — FTS5 index for instant /log search results
  • Optional encryption — AES-256-GCM per message, key auto-generated
  • Read markers — per-client unread tracking (ready for web frontend)
  • Retention — auto-purge messages older than N days

Architecture

src/
├── index.tsx                # Entry point — creates OpenTUI renderer
├── app/
│   └── App.tsx              # Lifecycle: config → storage → theme → connect
├── core/
│   ├── init.ts              # Application initialization
│   ├── constants.ts         # Global constants
│   ├── irc/                 # IRC client, event binding, middlewares
│   ├── state/               # Zustand store (UI-agnostic)
│   ├── commands/            # Command registry, parser, help system
│   ├── config/              # TOML loader, defaults, merge logic
│   ├── storage/             # SQLite logging, encryption, queries
│   ├── scripts/             # Script loader, API, event bus
│   ├── theme/               # Format string parser, theme loader
│   ├── image-preview/       # Inline image display (kitty, iTerm2, sixel)
│   └── utils/               # Message ID generator
├── ui/                      # React components (no IRC imports)
│   ├── layout/              # AppLayout, TopicBar
│   ├── chat/                # ChatView, MessageLine
│   ├── input/               # CommandInput
│   ├── sidebar/             # BufferList, NickList
│   ├── statusbar/           # StatusLine
│   ├── overlay/             # ImagePreview
│   ├── splash/              # SplashScreen
│   ├── hooks/               # useStatusbarColors
│   └── ErrorBoundary.tsx    # React error boundary
└── types/                   # TypeScript definitions

The UI layer never imports from core/irc/ — all communication goes through the Zustand store. This keeps the architecture clean and makes it possible to drive the same store from a web frontend.

Development

bun run dev          # Start with --watch (auto-reload on file changes)
bun test             # Run test suite
bun run build        # Compile to standalone binary

License

MIT