Create topic-specific agent skills from interchangeable Markdown knowledge bases. Local Node.js semantic retrieval adapted from Google Modern Web Guidance.
JavaScript
0
4 commits
updated Sep 18, 2026
Create topic-specific agent skills from interchangeable Markdown knowledge bases. Local Node.js semantic retrieval adapted from Google Modern Web Guidance.
The original idea comes from Google's Modern Web Guidance: a small skill instructs an agent to search a curated collection using local embeddings, then retrieve only the relevant Markdown guides. This project generalizes that approach so the same framework can build skills for other topics by replacing the knowledge base.
The implementation is adapted from Google's source repository, specifically its heading-based chunking, embedding pipeline and best-chunk-per-document ranking. NOTICE records the source revision and adapted files; LICENSE includes Apache-2.0. The generic configuration, incremental rebuilding, watcher and skill export are additions in this project. This is an independent derivative, not an official Google project.
Requires Node.js 22+. No Python, GPU or API key. Installation downloads npm dependencies; the first build downloads a local embedding model. Documents and queries are processed locally.
git clone https://github.com/ulrischa/semantic-skill-kit.git
cd semantic-skill-kit
npm ci
node src/cli.mjs init my-topic
Replace my-topic/knowledge/example.md with your Markdown files, and edit name and description in my-topic/skill-kit.json.
node src/cli.mjs build --project my-topic
node src/cli.mjs watch --project my-topic
The watcher stays running; stop it with Ctrl+C. In another terminal:
node src/cli.mjs search "How can I recover deleted files?" --project my-topic
node src/cli.mjs retrieve "<id-from-search>" --project my-topic
node src/cli.mjs export dist/my-topic --project my-topic
Run npm ci once inside the exported skill. Its SKILL.md instructs an agent to search, retrieve only selected documents, and apply their guidance. The target environment must support Node execution and dependency installation. An upload alone does not provide that capability. This framework does not automatically install personal ChatGPT skills.
Optional: run npm link in this repository and use skill-kit instead of node src/cli.mjs. This project has not been published under a guaranteed npm package name.
| Mode | Build | Exported skill at runtime |
|---|---|---|
semantic (default) | Local MiniLM embeddings | Node.js, local embedding model, vector search |
routed | Full generative LLM via an API | Markdown instructions, indexes and original references only |
The routed path does not build vectors or load MiniLM. It creates a task-oriented navigation tree that ChatGPT or another agent can read directly. A compatible environment must expose the bundled Markdown references to the agent; uploading a package is not, by itself, a guarantee of platform support.
Add or edit this object in your project's skill-kit.json (new projects include these settings):
"routing": {
"baseUrl": "https://api.openai.com/v1",
"model": "YOUR_CHAT_MODEL_ID",
"apiKeyEnv": "OPENAI_API_KEY",
"language": "English",
"maxInputChars": 12000,
"pageSize": 8,
"timeoutMs": 120000
}
Use a generative chat model supporting the Chat Completions API and JSON object responses. The API base URL is configurable for compatible providers and local servers. Native Anthropic or other non-compatible protocols are not implemented. HTTPS is required except for loopback addresses. No particular paid model is chosen automatically. Alternatively, leave model empty and set SKILL_KIT_LLM_MODEL.
Set the key outside the configuration file. Bash:
export OPENAI_API_KEY="your-api-key"
node src/cli.mjs export dist/my-topic-routed --project my-topic --mode routed
PowerShell:
$env:OPENAI_API_KEY = "your-api-key"
node src/cli.mjs export dist/my-topic-routed --project my-topic --mode routed
For a local compatible server that ignores authentication, set the configured key variable to a non-secret placeholder. Environment files are not loaded automatically. Neither keys nor API settings are copied into the generated skill.
The build sends document sections and frontmatter to the configured provider and can incur API charges. Requests run sequentially. There are no automatic retries; errors stop the build without publishing a partial skill. Valid responses are cached in <output>/routing-cache/ by model, endpoint, prompt and source content. --force regenerates responses. --offline allows cached builds only and fails on a cache miss. The cache contains generated descriptions and should be treated as knowledge-base data.
The generator:
maxInputChars controls section content, not the complete request token count. Metadata and instructions add overhead.category and categories assignments; a reference may appear under several categories. If none is provided, the LLM proposes a topic. Existing descriptions, tags and other frontmatter are included as context.pageSize items, generates page summaries, and recursively builds parent indexes to keep the skill entrypoint small.Generated descriptions are bounded and validated as JSON. Their factual quality still depends on the model: review routing with representative questions before relying on it. The runtime instructions require reading the actual references before answering. Categories are retained verbatim; generated descriptions use routing.language and source text retains its original language. Non-Markdown linked assets are not copied.
The result contains:
my-topic-routed/
SKILL.md
indexes/
page-1.md
...
catalog.md
catalog-1.md
...
references/guides/
original-document.md
...
LICENSE
NOTICE
No npm installation, embedding model, vector index, API call or executable script is needed to use this exported skill. --include-model is rejected for routed exports. The existing build and watch commands maintain the semantic index; routed generation is explicitly invoked through export --mode routed so background edits cannot silently trigger paid LLM requests. Re-export to a new destination after updates; unchanged requests reuse the cache.
node src/cli.mjs build --project examples/home-guidance
node src/cli.mjs search "recover deleted documents" --project examples/home-guidance
node src/cli.mjs retrieve restore-backup --project examples/home-guidance
A document can be plain Markdown or include YAML metadata:
---
id: restore-backup
description: Restore deleted files from a backup archive.
category: backups
tags: [restore, recovery]
---
# Restore a backup
Write your actual guidance here.
Nested folders are supported. Without frontmatter, the relative filename becomes the ID. IDs must be unique. Empty documents, invalid YAML, duplicate IDs and symlinks cause a build error. Hidden files and non-Markdown files are ignored. Linked images, PDFs and other assets are not imported.
marked, plus a metadata chunk.Xenova/all-MiniLM-L6-v2 feature extraction with mean pooling and normalization.Search ranking and heading chunking derive from Google's source. See NOTICE for exact source files and commits. Both build and query use Transformers.js/ONNX here; Google uses a separate TFJS query runtime. Scores are not promised to be identical. Google's telemetry, web-specific macros and browser-baseline processing are not included.
Incremental builds reuse unchanged chunk vectors. Model and chunking changes invalidate the cache. Index and document text are published together, so readers cannot mix different versions. Search returns metadata only; loading documents into process memory is not the same as putting them into an LLM context.
| Command | Purpose |
|---|---|
init <directory> | Create an independent knowledge project |
build | Incrementally rebuild the index |
build --force | Recompute all vectors; model cache is retained |
watch | Continuously rebuild after stable changes |
search "query" | Return ranked JSON metadata |
retrieve <id> | Print one complete guide |
status | Inspect index freshness and counts |
model | Download/warm the configured model |
export <new-directory> | Export a reusable skill snapshot |
All except init support --project <directory> (default: current directory). search also supports --top-k and --threshold. --offline requires a prepared model cache. Output is JSON or document text on stdout, diagnostics on stderr.
The built-in polling watcher hashes file contents every 1,000 ms and requires 800 ms of stability. It handles edits, additions, renames, deletions, whole-directory replacement, and configuration changes without a third-party watcher. Builds are serialized. A mutation during inference prevents publishing that snapshot and is retried on the next cycle.
A missing directory or invalid document preserves the last valid index. An existing empty directory intentionally publishes an empty index. CLI search/retrieve reject stale indexes. To replace a large knowledge base, prepare a new folder first and swap it in; a paused copy can otherwise expose a valid intermediate state.
The watcher must remain running; it is not installed as a background service. Exports are independent snapshots and require a new export to update.
init writes all defaults. Configure knowledge, output, cache, model, chunk, search and watch in skill-kit.json. Paths must be separate subdirectories of the project. The default model revision is pinned to 751bff37182d3f1213fa05d7196b954e230abad9.
| Setting | Purpose |
|---|---|
name, description | Skill name and instructions for when agents should select it |
knowledge | Markdown directory relative to the project |
output | Index directory; defaults to .skill-kit |
cache | Local model cache; defaults to .cache/models |
model.id, model.revision, model.dtype | Embedding model, revision and quantization |
model.maxTokens | Maximum model input length, including special tokens |
model.queryPrefix, model.documentPrefix | Prefixes required by some embedding models |
chunk.maxTokens, chunk.overlap | Token window size and overlap |
search.topK, search.threshold | Result limit and minimum similarity |
watch.intervalMs, watch.settleMs | Polling interval and stability delay |
The default MiniLM model is primarily intended for English. Use an appropriate multilingual Transformers.js-compatible sentence-embedding model for German or multilingual corpora. Mean pooling is used; models requiring another pooling strategy need code changes. Set the correct token limit, quantization and optional queryPrefix / documentPrefix. Changing model settings triggers a full rebuild. Prefer immutable model revisions over main.
chunk.maxTokens includes metadata context, and must leave room for model special tokens. Long sections are split into overlapping windows; retrieved documents remain unchanged. Scores are similarity values, not probabilities. Validate thresholds with representative questions. Linear vector scanning and in-memory snapshots suit curated knowledge bases, not millions of documents.
The export destination must not already exist. Export first ensures the index is current, then produces an independent skill directory:
my-topic/
SKILL.md
skill-kit.json
guides/
data/index.json.gz
src/
assets/runtime-package-lock.json
package.json
package-lock.json
LICENSE
NOTICE
Install dependencies inside this directory with npm ci, then use the target agent's skill installation mechanism. Ordinary retrieval runs node src/cli.mjs search "..." followed by node src/cli.mjs retrieve "<id>" from the skill directory. Maintaining the knowledge base and rebuilding the index remain separate from ordinary agent use.
node src/cli.mjs model --project my-topic
node src/cli.mjs export dist/my-topic-offline --project my-topic --include-model
This includes the project's model cache and makes the generated skill use search --offline. Node and platform-specific npm dependencies still need installation on the target system first. node_modules is deliberately not copied between platforms. Model files are about 23 MB for the default model; a cache containing multiple models makes larger exports.
npm test
npm run test:integration
Routed tests exercise the HTTP client against a local mock API, including invalid responses, timeouts, cache invalidation, manual categories, long documents, and complete reference reachability. They do not measure real-model routing quality or call a paid provider.
Unit/integration tests with controlled vectors cover chunking, ranking, caching, deletion, atomic failure recovery, concurrent builds, folder replacement and export. The separate real-model test verifies MiniLM search, token windows and offline export. Its cache defaults to examples/home-guidance/.cache/models; override with SKILL_KIT_TEST_CACHE.
CI is configured for Node 22/24 on Linux and Windows; local validation was on Linux, Node 24. No TypeScript or compilation step. Library exports are available from src/index.mjs.
After dependency updates, copy package-lock.json to assets/runtime-package-lock.json. The latter ships with npm distributions so exports have a reproducible lockfile. Increment the pipeline version in fingerprint() when changing embedding semantics.
A crash can leave .skill-kit/build.lock; remove it only after checking no builder is running. For a damaged index, use build --force. If ONNX Runtime tries downloading CUDA, run npm ci --onnxruntime-node-install-cuda=skip; .npmrc already configures CPU-only installation. npm 11 may warn about this package-specific setting.
Independent derivative; not endorsed by Google. Dependency and model licenses remain applicable.
4 commits
JavaScript
100.0%
Create topic-specific agent skills from interchangeable Markdown knowledge bases. Local Node.js semantic retrieval adapted from Google Modern Web Guidance.
JavaScript
0
4 commits
updated Sep 18, 2026
Create topic-specific agent skills from interchangeable Markdown knowledge bases. Local Node.js semantic retrieval adapted from Google Modern Web Guidance.
The original idea comes from Google's Modern Web Guidance: a small skill instructs an agent to search a curated collection using local embeddings, then retrieve only the relevant Markdown guides. This project generalizes that approach so the same framework can build skills for other topics by replacing the knowledge base.
The implementation is adapted from Google's source repository, specifically its heading-based chunking, embedding pipeline and best-chunk-per-document ranking. NOTICE records the source revision and adapted files; LICENSE includes Apache-2.0. The generic configuration, incremental rebuilding, watcher and skill export are additions in this project. This is an independent derivative, not an official Google project.
Requires Node.js 22+. No Python, GPU or API key. Installation downloads npm dependencies; the first build downloads a local embedding model. Documents and queries are processed locally.
git clone https://github.com/ulrischa/semantic-skill-kit.git
cd semantic-skill-kit
npm ci
node src/cli.mjs init my-topic
Replace my-topic/knowledge/example.md with your Markdown files, and edit name and description in my-topic/skill-kit.json.
node src/cli.mjs build --project my-topic
node src/cli.mjs watch --project my-topic
The watcher stays running; stop it with Ctrl+C. In another terminal:
node src/cli.mjs search "How can I recover deleted files?" --project my-topic
node src/cli.mjs retrieve "<id-from-search>" --project my-topic
node src/cli.mjs export dist/my-topic --project my-topic
Run npm ci once inside the exported skill. Its SKILL.md instructs an agent to search, retrieve only selected documents, and apply their guidance. The target environment must support Node execution and dependency installation. An upload alone does not provide that capability. This framework does not automatically install personal ChatGPT skills.
Optional: run npm link in this repository and use skill-kit instead of node src/cli.mjs. This project has not been published under a guaranteed npm package name.
| Mode | Build | Exported skill at runtime |
|---|---|---|
semantic (default) | Local MiniLM embeddings | Node.js, local embedding model, vector search |
routed | Full generative LLM via an API | Markdown instructions, indexes and original references only |
The routed path does not build vectors or load MiniLM. It creates a task-oriented navigation tree that ChatGPT or another agent can read directly. A compatible environment must expose the bundled Markdown references to the agent; uploading a package is not, by itself, a guarantee of platform support.
Add or edit this object in your project's skill-kit.json (new projects include these settings):
"routing": {
"baseUrl": "https://api.openai.com/v1",
"model": "YOUR_CHAT_MODEL_ID",
"apiKeyEnv": "OPENAI_API_KEY",
"language": "English",
"maxInputChars": 12000,
"pageSize": 8,
"timeoutMs": 120000
}
Use a generative chat model supporting the Chat Completions API and JSON object responses. The API base URL is configurable for compatible providers and local servers. Native Anthropic or other non-compatible protocols are not implemented. HTTPS is required except for loopback addresses. No particular paid model is chosen automatically. Alternatively, leave model empty and set SKILL_KIT_LLM_MODEL.
Set the key outside the configuration file. Bash:
export OPENAI_API_KEY="your-api-key"
node src/cli.mjs export dist/my-topic-routed --project my-topic --mode routed
PowerShell:
$env:OPENAI_API_KEY = "your-api-key"
node src/cli.mjs export dist/my-topic-routed --project my-topic --mode routed
For a local compatible server that ignores authentication, set the configured key variable to a non-secret placeholder. Environment files are not loaded automatically. Neither keys nor API settings are copied into the generated skill.
The build sends document sections and frontmatter to the configured provider and can incur API charges. Requests run sequentially. There are no automatic retries; errors stop the build without publishing a partial skill. Valid responses are cached in <output>/routing-cache/ by model, endpoint, prompt and source content. --force regenerates responses. --offline allows cached builds only and fails on a cache miss. The cache contains generated descriptions and should be treated as knowledge-base data.
The generator:
maxInputChars controls section content, not the complete request token count. Metadata and instructions add overhead.category and categories assignments; a reference may appear under several categories. If none is provided, the LLM proposes a topic. Existing descriptions, tags and other frontmatter are included as context.pageSize items, generates page summaries, and recursively builds parent indexes to keep the skill entrypoint small.Generated descriptions are bounded and validated as JSON. Their factual quality still depends on the model: review routing with representative questions before relying on it. The runtime instructions require reading the actual references before answering. Categories are retained verbatim; generated descriptions use routing.language and source text retains its original language. Non-Markdown linked assets are not copied.
The result contains:
my-topic-routed/
SKILL.md
indexes/
page-1.md
...
catalog.md
catalog-1.md
...
references/guides/
original-document.md
...
LICENSE
NOTICE
No npm installation, embedding model, vector index, API call or executable script is needed to use this exported skill. --include-model is rejected for routed exports. The existing build and watch commands maintain the semantic index; routed generation is explicitly invoked through export --mode routed so background edits cannot silently trigger paid LLM requests. Re-export to a new destination after updates; unchanged requests reuse the cache.
node src/cli.mjs build --project examples/home-guidance
node src/cli.mjs search "recover deleted documents" --project examples/home-guidance
node src/cli.mjs retrieve restore-backup --project examples/home-guidance
A document can be plain Markdown or include YAML metadata:
---
id: restore-backup
description: Restore deleted files from a backup archive.
category: backups
tags: [restore, recovery]
---
# Restore a backup
Write your actual guidance here.
Nested folders are supported. Without frontmatter, the relative filename becomes the ID. IDs must be unique. Empty documents, invalid YAML, duplicate IDs and symlinks cause a build error. Hidden files and non-Markdown files are ignored. Linked images, PDFs and other assets are not imported.
marked, plus a metadata chunk.Xenova/all-MiniLM-L6-v2 feature extraction with mean pooling and normalization.Search ranking and heading chunking derive from Google's source. See NOTICE for exact source files and commits. Both build and query use Transformers.js/ONNX here; Google uses a separate TFJS query runtime. Scores are not promised to be identical. Google's telemetry, web-specific macros and browser-baseline processing are not included.
Incremental builds reuse unchanged chunk vectors. Model and chunking changes invalidate the cache. Index and document text are published together, so readers cannot mix different versions. Search returns metadata only; loading documents into process memory is not the same as putting them into an LLM context.
| Command | Purpose |
|---|---|
init <directory> | Create an independent knowledge project |
build | Incrementally rebuild the index |
build --force | Recompute all vectors; model cache is retained |
watch | Continuously rebuild after stable changes |
search "query" | Return ranked JSON metadata |
retrieve <id> | Print one complete guide |
status | Inspect index freshness and counts |
model | Download/warm the configured model |
export <new-directory> | Export a reusable skill snapshot |
All except init support --project <directory> (default: current directory). search also supports --top-k and --threshold. --offline requires a prepared model cache. Output is JSON or document text on stdout, diagnostics on stderr.
The built-in polling watcher hashes file contents every 1,000 ms and requires 800 ms of stability. It handles edits, additions, renames, deletions, whole-directory replacement, and configuration changes without a third-party watcher. Builds are serialized. A mutation during inference prevents publishing that snapshot and is retried on the next cycle.
A missing directory or invalid document preserves the last valid index. An existing empty directory intentionally publishes an empty index. CLI search/retrieve reject stale indexes. To replace a large knowledge base, prepare a new folder first and swap it in; a paused copy can otherwise expose a valid intermediate state.
The watcher must remain running; it is not installed as a background service. Exports are independent snapshots and require a new export to update.
init writes all defaults. Configure knowledge, output, cache, model, chunk, search and watch in skill-kit.json. Paths must be separate subdirectories of the project. The default model revision is pinned to 751bff37182d3f1213fa05d7196b954e230abad9.
| Setting | Purpose |
|---|---|
name, description | Skill name and instructions for when agents should select it |
knowledge | Markdown directory relative to the project |
output | Index directory; defaults to .skill-kit |
cache | Local model cache; defaults to .cache/models |
model.id, model.revision, model.dtype | Embedding model, revision and quantization |
model.maxTokens | Maximum model input length, including special tokens |
model.queryPrefix, model.documentPrefix | Prefixes required by some embedding models |
chunk.maxTokens, chunk.overlap | Token window size and overlap |
search.topK, search.threshold | Result limit and minimum similarity |
watch.intervalMs, watch.settleMs | Polling interval and stability delay |
The default MiniLM model is primarily intended for English. Use an appropriate multilingual Transformers.js-compatible sentence-embedding model for German or multilingual corpora. Mean pooling is used; models requiring another pooling strategy need code changes. Set the correct token limit, quantization and optional queryPrefix / documentPrefix. Changing model settings triggers a full rebuild. Prefer immutable model revisions over main.
chunk.maxTokens includes metadata context, and must leave room for model special tokens. Long sections are split into overlapping windows; retrieved documents remain unchanged. Scores are similarity values, not probabilities. Validate thresholds with representative questions. Linear vector scanning and in-memory snapshots suit curated knowledge bases, not millions of documents.
The export destination must not already exist. Export first ensures the index is current, then produces an independent skill directory:
my-topic/
SKILL.md
skill-kit.json
guides/
data/index.json.gz
src/
assets/runtime-package-lock.json
package.json
package-lock.json
LICENSE
NOTICE
Install dependencies inside this directory with npm ci, then use the target agent's skill installation mechanism. Ordinary retrieval runs node src/cli.mjs search "..." followed by node src/cli.mjs retrieve "<id>" from the skill directory. Maintaining the knowledge base and rebuilding the index remain separate from ordinary agent use.
node src/cli.mjs model --project my-topic
node src/cli.mjs export dist/my-topic-offline --project my-topic --include-model
This includes the project's model cache and makes the generated skill use search --offline. Node and platform-specific npm dependencies still need installation on the target system first. node_modules is deliberately not copied between platforms. Model files are about 23 MB for the default model; a cache containing multiple models makes larger exports.
npm test
npm run test:integration
Routed tests exercise the HTTP client against a local mock API, including invalid responses, timeouts, cache invalidation, manual categories, long documents, and complete reference reachability. They do not measure real-model routing quality or call a paid provider.
Unit/integration tests with controlled vectors cover chunking, ranking, caching, deletion, atomic failure recovery, concurrent builds, folder replacement and export. The separate real-model test verifies MiniLM search, token windows and offline export. Its cache defaults to examples/home-guidance/.cache/models; override with SKILL_KIT_TEST_CACHE.
CI is configured for Node 22/24 on Linux and Windows; local validation was on Linux, Node 24. No TypeScript or compilation step. Library exports are available from src/index.mjs.
After dependency updates, copy package-lock.json to assets/runtime-package-lock.json. The latter ships with npm distributions so exports have a reproducible lockfile. Increment the pipeline version in fingerprint() when changing embedding semantics.
A crash can leave .skill-kit/build.lock; remove it only after checking no builder is running. For a damaged index, use build --force. If ONNX Runtime tries downloading CUDA, run npm ci --onnxruntime-node-install-cuda=skip; .npmrc already configures CPU-only installation. npm 11 may warn about this package-specific setting.
Independent derivative; not endorsed by Google. Dependency and model licenses remain applicable.
4 commits
JavaScript
100.0%