# 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](https://github.com/eyadhammouda/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.

macOS only

Island is a no-op on Windows and Linux. The native app is a macOS-only notch UI. The `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.

1.  **The bridge.** `@nikcli-ai/util/island-bridge` is started unconditionally from `src/index.ts`'s top-level middleware. It listens on `GlobalBus` (`src/bus/global.ts`), which every `Bus.publish(...)` call already forwards to regardless of mode — so the island reflects any nikcli process running on the machine, not just the TUI.
2.  **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.
3.  **The Swift app.** `NikcliIsland.app` reads the snapshot directory (port of Pookify's `SessionAggregator.swift`) and draws the notch (`IslandView.swift`, `NotchWindow.swift`, `IslandShape.swift`, `NSScreen+Notch.swift`).

Bidirectional — not just display

Because the snapshot also carries the writer's local server port and URL, the island can call back into `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`:

Terminal

bash 

```
# Inside the nikcli monorepo
      ./packages/nikcli-island/scripts/install.sh
```

$ shell · paste into your terminal

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:

Terminal

bash 

```
nikcli --island
      nikcli tui --island
      nikcli serve --island
```

$ shell · paste into your terminal

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.

Atomic writes

The bridge writes snapshots with the standard temp-file-then-rename idiom so a half- written file is never observable by the polling app. See `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.

Terminal

bash 

```
# 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"}'
```

$ shell · paste into your terminal

The reply handler is the same one the TUI and mobile composer use; see [Permissions](/docs/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:

Terminal

bash 

```
NIKCLI_ISLAND_DISABLE=1 nikcli
```

$ shell · paste into your terminal

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:

Terminal

bash 

```
./packages/nikcli-island/scripts/uninstall.sh
```

$ shell · paste into your terminal

Don't set NIKCLI\_ISLAND\_TEST\_FORCE\_DARWIN

The `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 |

---

Source: https://nikcli-ai.dev/docs/island
