Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Orchestrator: `scripts/build-vite.js`. Flags: `--local`, `--headless`.
- `scripts/artifacts.js` — Safe tarball extraction + tree copy, shared by both fetch paths. Validates archive members (no traversal, no absolute paths, no symlinks) before anything is written, and replaces the old `cp -r`/`tar` shell-outs so the build runs on Windows
- `scripts/hoist-inline-scripts.js` — Moves inline `<script>` bodies in sub-app HTML into files before the Astro build, so the deployment can serve `script-src 'self'`. Needed because bundles published before the action stopped emitting an inline mermaid bootstrap still contain one. A sub-app's dark-mode bootstrap is deleted here rather than hoisted — light only, and hoisting would put it beyond the reach of `transform.js`
- `actions/publish-single-page-docs/` — Reusable GitHub Action that turns a repo's markdown into a single-page bundle
- `skills/kb-docs-add/` — Agent skill (Claude Code, GitHub Copilot, `npx skills add`) that walks an agent through onboarding a docs repo: classify, write only the contract-required files, verify, troubleshoot. Guidance only — no scripts; `examples/` are the contract's own code blocks and `tests/skill.spec.js` fails if they drift. Eval fixtures live in `tests/fixtures/kb-docs-add/`

### Onboarding Types

Expand Down Expand Up @@ -161,6 +162,10 @@ commands listed in `AGENTS.md`:
only that host to a configured mirror), the `npm-registry`/`npm-token`/`node-mirror` inputs
exist on both actions and `build-image.yml`, and `actions/lib/npm-registry.sh` writes the
project `.npmrc` without ever putting the token on disk.
- `skill.spec.js` — `skills/kb-docs-add/`: frontmatter satisfies the Agent Skills spec
(name ↔ directory, portable fields only), no scripts shipped, every referenced file exists,
`examples/` are byte-identical to the contract's code blocks, and the docs carry the install
command.
- `standalone.spec.js` — the `:3000` fragment server directly (`playwright.config.ci.js`).
- `container.spec.js` — the real nginx image (`playwright.config.docker.js`, needs Docker).
- `support/fragment.js` — shadow-DOM traversal + reframed-body wait/query helpers.
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,24 @@ Apps must comply with the knowledge base contract before they can be registered:
> The checklist and workflows below apply to **packaged** doc apps. Single-page
> docs skip all of it — the action produces a compliant artifact for you.

### With an AI agent

The fastest route through the contract is the `kb-docs-add` agent skill in
[`skills/kb-docs-add/`](skills/kb-docs-add). It classifies a repo (single-page,
packaged or iframe), writes only the files the contract requires — one workflow
for markdown, `kb-docs.json` + headless build + workflow for a site — and maps
every message the actions emit to its fix. Install it into Claude Code, GitHub
Copilot or any agent that reads `SKILL.md`:

```bash
npx skills add AbsaOSS/knowledge-base --skill kb-docs-add
```

then ask the agent to "add this repo's docs to the knowledge base", or invoke
`/kb-docs-add` where slash commands are supported. The skill guides; the
actions validate. Its examples are the contract's own code blocks and
`tests/skill.spec.js` fails if they drift.

### Checklist
- [ ] `kb-docs.json` in repo root, valid against `contract/kb-docs.schema.json`
- [ ] `npm run build -- --headless` produces a headless `dist/`
Expand Down
7 changes: 7 additions & 0 deletions contract/HEADLESS_RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ branding and the Library / current-app navigation.
> external site must allow embedding (its CSP `frame-ancestors` / `X-Frame-Options` must
> not block the knowledge base origin). See issue #10.

> **With an AI agent.** The `kb-docs-add` skill in
> [`skills/kb-docs-add/`](../skills/kb-docs-add) applies this page for you: it makes
> the existing build produce headless output, writes `kb-docs.json` and the release
> workflow below, and nothing else. Install with
> `npx skills add AbsaOSS/knowledge-base --skill kb-docs-add` (Claude Code, GitHub
> Copilot and other `SKILL.md` readers). This page stays the normative text.

