| name | ai-docs-autopilot |
| description | 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. |
| argument-hint | Specify a header name (e.g. soundwireclass) and the path to the CSV file. |
Autopilot: Inventory → Generate → Submit
Run the full DDI documentation pipeline end-to-end with no user interaction. The agent inventories APIs from a CSV, generates documentation pages, and submits them as a PR — stopping only on error.
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 | wdk-ddi-build (branch: live) |
| Published Docs Repo (staging) | windows-driver-docs-ddi (branch: staging) |
| Content Path | wdk-ddi-src/content/{header}/ |
| Stubs Branch | stubs/main (default; user may specify alternate like stubs/release-amethyst) |
| Header Name | Provided by the user (e.g. soundwireclass) |
| CSV Input | 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 |
| User Alias | Auto-detected from CSV Owner column, $env:USERNAME, or az account show |
| Source Branch | Auto-generated as {user-alias}/{header}-update |
Prerequisites
- The user provides a header name and a path to a CSV file. That is the only user requirement.
- 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.
- 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).
Autopilot Mode
This skill runs all three phases (inventory, generate, submit) in sequence with no user interaction between them. The process only stops if an error is encountered. Progress is reported to the console after each phase.
Progress Tracking
Track elapsed time and documents written across all three phases. Because each phase may run in a separate PowerShell process (variables don't survive between invocations), persist the start timestamp in a file so it can be read by later phases.
Start the timer (Phase 1 inventory script)
At the very beginning of the inventory.ps1 script, record the pipeline start time to a file in the working directory:
$pipelineStartTime = Get-Date
$pipelineStartTime.ToString('o') | Out-File -FilePath (Join-Path $workingDir '.pipeline-start') -Encoding utf8 -Force
$docsWrittenCount = 0
Log per-document progress (Phase 2)
Phase 2 is orchestrated by the agent (not a single script). After writing each documentation file, the agent must:
- Read the start time from
{workingDir}\.pipeline-start.
- Increment a running document counter.
- Report progress to the user with elapsed time.
Run this in the terminal after each file is written:
$start = [DateTime]::Parse((Get-Content '{workingDir}\.pipeline-start' -Raw).Trim())
$elapsed = (Get-Date) - $start
Write-Host "[$($elapsed.ToString('hh\:mm\:ss'))] Wrote doc {N}: {filename.md}"
Alternatively, the agent can compute elapsed time from the stored timestamp itself and include it in its console message — the key requirement is that each doc written produces a visible [HH:MM:SS] Wrote doc N: filename progress line.
Report totals (Phase 3 submit script)
At the end of the submit.ps1 script, read the start time back and compute the total elapsed time:
$startFile = Join-Path $workingDir '.pipeline-start'
if (Test-Path $startFile) {
$pipelineStartTime = [DateTime]::Parse((Get-Content $startFile -Raw).Trim())
$totalElapsed = (Get-Date) - $pipelineStartTime
} else {
$totalElapsed = [TimeSpan]::Zero
}
Include the elapsed time and document count in the final summary banner (see Phase 3, step 15).
Phase 1: Inventory
Read a pre-provided CSV of target API filenames, cross-reference each entry against existing docs, stubs, and published content via the ADO REST API, classify their status, and finalize the CSV.
File Naming Conventions
Map each API entity to a filename using these prefixes:
| Prefix | Type | Example |
|---|
nf | Function | nf-soundwireclass-somefunc.md |
ns | Structure | ns-soundwireclass-some_struct.md |
ne | Enumeration | ne-soundwireclass-some_enum.md |
nc | Callback | nc-soundwireclass-evt_some_callback.md |
ni | IOCTL | ni-soundwireclass-ioctl_some_code.md |
nn | Interface | nn-soundwireclass-isome_interface.md |
nl | Class | nl-soundwireclass-some_class.md |
The filename pattern is: {prefix}-{header}-{api_name_lowercase}.md
Where {header} is the header name without the .h extension.
Header Landing Page
On the stubs branch, the header landing page is always named na-{header}.md. When it is copied to wdk-ddi-src/content/{header}/ on the main branch, it is renamed to index.md. In the published docs repo, it also appears as index.md. When cross-referencing, check for na-{header}.md on the stubs branch and index.md on main and in the published docs repo.
Legacy Filename Exceptions
Some existing files in the published docs repo or on main may have non-standard filenames (e.g. an extra underscore like ns-header-_struct_name.md instead of ns-header-struct_name.md). These are historical naming errors. Do not rename existing files — doing so would break published links. When cross-referencing, also check for these variant filenames. For new APIs, always use the approved naming convention.
Execution Strategy
Write all PowerShell logic into a single self-contained .ps1 script file, then execute it in one terminal call. Do NOT run ADO REST calls or variable assignments as separate interactive terminal commands — PowerShell variables are lost between terminal invocations and long-running commands may time out and get moved to the background, breaking the workflow.
The pattern is:
- Create a script file at
{workingDir}\inventory.ps1 containing all the logic from the Inventory Procedure below. Use the create_file tool to write the script — this auto-saves the file to disk immediately, avoiding any unsaved-buffer issues.
- Execute it in a single terminal call:
powershell -ExecutionPolicy Bypass -File "{workingDir}\inventory.ps1"
- Parse the script's console output to present results to the user.
The script should accept no parameters — hardcode the {header}, {csvPath}, {workingDir}, and {userAlias} values directly into the generated script.
Inventory Procedure
Write a single inventory.ps1 script that performs all of the following steps, then execute it.
-
Strip the .h extension from the user-provided header name to get {header} (e.g. soundwireclass.h → soundwireclass).
-
Resolve the user alias for branch naming ({user-alias}). The alias identifies who is submitting the PR, not who owns the APIs. Try these sources in order and use the first non-empty value:
a. The Windows username: $env:USERNAME.
b. The Azure CLI identity: az account show --query user.name -o tsv, extracting the alias portion before @.
The resolved alias is used later for the branch name {user-alias}/{header}-update. Hardcode it into the generated scripts.
-
Resolve paths. The user provides a CSV path. Derive the working directory from it. The script should validate the CSV exists and read it:
$ErrorActionPreference = "Stop"
$header = "{header}"
$csvPath = "{user-provided CSV path}"
$workingDir = Split-Path $csvPath -Parent
# Start the pipeline timer and persist it for later phases
$pipelineStartTime = Get-Date
$pipelineStartTime.ToString('o') | Out-File -FilePath (Join-Path $workingDir '.pipeline-start') -Encoding utf8 -Force
$docsWrittenCount = 0
if (-not (Test-Path $csvPath)) {
Write-Error "CSV not found at $csvPath."
exit 1
}
Read the CSV:
$csvData = Import-Csv $csvPath
The CSV has a header row. Look for a column containing file paths (commonly FilePath or filename) with values like wdk-ddi-src/content/{header}/{filename}.md. Filter to rows with valid .md paths.
-
Obtain ADO auth token. Try Azure CLI first, then fail with a clear message (PAT prompting cannot work inside a non-interactive script):
$token = (az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv 2>$null)
if (-not $token) {
Write-Error "Failed to get ADO token. Run 'az login' first, or set `$env:ADO_PAT before running."
exit 1
}
$h = @{ Authorization = "Bearer $token" }
$adoBase = "https://dev.azure.com/cpubwin/drivers/_apis/git/repositories"
-
For each entry in the CSV, extract the target filename from the path (e.g. wdk-ddi-src/content/{header}/nf-soundwireclass-somefunc.md → nf-soundwireclass-somefunc.md).
-
(one API call per branch, not per file). This detects legacy filename variants (see ):
After the script finishes, the agent should verify the CSV was written successfully. If the inventory found zero entries for documentation (all "stub not found"), stop with an error. Otherwise, proceed immediately to Phase 2 with no user prompt.
Phase 2: 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.
Generate Procedure
-
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, stop with an error.
-
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:
a. Get the stub or existing 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
Phase 3: Submit as PR
Submit generated API reference documentation as a pull request to the wdk-ddi Azure DevOps repo using the ADO REST API.
No local repo clone required. Branch creation, file push, and PR creation are all done via the ADO REST API.
Submit Procedure
-
Strip the .h extension from the user-provided header name to get {header} (e.g. soundwireclass.h → soundwireclass).
-
Resolve paths. The user provides the CSV path. Derive the working and output directories:
$csvPath = "{user-provided CSV path}"
if (-not (Test-Path $csvPath)) {
Write-Error "CSV not found at $csvPath."
return
}
$workingDir = Split-Path $csvPath -Parent
$outputDir = Join-Path $workingDir "output"
$entries = Import-Csv $csvPath
If the CSV does not exist, stop with an error. Use the CSV entries to identify the API entities for the commit message and PR description.
-
Verify the output directory exists and contains files:
$outputFiles = Get-ChildItem -Path $outputDir -Filter "*.md" -ErrorAction SilentlyContinue
if (-not $outputFiles -or $outputFiles.Count -eq 0) {
Write-Error "No generated docs found in $outputDir. Generation phase may have failed."
return
}
-
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"; "Content-Type" = "application/json" }
} catch {
$pat = Read-Host "Enter ADO PAT (scope: Code Read+Write, PR Contribute)"
$base64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$pat"))
$headers = @{ Authorization = "Basic $base64"; "Content-Type" = "application/json" }
}
$adoBase = "https://dev.azure.com/cpubwin/drivers/_apis/git/repositories"
-
Get the latest commit SHA on main. This is required as the oldObjectId for the push:
$refs = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/refs?filter=heads/main&api-version=7.0" -Headers $headers
$mainSha = $refs.value[0].objectId
-
Determine change type for each file. Check which files already exist on main to set the correct changeType (add vs edit):
$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
$existingNames = $mainFiles.value | ForEach-Object { Split-Path $_.path -Leaf }