Island
@nikcli-ai/island — NikcliIsland.app, the macOS Dynamic-Island-style notch HUD for live nikcli sessions.
Overview
@nikcli-ai/island (the Swift package under
packages/nikcli-island/) ships NikcliIsland.app — a
Dynamic-Island-style status HUD that draws over the macOS notch and shows the live
state of every nikcli session running on the machine. It is modeled on
Pookify
(the equivalent for Claude Code), rebuilt around nikcli's own event bus instead of a
one-way hook file.
Unlike every other package on the docs site, @nikcli-ai/island is a
Swift package — it does not participate in the JS build graph. Its only dependency
on nikcli itself is the file contract documented below.
NIKCLI_ISLAND_TEST_FORCE_DARWIN=1 flag exists only for the bridge's own
test suite and must never be set in production.
How it works
Three moving parts. The data flows from nikcli's process to the notch UI through a small JSON file contract that the Swift app polls.
- The bridge.
@nikcli-ai/util/island-bridgeis started unconditionally fromsrc/index.ts's top-level middleware. It listens onGlobalBus(src/bus/global.ts), which everyBus.publish(...)call already forwards to regardless of mode — so the island reflects any nikcli process running on the machine, not just the TUI. - The snapshot file. One JSON file per session under
~/Library/Application Support/NikcliIsland/state.d/, written atomically (temp file + rename). Each snapshot carries the session id, agent title, project, cwd, the writing process's pid, the local server port (when one is running), and a server URL. - The Swift app.
NikcliIsland.appreads the snapshot directory (port of Pookify'sSessionAggregator.swift) and draws the notch (IslandView.swift,NotchWindow.swift,IslandShape.swift,NSScreen+Notch.swift).
POST /permission/:requestID/reply to approve
or deny a permission request from the notch itself. Pookify can only display
permission state; the nikcli island can actually answer it.
Install
Build the Swift package and drop the app into /Applications:
# Inside the nikcli monorepo
./packages/nikcli-island/scripts/install.sh
The install.sh script runs swift build -c release, assembles
NikcliIsland.app, and installs it to /Applications. An
uninstall.sh script is shipped alongside it.
To enable the bridge, start any nikcli command with the --island flag:
nikcli --island
nikcli tui --island
nikcli serve --island
The first session activity wakes the app via
open -g -b com.nikcli.island; subsequent publishes are no-ops.
State file contract
Each *.json file under ~/Library/Application Support/NikcliIsland/state.d/
is one session's snapshot. The Swift app polls the directory and renders the union.
The schema is owned by packages/nikcli-island/Sources/IslandCore/ on the
read side and mirrored in src/plugin/island/bridge.ts on the write side.
| Field | Source | Display |
|---|---|---|
idle | session.status → idle | Empty pill on the notch. |
thinking | session.status → busy / retry | "Thinking…" or "Retrying (N)…". |
tool | message.part.updated tool status | Friendly label plus file basename. Lingers for ~1.9s after completion so the last action stays visible. |
permission | permission.asked | "Awaiting permission" plus the request id for one-tap approval from the notch. |
error | session.error | "Error" pill. |
done | (reserved) | Used by the native app to mark a completed row before archival. |
Beyond the state, each snapshot also carries the parent session id, agent title,
project, cwd, the writing process pid, the local server port (when one is running),
and a server URL — enough for the native app to invoke
POST /permission/:requestID/reply directly when you approve a pending
request from the notch.
IslandBridge.write in src/plugin/island/bridge.ts.
Permission reply from the notch
The whole reason nikcli's island is more useful than Pookify's is the
bidirectional channel. Pookify's hook is one-way: it can render
permission.asked, but it has no way to answer it. nikcli's island can,
because the writing process is a live nikcli with a real HTTP API and a known
server URL.
# What the island POSTs when you tap "Allow" on the notch
curl -X POST http://127.0.0.1:4096/permission/per_example/reply \
-H "Content-Type: application/json" \
-d '{"reply":"once"}' The reply handler is the same one the TUI and mobile composer use; see Permissions for the full request / reply lifecycle.
Disable & rollback
The bridge is opt-in per invocation. To disable the bridge without uninstalling the native app, set the environment variable:
NIKCLI_ISLAND_DISABLE=1 nikcli
Toggling from inside the TUI uses the island feature plugin, which
calls IslandBridge.setEnabled(). Turning it off clears the current
snapshots and stops writing; turning it back on resumes through the already-
registered listener without any race window.
To uninstall the native app entirely:
./packages/nikcli-island/scripts/uninstall.sh NIKCLI_ISLAND_TEST_FORCE_DARWIN=1 flag exists only for the bridge's
own test suite. Never set it in production: the feature is Windows/Linux no-op by
design and forcing it on a non-macOS host would silently write macOS-only paths.
Source anchors
| Concern | File |
|---|---|
| Swift package | packages/nikcli-island/ |
| Install / uninstall scripts | packages/nikcli-island/scripts/install.sh, uninstall.sh |
| Snapshot schema (read side) | packages/nikcli-island/Sources/IslandCore/ |
| Notch rendering | packages/nikcli-island/Sources/NikcliIsland/ |
| Bridge (write side) | @nikcli-ai/util/island-bridge |
| TUI plugin (toggle) | packages/tui/src/feature-plugins/island/index.tsx |
| Worker shutdown hook | packages/nikcli/src/cli/cmd/tui/worker.ts → shutdown RPC |