| name | pwsh-review-bootstrap |
| description | How to bootstrap a PowerShell project with pwsh-code-review. Use when the user is setting up the reviewer on a new repo, when /pwsh-review-bootstrap runs, or when the existing profile needs refreshing. |
pwsh-review bootstrap skill
This skill is invoked by /pwsh-review-bootstrap. It contains the procedure for walking a PowerShell repo, generating the project profile, and producing a usable starting point.
What "good bootstrap" looks like
The user runs the bootstrap once. They get back a .pwsh-review/ directory with files that:
- Reflect the actual project (not generic templates)
- Are honest about uncertainty (mark
<!-- TODO --> where unsure)
- Make the user's first edits cheap (the right structure, just needs hand-curation)
- Do not break or trigger spurious findings on first review
Bad bootstrap looks like: generic boilerplate, made-up architecture descriptions, hallucinated function lists, glossary terms with invented definitions. Avoid this aggressively.
Walk procedure
Inventory pass
$psFiles = Get-ChildItem -Recurse -File -Include *.ps1, *.psm1, *.psd1
For each file, classify:
.psd1 with RootModule set: module manifest
.psd1 without RootModule: data file (like our config files)
.psm1: module
.ps1 in tests/ or matching *.Tests.ps1: Pester test
.ps1 in Public/: public function file
.ps1 in Private/: private function file
.ps1 at root or scripts/: entry script
Build a table:
File | Role | Lines | Functions defined | Functions called externally
AST pass
For each file, parse with [System.Management.Automation.Language.Parser]::ParseFile. Extract:
- All
FunctionDefinitionAst
- All
CommandAst (function/cmdlet calls)
- All
VariableExpressionAst for $script:, $global:, $env:
- All
AssignmentStatementAst writing to those scopes
- Module manifest contents via
Import-PowerShellDataFile
Identify cross-platform signals by scanning for:
- Hard-coded
\ in string literals representing paths
Get-CimInstance, Get-WmiObject, Get-ItemProperty 'HKLM:..', New-PSDrive -PSProvider Registry
$env:USERPROFILE, $env:APPDATA, $env:LOCALAPPDATA, $env:PROGRAMFILES
powershell.exe references
$IsWindows, $IsLinux, $IsMacOS references (good signal: project is platform-aware)
New-Object -ComObject, [System.__ComObject]
Test-NetConnection, Get-NetAdapter, *-Service, etc.
Identify dependencies:
- Module manifest
RequiredModules
Import-Module calls
using module statements
- Native command invocations (
& <name> where <name> is not a function)
Pattern detection
Look for the cleanest existing example of each canonical pattern:
pattern-pipeline-cmdlet.ps1: search for FunctionDefinitionAst where:
- Has
[CmdletBinding()]
- Has
[OutputType()]
- Has at least one parameter with
ValueFromPipeline = $true
- Has a
process block
- Has comment-based help with at least synopsis, description, parameter, example
Score each candidate by:
- 5 points for
[OutputType()] matching what is actually returned
- 3 points for
SupportsShouldProcess if state-changing
- 3 points for proper error handling pattern
- 2 points for length under 60 lines
- -10 points if it has obvious issues (Write-Host in a non-CLI module,
+= in a loop)
Pick the highest-scoring candidate. Copy verbatim with a header comment:
# Canonical example from <relative-path>
# Picked because: pipeline-shaped, OutputType honest, CBH complete
If no candidate scores above zero, copy the template version.
pattern-error-handling.ps1: search for the cleanest existing function that uses try/catch with Write-Error -ErrorAction Stop (not bare throw "string"), distinguishes terminating from non-terminating, and has a justifying comment.
pattern-module-init.psm1: search for the cleanest .psm1 that does dot-source loading of Public/ and Private/, calls Export-ModuleMember correctly, and handles errors during load.
Glossary detection
Walk all source files and extract identifiers (function names, parameter names, comment words) that:
- Appear 5+ times across the codebase
- Are not in standard pwsh vocabulary
- Are not in standard English (a small stopword list)
- Look domain-specific (e.g. uppercase project nouns:
DOTBOT, Worktree, Whisper, Outpost)
Take the top 20. For each, find the first definition or descriptive use in a README or .SYNOPSIS block. Draft a glossary entry:
### Term
<!-- TODO: confirm definition -->
<inferred definition from first descriptive use, or "Domain term used in <count> places, definition not auto-inferable">
Used in: file:line, file:line, file:line
The user fills in or corrects. Never invent definitions.
Public surface
From the module manifests' FunctionsToExport (if not '*'), build the list of public functions. If FunctionsToExport = '*', fall back to: every function defined in a Public/ directory, or every function whose file matches the function name.
For each public function, capture:
- Signature (parameters, types, attributes)
- Output type (declared and inferred)
- Comment-based help completeness
- Caller count within the repo
This goes into architecture.md and profile.lock.json.
File generation rules
architecture.md
Honest. Where uncertain, leave a TODO. Do not write paragraphs of generic prose about how "this project follows clean architecture". Stick to facts you can verify from the code:
- Number of modules
- Module dependency direction (use the AST call graph)
- Public function count
- Test count and Pester version
- Cross-platform signals detected (yes/no for each platform)
standards.md
Start from templates/standards.md. Then merge in any rules detected from existing files:
.editorconfig: line endings, indent style, charset
- Existing
PSScriptAnalyzerSettings.psd1: rule excludes and includes
STYLE.md or CODING_STANDARDS.md if present at repo root: copy relevant sections
CONTRIBUTING.md: pull in any "code style" section
If the project has its own conventions that contradict the template, preserve the project's.
glossary.md
20 entries maximum. All marked TODO. If you cannot infer the definition, say so explicitly:
### Widget
<!-- TODO: confirm definition -->
Domain term, appears in 47 places. Not auto-inferable from context.
Used in: src/server.ps1:42, src/Modules/WidgetCore/Public/New-Widget.ps1:1, ...
patterns/
Three files minimum (pipeline, error handling, module init). Add more if obvious patterns emerge from the codebase (e.g. a clear "configuration loading" pattern, a clear "logging" pattern).
PSScriptAnalyzerSettings.psd1
Copy templates/PSScriptAnalyzerSettings.psd1. Set Rules.PSUseCompatibleSyntax.TargetVersions based on the lowest PowerShellVersion in any manifest, or 7.4 if no manifest. Set Rules.PSUseCompatibleCmdlets.compatibility based on detected platforms.
config.psd1
Set Platforms based on detection. Default ConfidenceThreshold to 80, NitCap to 3.
profile.lock.json
Schema:
{
"version": "1",
"generated": "<ISO timestamp>",
"public_surface": {
"<function-name>": {
"signature_hash": "<sha256>",
"file": "<relative-path>"
}
},
"manifests": {
"<relative-path>": {
"hash": "<sha256>",
"version": "<from manifest>"
}
},
"top_dirs": {
"<dir>": "<sha256 of sorted file list>"
},
"ruleset_version": "1"
}
The freshness check on every review compares this against the current state.
Refresh mode behaviour
--refresh regenerates intelligently:
- Re-run discovery and pattern detection.
- For each file in
.pwsh-review/, compute a structural diff between the new version and the existing.
- For
architecture.md, standards.md: show the user a section-by-section diff and ask which to apply. Default: apply factual updates (counts, lists), preserve prose edits.
- For
glossary.md, patterns/*.ps1: never overwrite. These are hand-curated. Just print "Skipped, hand-curated."
- For
PSScriptAnalyzerSettings.psd1: if the user has not modified it (matches the original generated version), update freely. If modified, show diff and ask.
- For
profile.lock.json and cache/: always regenerate.
Force mode
--force overwrites everything except cache. One confirmation prompt: "This will overwrite all hand edits in .pwsh-review/. Type 'overwrite' to confirm:". Anything other than exact match aborts.