knowledge-base
Self-hosted markdown documentation site with categories, full-text search, and version history
Best use case
knowledge-base is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Self-hosted markdown documentation site with categories, full-text search, and version history
Teams using knowledge-base should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/knowledge-base/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How knowledge-base Compares
| Feature / Agent | knowledge-base | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Self-hosted markdown documentation site with categories, full-text search, and version history
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# knowledge-base skill
## Overview
knowledge-base is a single-binary self-hosted documentation platform. Authors write documents in Markdown via an admin UI. Readers browse published content on a public site with full-text search. Documents are organized into categories and every save creates an immutable version snapshot.
## Base URL
```
http://localhost:3000
```
Admin routes are all under `/api/admin/`. Public routes are under `/api/pub/`.
## TypeScript interfaces
```typescript
interface Category {
id: number;
name: string; // display name
slug: string; // URL-safe identifier
description: string;
color: string; // hex color for UI icon, e.g. "#2563eb"
icon: string; // SVG path string or named preset
doc_count: number; // live count of published docs
created_at: string; // ISO 8601
}
interface Doc {
id: number;
category_id: number | null;
title: string;
slug: string; // auto-generated from title, URL-safe
summary: string; // 200-char plain-text excerpt; auto-extracted if blank
body_md: string; // raw markdown source
body_html: string; // sanitized HTML rendered from body_md
status: 'draft' | 'published';
version_num: number; // increments per doc on each save
word_count: number;
read_time_min: number;
published_at: string | null;
updated_at: string;
created_at: string;
}
interface DocVersion {
id: number;
doc_id: number;
version_num: number;
title: string;
body_md: string;
body_html: string;
saved_at: string;
}
interface SearchResult {
id: number;
title: string;
slug: string;
summary: string;
snippet: string; // FTS5 snippet() with <mark> tags for matched terms
category_id: number | null;
category_name: string | null;
rank: number; // FTS5 rank, lower is better match
}
interface Settings {
site_name: string;
site_description: string;
site_url: string;
docs_per_page: number;
search_enabled: number; // 0 or 1
fts_last_rebuilt: string | null;
}
```
## Admin API
### Categories
**List categories**
```bash
curl http://localhost:3000/api/admin/categories
```
Response: `Category[]`
**Create category**
```bash
curl -X POST http://localhost:3000/api/admin/categories \
-H 'Content-Type: application/json' \
-d '{
"name": "API Reference",
"description": "Complete endpoint documentation",
"color": "#2563eb",
"icon": "code"
}'
```
Response: `Category`
**Update category**
```bash
curl -X PUT http://localhost:3000/api/admin/categories/1 \
-H 'Content-Type: application/json' \
-d '{ "name": "API Reference", "description": "Updated description" }'
```
**Delete category**
```bash
curl -X DELETE http://localhost:3000/api/admin/categories/1
```
Docs in the deleted category have `category_id` set to `null`.
### Documents
**List docs (all statuses)**
```bash
curl 'http://localhost:3000/api/admin/docs?page=1&per_page=20&status=all'
```
Query params:
- `status`: `all` | `published` | `draft` (default: `all`)
- `category_id`: filter by category
- `page`: 1-indexed (default: 1)
- `per_page`: 1-100 (default: 20)
Response:
```json
{
"docs": [Doc],
"total": 42,
"page": 1,
"per_page": 20
}
```
**Get single doc**
```bash
curl http://localhost:3000/api/admin/docs/7
```
**Create doc**
```bash
curl -X POST http://localhost:3000/api/admin/docs \
-H 'Content-Type: application/json' \
-d '{
"title": "Deploying with Docker Compose",
"category_id": 2,
"body_md": "# Deploying with Docker Compose\n\nThis guide walks through...",
"status": "draft"
}'
```
Response: `Doc` with auto-generated `slug`, `body_html`, and `summary`.
**Update doc**
```bash
curl -X PUT http://localhost:3000/api/admin/docs/7 \
-H 'Content-Type: application/json' \
-d '{
"title": "Deploying with Docker Compose",
"body_md": "Updated content...",
"status": "draft"
}'
```
Every PUT that changes `body_md` or `title` creates a new `DocVersion` snapshot and increments `version_num`.
**Publish doc**
```bash
curl -X POST http://localhost:3000/api/admin/docs/7/publish
```
Sets `status` to `published`, records `published_at`, updates FTS5 index.
**Unpublish doc**
```bash
curl -X POST http://localhost:3000/api/admin/docs/7/unpublish
```
Sets `status` to `draft`. Doc is immediately removed from public site and FTS index.
**Delete doc**
```bash
curl -X DELETE http://localhost:3000/api/admin/docs/7
```
Deletes all versions. Cascades to FTS5 via DELETE trigger.
### Version history
**List versions for a doc**
```bash
curl http://localhost:3000/api/admin/docs/7/versions
```
Response: `DocVersion[]` ordered by `version_num DESC`.
**Get specific version**
```bash
curl http://localhost:3000/api/admin/docs/7/versions/5
```
**Restore a version**
```bash
curl -X POST http://localhost:3000/api/admin/docs/7/versions/5/restore
```
Copies `body_md` and `title` from version 5 into the doc, increments `version_num`, creates a new snapshot. Does not change `status`.
### Settings
**Get settings**
```bash
curl http://localhost:3000/api/admin/settings
```
**Update settings**
```bash
curl -X PUT http://localhost:3000/api/admin/settings \
-H 'Content-Type: application/json' \
-d '{
"site_name": "Acme App Docs",
"site_description": "Documentation for Acme App",
"site_url": "https://docs.acmeapp.com",
"docs_per_page": 20,
"search_enabled": 1
}'
```
**Rebuild FTS index**
```bash
curl -X POST http://localhost:3000/api/admin/settings/rebuild-fts
```
Runs `INSERT INTO docs_fts(docs_fts) VALUES('rebuild')`. Updates `fts_last_rebuilt`. Returns `{ ok: true, rebuilt_at: "..." }`.
## Public API
**Home: recent published docs**
```bash
curl 'http://localhost:3000/api/pub/docs?page=1&per_page=10'
```
**List published docs in category**
```bash
curl 'http://localhost:3000/api/pub/categories/guides/docs'
```
**Get single published doc by slug**
```bash
curl http://localhost:3000/api/pub/docs/deploying-with-docker-compose
```
Returns `404` if doc is in `draft` status.
**Get all categories (public)**
```bash
curl http://localhost:3000/api/pub/categories
```
Returns only categories that have at least one published doc.
## Error responses
| HTTP status | `code` field | Meaning |
|------------|--------------|---------|
| 400 | `VALIDATION_ERROR` | Invalid request body; `details` array has per-field messages |
| 404 | `NOT_FOUND` | Doc or category does not exist |
| 404 | `DOC_NOT_PUBLISHED` | Public request for a draft doc |
| 409 | `SLUG_CONFLICT` | Auto-generated slug already exists; append `-2` suffix |
| 500 | `INTERNAL_ERROR` | Unexpected server error |
Error response shape:
```json
{
"error": "VALIDATION_ERROR",
"message": "title is required",
"details": [{ "field": "title", "message": "Required" }]
}
```
## Markdown rendering pipeline
1. Parse `body_md` with `marked` (GFM enabled, line breaks enabled).
2. Sanitize rendered HTML with `DOMPurify` (Node adapter), allowing `id` attributes on headings so ToC anchor links work.
3. Store sanitized HTML in `body_html`.
4. Extract `summary`: strip all HTML tags from `body_html`, take first 200 characters, trim at last word boundary.
5. Update FTS5 index with new `title` and plain-text `summary`.
## Slug generation
```typescript
function toSlug(title: string): string {
return title
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
.trim()
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.substring(0, 80);
}
```
On conflict, append `-2`, `-3`, etc. until unique.
## Environment variables
| Variable | Default | Description |
|----------|---------|-------------|
| `PORT` | `3000` | HTTP port |
| `DATABASE_PATH` | `./data/kb.db` | SQLite file path |
| `NODE_ENV` | `development` | Set to `production` for prod builds |
## Docker
```bash
docker compose up -d
```
Data volume: `./data:/app/data`. The SQLite file is at `/app/data/kb.db` inside the container.Related Skills
food-database
No description provided.
database-size-monitor
Dashboard for monitoring PostgreSQL and MySQL table sizes over time, with growth tracking, threshold alerts, and snapshot comparison
Skill: Uptime Monitoring
## Overview
Skill: Status Page
## Overview
Skill: unit-conversion
## Overview
Skill: recipe-scaler
## Overview
reading-list
Operate the reading-list API to save, manage, tag, search, and export articles.
email-digest
Configure, test, and troubleshoot the reading-list daily email digest delivered via nodemailer.
websocket-realtime
Use the WebSocket connection in poll-builder to receive live vote updates. Use when you need to stream real-time poll results, monitor a poll for new votes, or build a live dashboard. Triggers include "live results", "real-time updates", "stream votes", "watch poll", or "WebSocket".
poll-builder
Self-hosted poll creation tool with real-time results. Use when you need to create a poll, check vote counts, close a poll, export results, or get the shareable link for a poll. Triggers include "create poll", "vote", "poll results", "survey", "collect votes", "share poll", or any task involving polling or voting.
Skill: personal-finance
## Overview
Skill: csv-import
## Overview