diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..5fc163a9 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,52 @@ +# Process-PSModule Development Guidelines + +Auto-generated from all feature plans. Last updated: 2025-10-01 + +## Active Technologies + +- PowerShell 7.4+ (GitHub Actions composite actions) + PSModule/GitHub-Script@v1, PSModule/Install-PSModuleHelpers@v1 (001-building-on-this) + +## Project Structure + +```plaintext +src/ +tests/ +``` + +## Commands + +Add commands for PowerShell 7.4+ (GitHub Actions composite actions) + +## Code Style + +PowerShell 7.4+ (GitHub Actions composite actions): Follow standard conventions + +## Recent Changes + +- 001-building-on-this: Added PowerShell 7.4+ (GitHub Actions composite actions) + PSModule/GitHub-Script@v1, PSModule/Install-PSModuleHelpers@v1 + + + +## Terminal Commands + +When executing terminal commands (using `run_in_terminal` or similar tools): +- **ALWAYS** prefix shell commands with `pwsh` unless it's a GitHub MCP call +- This applies to all PowerShell scripts, git commands, and other shell operations +- Exception: GitHub MCP Server calls should use their native format without `pwsh` prefix + +Examples: +```bash +# Correct - PowerShell script +pwsh -Command "& './.specify/scripts/powershell/setup-plan.ps1' -Json" + +# Correct - Git command +pwsh -Command "git status" + +# Correct - Any shell command +pwsh -Command "ls -Recurse" + +# Exception - GitHub MCP calls (no pwsh prefix) +gh issue create --title "Feature" --body "Description" +``` + + diff --git a/.github/instructions/md.instructions.md b/.github/instructions/md.instructions.md new file mode 100644 index 00000000..3b41f789 --- /dev/null +++ b/.github/instructions/md.instructions.md @@ -0,0 +1,304 @@ +--- +applyTo: '**/*.md' +description: Markdown style guidelines for consistency across documentation. +--- + +# Markdown Style Guidelines + +This document defines the Markdown style guidelines for all Markdown files in this repository. These rules follow common Markdown linter best practices and ensure consistency across documentation. + +## Headings + +- Use ATX-style headings (`#`) instead of Setext-style (underlines) +- Include a space after the hash marks: `# Heading` not `#Heading` +- Use only one top-level heading (`#`) per document +- Do not skip heading levels (e.g., don't go from `#` to `###`) +- Surround headings with blank lines (one before, one after, excluding the first heading in the document unless preceded by frontmatter) +- Do not use trailing punctuation in headings (no periods, colons, etc.) +- Use sentence case for headings unless referring to proper nouns or code identifiers + +**Good:** + +```markdown +# Main heading + +## Subsection + +### Details +``` + +**Bad:** + +```markdown +#No space after hash +### Skipped level 2 +## Heading with period. +``` + +## Lists + +- Use consistent list markers throughout the document (`-` for unordered, `1.` for ordered) +- Do not add blank lines between list items (unless item contains multiple paragraphs) +- Indent nested lists by 2 spaces for unordered, 3 spaces for ordered +- Use `1.` for all ordered list items (auto-numbering) or number them sequentially +- Surround lists with blank lines (one before, one after) +- Use `-` for unordered lists (not `*` or `+`) + +**Good:** + +```markdown +Here is a list: + +- First item +- Second item +- Third item + +Another list: + +1. First step +1. Second step +1. Third step +``` + +**Bad:** + +```markdown +No blank line before list: +- Item one + +- Blank lines between items +- Not needed + +* Wrong marker ++ Mixed markers +``` + +## Code Blocks + +- Always use fenced code blocks (triple backticks) with language identifiers +- Always include a blank line before and after code blocks +- Specify the language for syntax highlighting (`bash`, `python`, `markdown`, `json`, etc.) +- Use `plaintext` or `text` if no specific language applies +- Indent code blocks at the same level as surrounding content + +**Good:** + +```markdown +Here is an example: + +\`\`\`bash +echo "Hello, world!" +\`\`\` + +The command prints a message. +``` + +**Bad:** + +```markdown +No language identifier: +\`\`\` +code here +\`\`\` +No blank lines before/after code blocks. +``` + +## Links + +- Use reference-style links for repeated URLs +- Use relative paths for internal links (relative to the current file) +- Always provide link text in square brackets: `[text](url)` +- Do not use bare URLs (wrap them: ``) +- For internal repository links, use relative paths starting with `./` or `../` +- Use `.md` extension for links to Markdown files + +**Good:** + +```markdown +See the [installation guide](../docs/installation.md) for details. + +Check out [GitHub][gh] and [GitLab][gl] for hosting. + +[gh]: https://github.com +[gl]: https://gitlab.com +``` + +**Bad:** + +```markdown +Absolute path: [guide](/docs/installation.md) +Missing extension: [guide](../docs/installation) +Bare URL: Visit https://example.com +``` + +## Tables + +- Use tables when content follows a consistent structure (instead of lists) +- Align columns using hyphens for readability +- Include header row separator with at least 3 hyphens per column +- Surround tables with blank lines (one before, one after) +- Use pipes (`|`) to separate columns +- Align content within columns for readability (optional but recommended) + +**Good:** + +```markdown +Here is a comparison: + +| Feature | Supported | Notes | +|---------|-----------|-------| +| Feature A | Yes | Fully supported | +| Feature B | No | Planned for v2 | +| Feature C | Partial | Beta feature | + +The table shows current status. +``` + +**Bad:** + +```markdown +Using list when table is better: +- Feature A: Yes - Fully supported +- Feature B: No - Planned for v2 +- Feature C: Partial - Beta feature +``` + +## Requirement Number Formatting + +When writing or referencing requirement numbers (NFR and FR) in documentation: +- **Always use bold formatting** for requirement numbers +- **Replace hyphens with non-breaking hyphens** (`‑`) between letters and numbers +- This prevents line breaks within requirement numbers and ensures consistent formatting +- This applies to all specification documents, plans, and tables + +Examples: +```markdown +# Correct formatting +**NFR‑001**: The system must respond within 200ms +**FR‑042**: User authentication shall support OAuth 2.0 + +# In tables +| ID | Description | +|----|-------------| +| **NFR‑001** | Performance requirement | +| **FR‑042** | Authentication feature | + +# Incorrect formatting (do not use) +NFR-001: Without bold or non-breaking hyphen +**NFR-001**: Bold but with regular hyphen (can break across lines) +NFR‑001: Non-breaking hyphen but not bold +``` + +## Emphasis + +- Use `*` or `_` for emphasis (italic), `**` or `__` for strong emphasis (bold) +- Be consistent within a document (prefer `*` and `**`) +- Do not use emphasis for headings +- Use backticks for code/technical terms, not emphasis + +**Good:** + +```markdown +This is *emphasized* text. +This is **strong** text. +Use the `--verbose` flag for details. +``` + +**Bad:** + +```markdown +This is _emphasized_ text with **strong** mixed styles. +Use the *--verbose* flag (should be backticks). +``` + +## Line Length + +- Wrap prose at 80-120 characters per line +- Do not wrap code blocks, tables, or URLs +- Break after sentences or at natural phrase boundaries +- Empty lines do not count toward line length + +## Whitespace + +- Use a single blank line to separate blocks of content +- Do not use multiple consecutive blank lines +- End files with a single newline character +- Do not use trailing whitespace at the end of lines +- Use spaces (not tabs) for indentation + +## Other Rules + +### Horizontal Rules + +- Use three hyphens (`---`) for horizontal rules +- Surround horizontal rules with blank lines + +**Good:** + +```markdown +Section one content. + +--- + +Section two content. +``` + +### Blockquotes + +- Use `>` for blockquotes with a space after +- Surround blockquotes with blank lines +- Use multiple `>` for nested quotes + +**Good:** + +```markdown +As the docs state: + +> This is an important note. +> It spans multiple lines. + +Back to regular text. +``` + +### Images + +- Use alt text for all images: `![alt text](path/to/image.png)` +- Use relative paths for repository images +- Prefer reference-style for repeated images + +**Good:** + +```markdown +![Architecture diagram](../media/architecture.png) + +See the [logo][logo-img] above. + +[logo-img]: ./images/logo.png +``` + +### HTML + +- Avoid HTML in Markdown when possible +- Use HTML only for features not supported by Markdown +- Close all HTML tags properly + +### Filenames + +- Use lowercase for Markdown filenames +- Use hyphens (`-`) not underscores (`_`) to separate words +- Use `.md` extension (not `.markdown`) + +**Examples:** + +- `installation-guide.md` ✅ +- `Installation_Guide.markdown` ❌ + +## Linting + +To validate Markdown files against these guidelines, use a Markdown linter such as: + +- [markdownlint](https://github.com/DavidAnson/markdownlint) +- [remark-lint](https://github.com/remarkjs/remark-lint) +- [superlinter](https://github.com/super-linter/super-linter) + +Configure the linter to enforce these rules in your CI/CD pipeline. diff --git a/.github/instructions/pwsh.instructions.md b/.github/instructions/pwsh.instructions.md new file mode 100644 index 00000000..fc1f4639 --- /dev/null +++ b/.github/instructions/pwsh.instructions.md @@ -0,0 +1,837 @@ +--- +applyTo: '**/*.{ps1,psm1}' +description: PowerShell style guidelines for consistency across scripts and modules. +--- + +# PowerShell Style Guidelines + +This document defines the PowerShell style guidelines for all PowerShell files in this repository. These rules follow PowerShell best practices, the One True Brace Style (OTBS), and community standards. + +## Brace Style (OTBS - One True Brace Style) + +- Opening braces on the same line as the statement (OTBS) +- Closing braces on their own line, aligned with the statement +- Use braces even for single-line statements in control structures +- No empty lines immediately after opening braces or before closing braces + +**Good:** + +```powershell +function Get-Example { + param($Name) + + if ($Name) { + Write-Output "Hello, $Name" + } else { + Write-Output "Hello, World" + } +} + +foreach ($item in $items) { + Get-Item $item +} +``` + +**Bad:** + +```powershell +function Get-Example +{ + # Opening brace should be on same line +} + +if ($condition) + Write-Output "Missing braces" + +if ($condition) { Write-Output "All on one line" } +``` + +## Naming Conventions + +### Functions and Cmdlets + +- Use approved PowerShell verbs (Get, Set, New, Remove, etc.) +- Follow Verb-Noun naming pattern with PascalCase +- Use singular nouns +- Be specific and descriptive + +**Good:** + +```powershell +function Get-UserProfile { } +function Set-ConfigValue { } +function New-DatabaseConnection { } +function Remove-TempFile { } +``` + +**Bad:** + +```powershell +function GetUser { } # Missing hyphen +function get-user { } # Wrong case +function Do-Something { } # Non-standard verb +function Get-Users { } # Should be singular unless always plural +``` + +### Parameters and Variables + +- Use PascalCase for parameters and public variables +- Use camelCase for private/local variables +- Use descriptive names, avoid abbreviations unless well-known +- Prefix boolean variables with verbs like `is`, `has`, `should` +- Append 'At' 'In' 'On' for timestamp/location variables +- Use `$_` for pipeline variables +- Avoid using reserved words as names +- Avoid using automatic variables for custom variables +- Parameter and variable names can be alphanumeric and include underscores. +- The colon character `:` and `.` are significant in PowerShell syntax, if they are a part of text, they must be escaped (`). + +**Good:** + +```powershell +$userName = "John" +$isValid = $true +$hasPermission = $false +$totalCount = 0 + +param( + [string]$ConfigPath, + [switch]$Force +) +``` + +**Bad:** + +```powershell +$usr = "John" # Too abbreviated +$valid = $true # Boolean should be $isValid +$TOTAL_COUNT = 0 # Wrong case style +``` + +### Constants + +- Use PascalCase with descriptive names +- Mark as `[System.Management.Automation.Language.ReadOnlyAttribute]` or use `Set-Variable -Option ReadOnly` + +**Good:** + +```powershell +$MaxRetries = 3 +$DefaultTimeout = 30 +Set-Variable -Name ApiEndpoint -Value "https://api.example.com" -Option ReadOnly +``` + +## Parameters + +- Always use `[OutputType()]`, `[CmdletBinding()]` and `param()` block at the top of functions +- Use parameter attributes for validation +- Provide meaningful parameter names with PascalCase +- Use type constraints for parameters. +- Have a space between the type and the parameter name +- Group mandatory parameters first +- Use `[switch]` for boolean flags that default to `$false`. +- Add help text with parameter descriptions (inside the param block) + + +**Good:** + +```powershell +function Get-UserData { + <# + .SYNOPSIS + Retrieves user data from the database. + + .DESCRIPTION + Retrieves user data from the database. + + .EXAMPLE + Get-UserData -UserId "12345" -IncludeDeleted + #> + [CmdletBinding()] + param( + # The unique identifier of the user. + [Parameter(Mandatory, Position = 0)] + [ValidateNotNullOrEmpty()] + [string] $UserId, + + # Include deleted users in the results. + [Parameter()] + [switch] $IncludeDeleted + ) + + # Function body +} +``` + +**Bad:** + +```powershell +function Get-UserData($id, $del) { + # No param block, no types, unclear names +} +``` + +## Indentation and Whitespace + +- Use 4 spaces for indentation (not tabs) +- No trailing whitespace at end of lines +- End files with a single newline character +- Use blank lines to separate logical blocks of code +- No blank lines immediately after opening braces or before closing braces +- One space after commas in arrays and parameters +- One space around operators (`=`, `+`, `-`, `-eq`, `-ne`, etc.) + +**Good:** + +```powershell +function Process-Data { + <# + ... Docs + #> + [CmdletBinding()] + param( + [Parameter(Mandatory, Position = 0)] + [ValidateNotNullOrEmpty()] + [array] $Items + ) + + $results = @() + + foreach ($item in $Items) { + $processed = Format-Item $item + $results += $processed + } + + return $results +} +``` + +**Bad:** + +```powershell +function Process-Data{ + param($Items) + + $results=@() + foreach($item in $Items){ + $processed=Format-Item $item + $results+=$processed + } + + return $results + +} +``` + +## Comments + +- Use `#` for single-line comments +- Use `<# ... #>` for multi-line comments and help documentation +- Place comments on their own line above the code they describe +- Use comment-based help for functions (`.SYNOPSIS`, `.DESCRIPTION`, `.EXAMPLE`) +- Put comment-based help first, inside the function body, before any code. This makes it cleaner to move, and collapse code. +- Do not use '.PARAMETER' for parameters in comment-based help, use inline comments instead inside the param block above + each of the parameters. +- Each section of comment-based help should have a blank line between them. +- The comment-based help must be indented to align with the function definition. +- Keep comments up to date with code changes. +- The code should say what it is doing, comments should explain why. + +**Good:** + +```powershell +function New-UserAccount { +<# + .SYNOPSIS + Creates a new user account. + + .DESCRIPTION + Creates a new user account with the specified username and email. + Validates the email format before creation. + + .EXAMPLE + New-UserAccount -UserName 'jdoe' -Email 'jdoe@example.com' + + Creates a new user account for jdoe with email jdoe@example.com. + + .LINK + https://example.com/docs/New-UserAccount +#> + [CmdletBinding()] + param( + # The username for the new account. + [Parameter(Mandatory)] + [string] $UserName, + + # The email address for the new account. + [Parameter(Mandatory)] + [string] $Email + ) + + # Validate email format before processing + if ($Email -notmatch '^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$') { + throw "Invalid email format" + } + + # Create the user account + New-Object PSObject -Property @{ + UserName = $UserName + Email = $Email + } +} +``` + +**Bad:** + +```powershell +function New-UserAccount { + param($UserName, $Email) + # Check email + if ($Email -notmatch '^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$') { throw "Invalid email format" } + New-Object PSObject -Property @{UserName = $UserName; Email = $Email} # Create user +} +``` + +## String Handling + +- Use single quotes for strings that don't need variable expansion +- Use double quotes only for strings with variables or escape sequences +- Use here-strings (`@"..."@` or `@'...'@`) for multi-line strings +- Use `-f` operator or string interpolation for formatting +- Avoid string concatenation with `+` in loops (use arrays or StringBuilder) + +**Good:** + +```powershell +$name = 'John' +$greeting = "Hello, $name" +$path = 'C:\Temp\file.txt' +$message = "The value is: {0}" -f $value + +$multiLine = @" +This is a +multi-line string +with variable: $name +"@ +``` + +**Bad:** + +```powershell +$name = "John" # Should use single quotes (no variables) +$greeting = 'Hello, $name' # Variable won't expand +$message = "The value is: " + $value # Use formatting instead +``` + +## Control Structures + +### If/Else Statements + +- Always use braces, even for single statements +- Opening brace on same line (OTBS) +- Use `elseif` (one word) not `else if` +- One space before opening brace +- Comparison operators on separate lines for readability in complex conditions + +**Good:** + +```powershell +if ($condition) { + Do-Something +} elseif ($otherCondition) { + Do-SomethingElse +} else { + Do-Default +} + +# Complex condition +if ($user.IsActive -and + $user.HasPermission -and + $user.Age -gt 18) { + Grant-Access +} +``` + +**Bad:** + +```powershell +if ($condition) +{ # Brace should be on same line + Do-Something +} + +if ($condition) Do-Something # Missing braces + +if($condition){ # Missing spaces + Do-Something +} +``` + +### Loops + +- Use appropriate loop construct (foreach, for, while, do-while) +- Always use braces +- Opening brace on same line (OTBS) +- Prefer `foreach` for collections over `for` when index not needed + +**Good:** + +```powershell +foreach ($item in $collection) { + Process-Item $item +} + +for ($i = 0; $i -lt $count; $i++) { + Process-Index $i +} + +while ($condition) { + Update-Condition +} +``` + +**Bad:** + +```powershell +foreach ($item in $collection) +{ # Brace should be on same line + Process-Item $item +} + +foreach ($item in $collection) Process-Item $item # Missing braces +``` + +### Switch Statements + +- Opening brace on same line +- Indent case statements by 4 spaces +- Use `break` or `continue` explicitly when needed +- Use `default` for fallback cases + +**Good:** + +```powershell +switch ($value) { + 'Option1' { + Do-FirstThing + } + 'Option2' { + Do-SecondThing + } + default { + Do-DefaultThing + } +} +``` + +## Error Handling + +- Use try/catch/finally blocks for error handling +- Be specific with catch blocks (catch specific exception types) +- Always provide meaningful error messages +- Use `throw` for unrecoverable errors +- Use `Write-Error` for non-terminating errors +- Set `$ErrorActionPreference` appropriately + +**Good:** + +```powershell +function Get-FileContent { + param([string]$Path) + + try { + if (-not (Test-Path $Path)) { + throw "File not found: $Path" + } + + $content = Get-Content -Path $Path -ErrorAction Stop + return $content + } catch [System.IO.IOException] { + Write-Error "IO error reading file: $_" + throw + } catch { + Write-Error "Unexpected error: $_" + throw + } finally { + # Cleanup code here + } +} +``` + +**Bad:** + +```powershell +function Get-FileContent { + param([string]$Path) + + try { + Get-Content -Path $Path + } catch { + # Swallowing errors silently + } +} +``` + +## Output and Logging + +- Use `Write-Output` for function return values (or implicit return) +- Avoid `Write-Host` use `Write-Information` or `Write-Output` instead. +- Use `Write-Verbose` for detailed operation information +- Use `Write-Debug` for debugging information +- Use `Write-Warning` for warnings +- Use `Write-Error` for errors +- Use `Write-Information` for informational messages (PS 5.0+) + +**Good:** + +```powershell +function Get-ProcessedData { + [CmdletBinding()] + param($Data) + + Write-Verbose "Processing $($Data.Count) items" + + $result = Process-Data $Data + + if ($result.Warnings) { + Write-Warning "Processing completed with warnings" + } + + Write-Output $result +} +``` + +**Bad:** + +```powershell +function Get-ProcessedData { + param($Data) + + Write-Host "Processing data..." # Should use Write-Verbose + + $result = Process-Data $Data + + Write-Host $result # Should use Write-Output +} +``` + +## Pipeline + +- Design functions to accept pipeline input when appropriate +- Use `[Parameter(ValueFromPipeline = $true)]` for pipeline parameters +- Implement `process` block for pipeline-aware functions +- Use `begin` and `end` blocks when initialization or cleanup needed + +**Good:** + +```powershell +function Update-Item { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [PSCustomObject]$Item + ) + + begin { + $count = 0 + } + + process { + # Process each item from pipeline + $Item.LastModified = Get-Date + $count++ + Write-Output $Item + } + + end { + Write-Verbose "Processed $count items" + } +} + +# Usage +$items | Update-Item +``` + +## Arrays and Hashtables + +- Use `@()` for empty arrays +- Use `@{}` for empty hashtables +- Use consistent formatting for multi-line collections +- One item per line for readability in multi-line collections +- Trailing comma optional but allowed on last item +- Align key-value pairs in hashtables for readability +- Use splatting for functions with many parameters + +**Good:** + +```powershell +$emptyArray = @() +$numbers = @(1, 2, 3, 4, 5) + +$multiLineArray = @( + 'First item' + 'Second item' + 'Third item' +) + +$hashtable = @{ + Name = 'John' + Age = 30 + City = 'Seattle' +} + +$complexHash = @{ + Server = @{ + Name = 'WebServer01' + Port = 8080 + } + Database = @{ + Name = 'MainDB' + Port = 5432 + } +} +``` + +**Bad:** + +```powershell +$array = 1, 2, 3, 4, 5 # Use @() syntax + +$hashtable = @{Name = 'John'; Age = 30; City = 'Seattle'} # Multi-line for readability +``` + +## Splatting + +- Use splatting for functions with many parameters +- Create hashtable with parameters before splatting +- Use `@` symbol for splatting (not `$`) + +**Good:** + +```powershell +$params = @{ + Path = 'C:\Temp' + Filter = '*.txt' + Recurse = $true + ErrorAction = 'Stop' +} + +Get-ChildItem @params +``` + +**Bad:** + +```powershell +Get-ChildItem -Path 'C:\Temp' -Filter '*.txt' -Recurse $true -ErrorAction 'Stop' +``` + +## Comparison Operators + +- Use PowerShell comparison operators (`-eq`, `-ne`, `-gt`, `-lt`, `-ge`, `-le`) +- Don't use C-style operators (`==`, `!=`, `>`, `<`) +- Use `-like` for wildcard matching, `-match` for regular expression +- Use `-contains` for collection membership, not `-eq` +- Add `-i` prefix for case-insensitive (default) or `-c` for case-sensitive +- Caution with $null comparisons. Comparison order is important depending if the variable is a single item or a collection. + - `$null -eq $var` is usually safer for collections as it won't error if $var is $null or empty. + +**Good:** + +```powershell +if ($value -eq 10) { } +if ($name -like 'John*') { } +if ($email -match '^[\w-\.]+@') { } +if ($list -contains $item) { } +if ($name -ceq 'JOHN') { } # Case-sensitive +if ($null -eq $collection) { } # Safe null check for collections +``` + +**Bad:** + +```powershell +if ($value == 10) { } # Wrong operator +if ($list -eq $item) { } # Use -contains for collections +if ($collection -eq $null) { } # Can error if $collection is $null or empty +``` + +## Script Structure + +- Use `#Requires` statements at the top for version/module requirements +- Place param block after `#Requires` and comment-based help +- Group related functions together +- Separate sections with comments +- End script with single newline + +**Good:** + +```powershell +#Requires -Version 7.4 + +<# + .SYNOPSIS + Script for managing user accounts. + + .DESCRIPTION + This script provides functions to create, update, and delete user accounts. +#> + +[CmdletBinding()] +param( + [string]$ConfigPath = ".\config.json" +) + +# Script-level variables +$ErrorActionPreference = 'Stop' + +#region Helper functions +function Get-ConfigData { + param([string]$Path) + # Implementation +} + +function New-UserAccount { + param($UserName, $Email) + # Implementation +} +#endregion + +# Script execution +try { + $config = Get-ConfigData -Path $ConfigPath + # Main logic +} catch { + Write-Error "Script failed: $_" + exit 1 +} +``` + +## Performance Considerations + +- Avoid `Write-Host` in production scripts, instead use `Write-Information` +- Use `ArrayList` collection instead of `@()` arrays in loops. +- Use `-Filter` parameter instead of piping to `Where-Object` when available +- Avoid unnecessary pipeline operations +- Use `.ForEach()` and `.Where()` methods for better performance on large collections + +**Good:** + +```powershell +# Efficient array building +$results = [System.Collections.Generic.List[PSObject]]::new() +foreach ($item in $collection) { + $results.Add($processedItem) +} + +# Efficient filtering +Get-ChildItem -Path C:\Temp -Filter *.txt + +# Method syntax for performance +$filtered = $collection.Where({ $_.Value -gt 10 }) +``` + +**Bad:** + +```powershell +# Inefficient array building +$results = @() +foreach ($item in $collection) { + $results += $processedItem # Creates new array each iteration +} + +# Inefficient filtering +Get-ChildItem -Path C:\Temp | Where-Object { $_.Name -like '*.txt' } +``` + +## Line Length + +- Wrap lines at 100-120 characters +- Use backtick (`` ` ``) for line continuation (sparingly), prefer splatting +- Prefer breaking at natural points (after commas, operators, pipes) +- Align continued lines for readability + +**Good:** + +```powershell +$result = Get-Something -Parameter1 $value1 ` + -Parameter2 $value2 ` + -Parameter3 $value3 + +# Better: Use splatting +$params = @{ + Parameter1 = $value1 + Parameter2 = $value2 + Parameter3 = $value3 +} +$result = Get-Something @params +``` + +## Testing + +- Write Pester tests for all functions +- Name test files `*.Tests.ps1` +- Group tests with `Describe` and `Context` blocks +- Use `It` blocks for individual test cases +- Use `Should` assertions +- Use `BeforeAll` and `AfterAll` for setup/teardown + +**Good:** + +```powershell +Describe 'Get-UserAccount' { + Context 'When user exists' { + It 'Should return user object' { + $result = Get-UserAccount -UserId '123' + $result | Should -Not -BeNullOrEmpty + $result.UserId | Should -Be '123' + } + } + + Context 'When user does not exist' { + It 'Should throw error' { + { Get-UserAccount -UserId '999' } | Should -Throw + } + } +} +``` + +## Security Best Practices + +- Never hardcode credentials or secrets +- Use `SecureString` for sensitive data +- Use `Get-Credential` for credential prompts +- Validate all user input +- Use `-WhatIf` and `-Confirm` for destructive operations +- Avoid `Invoke-Expression` with user input + +**Good:** + +```powershell +function Remove-UserData { + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$UserId + ) + + if ($PSCmdlet.ShouldProcess($UserId, "Remove user data")) { + # Perform deletion + } +} + +# Get credentials securely +$cred = Get-Credential -Message "Enter admin credentials" +``` + +**Bad:** + +```powershell +$password = "MyPassword123" # Hardcoded password +Invoke-Expression $userInput # Security risk +``` + +## Related Resources + +- [PowerShell Practice and Style Guide](https://poshcode.gitbook.io/powershell-practice-and-style/) +- [PowerShell Best Practices](https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/cmdlet-development-guidelines) +- [PSScriptAnalyzer Rules](https://github.com/PowerShell/PSScriptAnalyzer) diff --git a/.github/linters/.jscpd.json b/.github/linters/.jscpd.json new file mode 100644 index 00000000..15d4513d --- /dev/null +++ b/.github/linters/.jscpd.json @@ -0,0 +1,11 @@ +{ + "threshold": 0, + "reporters": [ + "consoleFull" + ], + "ignore": [ + "**/tests/**", + "**/workflows/**" + ], + "absolute": true +} diff --git a/.github/linters/.powershell-psscriptanalyzer.psd1 b/.github/linters/.powershell-psscriptanalyzer.psd1 index 40d11d60..09cc3d0c 100644 --- a/.github/linters/.powershell-psscriptanalyzer.psd1 +++ b/.github/linters/.powershell-psscriptanalyzer.psd1 @@ -1,18 +1,56 @@ -#Documentation: https://github.com/PowerShell/PSScriptAnalyzer/blob/master/docs/Cmdlets/Invoke-ScriptAnalyzer.md#-settings -@{ - #CustomRulePath='path\to\CustomRuleModule.psm1' - #RecurseCustomRulePath='path\of\customrules' - #Severity = @( - # 'Error' - # 'Warning' - #) - #IncludeDefaultRules=${true} +@{ + Rules = @{ + PSAlignAssignmentStatement = @{ + Enable = $true + CheckHashtable = $true + } + PSAvoidLongLines = @{ + Enable = $true + MaximumLineLength = 150 + } + PSAvoidSemicolonsAsLineTerminators = @{ + Enable = $true + } + PSPlaceCloseBrace = @{ + Enable = $true + NewLineAfter = $false + IgnoreOneLineBlock = $true + NoEmptyLineBefore = $false + } + PSPlaceOpenBrace = @{ + Enable = $true + OnSameLine = $true + NewLineAfter = $true + IgnoreOneLineBlock = $true + } + PSProvideCommentHelp = @{ + Enable = $true + ExportedOnly = $false + BlockComment = $true + VSCodeSnippetCorrection = $false + Placement = 'begin' + } + PSUseConsistentIndentation = @{ + Enable = $true + IndentationSize = 4 + PipelineIndentation = 'IncreaseIndentationForFirstPipeline' + Kind = 'space' + } + PSUseConsistentWhitespace = @{ + Enable = $true + CheckInnerBrace = $true + CheckOpenBrace = $true + CheckOpenParen = $true + CheckOperator = $true + CheckPipe = $true + CheckPipeForRedundantWhitespace = $true + CheckSeparator = $true + CheckParameter = $true + IgnoreAssignmentOperatorInsideHashTable = $true + } + } ExcludeRules = @( - 'PSMissingModuleManifestField' - 'PSAvoidUsingWriteHost' + 'PSMissingModuleManifestField', # This rule is not applicable until the module is built. + 'PSUseToExportFieldsInManifest' ) - #IncludeRules = @( - # 'PSAvoidUsingWriteHost', - # 'MyCustomRuleName' - #) } diff --git a/.github/linters/.textlintrc b/.github/linters/.textlintrc new file mode 100644 index 00000000..db48de80 --- /dev/null +++ b/.github/linters/.textlintrc @@ -0,0 +1,513 @@ +{ + "filters": { + "comments": true + }, + "rules": { + "terminology": { + "defaultTerms": false, + "terms": [ + "Airbnb", + "Android", + "AppleScript", + "AppVeyor", + "AVA", + "BrowserStack", + "Browsersync", + "Codecov", + "CodePen", + "CodeSandbox", + "DefinitelyTyped", + "EditorConfig", + "ESLint", + "GitHub", + "GraphQL", + "GraphiQL", + "iOS", + "JavaScript", + "JetBrains", + "jQuery", + "LinkedIn", + "Lodash", + "MacBook", + "Markdown", + "OpenType", + "PayPal", + "PhpStorm", + "PowerShell", + "PlayStation", + "RubyMine", + "Sass", + "SemVer", + "TypeScript", + "UglifyJS", + "Wasm", + "WebAssembly", + "WebStorm", + "WordPress", + "YouTube", + [ + "Common[ .]js", + "CommonJS" + ], + [ + "JSDocs?", + "JSDoc" + ], + [ + "Node[ .]js", + "Node.js" + ], + [ + "React[ .]js", + "React" + ], + [ + "SauceLabs", + "Sauce Labs" + ], + [ + "StackOverflow", + "Stack Overflow" + ], + [ + "styled ?components", + "styled-components" + ], + [ + "HTTP[ /]2(?:\\.0)?", + "HTTP/2" + ], + [ + "OS X", + "macOS" + ], + [ + "Mac ?OS", + "macOS" + ], + [ + "a npm", + "an npm" + ], + "ECMAScript", + [ + "ES2015", + "ES6" + ], + [ + "ES7", + "ES2016" + ], + "3D", + [ + "3-D", + "3D" + ], + "Ajax", + "API", + "APIs", + "API's", + [ + "(? `user-can-upload-file`). + - User story/action inventory. + - Task coverage mapping: Map each task to one or more requirements or stories (inference by keyword / explicit reference patterns like IDs or key phrases). + - Constitution rule set: Extract principle names and any MUST/SHOULD normative statements. + +4. Detection passes: + A. Duplication detection: + - Identify near-duplicate requirements. Mark lower-quality phrasing for consolidation. + B. Ambiguity detection: + - Flag vague adjectives (fast, scalable, secure, intuitive, robust) lacking measurable criteria. + - Flag unresolved placeholders (TODO, TKTK, ???, , etc.). + C. Underspecification: + - Requirements with verbs but missing object or measurable outcome. + - User stories missing acceptance criteria alignment. + - Tasks referencing files or components not defined in spec/plan. + D. Constitution alignment: + - Any requirement or plan element conflicting with a MUST principle. + - Missing mandated sections or quality gates from constitution. + E. Coverage gaps: + - Requirements with zero associated tasks. + - Tasks with no mapped requirement/story. + - Non-functional requirements not reflected in tasks (e.g., performance, security). + F. Inconsistency: + - Terminology drift (same concept named differently across files). + - Data entities referenced in plan but absent in spec (or vice versa). + - Task ordering contradictions (e.g., integration tasks before foundational setup tasks without dependency note). + - Conflicting requirements (e.g., one requires to use Next.js while other says to use Vue as the framework). + +5. Severity assignment heuristic: + - CRITICAL: Violates constitution MUST, missing core spec artifact, or requirement with zero coverage that blocks baseline functionality. + - HIGH: Duplicate or conflicting requirement, ambiguous security/performance attribute, or untestable acceptance criterion. + - MEDIUM: Terminology drift, missing non-functional task coverage, or underspecified edge case. + - LOW: Style/wording improvements, or minor redundancy not affecting execution order. + +6. Produce a Markdown report (no file writes) with sections: + + ```markdown + ### Specification Analysis Report + + | ID | Category | Severity | Location(s) | Summary | Recommendation | + |----|----------|----------|-------------|---------|----------------| + | A1 | Duplication | HIGH | spec.md:L120-134 | Two similar requirements ... | Merge phrasing; keep clearer version | + (Add one row per finding; generate stable IDs prefixed by category initial.) + ``` + + Additional subsections: + - Coverage Summary Table: + + ```markdown + | Requirement Key | Has Task? | Task IDs | Notes | + |-----------------|-----------|----------|-------| + ``` + + - Constitution Alignment Issues (if any) + - Unmapped Tasks (if any) + - Metrics: + * Total Requirements + * Total Tasks + * Coverage % (requirements with >=1 task) + * Ambiguity Count + * Duplication Count + * Critical Issues Count + +7. **Post issue comment** with analysis results organized by severity: + - Post a comment to the GitHub issue with the analysis findings + - Format the comment with separate tables for each severity level (only include levels that have findings): + ```markdown + ## Analysis Report + + ### Summary + - Total Requirements: X + - Total Tasks: Y + - Coverage: Z% + - Issues Found: N (A critical, B high, C medium, D low) + + ### Critical Issues + | ID | Category | Location(s) | Summary | Recommendation | + |----|----------|-------------|---------|----------------| + | C1 | ... | ... | ... | ... | + + ### High Priority Issues + | ID | Category | Location(s) | Summary | Recommendation | + |----|----------|-------------|---------|----------------| + | H1 | ... | ... | ... | ... | + + ### Medium Priority Issues + | ID | Category | Location(s) | Summary | Recommendation | + |----|----------|-------------|---------|----------------| + | M1 | ... | ... | ... | ... | + + ### Low Priority Issues + | ID | Category | Location(s) | Summary | Recommendation | + |----|----------|-------------|---------|----------------| + | L1 | ... | ... | ... | ... | + + ### Next Actions + - [Recommendation based on findings] + ``` + - Keep tables concise, limit to 20 findings per severity level + - If more than 20 findings in a category, add a note: "_(Additional N issues not shown - see full report)_" + - Include the complete "Next Actions" block with specific recommendations + +8. At end of report, output a concise Next Actions block: + - If CRITICAL issues exist: Recommend resolving them before `/implement`. + - If only LOW/MEDIUM issues: User may proceed, but provide improvement suggestions. + - Provide explicit command suggestions: e.g., "Run /specify with refinement", "Run /plan to adjust architecture", or "Manually edit tasks.md to add coverage for 'performance-metrics'". + +8. Ask the user: "Would you like me to suggest concrete remediation edits for the top N issues?" (Do NOT apply them automatically.) + +Behavior rules: +- NEVER modify files. +- NEVER hallucinate missing sections—if absent, report them. +- KEEP findings deterministic: if rerun without changes, produce consistent IDs and counts. +- LIMIT total findings in the main table to 50; aggregate remainder in a summarized overflow note. +- If zero issues found, emit a success report with coverage statistics and proceed recommendation. + +Context: $ARGUMENTS diff --git a/.github/prompts/clarify.prompt.md b/.github/prompts/clarify.prompt.md new file mode 100644 index 00000000..ed8a26d0 --- /dev/null +++ b/.github/prompts/clarify.prompt.md @@ -0,0 +1,180 @@ +--- +description: Identify underspecified areas in the current feature spec by asking up to 5 highly targeted clarification questions and encoding answers back into the spec. +--- + +# Clarify + +The user input to you can be provided directly by the agent or as a command argument - you **MUST** consider it before proceeding with the prompt (if not empty). + +User input: + +$ARGUMENTS + +Goal: Detect and reduce ambiguity or missing decision points in the active feature specification and record the clarifications directly in the spec file. + +Note: This clarification workflow is expected to run (and be completed) BEFORE invoking `/plan`. If the user explicitly states they are skipping clarification (e.g., exploratory spike), you may proceed, but must warn that downstream rework risk increases. + +Execution steps: + +1. Run `.specify/scripts/powershell/check-prerequisites.ps1 -Json -PathsOnly` from repo root **once** (combined `--json --paths-only` mode / `-Json -PathsOnly`). Parse minimal JSON payload fields: + - `FEATURE_DIR` + - `FEATURE_SPEC` + - (Optionally capture `IMPL_PLAN`, `TASKS` for future chained flows.) + - If JSON parsing fails, abort and instruct user to re-run `/specify` or verify feature branch environment. + +2. Load the current spec file. Perform a structured ambiguity & coverage scan using this taxonomy. For each category, mark status: Clear / Partial / Missing. Produce an internal coverage map used for prioritization (do not output raw map unless no questions will be asked). + + Functional Scope & Behavior: + - Core user goals & success criteria + - Explicit out-of-scope declarations + - User roles / personas differentiation + + Domain & Data Model: + - Entities, attributes, relationships + - Identity & uniqueness rules + - Lifecycle/state transitions + - Data volume / scale assumptions + + Interaction & UX Flow: + - Critical user journeys / sequences + - Error/empty/loading states + - Accessibility or localization notes + + Non-Functional Quality Attributes: + - Performance (latency, throughput targets) + - Scalability (horizontal/vertical, limits) + - Reliability & availability (uptime, recovery expectations) + - Observability (logging, metrics, tracing signals) + - Security & privacy (authN/Z, data protection, threat assumptions) + - Compliance / regulatory constraints (if any) + + Integration & External Dependencies: + - External services/APIs and failure modes + - Data import/export formats + - Protocol/versioning assumptions + + Edge Cases & Failure Handling: + - Negative scenarios + - Rate limiting / throttling + - Conflict resolution (e.g., concurrent edits) + + Constraints & Tradeoffs: + - Technical constraints (language, storage, hosting) + - Explicit tradeoffs or rejected alternatives + + Terminology & Consistency: + - Canonical glossary terms + - Avoided synonyms / deprecated terms + + Completion Signals: + - Acceptance criteria testability + - Measurable Definition of Done style indicators + + Misc / Placeholders: + - TODO markers / unresolved decisions + - Ambiguous adjectives ("robust", "intuitive") lacking quantification + + For each category with Partial or Missing status, add a candidate question opportunity unless: + - Clarification would not materially change implementation or validation strategy + - Information is better deferred to planning phase (note internally) + +3. Generate (internally) a prioritized queue of candidate clarification questions (maximum 5). Do NOT output them all at once. Apply these constraints: + - Maximum of 5 total questions across the entire session. + - Each question must be answerable with EITHER: + * A short multiple‑choice selection (2–5 distinct, mutually exclusive options), OR + * A one-word / short‑phrase answer (explicitly constrain: "Answer in ≤5 words"). + - Only include questions whose answers materially impact architecture, data modeling, task decomposition, test design, UX behavior, operational readiness, or compliance validation. + - Ensure category coverage balance: attempt to cover the highest impact unresolved categories first; avoid asking two low-impact questions when a single high-impact area (e.g., security posture) is unresolved. + - Exclude questions already answered, trivial stylistic preferences, or plan-level execution details (unless blocking correctness). + - Favor clarifications that reduce downstream rework risk or prevent misaligned acceptance tests. + - If more than 5 categories remain unresolved, select the top 5 by (Impact * Uncertainty) heuristic. + +4. Sequential questioning loop (interactive): + - Present EXACTLY ONE question at a time. + - For multiple‑choice questions render options as a Markdown table: + + ```markdown + | Option | Description | + |--------|-------------| + | A |