From narnia
Start, stop, restart, update, and check the status of the Narnia web UI — a Blazor Server app for browsing your GitHub Copilot CLI session history on http://127.0.0.1:5244. Runs from a published copy with a cross-session run-state file and a graceful HTTP shutdown, so rebuilds and updates never hit a file lock and any session can control a server another session started.
How this skill is triggered — by the user, by Claude, or both
Slash command
/narnia:narnia-web-serverThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Manage the lifecycle of the Narnia server (a Blazor Server app) on `http://127.0.0.1:5244`. The
Manage the lifecycle of the Narnia server (a Blazor Server app) on http://127.0.0.1:5244. The
same process also serves the MCP endpoint at /mcp (Streamable HTTP), so starting this one
server makes narnia's session-history MCP tools available to every Copilot CLI session — they
all share this single instance (the plugin's .mcp.json points there as type: http).
open_narnia_ui MCP tool failed or is unavailable.The plugin ships a sessionStart hook (hooks.json) that keeps the one server alive: at the
start of every Copilot CLI session it checks http://127.0.0.1:5244/health and, if the server is
down and already published to the run dir, relaunches it (a fast no-op when it is already up).
This is what makes the shared HTTP MCP endpoint reliably reachable across sessions and machine
restarts.
The hook deliberately does not build/publish — that is this skill's job. So the first start after a fresh install (run dir empty) is still done here (publish → launch); after that, the hook relaunches the published server automatically. If a session reports narnia's MCP tools or UI unavailable and the run dir has never been populated, run Start once.
dotnet published to a run
directory and launched from there — not dotnet run from the source tree. Because the
running process only locks the run directory, the source tree can be rebuilt or updated while
the server is running, and an update simply re-publishes after a clean stop.<LocalAppData>/narnia/web-server.json (Pid, Port, Url, Version, ExePath,
StartedAt) and deletes it on graceful shutdown. Read it to discover and control a server that
any session started.POST /shutdown (loopback-only). A hard
Stop-Process of the recorded Pid is a last resort, and only after verifying the live
process's executable path equals the recorded ExePath (guards against PID reuse).| Thing | Location |
|---|---|
| Source | the narnia plugin bundle (resolve as below) |
| Run dir (published server) | <LocalAppData>/narnia/app |
| Run-state file (written by the server) | <LocalAppData>/narnia/web-server.json |
| Web UI | http://127.0.0.1:5244 (loopback only) |
| MCP endpoint | http://127.0.0.1:5244/mcp (Streamable HTTP; shared by all sessions) |
<LocalAppData> is %LOCALAPPDATA% on Windows and the platform per-user data directory
elsewhere (~/.local/share or $XDG_DATA_HOME on Linux, ~/Library/Application Support on macOS).
The server is built from the narnia plugin bundle — the repository this skill ships inside of.
$env:NARNIA_REPO_PATH
is set, use it. It must contain src/NexusLabs.Narnia.Web/NexusLabs.Narnia.Web.csproj.SKILL.md
(skills/narnia-web-server/ → bundle root). Confirm
<bundle>/src/NexusLabs.Narnia.Web/NexusLabs.Narnia.Web.csproj exists.There is no git clone and no well-known-path search. If neither resolves, abort with a
clear error. Getting newer narnia code is done by updating the plugin (/plugin update narnia),
not by cloning here. Record the resolved path as $NARNIA_ROOT.
Narnia's canonical development version lives in Directory.Build.props. A source build also needs
a content-sensitive identity: a plain plugin bundle is not a git checkout, so the SDK cannot append
a commit SHA on its own and two different bundles could otherwise report the same version.
The bundled script
scripts/Get-NarniaBuildVersion.ps1 reads the canonical
version through MSBuild and appends a source identity — the short git SHA for a checkout, else a
deterministic SHA-256 of the src content (which changes iff the code changes). Invoke it with the
resolved $NARNIA_ROOT:
$buildVersion = & "$NARNIA_ROOT/skills/narnia-web-server/scripts/Get-NarniaBuildVersion.ps1" -Root $NARNIA_ROOT
Then always publish with the stamp so /health (and the run-state file) report it verbatim:
dotnet publish "$NARNIA_ROOT/src/NexusLabs.Narnia.Web/NexusLabs.Narnia.Web.csproj" `
-c Release -o $runDir `
-p:IncludeSourceRevisionInInformationalVersion=false `
-p:InformationalVersion="$buildVersion"
IncludeSourceRevisionInInformationalVersion=false stops the SDK appending its own git suffix so
the stamped value is used verbatim. Program.cs reads this informational version, so /health
reports values such as 0.1.0-dev+git.0ef3ff203603 or
0.1.0-dev+content.f1a82e159aba, making the Update comparison meaningful for every install
type.
| Action | What happens |
|---|---|
| Status | GET /health → up/down; read the run-state file for pid/port/version |
| Start | If /health is up → reuse. Else publish → launch detached → poll /health |
| Stop | POST /shutdown → wait for /health to go dark → fallback: identity-checked Stop-Process of the recorded pid |
| Restart | Stop → Start |
| Update | Record running version → Stop → re-publish from the bundle → Start → report old → new version |
GET http://127.0.0.1:5244/health:
<LocalAppData>/narnia/web-server.json for pid/port/version./health returns 200, it is already running — report the URL (and
open the browser if asked) and stop. Do not launch another instance.$NARNIA_ROOT (see above).$runDir = Join-Path $env:LOCALAPPDATA 'narnia\app'
$buildVersion = & "$NARNIA_ROOT/skills/narnia-web-server/scripts/Get-NarniaBuildVersion.ps1" -Root $NARNIA_ROOT
dotnet publish "$NARNIA_ROOT/src/NexusLabs.Narnia.Web/NexusLabs.Narnia.Web.csproj" `
-c Release -o $runDir `
-p:IncludeSourceRevisionInInformationalVersion=false `
-p:InformationalVersion="$buildVersion"
If publish fails, show the full output to the user and stop.# Windows
Start-Process -FilePath (Join-Path $runDir 'NexusLabs.Narnia.Web.exe') `
-ArgumentList '--urls','http://127.0.0.1:5244' `
-WorkingDirectory $runDir `
-WindowStyle Hidden
# macOS / Linux — launch detached
(cd "$runDir" && nohup dotnet ./NexusLabs.Narnia.Web.dll \
--urls http://127.0.0.1:5244 >/dev/null 2>&1 &)
Use a detached background process (the CLI's detached/async mode) so the server keeps running
after the session ends. Always launch with the run directory as the process working directory;
otherwise Windows can keep the plugin bundle directory locked and prevent a later plugin update.http://127.0.0.1:5244/health every second for up to ~40 seconds until
it returns 200.Start-Process "http://127.0.0.1:5244" # Windows; macOS: open, Linux: xdg-open
<LocalAppData>/narnia/web-server.json
(fields Pid, Url, ExePath). If it is absent, fall back to the process listening on port
5244:
(Get-NetTCPConnection -LocalPort 5244 -State Listen -ErrorAction SilentlyContinue).OwningProcess
lsof -ti :5244
POST <Url>/shutdown (default http://127.0.0.1:5244/shutdown), then
poll /health until it stops responding (up to ~15 seconds). The server removes its run-state
file on exit.Pid is still alive and its
executable path equals the recorded ExePath (never terminate an unrelated process that may
have reused the pid), then Stop-Process -Id <Pid> (Unix: kill <Pid>). If the run-state
file is stale (recorded pid is dead), just delete the file.Run Stop, then Start.
Newer narnia code arrives by updating the plugin (/plugin update narnia), which replaces the
bundle this skill resolves. To roll a running server onto the current bundle:
GET /health and keep its version field (or read Version
from the run-state file <LocalAppData>/narnia/web-server.json) as the before version. If the
server is down, note that — this becomes a first Start, not a version swap.scripts/Get-NarniaBuildVersion.ps1 -Root $NARNIA_ROOT and compare the returned value to the
running version. If they match, the server is already on the bundle's code — report "already
current" and stop. Because the build identity is content-derived, this works for bundle
installs too, not just git checkouts.$NARNIA_ROOT to the run dir with the build-identity stamp (see Stamp a
build identity); overwrites the previous copy, safe because the server is stopped./health until it returns 200.version from /health and report the transition
(e.g. 0.1.0-dev+content.ab12cd34ef56 → 0.1.0-dev+content.99aa88bb77cc). Because the identity
is stamped, a real code change always shows a changed value, and an identical before/after
genuinely means the code did not change.Never re-publish or rebuild into the run dir while the server is running — stop it first.
127.0.0.1; /shutdown rejects non-loopback callers./health 200 means it is already up — reuse it.POST /shutdown over Stop-Process.dotnet is not found, tell the user to install it from
https://dot.net/download.~/.copilot/session-store.db (owned by the Copilot CLI — never
modify its schema) and migrates its own settings database at
<LocalAppData>/narnia/settings.db (overrides, settings, and the recorded terminal windows
used by the 🪟 Windows recovery console; migrated automatically from the legacy
~/.copilot/narnia-settings.db). While the server is running, a background snapshotter
records open Windows Terminal windows of Copilot tabs once a minute so a closed window can be
reopened.npx claudepluginhub ncosentino/narnia --plugin narniaGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.