Global Viewer Directory Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Make almanac serve a cwd-independent local wiki browser that opens an all-wikis directory and lets users browse any reachable registered wiki.
Architecture: Keep each repo wiki sovereign and keep each wiki's existing viewer API as the per-repo primitive. Add a thin global viewer layer that reads ~/.almanac/registry.json, filters reachable entries, and dispatches wiki-scoped API requests to createViewerApi({ repoRoot }). The frontend becomes two modes in one shell: a global directory at /, and the existing wiki viewer under /w/:wiki.
Tech Stack: Node http, existing registry helpers, existing better-sqlite3 index, plain browser JavaScript/CSS static viewer assets, Vitest.
Read Before Coding#
docs/bugs/codebase-wiki.mdfor the current product contract, especially local-only storage, registry semantics, and cross-wiki references.docs/plans/2026-05-10-local-viewer.mdfor the existing viewer design and scope.src/paths.tsfor the registry path contract.src/registry/index.tsandsrc/commands/list.tsfor registry read/filter behavior.src/viewer/api.ts,src/viewer/server.ts,viewer/app.js,viewer/app.css,viewer/search-suggestions.js, andviewer/jobs-view.jsfor the current single-wiki viewer.
Non-Goals#
- No cwd-specific behavior.
almanac servebehaves the same from any directory. - No "current wiki" pinning, highlighting, or auto-redirect.
- No global full-text search across all wikis in this slice.
- No editing UI.
- No hosted service, shared database, or central merged index.
- No new frontend build step or framework.
Design Decisions#
/is always the all-wikis directory.- Wiki routes live under
/w/:wikiName. - API routes mirror the UI namespace:
GET /api/wikisGET /api/wikis/:wiki/overviewGET /api/wikis/:wiki/page/:slugGET /api/wikis/:wiki/topic/:slugGET /api/wikis/:wiki/search?q=...GET /api/wikis/:wiki/suggest?q=...GET /api/wikis/:wiki/file?path=...GET /api/wikis/:wiki/jobsGET /api/wikis/:wiki/jobs/:runId
- The old single-wiki API paths may remain as internal compatibility only if tests or existing code need them, but the browser UI should use the new wiki-scoped paths.
- Unreachable registry entries are omitted from
/api/wikis, matchingalmanac list. - A registry entry is browseable only when
entry.path/.almanacexists. A path that exists but is not a wiki should not appear in the directory. - Malformed registry JSON should surface as a server error. Do not silently erase or repair it.
Desired UI Shape#
The homepage should reuse the current visual language: warm paper palette, serif type, restrained borders, and the existing panel/list patterns. It should feel like a library index, not a landing page.
Directory cards show:
- wiki name
- description, or a quiet fallback
- repo path
- page count and topic count, loaded from that wiki's overview
Inside /w/:wiki, the existing viewer should feel almost unchanged. The left rail keeps the brand, search, nav, jobs, and topics. Add only a small "All wikis" route near the top or brand area so users can return to /. Do not add a global picker unless later usage proves it is needed.
Task 1: Add A Global Viewer API#
Files:
- Create:
src/viewer/global-api.ts - Modify:
src/viewer/api.tsonly if a shared type export is needed - Test:
test/viewer-global-api.test.ts
Steps:
- Write a failing test using
withTempHome, two repos,scaffoldWiki,writePage, andaddEntry. - Test that
listWikis()returns only entries whosepath/.almanacexists. - Test that list rows include
name,description,path,pageCount,topicCount, and maybe recent-page metadata needed by the directory. - Test that unreachable paths and non-wiki paths are skipped but remain in the registry.
- Implement
createGlobalViewerApi()with:wikis(): Promise<{ wikis: ViewerWikiSummary[] }>forWiki(name): Promise<ViewerApi>
- Internally read the registry once per request and resolve the named entry exactly. Do not cache repo roots in module state.
- Use
createViewerApi({ repoRoot: entry.path })after validatingentry.path/.almanac. - Run
npm test -- viewer-global-api.test.ts.
Task 2: Make The Server Wiki-Scoped#
Files:
- Modify:
src/viewer/server.ts - Modify:
src/commands/serve.ts - Modify:
src/cli/register-query-commands.ts - Test:
test/serve-command.test.ts
Steps:
- Change
ViewerServerOptionssorepoRootis no longer required. - Change
runServeso it does not callresolveWikiRoot. - Keep
hostandportbehavior unchanged. - Add
/api/wikisroute. - Add
/api/wikis/:wiki/...route parsing. - Dispatch each wiki-scoped route to
createGlobalViewerApi().forWiki(wiki). - Return
404JSON for unknown wiki names and page/topic/job misses. - Preserve static fallback behavior for client-side routes.
- Update tests to start the server without
repoRoot. - Add a server test that fetches
/api/wikis,/api/wikis/alpha/overview, and/api/wikis/beta/page/<slug>. - Run
npm test -- serve-command.test.ts viewer-global-api.test.ts.
Task 3: Refactor Frontend Routing Around A Wiki Context#
Files:
- Modify:
viewer/app.js - Modify:
viewer/search-suggestions.js - Modify:
viewer/jobs-view.js - Test:
test/viewer-ui-assets.test.ts
Steps:
- Add
state.wikis,state.currentWiki, and keepstate.overviewas the selected wiki overview. - Add helpers:
wikiBase() -> /w/:wikiwikiRoute(path) -> /w/:wiki + pathwikiApi(path) -> /api/wikis/:wiki + path
- Update
boot()to fetch/api/wikisfirst. - Route
/torenderWikiDirectory(). - Route
/w/:wikito the existing overview renderer after loading that wiki's overview. - Route
/w/:wiki/page/:slug,/topic/:slug,/search,/file,/jobs, and/jobs/:runIdthrough the selected wiki context. - Refactor existing render functions to call
wikiRoute(...)for links andwikiApi(...)for API calls. - Change search suggestions to accept an API path builder or let the parent pass a scoped
apifunction. - Change jobs view so links and polling compare against
/w/:wiki/jobs/:runId. - Keep all DOM ownership in
app.js; do not create a second app or duplicate page rendering logic. - Run
npm test -- viewer-ui-assets.test.ts.
Task 4: Build The Directory UI With Existing Design Primitives#
Files:
- Modify:
viewer/app.js - Modify:
viewer/app.css - Modify:
viewer/index.htmlonly if structural hooks are needed
Steps:
- Implement
renderWikiDirectory(). - Reuse
.ca-hero,.ca-grid,.ca-panel,.ca-page-list, and.ca-page-rowwhere possible. - Add small, specific classes only where directory layout needs them, for example
.ca-wiki-directory,.ca-wiki-row,.ca-wiki-stats. - Empty state copy should be direct: no wikis registered, run
almanac initin a repo. - On the directory page, hide the right rail.
- On the directory page, hide wiki-specific topics and search or disable them cleanly. Prefer a simple global directory shell over a half-working global search.
- Inside a wiki, show the existing search/nav/topics behavior.
- Add one "All wikis" nav item that routes to
/. - Run the viewer manually and inspect both empty and populated states.
Task 5: Cross-Wiki Links Become Local Navigation#
Files:
- Modify:
viewer/app.js - Test:
test/viewer-ui-assets.test.ts
Steps:
- Update
inline()wikilink rendering. - Preserve current file detection: links containing
/are file references unless they are cross-wiki refs. - Detect cross-wiki refs by
:before any/, matching the indexer contract. - Render
[[other-wiki:slug]]as/w/other-wiki/page/slug. - Render normal page links as
/w/current-wiki/page/slug. - Render file links as
/w/current-wiki/file?path=.... - Do not preflight whether the target wiki/page exists from the renderer. Let navigation show a normal 404/error if missing.
- Add asset tests that assert the classifier order is present in
viewer/app.js. - Run
npm test -- viewer-ui-assets.test.ts.
Task 6: End-To-End Verification#
Files:
- Existing files only
Steps:
- Run focused tests:
npm test -- viewer-global-api.test.ts serve-command.test.ts viewer-api.test.ts viewer-ui-assets.test.ts
- Run full test suite:
npm test
- Run build:
npm run build
- Start the built CLI:
node dist/bin/codealmanac.js serve --port 3927
- Open the printed URL.
- Verify
/shows the all-wikis directory. - Verify clicking each wiki opens
/w/:wiki. - Verify overview, page, topic, search, file, and jobs routes work inside a wiki.
- Verify
almanac servefrom outside any repo behaves identically.
Commit#
After implementation and verification:
git add src/viewer src/commands/serve.ts src/cli/register-query-commands.ts viewer test docs/plans/2026-05-11-global-viewer-directory.md
git commit -m "feat(viewer): add global wiki directory"