Manifest
Functions
hashSignature
function hashSignature(signature: string): string
Use hashSignature to generate a short, stable fingerprint of an API signature string so skrypt can detect when a function's interface has changed between documentation runs.
This is the mechanism behind skrypt's incremental generation: before regenerating docs for an element, skrypt hashes its current signature and compares it against the hash stored in manifest.json. If the hashes match, the element is skipped — saving AI API calls and keeping generation fast.
| Name | Type | Required | Description |
|---|---|---|---|
signature | string | Yes | The raw API signature to fingerprint — typically the full function or type declaration extracted from source code. |
Returns a 16-character hex string (truncated SHA-256). Store this alongside the documented element in your manifest; on the next run, compare it against a freshly computed hash to decide whether re-generation is needed.
Heads up:
- The hash is truncated to 16 characters for compactness. Collision probability is negligible for typical codebase sizes, but don't use this for security-sensitive purposes.
- Whitespace and formatting are part of the input —
function foo( x: string )andfunction foo(x: string)produce different hashes. Normalize signatures before hashing if your scanner's output isn't stable.
Example:
import { createHash } from 'crypto'
function hashSignature(signature: string): string {
return createHash('sha256').update(signature).digest('hex').slice(0, 16)
}
// Simulate a manifest entry from a previous skrypt run
const manifest: Record<string, { hash: string; lastGenerated: string }> = {
'UserService.createUser': {
hash: 'a3f8c21d9e4b7f02',
lastGenerated: '2024-11-01T10:00:00Z',
},
}
// Current signature extracted from source during this run
const currentSignature =
'function createUser(email: string, role: "admin" | "member"): Promise<User>'
const currentHash = hashSignature(currentSignature)
const stored = manifest['UserService.createUser']
if (stored && stored.hash === currentHash) {
console.log('Signature unchanged — skipping AI generation')
console.log(`Reusing docs generated at ${stored.lastGenerated}`)
} else {
console.log('Signature changed — regenerating documentation')
console.log(`Old hash: ${stored?.hash ?? 'none'}`)
console.log(`New hash: ${currentHash}`)
// → trigger AI doc generation here, then update manifest
}
// Expected output (hashes will differ since stored hash is a placeholder):
// Signature changed — regenerating documentation
// Old hash: a3f8c21d9e4b7f02
// New hash: 9d4e1a7c3b82f051
buildManifestEntries
function buildManifestEntries(elements: APIElement[], outputPath: string): ManifestEntry[]
Use buildManifestEntries to convert your scanned API elements into manifest entries that map each element to its generated documentation file on disk.
Call this after scanning your source code and before writing the manifest file. It bridges the gap between raw APIElement objects (what the scanner produces) and the structured index that your doc site uses to resolve links, search, and navigation.
Each APIElement is mapped to a ManifestEntry that pairs the element's name with its expected output path, giving downstream tooling a stable lookup table for the entire API surface.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
elements | APIElement[] | Yes | The API elements returned by your scanner — each one becomes a single manifest entry. An empty array produces an empty manifest. |
outputPath | string | Yes | The root directory where generated .mdx files will be written. Used to construct the file path for each entry — must match the -o path you passed to skrypt generate. |
Returns
Returns a ManifestEntry[] where each entry contains the element's name and its resolved output path. Pass this array directly to your manifest writer to produce the manifest.json file that the doc site reads at build time.
Heads up
- The output path is used as-is to construct entry paths — if it doesn't match the actual output directory your doc site serves from, links and navigation will silently resolve to the wrong locations.
- This function does not write anything to disk; it only builds the data structure. You still need to serialize and write the result yourself.
Example:
import { join } from "path";
import { createHash } from "crypto";
interface APIElement {
name: string;
kind: "function" | "class" | "interface" | "type";
signature: string;
filePath: string;
docstring?: string;
}
interface ManifestEntry {
name: string;
outputFile: string;
kind: string;
hash: string;
}
function buildManifestEntries(
elements: APIElement[],
outputPath: string
): ManifestEntry[] {
return elements.map((el) => ({
name: el.name,
outputFile: join(outputPath, `${el.name}.mdx`),
kind: el.kind,
hash: createHash("md5").update(el.signature).digest("hex"),
}));
}
const scannedElements: APIElement[] = [
{
name: "createPayment",
kind: "function",
signature: "function createPayment(amount: number, currency: string): Promise<Payment>",
filePath: "src/payments/create.ts",
docstring: "Creates a new payment intent and returns the client secret.",
},
{
name: "PaymentClient",
kind: "class",
signature: "class PaymentClient { constructor(apiKey: string) }",
filePath: "src/payments/client.ts",
docstring: "Main client for interacting with the payments API.",
},
{
name: "WebhookHandler",
kind: "class",
signature: "class WebhookHandler { verify(payload: string, secret: string): boolean }",
filePath: "src/webhooks/handler.ts",
},
];
try {
const outputDir = "./content/docs/api";
const entries = buildManifestEntries(scannedElements, outputDir);
console.log("Manifest entries:", JSON.stringify(entries, null, 2));
// Expected output:
// Manifest entries: [
// {
// "name": "createPayment",
// "outputFile": "content/docs/api/createPayment.mdx",
// "kind": "function",
// "hash": "a3f1c..."
// },
// {
// "name": "PaymentClient",
// "outputFile": "content/docs/api/PaymentClient.mdx",
// "kind": "class",
// "hash": "b92d4..."
// },
// ...
// ]
} catch (err) {
console.error("Failed to build manifest entries:", err);
}
writeManifest
function writeManifest(outputPath: string, entries: ManifestEntry[]): void
Use writeManifest to persist a documentation manifest to disk so skrypt can track what was generated, detect stale docs, and power incremental rebuilds.
Call this after your documentation entries have been generated — typically as the final step in a generation pipeline. It's what allows skrypt to know which files exist, when they were last built, and whether they need to be regenerated on the next run.
writeManifest writes a manifest.json file into a .skrypt/ directory inside your output path, creating that directory if it doesn't exist. The manifest is stamped with a generatedAt ISO timestamp alongside your entries.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
outputPath | string | Yes | Root directory where your docs are written — the .skrypt/manifest.json file will be created inside this directory |
entries | ManifestEntry[] | Yes | The documentation entries to record — each entry represents a generated doc file and its metadata, used for change detection on subsequent runs |
Returns
void. After this call, .skrypt/manifest.json exists on disk. On the next skrypt generate run, skrypt reads this file to skip unchanged entries and only regenerate what's new or modified.
Heads up
outputPathmust be an absolute or correctly resolved relative path — if it points to the wrong directory, the manifest will be written there silently with no error.- The
.skrypt/directory is created automatically, but the parentoutputPathdirectory must already exist.
Example:
import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'fs'
import { join } from 'path'
import { tmpdir } from 'os'
// Inline types — do not import from autodocs
interface ManifestEntry {
slug: string
title: string
filePath: string
hash: string
generatedAt: string
}
interface DocManifest {
generatedAt: string
entries: ManifestEntry[]
}
const MANIFEST_DIR = '.skrypt'
const MANIFEST_FILE = 'manifest.json'
// Inline implementation matching skrypt's writeManifest behavior
function writeManifest(outputPath: string, entries: ManifestEntry[]): void {
const manifestDir = join(outputPath, MANIFEST_DIR)
mkdirSync(manifestDir, { recursive: true })
const manifest: DocManifest = {
generatedAt: new Date().toISOString(),
entries,
}
writeFileSync(
join(manifestDir, MANIFEST_FILE),
JSON.stringify(manifest, null, 2),
'utf-8'
)
}
// Simulate a completed generation run
const outputDir = join(tmpdir(), 'my-api-docs')
mkdirSync(outputDir, { recursive: true })
const entries: ManifestEntry[] = [
{
slug: 'auth-create-user',
title: 'createUser',
filePath: join(outputDir, 'auth/create-user.mdx'),
hash: 'a3f8c2d1e9b74056',
generatedAt: new Date().toISOString(),
},
{
slug: 'auth-delete-user',
title: 'deleteUser',
filePath: join(outputDir, 'auth/delete-user.mdx'),
hash: 'b7e1d4c9f2a83017',
generatedAt: new Date().toISOString(),
},
]
try {
writeManifest(outputDir, entries)
const manifestPath = join(outputDir, '.skrypt', 'manifest.json')
const written = JSON.parse(readFileSync(manifestPath, 'utf-8')) as DocManifest
console.log('Manifest written to:', manifestPath)
console.log('Generated at:', written.generatedAt)
console.log('Entries recorded:', written.entries.length)
// Manifest written to: /tmp/my-api-docs/.skrypt/manifest.json
// Generated at: 2024-11-12T15:42:03.217Z
// Entries recorded: 2
} catch (err) {
console.error('Failed to write manifest:', err)
}
readManifest
function readManifest(outputPath: string): DocManifest | null
Use readManifest to load a previously generated documentation manifest so you can diff against it before regenerating docs.
Call this at the start of a generation pipeline to check whether docs already exist for a given output directory. If a manifest is present, you can compare file hashes or topic lists to skip unchanged files and avoid unnecessary AI calls.
readManifest looks for a manifest file inside a fixed .skrypt subdirectory within outputPath. If that file doesn't exist — such as on a first run — it returns null rather than throwing, so you can safely use it as an existence check.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
outputPath | string | Yes | Path to the directory where docs were previously generated — the same value you passed to skrypt generate -o. The manifest is read from <outputPath>/.skrypt/manifest.json. |
Returns
Returns a DocManifest object containing metadata about the last generation run (file hashes, documented elements, topics), or null if no manifest exists at that path. Use the returned manifest to compare against the current source scan and selectively regenerate only what has changed.
Heads up
- Returns
nullon a first run or if the output directory was created outside ofskrypt generate— always null-check before accessing manifest properties. - If you manually delete or move the
.skryptsubdirectory inside your output path,readManifestwill returnnulleven if your generated.mdxfiles are still present.
Example:
import { existsSync, readFileSync } from "fs";
import { join } from "path";
// Inline types — do not import from autodocs
interface DocElement {
name: string;
file: string;
hash: string;
}
interface DocManifest {
version: string;
generatedAt: string;
elements: DocElement[];
topics: string[];
}
// Self-contained readManifest implementation
function readManifest(outputPath: string): DocManifest | null {
const manifestPath = join(outputPath, ".skrypt", "manifest.json");
if (!existsSync(manifestPath)) {
return null;
}
const raw = readFileSync(manifestPath, "utf-8");
return JSON.parse(raw) as DocManifest;
}
// --- Usage ---
const OUTPUT_DIR = "./docs";
try {
const manifest = readManifest(OUTPUT_DIR);
if (manifest === null) {
console.log("No existing manifest found — this looks like a first run.");
console.log("Proceeding with full documentation generation.");
} else {
console.log(`Manifest found (generated ${manifest.generatedAt})`);
console.log(` Version: ${manifest.version}`);
console.log(` Elements: ${manifest.elements.length}`);
console.log(` Topics: ${manifest.topics.join(", ")}`);
// You would now diff manifest.elements against your current scan
// to skip files whose hashes haven't changed.
}
} catch (err) {
console.error("Failed to read manifest:", err);
}
// Expected output (first run):
// No existing manifest found — this looks like a first run.
// Proceeding with full documentation generation.
// Expected output (subsequent run):
// Manifest found (generated 2024-11-03T14:22:10.000Z)
// Version: 1.2.0
// Elements: 42
// Topics: authentication, payments, webhooks