with one click
ai-docs-generate
// 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.
// 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.
End-to-end autopilot: inventory, generate, and submit WDK DDI API reference docs from a CSV file with no user interaction. Use when: running the full doc pipeline unattended, auto-generating and submitting DDI docs.
Submit generated WDK DDI API reference documentation as a PR to the wdk-ddi repo. Use when: submitting docs, creating a PR for DDI docs, pushing documentation changes.
Inventory and classify APIs listed in a pre-provided CSV for a WDK header file. Use when: inventorying a header, classifying APIs, validating a CSV for doc generation, checking what needs to be documented.
| 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 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.
| 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 |
ai-docs-inventory skill). That is the only user requirement.substrate-mcp MCP server must be accessible for source code retrievalmicrosoft.docs.mcp MCP server should be accessible for supplemental info (non-fatal if unavailable)az) should be available for auth token acquisition. If not, the agent will prompt for an ADO Personal Access Token (PAT) once per session.Strip the .h extension from the user-provided header name to get {header} (e.g. soundwireclass.h → soundwireclass).
Resolve paths. The user provides a CSV path. Derive the working directory from it:
$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.
Obtain ADO auth token. Try Azure CLI first, then fall back to prompting for a PAT:
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"
Read the style guide remotely from the wdk-ddi repo to ensure all formatting rules are followed:
$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
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.
Create the output directory if it doesn't exist; if it does, clear it:
if (Test-Path $outputDir) {
Remove-Item "$outputDir\*" -Force -ErrorAction SilentlyContinue
} else {
New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
}
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:
Use the ADO REST API to check if the file already exists on main:
# 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.
$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:
$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
Use the substrate-mcp MCP server to find the API's source declaration:
nf-soundwireclass-somefunc.md → SomeFunc)search_code(query="def:{ApiName}", ext="h")
def: doesn't find it, try keyword search:
search_code(query="{ApiName}", ext="h", path="**/{header}.h")
get_file_content with a line range to retrieve the full declaration including:
_In_, _Out_, _Inout_, _In_opt_, _Out_opt_, _Reserved_)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")
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:
# 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:
$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.
Write the completed file to {outputDir}\{filename.md}. Follow these rules:
ai-usage: ai-assisted if not already presenttechroot to the same value as existing files in the same header folder. If no existing files, prompt user for value.ms.date to today's date in MM/DD/YYYY format — both for new files and when updating existing files with changesreq.header, f1_keywords, api_name, topic_type are correctreq.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)).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.## -description
## -parameters (functions/callbacks/macros)
### -param {Name} [{direction}] subsection per parameter_In_ → [in]_Out_ → [out]_Inout_ → [in, out]_In_opt_ → [in, optional]_Out_opt_ → [out, optional]_Reserved_ → [in] with note "Reserved. Must be zero/NULL."&): 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)
### -field {FieldName} subsection per fieldunion, 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)
### -field {ValueName}:{NumericValue} subsection per value (if numeric value is known)## -ioctlparameters (IOCTLs only)
## -returns (functions with return values)
## -remarks
Ex, or a number like 2, 3, or V2), reference the base entity and document only the extensions## -see-also
[**OtherFunc**](nf-{header}-otherfunc.md) for same-header APIs../ paths for cross-header: [**CrossFunc**](../otherheader/nf-otherheader-crossfunc.md)**IoCreateFile***DesiredAccess*[title](/windows-hardware/drivers/...)cpp language tagHandle 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.
index.md already exists on main of wdk-ddi or staging of the published docs repo using the ADO REST API:
# 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
index.md appears in either listing, skip it — no action needed.index.md exists, retrieve the stub from the stubs branch and save it as index.md:
$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
index.md: After retrieving the stub, ensure the ## -description section follows this strict format:
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/)).-description. There must be no -remarks or -see-also sections in an index.md file.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.
Pause for human review. Display:
{outputDir}req.lib, req.dll, req.irql if not determinable from source){outputDir}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.
ai-docs-submit skill, passing the same header name. Behave as if the user typed /ai-docs-submit {header}./ai-docs-submit {header}Tip: To run all three steps (inventory → generate → submit) with no interaction, use the
ai-docs-autopilotskill instead.