ai-docs-generate

$npx mdskill add MicrosoftDocs/windows-driver-docs-ddi/ai-docs-generate

Generate WDK DDI API reference pages from code and stubs.

  • Creates complete documentation pages using stub files and OS source declarations.
  • Integrates Azure DevOps REST API and substrate-mcp MCP server for data.
  • Fetches remote style guides and published docs from specified repositories.
  • Outputs generated content to a local working directory folder.
SKILL.md
.github/skills/ai-docs-generateView on GitHub ↗
---
name: ai-docs-generate
description: 'Generate WDK DDI API reference documentation pages from source code and stubs. Use when: writing API docs, generating reference pages, documenting a header, creating DDI documentation.'
argument-hint: 'Specify a header name (e.g. soundwireclass) and the path to the CSV file.'
---

# Generate API Docs

Generate complete API reference documentation pages for WDK DDI entities by combining stub files, OS source code declarations, and supplemental information from published docs.

**No local repo clones required.** All repo interactions use the Azure DevOps REST API. OS source lookups use the `substrate-mcp` MCP server.

## Parameters

| Parameter | Value |
|-----------|-------|
| ADO Org | `https://dev.azure.com/cpubwin` |
| ADO Project | `drivers` |
| Docs Repo | `wdk-ddi` (branches: `main`, `stubs/main`) |
| Published Docs Repo | `windows-driver-docs-ddi` (branch: `staging`) |
| Content Path | `wdk-ddi-src/content/{header}/` |
| Stubs Branch | `stubs/main` (default; user may specify alternate) |
| Header Name | Provided by the user (without `.h` extension for path operations) |
| CSV Path | Provided by the user at any local path (e.g. `D:\work\soundwireclass.csv`) |
| Working Directory | Derived from CSV path (parent folder of the CSV file) |
| Output Path | `{working_dir}\output\` |
| Style Guide | Read remotely from `wdk-ddi` repo: `.github/copilot-instructions.md` on `main` |

## Prerequisites

- The user provides a header name and a path to a CSV file (pre-provided or from the `ai-docs-inventory` skill). **That is the only user requirement.**
- The `substrate-mcp` MCP server must be accessible for source code retrieval
- The `microsoft.docs.mcp` MCP server should be accessible for supplemental info (non-fatal if unavailable)
- Azure CLI (`az`) should be available for auth token acquisition. If not, the agent will prompt for an ADO Personal Access Token (PAT) once per session.

## Procedure

1. Strip the `.h` extension from the user-provided header name to get `{header}` (e.g. `soundwireclass.h` → `soundwireclass`).

2. **Resolve paths.** The user provides a CSV path. Derive the working directory from it:

   ```powershell
   $csvPath = "{user-provided CSV path}"
   if (-not (Test-Path $csvPath)) {
       Write-Error "CSV not found at $csvPath. Please provide the CSV file first."
       return
   }
   $workingDir = Split-Path $csvPath -Parent
   $outputDir = Join-Path $workingDir "output"
   Import-Csv $csvPath
   ```

   If the CSV does not exist, inform the user and stop.

3. **Obtain ADO auth token.** Try Azure CLI first, then fall back to prompting for a PAT:

   ```powershell
   try {
       $token = (az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv 2>$null)
       if (-not $token) { throw "No token" }
       $headers = @{ Authorization = "Bearer $token" }
   } catch {
       $pat = Read-Host "Enter ADO PAT (scope: Code Read)"
       $base64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$pat"))
       $headers = @{ Authorization = "Basic $base64" }
   }
   $adoBase = "https://dev.azure.com/cpubwin/drivers/_apis/git/repositories"
   ```

4. **Read the style guide** remotely from the wdk-ddi repo to ensure all formatting rules are followed:

   ```powershell
   $styleGuide = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/items?path=.github/copilot-instructions.md&versionDescriptor.version=main&versionDescriptor.versionType=branch&api-version=7.0" -Headers $headers
   ```

5. Parse the CSV file to get the list of target filenames. The CSV has a header row and one `filename` column with paths like `wdk-ddi-src/content/{header}/{filename}.md`.

6. Create the output directory if it doesn't exist; if it does, clear it:

   ```powershell
   if (Test-Path $outputDir) {
       Remove-Item "$outputDir\*" -Force -ErrorAction SilentlyContinue
   } else {
       New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
   }
   ```

7. For each target file listed in the CSV, **skip `index.md`** (the header landing page is handled separately in step 8).

   For each remaining target file:

   ### a. Get the stub or existing file

   Use the ADO REST API to check if the file already exists on `main`:
   ```powershell
   # List files on main branch
   $mainFiles = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/items?scopePath=wdk-ddi-src/content/{header}/&recursionLevel=OneLevel&versionDescriptor.version=main&versionDescriptor.versionType=branch&api-version=7.0" -Headers $headers
   ```
   If the target filename appears in the listing, retrieve it from `main` and use its content as the starting point — preserve existing text, but **always update `ms.date` to today's date** when making any content changes. **Only write the file to the output folder if you make changes.** Do not copy unchanged files.
   ```powershell
   $content = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/items?path=wdk-ddi-src/content/{header}/{filename.md}&versionDescriptor.version=main&versionDescriptor.versionType=branch&api-version=7.0" -Headers $headers
   $content | Out-File -FilePath (Join-Path $outputDir "{filename.md}") -Encoding utf8
   ```

   If it does not exist on `main`, retrieve the stub from the stubs branch:
   ```powershell
   $content = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/items?path=wdk-ddi-src/content/{header}/{filename.md}&versionDescriptor.version=stubs/main&versionDescriptor.versionType=branch&api-version=7.0" -Headers $headers
   $content | Out-File -FilePath (Join-Path $outputDir "{filename.md}") -Encoding utf8
   ```

   ### b. Retrieve source declaration from the OS source tree

   Use the `substrate-mcp` MCP server to find the API's source declaration:

   - Extract the API name from the filename (e.g. `nf-soundwireclass-somefunc.md` → `SomeFunc`)
   - Search for the definition:
     ```
     search_code(query="def:{ApiName}", ext="h")
     ```
   - If `def:` doesn't find it, try keyword search:
     ```
     search_code(query="{ApiName}", ext="h", path="**/{header}.h")
     ```
   - Once found, use `get_file_content` with a line range to retrieve the full declaration including:
     - SAL annotations (`_In_`, `_Out_`, `_Inout_`, `_In_opt_`, `_Out_opt_`, `_Reserved_`)
     - Return type
     - All parameter names and types
     - Any adjacent comments describing the function
   - For structures/enums, retrieve the full type definition with all fields/values

   ### c. Gather supplemental information

   Use the `microsoft.docs.mcp` server's `microsoft_docs_search` tool to search for any existing published docs or related conceptual content for the API:
   ```
   microsoft_docs_search(query="{ApiName} WDK driver")
   ```

   ### d. Find formatting models

   Find 1-2 completed reference pages in the same header folder to use as formatting models. Use the ADO REST API to list files, then retrieve them:
   ```powershell
   # List existing completed docs of the same entity type in the {header} folder
   $mainFiles = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/items?scopePath=wdk-ddi-src/content/{header}/&recursionLevel=OneLevel&versionDescriptor.version=main&versionDescriptor.versionType=branch&api-version=7.0" -Headers $headers
   # Filter for files matching the same prefix (e.g. "nf-") and pick 1-2
   ```
   Then read the model files:
   ```powershell
   $modelContent = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/items?path=wdk-ddi-src/content/{header}/{model-filename.md}&versionDescriptor.version=main&versionDescriptor.versionType=branch&api-version=7.0" -Headers $headers
   ```
   If no completed pages exist in the same folder, look in a related header folder. Read these model files to match their style.

   ### e. Write the completed documentation page

   Write the completed file to `{outputDir}\{filename.md}`. Follow these rules:

   #### YAML Frontmatter
   - Preserve all existing metadata from the stub
   - Add `ai-usage: ai-assisted` if not already present
   - Set `techroot` to the same value as existing files in the same header folder. If no existing files, prompt user for value.
   - **Always** set `ms.date` to today's date in `MM/DD/YYYY` format — both for new files and when updating existing files with changes
   - Verify `req.header`, `f1_keywords`, `api_name`, `topic_type` are correct
   - **`req.header` uses lowercase** (e.g. `classpnp.h`). **`req.include-header` uses sentence capitalization** (e.g. `Classpnp.h`). Note that `req.include-header` does not always require a value — omit it or leave it empty when an include header is not applicable.
   - **`req.construct-type` for macros:** If the source declaration is a preprocessor macro (`#define`), set `req.construct-type: macro`, not `function`. The file prefix remains `nf-` and the title should say "macro" (e.g. `CLEAR_FLAG_NOFENCE macro (classpnp.h)`).
   - **Leading underscores in UIDs:** If the stub file's UID uses a leading underscore (e.g. `NE:wdbgexts._POOL_HEADER_FIELD_NAME`), preserve that underscore in the UID of the generated document. However, all customer-facing content (title, description, headings, prose) must use the version without the leading underscore (e.g. `POOL_HEADER_FIELD_NAME`). For `api_name:` and `f1_keywords:`, include **both** versions — the underscored name and the public name — so the page is discoverable by either form.

   #### Content Sections (in order)

   **`## -description`**
   - Write a concise one-paragraph summary of what the API does
   - Focus on the public API behavior, not internal implementation
   - Use **bold** for API names referenced in text

   **`## -parameters`** (functions/callbacks/macros)
   - One `### -param {Name} [{direction}]` subsection per parameter
   - Map SAL annotations to direction labels:
     - `_In_` → `[in]`
     - `_Out_` → `[out]`
     - `_Inout_` → `[in, out]`
     - `_In_opt_` → `[in, optional]`
     - `_Out_opt_` → `[out, optional]`
     - `_Reserved_` → `[in]` with note "Reserved. Must be zero/NULL."
   - Describe each parameter concisely
   - **Macro parameters requiring address-of (`&`):** If the macro implementation takes the address of a parameter (e.g. `&(Flags)`), describe the parameter as "An addressable {type} storage location (such as a variable or structure field)" rather than "A {type} value." A macro that takes `&(param)` requires an lvalue, not an arbitrary expression.

   **`## -struct-fields`** (structures only)
   - One `### -field {FieldName}` subsection per field
   - **Union types use the structure classification.** If the C source declares a `union`, it is still documented with the `ns-` file prefix and `req.construct-type: structure`. All prose (description, field descriptions, remarks, and cross-references from other pages) must call it a "structure", never a "union." The heading should read `# {NAME} structure`, not `# {NAME} union`.

   **`## -enum-fields`** (enumerations only)
   - One `### -field {ValueName}:{NumericValue}` subsection per value (if numeric value is known)

   **`## -ioctlparameters`** (IOCTLs only)
   - Document input buffer, output buffer, and status block

   **`## -returns`** (functions with return values)
   - Document the return type and possible return values
   - For NTSTATUS returns, list common status codes

   **`## -remarks`**
   - Add usage notes, IRQL requirements, important caveats
   - Include code examples when helpful
   - If the entity is an extended version (ends in `Ex`, or a number like `2`, `3`, or `V2`), reference the base entity and document only the extensions
   - Don't generate repetitive information
   - Don't include internal implementation details

   **`## -see-also`**
   - List related APIs as relative Markdown links, one per line
   - Write entries in **alphabetical order**
   - Use relative paths: `[**OtherFunc**](nf-{header}-otherfunc.md)` for same-header APIs
   - Use `../` paths for cross-header: `[**CrossFunc**](../otherheader/nf-otherheader-crossfunc.md)`

   #### Formatting Rules
   - Use **bold** for API names in prose: `**IoCreateFile**`
   - Use *italic* for parameter name references in prose: `*DesiredAccess*`
   - Use site-relative links for learn.microsoft.com content: `[title](/windows-hardware/drivers/...)`
   - Code blocks use `cpp` language tag
   - Ensure exactly one blank line at the end of the file

