This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Build-time aggregator that downloads pre-built static doc sites from GitHub Release artifacts, injects a persistent branded chrome (56px top nav), and produces a single static deployment. Served by nginx in Docker. Can also run as a web-fragment inside an Angular SSR gateway.
# Install
npm install # Node >= 24 required
# Build (needs GITHUB_TOKEN or gh CLI auth)
npm run build # fetch artifacts from GitHub + build
npm run build:local # build from local repos (no GitHub fetch)
npm run build:headless # headless mode for web-fragment embedding
npm run build:local:headless # local + headless
# Dev server
npm run dev # Astro dev (needs artifacts in apps/ already)
npm run dev:fetch # fetch artifacts then start dev
# Preview
npm run preview # preview built output
npm run preview:embedded # headless preview (no compression, for web-fragments)
# Test (Playwright E2E — self-contained; auto-starts both servers)
npm test # headless
npm run test:ui # Playwright UI
npm run test:headed # visible browser
npm run test:debug # debug mode
# Docker
docker build -t knowledge-base .
docker run -p 8080:8080 knowledge-baseapps.json (registry)
→ [1] Fetch Release artifacts OR build local repos → apps/{slug}/
→ [2] Copy non-HTML assets → public/{slug}/ (Astro serves as static)
→ [3] astro build: [...path].astro enumerates all HTML via getStaticPaths
→ transformSubAppHtml() rewrites URLs + injects chrome
→ dist/
Orchestrator: scripts/build-vite.js. Flags: --local, --headless, --path-prefix=.
- astro.config.mjs — Astro SSG config, base
/knowledge-base, used byastro build/astro dev - vite.config.js — standalone Vite config with custom
marketplacePluginfromplugins/marketplace.js, base/, used for the non-Astro build path
apps.json → scripts/fetch-apps.js downloads dist.tar.gz per app → apps/{slug}/ → Astro's src/pages/[...path].astro catchall uses getStaticPaths() from src/utils/apps.js to enumerate every HTML file → src/utils/transform.js rewrites URLs and injects chrome → static output in dist/.
src/pages/[...path].astro— Catchall route, renders every sub-app page. Injects<ClientRouter />for Astro client-side transitions.src/pages/index.astro— Landing catalog pagesrc/utils/apps.js—getAppPages()enumerates sub-app HTML (manifest-driven or filesystem crawl)src/utils/transform.js—transformSubAppHtml(): URL rewriting, chrome injection, headless transformssrc/templates/chrome.js— Chrome HTML template + client-side SPA router scriptscripts/build-vite.js— Build orchestrator (3-step pipeline)scripts/fetch-apps.js— GitHub Release artifact downloader
Non-headless (standalone): Injects chrome bar, SPA router, theme toggle. Client-side router intercepts navigation, fetches HTML, swaps DOM content.
Headless (web-fragment): Strips dark mode class, marks data-mp-headless="true", injects shadow DOM compat styles. No chrome bar. Designed for embedding in a web-fragments gateway.
transform.js rewrites all relative and root-relative URLs to absolute /{prefix}/{slug}/... paths. Runs before chrome injection to prevent double-rewriting. Removes <base> tags.
Apps registered in apps.json must comply with:
contract/schema.json— JSON Schema formarketplace.jsonmanifestcontract/HEADLESS_RULES.md— Structural requirements (headless HTML, relative paths,data-mp-headlessattribute)contract/STYLE_GUIDE.md— Design tokens, typography, dark mode support
Self-contained Playwright E2E — npm test auto-starts everything (no external gateway):
- :3000 fragment —
scripts/setup-test-apps.mjswrites a hermeticapps.jsonthat registers the local../knowledge-base-docs-exampleprebuiltdist.tar.gztwice (slugsuser-guide+guide-mirror, for cross-app nav).build:headlessbuilds it;tests/fragment-server.mjsservesdist/mirroring the production nginx rewrites (/__wf/knowledge-base/*→/knowledge-base/*). NB:astro previewis NOT used — its ViteconfigurePreviewServerrewrite hook does not run for static output, so the marketplace CSS 404s; the nginx-mirror server is the faithful fragment endpoint. - :4201 host —
tests/host/server.mjs, a minimal Express "wrapping web-fragment application" (FragmentGateway+getNodeMiddleware) that proxies/embeds the :3000 fragment on a single origin via<web-fragment fragment-id="knowledge-base">.
Tests drive the host origin (http://localhost:4201). Suites (tests/):
build-integrity.spec.js—dist/output: both apps enumerated, absolute URL rewriting, headless markup, stabledist/style.css(the marketplace CSS the sub-app pages reference).web-fragment.spec.js— shadow-DOM isolation (reframedwf-html/wf-body; chrome must not leak in), routing + smooth no-reload SPA transitions, cross-app navigation, asset loading (no host-origin 404s), and the documented history limitation (fragment routing is internal to the reframedwf:<id>iframe and is not mirrored to top-window history).support/fragment.js— shadow-DOM traversal + reframed-body wait/query helpers.
Two build-pipeline pieces support this: apps.json entries may carry a prebuilt path
(tarball or dist dir) consumed by scripts/build-vite.js (preparePrebuilt) for hermetic
offline builds; and the build aliases the bundled marketplace CSS to a stable dist/style.css.
GITHUB_TOKEN— GitHub API auth for fetching Release artifactsMP_HEADLESS—true/false, controls headless modeMP_PREFIX— URL prefix (default:knowledge-base)AWS_REGION,ECR_REPOSITORY,ECS_CLUSTER,ECS_SERVICE— deployment config