Skip to main content

snippet-automation

Generate and sync Bluehawk snippets across MongoDB docsets. Use when the user asks to "run snip", "generate snippets", "sync snippets", "add snippet", "list snippets", "setup snippet for a docset", "set up a snippet source", or wants to extract and distribute content from a source docset to target docsets.

Source facts

Repository
mongodb/docs
Last source activity
September 17, 2026 at 22:55
Detected SKILL.md language
English
Stars
777
Forks
1,699

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
4 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
snippet-automation
description
Generate and sync Bluehawk snippets across MongoDB docsets. Use when the user asks to "run snip", "generate snippets", "sync snippets", "add snippet", "list snippets", "setup snippet for a docset", "set up a snippet source", or wants to extract and distribute content from a source docset to target docsets.
# Snippet Automation: Cross-Docset Content Sharing Generate Bluehawk snippets from source files and distribute them to target docsets using the `snip.js` tooling with manifest-based change tracking. ## Scope **ALWAYS use when:** - User asks to "run snip", "generate snippets", "sync snippets", "add snippet", or "list snippets" - User wants to share content from one docset to others (atlas → search, vector-search, etc.) - User needs to register, remove, or check monitored snippet files - User wants to set up a new snippet source docset — add `snip.js`, `package.json`, and CI registration (also the version-flip setup for a versioned docset) **NEVER use when:** - Files are in `code-examples/tested/` → STOP, tell user to use Grove skill instead - User wants Grove test suite snippets → STOP, wrong skill - User wants to edit RST content directly → STOP, no skill needed ## Prerequisites **CI automation (recommended):** No local setup required. GitHub Action handles everything. **Local runs require:** - Node.js v18+ (MUST have) - npm (MUST have) - Bluehawk CLI: `npm install -g bluehawk` (MUST install first — installed globally, not as a docset dependency) ## Required Markers (Proprietary Syntax) Source files MUST contain these exact RST comments: ```rst .. :snippet-start: snippet-name .. :snippet-output: content/<target>/source/includes/path/filename.rst (content here) .. :snippet-end: ``` **Validation rules (enforce strictly):** - `:snippet-output:` is REQUIRED — STOP if missing, tell user to add it - Multi-snippet files MUST use explicit filenames, not directories — STOP if directory-only - Snippet names MUST be unique within a file — STOP on duplicate `:snippet-start:` names - Each block MUST have exactly one `:snippet-output:` — use comma-separated paths for multiple destinations, not repeated tags - Output filename gets `-snippet-from-<source>` suffix automatically (do NOT add manually) - Files in `code-examples/tested/` are FORBIDDEN — STOP immediately if detected. `snip.js` also enforces this: it skips these paths during directory traversal and rejects an explicit `--add` with `Excluded path, not added: <file>` **References:** - `references/bluehawk-syntax.md` — full syntax and validation rules - `references/snip-js-config.md` — configured docsets and error messages - `references/manifest-schema.md` — manifest structure for change tracking ## Versioned Docsets Only ONE version per versioned docset can be a snippet source, because output filenames carry the docset name but not the version. For `self-managed-search` that version is `current`. - Treat `:snippet-start:` markers in any other version (`upcoming`, `vX.Y`) as inert. They generate nothing. Do NOT add `:snippet-output:` tags to them to "fix" the invalid-marker report — that creates colliding output paths. - If the user asks to sync a non-`current` version, STOP and explain that its markers activate only at version flip. See `references/snip-js-config.md` for the flip checklist. ## Commands | Command | What It Does | Success Output | |---------|--------------|----------------| | `sync snippets` | Regenerate changed snippets across all docsets | `Sync complete: N file(s) changed, M snippet(s) written, X orphaned output(s) deleted.` | | `sync snippets for <docset>` | Sync one docset only | Same as above, scoped to that docset's manifest | | `add snippet <file>` | Register file for tracking | `Added to manifest: <path>` then ` Snippets: <names>` | | `register snippets <dir>` | Bulk-register directory | `Registered N file(s) from <dir>` | | `remove snippet <file>` | Stop tracking and delete output files | `Removed from manifest: <path> (N snippets, M output files deleted)` | | `list snippets` | Show tracked files | `Monitored files (N):` then each file with its snippets and output paths | | `setup snippet for <docset>` | Set up a new snippet source docset: copy `snip.js`, add `package.json`, register in CI, then init the manifest | drift check passes and `✓ Created empty manifest: <path>` | | `init manifest` | Create empty manifest (aborts if exists) | `✓ Created empty manifest: <path>` | | `reset manifest` | Clear all tracked files | `✓ Reset manifest: <path>` | | `validate manifest` | Check markers, files, and hashes | `N valid, M out of sync, X missing or invalid` | | `run snip` | One-off generation (no tracking) | Interactive prompt | **CLI equivalent**: `cd <source-dir> && npm run snip -- --<flag>`, where `<source-dir>` is the docset's snippet root: | Docset | `<source-dir>` | |--------|----------------| | atlas | `content/atlas/source` | | self-managed-search | `content/self-managed-search/current/source` | Only these two docsets have a `snip.js`. Destination docsets such as `search` and `vector-search` do not — they only receive generated output. Versioned docsets keep `snip.js` in one version only, so do NOT assume `content/<docset>/source`. **Example**: `/snippet-automation add snippet includes/nav/list-data-explorer.rst` ## Automation A GitHub Action (`.github/workflows/snippet-sync.yml`) automatically: 1. **Triggers** on PRs touching a configured docset's `includes/**` or its `snippet-manifest.json`. The paths are listed explicitly per docset, not globbed — `self-managed-search` is registered as `current/source/includes/**`, so markers in other versions never trigger it 2. **Installs Bluehawk** in the CI runner 3. **Runs `--sync`** for affected docsets with manifests 4. **Commits regenerated snippets** back to the PR branch Writers don't need Bluehawk installed locally — CI handles regeneration. ## Manual Workflows Every workflow starts by resolving the docset's source directory from the file path. Two shapes exist: - `content/<docset>/source/...` → source dir is `content/<docset>/source` - `content/<docset>/<version>/source/...` → source dir is `content/<docset>/<version>/source`, and only ONE version has `snip.js` (see Versioned Docsets above) VERIFY the directory has a `snip.js` before running any command: `ls <source-dir>/snip.js`. If it is absent, that version is not a snippet source — STOP, UNLESS the user is setting one up (see "Set up a new snippet source docset" below, the one workflow that runs before `snip.js` exists). All other commands below run from `<source-dir>`. ### Set up a new snippet source docset Use when a destination docset needs to become a source, or when a versioned docset flips and the incoming version must take over sourcing (see Versioned Docsets above). `<source-dir>` is the new docset's snippet root — `content/<docset>/source` or, for a versioned docset, the single owning version's `content/<docset>/<version>/source`. 1. CONFIRM it is not already a source: `ls <source-dir>/snip.js` MUST be absent. If it exists, STOP — the docset is already set up; use the tracking workflows instead. 2. COPY an existing copy verbatim — do NOT hand-write one: `cp content/atlas/source/snip.js <source-dir>/snip.js`. The drift check (`.github/scripts/check-snip-drift.js`) requires every copy to be byte-identical apart from one line. 3. EDIT only `DEFAULT_START_DIRECTORY` in the new copy so it equals `<source-dir>` exactly (for example `content/foo/source`). The drift check FAILS if this value is anything other than the copy's own directory. Change nothing else. 4. ADD `package.json` in `<source-dir>` with `"type": "module"` (required for the ESM `import` syntax) and the `snip` script: ```json { "name": "<docset>", "private": true, "type": "module", "scripts": { "snip": "node snip.js" } } ``` No `npm install` is needed — no dependencies are declared. Bluehawk is installed globally. 5. REGISTER the docset in `.github/workflows/snippet-sync.yml` with THREE edits: - Under `on.pull_request.paths`, add `content/<docset-path>/includes/**` and `content/<docset-path>/snippet-manifest.json`, where `<docset-path>` is `<source-dir>` minus the repo prefix. - In the `Detect changed docsets` step, add a `grep`/`DOCSETS` block appending the docset name when a matching file changes. - Add a `Sync <docset> snippets` step that `cd`s into `<source-dir>` and runs `--sync` when its manifest exists. Its `contains()` guard MUST use the space-padded form (`' {0} '`, `' <docset> '`) — an unpadded `search` would also match `self-managed-search` and fire the wrong step. Do NOT touch `check-snip-drift`: it discovers copies by walking `content/`, so the new copy is covered automatically. 6. INIT the manifest: `cd <source-dir> && npm run snip -- --init`. 7. VERIFY: run `node .github/scripts/check-snip-drift.js` (MUST exit 0) and `cd <source-dir> && npm run snip -- --validate`. 8. REPORT the files created (`snip.js`, `package.json`, `snippet-manifest.json`), the workflow edits, and the drift-check result. **Version-flip variant:** these steps also cover a versioned docset's flip, where the incoming version takes over sourcing. One extra consideration applies — source files branched forward into the new version keep their `:snippet-start:` markers but LOSE their `:snippet-output:` tags. Restore the `:snippet-output:` tag on every such file before step 6, or `--validate`/`--sync` reports them as invalid and skips them. **Worked example — converting `search` (a destination) into a source.** `<source-dir>` = `content/search/source`; each command maps to the numbered step above: ```bash ls content/search/source/snip.js # 1. MUST print "No such file" cp content/atlas/source/snip.js content/search/source/snip.js # 2. copy verbatim # 3. edit copy: const DEFAULT_START_DIRECTORY = "content/search/source"; # 4. add content/search/source/package.json (block above, "name": "search") # 5. snippet-sync.yml: add the two paths + a DOCSETS grep + a --sync step # guarded by contains(..., ' search ') — space-padded, else it also # matches self-managed-search cd content/search/source && npm run snip -- --init # 6. node .github/scripts/check-snip-drift.js # 7. MUST exit 0 cd content/search/source && npm run snip -- --validate # 7. ``` ### Add a file to tracking 1. VERIFY markers exist and are well-formed. The minimum valid block is: ```rst .. :snippet-start: list-data-explorer .. :snippet-output: content/search/source/includes/nav/list-data-explorer.rst In |service|, go to the :guilabel:`Data Explorer` page. .. :snippet-end: ``` If markers are missing or malformed, STOP and tell the user what to add. `--add` rejects the file rather than registering it, so bad markers cannot be fixed later by syncing. 2. EXECUTE: `npm run snip -- --add <file>` 3. VERIFY output exists: `ls content/<target>/source/includes/path/*-snippet-from-*.rst` 4. REPORT exactly: "Added [file] to manifest, generated [output] at [path]" ### Sync changed snippets 1. EXECUTE: `npm run snip -- --sync` 2. CHECK the exit code. Non-zero means files were skipped for invalid markers or failed to generate — the `Sync complete:` line still prints, so do NOT treat it alone as success. 3. If any `✗ Invalid: <file>` appeared, STOP and report which files were skipped and why. Fix markers, then re-run. 4. REPORT the `Sync complete: N file(s) changed, M snippet(s) written, X orphaned output(s) deleted.` counts. ### Remove a file from tracking `--remove` DELETES generated output files. Confirm with the user before running it. 1. RUN `npm run snip -- --list` first and show the user which outputs will be deleted. 2. EXECUTE: `npm run snip -- --remove <file>` 3. VERIFY the deleted outputs are no longer referenced: `grep -rn "<basename>-snippet-from-" content/`. Any surviving `.. include::` will break the target docset's build — report these to the user. 4. REPORT the `Removed from manifest: <path> (N snippets, M output files deleted)` line. ## Error Handling (STOP conditions) | Condition | Action | |-----------|--------| | Missing `:snippet-output:` | STOP. Tell user: "Add `:snippet-output:` tag with target path" | | Directory path in multi-snippet file | STOP. Tell user: "Each snippet MUST have explicit filename" | | Duplicate `:snippet-start:` name in one file | STOP. Tell user: "Rename so every snippet name in the file is unique" | | Repeated `:snippet-output:` in one block | STOP. Tell user: "Use one tag with comma-separated paths" | | `:snippet-output:` path has no file extension | STOP. Tell user: "Add the file extension, for example `.rst`" | | `--sync` reports `✗ Invalid: <file>` | STOP. The file was skipped and `--sync` exited non-zero. Fix the markers, then re-run | | `--sync` or `--validate` reports untracked snippet | Run `--sync` to add it to the manifest and generate its output | | `--sync` or `--validate` reports output path changed | Run `--sync` to delete the stale output and regenerate at the new path | | Target directory missing | CREATE it: `mkdir -p content/<target>/source/includes/path/` | | Bluehawk not installed locally | RECOMMEND CI. If local needed: `npm install -g bluehawk` | | File in `code-examples/tested/` | ABORT. Tell user: "Use Grove skill, not snippet-automation" |
View on GitHub