8. Handle the header landing page (`index.md`). The landing page is automatically updated during the build to reflect the current API set, so it almost never needs manual editing once published.

   - Check if `index.md` already exists on `main` of `wdk-ddi` or `staging` of the published docs repo using the ADO REST API:
     ```powershell
     # Check wdk-ddi main
     $mainItems = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/items?scopePath=wdk-ddi-src/content/{header}/&recursionLevel=OneLevel&versionDescriptor.version=main&versionDescriptor.versionType=branch&api-version=7.0" -Headers $headers
     
     # Check windows-driver-docs-ddi staging
     $pubItems = Invoke-RestMethod -Uri "$adoBase/windows-driver-docs-ddi/items?scopePath=wdk-ddi-src/content/{header}/&recursionLevel=OneLevel&versionDescriptor.version=staging&versionDescriptor.versionType=branch&api-version=7.0" -Headers $headers
     ```
   - If `index.md` appears in either listing, **skip it** — no action needed.
   - If no published `index.md` exists, retrieve the stub from the stubs branch and save it as `index.md`:
     ```powershell
     $stubContent = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/items?path=wdk-ddi-src/content/{header}/na-{header}.md&versionDescriptor.version=stubs/main&versionDescriptor.versionType=branch&api-version=7.0" -Headers $headers
     $stubContent | Out-File -FilePath (Join-Path $outputDir "index.md") -Encoding utf8
     ```
   - **Content rules for a new `index.md`:** After retrieving the stub, ensure the `## -description` section follows this strict format:
     1. Write **no more than one sentence** describing the header.
     2. Immediately follow it with: `This header is used by {tech.root display name}. For more information, see: [{tech.root display name}](../{tech.root}/index.md).`
        Replace `{tech.root display name}` with the human-readable technology area name and `{tech.root}` with the `tech.root` value from the frontmatter (e.g. `_netvista` → `[Networking](/windows-hardware/drivers/ddi/_netvista/)`).
     3. **Do not add any sections after `-description`.** There must be no `-remarks` or `-see-also` sections in an `index.md` file.

9. **Close editor tabs.** After all files have been written to `{outputDir}`, close all open editor tabs so the workspace is not cluttered with generated files. Run the VS Code command `workbench.action.closeAllEditors` to close them.

10. **Pause for human review.** Display:
   - List of all generated/updated files in `{outputDir}`
   - For each file, show the first 20 lines (frontmatter + description) as a sample
   - Highlight any metadata fields marked as TODO that the user should fill in manually (e.g. `req.lib`, `req.dll`, `req.irql` if not determinable from source)
   - Prompt the user to review the markdown files at: `{outputDir}`

11. **Continue or stop.** Ask the user:

    > Docs generated and ready for review in `{outputDir}`. **Please review the files before proceeding** — once submitted, a PR will be created in the wdk-ddi repo. Press **y** to submit them as a PR, or type **n** to stop and review further.

    - If the user types **y**: automatically invoke the `ai-docs-submit` skill, passing the same header name. Behave as if the user typed `/ai-docs-submit {header}`.
    - If the user types **n**: stop and tell them they can run it later with: `/ai-docs-submit {header}`

    > **Tip:** To run all three steps (inventory → generate → submit) with no interaction, use the `ai-docs-autopilot` skill instead.
More from MicrosoftDocs/windows-driver-docs-ddi