- 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:
```powershell
$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:
1. Read the start time from `{workingDir}\.pipeline-start`.
2. Increment a running document counter.
3. Report progress to the user with elapsed time.
Run this in the terminal after each file is written:
```powershell
$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:
```powershell
$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:
1. 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.
2. Execute it in a single terminal call: `powershell -ExecutionPolicy Bypass -File "{workingDir}\inventory.ps1"`
3. 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.
1. Strip the `.h` extension from the user-provided header name to get `{header}` (e.g. `soundwireclass.h` → `soundwireclass`).
2. **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.
3. **Resolve paths.** The user provides a CSV path. Derive the working directory from it. The script should validate the CSV exists and read it:
```powershell
$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:
```powershell
$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.
3. **Obtain ADO auth token.** Try Azure CLI first, then fail with a clear message (PAT prompting cannot work inside a non-interactive script):
```powershell
$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"
```
4. 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`).
5. **List all files in the directory on each branch** (one API call per branch, not per file). This detects legacy filename variants (see [Legacy Filename Exceptions](#legacy-filename-exceptions)):
```powershell
# List files on main branch of wdk-ddi
try {
$r1 = 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 $h
$mainFiles = @($r1.value | Where-Object { $_.path -like "*.md" } | ForEach-Object { Split-Path $_.path -Leaf })
} catch { $mainFiles = @() }
# List files on stubs branch of wdk-ddi
try {
$r2 = Invoke-RestMethod -Uri "$adoBase/wdk-ddi/items?scopePath=wdk-ddi-src/content/{header}/&recursionLevel=OneLevel&versionDescriptor.version=stubs/main&versionDescriptor.versionType=branch&api-version=7.0" -Headers $h
$stubFiles = @($r2.value | Where-Object { $_.path -like "*.md" } | ForEach-Object { Split-Path $_.path -Leaf })
} catch { $stubFiles = @() }
# List files on live branch of wdk-ddi-build (published docs)
try {
$r3 = Invoke-RestMethod -Uri "$adoBase/wdk-ddi-build/items?scopePath=wdk-ddi-src/content/{header}/&recursionLevel=OneLevel&versionDescriptor.version=live&versionDescriptor.versionType=branch&api-version=7.0" -Headers $h
$pubFiles = @($r3.value | Where-Object { $_.path -like "*.md" } | ForEach-Object { Split-Path $_.path -Leaf })
} catch { $pubFiles = @() }
Write-Host "Main: $($mainFiles.Count) files, Stubs: $($stubFiles.Count) files, Published: $($pubFiles.Count) files"
```
Compare each expected filename against the directory listings. If a file exists with a minor variant (e.g. `ns-header-_struct_name.md` instead of `ns-header-struct_name.md`), treat it as a match — the doc exists under the legacy name. **Use the legacy filename in the CSV** (do not rename it).
a. Check if the file exists on `main` of `wdk-ddi` (look for the filename in `$mainFiles`).
b. Check if a stub exists on the stubs branch (look for the filename in `$stubFiles`).
c. Check if the file exists on `live` of the published docs repo `wdk-ddi-build` (look for the filename in `$pubFiles`).
6. Classify each API into one of three categories:
- **new**: Stub exists on `stubs/main` but no completed doc on `main`
- **update**: File already exists on `main` — needs to be compared with the latest stub to identify changes
- **stub not found**: No stub and no existing doc
7. **Output grouped results** to the console for the agent to parse and present:
```
=== New (ready to document) ===
nf-soundwireclass-somefunc.md (function)
ns-soundwireclass-some_struct.md (structure)
=== Update (existing doc, needs diff) ===
nf-soundwireclass-otherfunc.md (function) [+2 params, -1 param, ~1 param]
=== Stub not found ===
ne-soundwireclass-some_enum.md (enumeration)
```
8. **Diff existing docs against stubs.** For each "update" entry, fetch the existing doc from `main` and the latest stub from `stubs/main` via the ADO REST API. Compare the parameter/field/value sections to identify:
- **Added** parameters, fields, or enum values (present in stub but not in existing doc)
- **Removed** parameters, fields, or enum values (present in existing doc but not in stub)
- **Modified** parameters, fields, or enum values (present in both but with different types or descriptions)
Output a per-file change summary (e.g. `[+2 params, -1 param, ~1 param]`) in the console results.
9. **Write the updated CSV** at the original CSV path. The format must be compatible with Phase 2:
```csv
filename,status
wdk-ddi-src/content/{header}/{filename1}.md,new
wdk-ddi-src/content/{header}/{filename2}.md,update
```
The script writes both "new" and "update" entries. "Stub not found" entries are excluded.
10. **Output a summary** to the console:
- Number of APIs in the CSV by type
- Number selected for documentation
- Working directory: `{workingDir}`
- CSV file location: `{csvPath}`
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
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, stop with an error.
3. **Obtain ADO auth token.** Try Azure CLI first, then fall back to prompting for a PAT:
```powershell
try {
GitHubで見る