---

## Required: Headless build flag
Expand Down
7 changes: 7 additions & 0 deletions contract/SINGLE_PAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ picks it up on its next build and gives every doc its own catalog card and URL.
> | *default* (packaged) | You have a real static docs site (mkdocs, Starlight…) | headless build + `kb-docs.json` + release workflow — see [HEADLESS_RULES.md](./HEADLESS_RULES.md) |
> | *iframe* | Your docs are already hosted elsewhere and can't be packaged yet | an `apps.json` entry — explicit stopgap, see [HEADLESS_RULES.md](./HEADLESS_RULES.md) |

> **With an AI agent.** The `kb-docs-add` skill in
> [`skills/kb-docs-add/`](../skills/kb-docs-add) walks an agent through this page:
> it picks the type, writes the one workflow file below and nothing else, and knows
> the fix for every message in [Troubleshooting](#troubleshooting). Install with
> `npx skills add AbsaOSS/knowledge-base --skill kb-docs-add` (Claude Code, GitHub
> Copilot and other `SKILL.md` readers). This page stays the normative text.

---

## 1. Add the workflow
Expand Down
56 changes: 56 additions & 0 deletions skills/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Agent skills

Skills this repository publishes for AI coding agents. Each is a directory with a
`SKILL.md` following the [Agent Skills](https://agentskills.io/specification) format,
so the same files load in Claude Code, GitHub Copilot, and any other agent that reads
`SKILL.md`.

| Skill | What it does |
|---|---|
| [`kb-docs-add`](./kb-docs-add) | Onboards a repository's documentation into the knowledge base: picks single-page / packaged / iframe, writes only the files the contract requires, and maps every publishing error to its fix |

## Install

With the [`skills`](https://github.com/vercel-labs/skills) CLI, which discovers
`skills/*/SKILL.md` in this repo and installs into the agent directories you pick:

```bash
# interactive: choose agents and scope
npx skills add AbsaOSS/knowledge-base --skill kb-docs-add

# non-interactive, project scope, named agents
npx skills add AbsaOSS/knowledge-base --skill kb-docs-add -a claude-code -a github-copilot -y

# user scope instead of the current project
npx skills add AbsaOSS/knowledge-base --skill kb-docs-add -g
```

Without the CLI, copy the skill directory to where your agent looks:

| Agent | Project | Personal |
|---|---|---|
| Claude Code | `.claude/skills/kb-docs-add/` | `~/.claude/skills/kb-docs-add/` |
| GitHub Copilot (cloud agent, CLI, VS Code) | `.github/skills/kb-docs-add/` — also reads `.claude/skills/` and `.agents/skills/` | `~/.copilot/skills/kb-docs-add/` |
| Anything on the open spec | `.agents/skills/kb-docs-add/` | `~/.agents/skills/kb-docs-add/` |

Then, in the docs repo you want to onboard, ask the agent to "add this repo's docs to
the knowledge base" — or `/kb-docs-add` where slash commands exist.

## Authoring rules

- **Guidance, not code.** The publishing actions in [`actions/`](../actions) own every
check. A skill points the agent at them; it never ships a script that re-implements one.
`tests/skill.spec.js` fails on any executable file under a skill.
- **Examples are the contract.** Files under `examples/` are byte-for-byte the code
blocks in `contract/`, extracted by heading. Change the contract, then regenerate the
example; the spec compares them.
- **Portable frontmatter only.** `name`, `description`, `license`, `metadata`. No
`allowed-tools` — it is Claude-only and experimental, and a skill installed into
`.github/skills/` must not depend on it.
- **`SKILL.md` stays short.** Under ~150 lines; anything longer moves to `references/`
and is loaded when the agent needs it.
- **Evals** for `kb-docs-add` are in [`tests/fixtures/kb-docs-add/`](../tests/fixtures/kb-docs-add):
three fixture repos and an `evals.json` with a prompt and assertions per case, in the
shape the `skill-creator` tooling reads. They are not part of `npm test` — they need an
agent to run — but the assertion "no files outside the allowed set" is the one that
matters most when changing the skill's wording.
110 changes: 110 additions & 0 deletions skills/kb-docs-add/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
name: kb-docs-add
description: Onboard a repository's documentation into the AbsaOSS knowledge base. Classifies the repo (markdown files → single-page, a static docs site → packaged, hosted elsewhere → iframe stopgap), writes only the files the contract requires (one workflow calling publish-single-page-docs, or kb-docs.json + a headless build flag + a publish-docs workflow), and explains how to verify the release carries kb-docs.tar.gz. Use whenever someone wants to publish docs to the knowledge base, add a repo or service to the knowledge base, write kb-docs.json, produce kb-docs.tar.gz, make a docs site headless for the knowledge base, or fix a failing publish-docs / publish-single-page-docs workflow — even when they only say "get our docs into the KB" or "our docs don't show up in the knowledge base".
license: Apache-2.0
metadata:
author: AbsaOSS
source: https://github.com/AbsaOSS/knowledge-base/tree/master/skills/kb-docs-add
---

# Add docs to the knowledge base

The knowledge base is a build-time aggregator. It reads a registry of repos, downloads
`kb-docs.tar.gz` from each repo's GitHub Release, and re-hosts the HTML inside under its
own masthead. The archive holds a `kb-docs.json` manifest plus one directory per app.

**You never assemble that archive.** Two reusable actions in `AbsaOSS/knowledge-base`
validate, pack and upload it. Your whole job is to write the smallest set of files that
lets one of those actions run, then tell the user how to cut the release and register
the repo. The contract lives in `contract/` of that repo and is normative; this skill is
a guided path through it, not a replacement.

## 1. Classify the repo

Look at what the repo actually contains before writing anything.

| You find | Type | What you will write |
|---|---|---|
| Markdown files (README, `docs/*.md`, runbooks) and **no** docs-site generator | **single-page** | one workflow file |
| A docs *site*: `mkdocs.yml`, Starlight/Astro, Docusaurus, Jekyll, a `build` script that emits HTML | **packaged** | `kb-docs.json`, a headless build variant, one workflow file |
| Docs hosted elsewhere and no packageable source in this repo | **iframe** | nothing — explain, then stop |

Rules of thumb:

- A repo with a real site but only a page or two of prose is still *packaged*. The site is
what the team maintains; do not replace it with single-page unless they ask.
- A repo with a generator config that is unused or broken is *single-page*. Onboarding is
not the moment to resurrect a dead site.
- When two readings are plausible, say so and recommend single-page: it is one file,
reversible, and the team can move to packaged later without touching the knowledge base.
- **iframe** is an explicit stopgap owned by the deployment repo's `apps.json`, not by
the docs repo. Tell the user what the entry looks like — `type: "iframe"`, `url`,
`slug`, `name`, `description`, optional `icon`/`tags`, and `temporary: true`; it is the
one entry kind that carries display fields, because there is no manifest to read them
from — that the external site must allow framing (`frame-ancestors` /
`X-Frame-Options`), and stop. Do not write files.

## 2. Write the minimum, and nothing else

The action does the validating, packing and uploading. Anything you add on top of the
files below duplicates it, drifts from the contract the next time it changes, and is
exactly what a reviewer has to read and reject. So:

| Type | Allowed files | Not allowed |
|---|---|---|
| single-page | `.github/workflows/publish-docs.yml` | a manifest, a build script, `package.json` changes |
| packaged | `kb-docs.json` · `.github/workflows/publish-docs.yml` · the smallest change to the *existing* build that yields headless output | a new generator, a packing script, a vendored schema |

Never write `dist.tar.gz`, `marketplace.json`, `bundle.json`, `data-mp-headless` or any
other pre-v1 name — the action rejects them with a message pointing here. Never write a
helper script, `Makefile` target or wrapper action: if a check is worth having it belongs
in the action, and the action already runs it.

Pin the action to `@v1`, never `@master`. A breaking contract change ships as `@v2`.

## 3. Follow the path

- **single-page** → read `references/single-page.md`. Derive `title`, `description` and
`slug` from the markdown; prefix every slug with the service name because slugs are
global URLs across the whole knowledge base.
- **packaged** → read `references/packaged.md`. Make the existing build produce headless
HTML, write `kb-docs.json` against the field table there, then the workflow. If the
site's theme cannot be made headless without forking it, report the exact gaps and
offer single-page instead — do not patch a third-party theme blindly.

`examples/` holds the workflow files and the manifest exactly as the contract prints
them. Copy from there and change only the values that describe this repo.

## 4. Verify

You cannot run the action locally — it needs a release to attach to — but you can make
its first run boring:

1. Every `md:` path (single-page) or `pages[].path` (packaged) exists in the checkout.
2. Every `slug` matches `^[a-z0-9]+(-[a-z0-9]+)*$`, 2–32 characters, and is
service-prefixed. Every `description` is 10–280 characters.
3. Packaged only: run the headless build and confirm, on the output,
`data-kb-headless="true"` on `<html>`, no `<base>`, no `href="/`/`src="/`. These are
the checks the action fails on; the same grep now saves a CI round trip.
4. The workflow has `permissions: contents: write` — the upload needs it and the error
without it (`Resource not accessible by integration`) does not say so.

Then tell the user the two things only they can do:

- **Publish a GitHub Release.** The workflow triggers on `release: published`; the action
attaches to an existing release and never creates one. Afterwards `kb-docs.tar.gz`
must appear among the release assets.
- **Register the repo** with a PR to the deployment's `apps.json` adding
`{ "repo": "owner/name", "version": "latest" }` — nothing more. Display fields come
from the manifest, so later doc changes never touch the registry again.

## 5. When the run fails

Read `references/troubleshooting.md`: it maps every message the actions and the knowledge
base build emit to its cause and fix. Do not guess from the symptom — the messages are
specific on purpose.

## Report back

End with: the files written (full paths), the type you chose and why, the manual steps
above, and any contract requirement you could not satisfy from inside this repo.
18 changes: 18 additions & 0 deletions skills/kb-docs-add/examples/kb-docs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"kbVersion": "1",
"apps": [
{
"slug": "my-app",
"name": "My App Documentation",
"description": "A clear, one-sentence description of what this documentation covers.",
"icon": "book-open",
"tags": ["example", "guide"],
"entryPoint": "index.html",
"pages": [
{ "title": "Overview", "path": "index.html", "order": 1 },
{ "title": "Getting Started", "path": "getting-started/index.html", "order": 2 },
{ "title": "Configuration", "path": "configuration/index.html", "order": 3, "section": "Reference" }
]
}
]
}
24 changes: 24 additions & 0 deletions skills/kb-docs-add/examples/packaged.publish-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# .github/workflows/publish-docs.yml in your doc repo
name: Publish docs

on:
release:
types: [published]
workflow_dispatch:

permissions:
contents: write # required — the action uploads a release asset

jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# Whatever your site generator is. The action does not build for you.
- run: npm ci && npm run build -- --headless

- uses: AbsaOSS/knowledge-base/actions/publish-docs@v1
with:
manifest: kb-docs.json
dist: dist
22 changes: 22 additions & 0 deletions skills/kb-docs-add/examples/single-page.publish-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# .github/workflows/publish-docs.yml in your docs repo
name: Publish docs

on:
release:
types: [published]
workflow_dispatch:

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: write # required — the action uploads a release asset
steps:
- uses: actions/checkout@v4
- uses: AbsaOSS/knowledge-base/actions/publish-single-page-docs@v1
with:
docs: |
- md: docs/overview.md
title: Service Overview
description: What the service does and how to use it.
slug: my-service
Loading