From c42001ccfc64a00b224ac8ffd2cb5270ac022b7f Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Mon, 8 Jun 2026 08:43:26 -0700 Subject: [PATCH 1/4] Adopt shared markdownlint config and lint-clean docs Carry the template's .markdownlint-cli2.jsonc verbatim so the davidanson IDE extension and CLI markdownlint-cli2 apply the same rules. Normalize blank lines around headings/fences/lists and label one fenced block in copilot-instructions.md so the repo passes cleanly under the shared config (0 errors). The existing .editorconfig already governs line endings (global crlf + lf overrides), so no editorconfig change was needed. Realigns with ptr727/ProjectTemplate. Part of #329. --- .github/copilot-instructions.md | 41 +++++++++++++++++++++++++++++++-- .markdownlint-cli2.jsonc | 14 +++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 .markdownlint-cli2.jsonc diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index cb80fe8b..83a47a79 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -7,6 +7,7 @@ This is a .NET utility library that provides generally useful C# classes and ext ## Code Style and Standards ### General Guidelines + - Follow C# coding conventions and .NET best practices - Use meaningful variable and method names - Keep methods focused and single-purpose @@ -22,6 +23,7 @@ This is a .NET utility library that provides generally useful C# classes and ext 2. **dotnet format** is run second for style enforcement #### Formatting Workflow + ```bash # Always format with CSharpier FIRST after editing code dotnet csharpier . @@ -34,7 +36,9 @@ dotnet format --verify-no-changes ``` #### Key Formatting Rules (from .editorconfig) + - **No `var` keyword**: Use explicit types everywhere + ```csharp // ✅ CORRECT string text = "hello"; @@ -44,6 +48,7 @@ dotnet format --verify-no-changes var text = "hello"; var numbers = new List(); ``` + - **Indentation**: 4 spaces (not tabs) - **Line endings**: CRLF (Windows) - **Charset**: UTF-8 @@ -53,13 +58,16 @@ dotnet format --verify-no-changes - **Collection expressions**: Preferred `[]` over `new List()` #### Pre-Commit Hook + The Husky.Net pre-commit hook automatically runs: + 1. `dotnet csharpier .` - Code formatting 2. `dotnet format` - Style enforcement **Commits will be rejected if formatting fails!** ### .NET 10 and AOT Considerations + - The project targets .NET 10 with PublishAot enabled - Avoid reflection where possible (not AOT-friendly) - Use source generators instead of runtime reflection when applicable @@ -105,12 +113,15 @@ The Husky.Net pre-commit hook automatically runs: ## Common Tasks ### Building + Run the ".NET Build" task or use: `dotnet build` ### Publishing + Run the ".NET Publish" task or use: `dotnet publish` ### Formatting (REQUIRED before commit) + ```bash # Step 1: Format with CSharpier dotnet csharpier . @@ -123,19 +134,24 @@ dotnet format --verify-no-changes ``` ### Running Tests + Use: `dotnet test` ## Commit Guidelines ### Pre-Commit Process (Automated by Husky.Net) + The following happens automatically on every commit: + 1. ✅ CSharpier formats all C# files 2. ✅ dotnet format applies .editorconfig rules 3. ✅ Commit proceeds if formatting passes 4. ❌ Commit is rejected if formatting fails ### Manual Pre-Commit Checklist + Before committing, ensure: + - [ ] Code formatted with CSharpier (`dotnet csharpier .`) - [ ] Style rules applied (`dotnet format`) - [ ] No formatting issues (`dotnet format --verify-no-changes`) @@ -146,15 +162,19 @@ Before committing, ensure: - [ ] Commit message is clear and descriptive ### Commit Message Format + Follow conventional commit format: -``` + +```text (): [optional body] [optional footer] ``` + Examples: + - `feat(download): add async download methods` - `fix(fileex): correct boundary condition in DeleteDirectory` - `docs(readme): update async method examples` @@ -165,7 +185,7 @@ Examples: - **Package ID**: InsaneGenius.Utilities - **Namespace**: InsaneGenius.Utilities - **License**: MIT -- **Repository**: https://github.com/ptr727/Utilities +- **Repository**: - **Target Framework**: .NET 10 - **C# Version**: 14.0 - **Version**: 3.5 (managed by Nerdbank.GitVersioning) @@ -186,6 +206,7 @@ Examples: ## Code Generation Preferences ### Modern C# Features (C# 14) + - Prefer modern C# language features (pattern matching, records, file-scoped namespaces, etc.) - Use nullable reference types consistently with `ArgumentNullException.ThrowIfNull()` - Leverage expression-bodied members where appropriate @@ -195,6 +216,7 @@ Examples: - Use primary constructors where appropriate ### Async/Await Patterns + - **Always use `ConfigureAwait(false)` in library code** - Provide async versions of I/O-bound methods - Use `CancellationToken` parameters (default to `default`) @@ -204,17 +226,20 @@ Examples: - Use `Memory` and `Span` for async I/O operations ### Input Validation + - Use `ArgumentNullException.ThrowIfNull()` for null checks - Validate parameters early in methods - Document all exceptions in XML comments ### Resource Management + - Use `using` statements for proper disposal - Use `await using` for async disposal - Avoid explicit `.Close()` calls (using handles it) - Use `leaveOpen` parameter when appropriate ### Thread Safety + - Use `Lazy` for thread-safe initialization - Use `Lock` (C# 13+) instead of `object` for locks - Avoid static mutable state @@ -243,6 +268,7 @@ Examples: ## Common Patterns in This Project ### Error Handling + ```csharp try { @@ -259,18 +285,21 @@ catch (Exception e) when (LogOptions.Logger.LogAndHandle(e)) ``` ### Retry Logic + - Use `Options.RetryCount` for retry attempts - Use `Options.Cancel.IsCancellationRequested` for cancellation - Use `Task.Delay()` for async waits - Log retry attempts with `LogOptions.Logger.Information()` ### Method Signatures + - I/O methods return `bool` for success/failure - Async methods have `Async` suffix - Async methods include optional `CancellationToken cancellationToken = default` - Use `out` parameters for additional return values ### XML Documentation + - Always include `` for all public members - Document all `` with descriptions - Document `` with descriptions @@ -295,24 +324,28 @@ catch (Exception e) when (LogOptions.Logger.LogAndHandle(e)) ## File-Specific Notes ### Download.cs + - Uses thread-safe `Lazy` initialization - Provides both sync and async versions - Returns tuples from async methods for multiple values - Uses `HttpCompletionOption.ResponseHeadersRead` for efficiency ### FileEx.cs + - All I/O methods have async versions - Uses `Options` for retry configuration - Returns `bool` for success/failure - Supports cancellation via `Options.Cancel` and method parameter ### StringCompression.cs + - Supports configurable compression levels - Has both sync and async versions - Uses `leaveOpen` for stream management - Proper error documentation ### Extensions.cs + - Uses C# 14 `extension` keyword - Must be inside static class - Provides extension methods for string compression and logger error handling @@ -320,6 +353,7 @@ catch (Exception e) when (LogOptions.Logger.LogAndHandle(e)) ## Development Workflow ### Making Changes + 1. Edit code 2. **Run CSharpier**: `dotnet csharpier .` 3. **Run dotnet format**: `dotnet format` @@ -328,6 +362,7 @@ catch (Exception e) when (LogOptions.Logger.LogAndHandle(e)) 6. Commit (Husky.Net pre-commit hook will verify formatting) ### Before Committing + ```bash # Format code (REQUIRED ORDER) dotnet csharpier . @@ -345,6 +380,7 @@ git commit -m "feat: your message" ``` ### If Pre-Commit Hook Fails + ```bash # Hook will show formatting errors # Re-run formatters @@ -365,6 +401,7 @@ git commit -m "feat: your message" ## EditorConfig Integration The project uses `.editorconfig` for style enforcement. Key rules: + - `csharp_style_var_*` = **false** (no var keyword) - `csharp_style_namespace_declarations` = **file_scoped** - `csharp_prefer_system_threading_lock` = **true** diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc new file mode 100644 index 00000000..c6a57141 --- /dev/null +++ b/.markdownlint-cli2.jsonc @@ -0,0 +1,14 @@ +{ + "config": { + // Prose paragraphs and data-heavy tables/URLs are intentionally long; + // reflowing at 80 cols hurts readability and churns diffs. + "MD013": false, + // Inline HTML is used for reference-link section dividers. + "MD033": false, + // Require fenced code blocks over the legacy 4-space-indented style. + "MD046": { "style": "fenced" }, + // Wide tables are intentional where wrapping cells breaks GitHub rendering. + "MD060": false + }, + "gitignore": true +} From 54205000130c7ca0681283a4d2d9bc4f43ef60f9 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Mon, 8 Jun 2026 09:00:36 -0700 Subject: [PATCH 2/4] Carry review-loop contract and Copilot runbook from template Add the template's 'PR Review Etiquette' contract to AGENTS.md and append the 'GitHub Copilot Review Runbook' to .github/copilot-instructions.md (owner/repo adapted to ptr727/Utilities). Both were previously absent, leaving an agent here with no in-repo pointer to the reliable Copilot review mechanics. Part of #329. --- .github/copilot-instructions.md | 875 ++++++++++++++++++-------------- AGENTS.md | 44 ++ 2 files changed, 543 insertions(+), 376 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 83a47a79..548c7f12 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,410 +1,533 @@ -# GitHub Copilot Instructions for Utilities Project - -## Project Overview - -This is a .NET utility library that provides generally useful C# classes and extensions. The project targets .NET 10 and includes AOT (Ahead-of-Time) compilation support for optimized runtime performance. - -## Code Style and Standards - -### General Guidelines - -- Follow C# coding conventions and .NET best practices -- Use meaningful variable and method names -- Keep methods focused and single-purpose -- **Add comprehensive XML documentation comments for ALL public APIs** (required) -- Maintain consistency with existing code style -- Follow the existing patterns in the codebase - -### Formatting Requirements - -**IMPORTANT:** This project uses **Husky.Net** pre-commit hooks that automatically enforce formatting: - -1. **CSharpier** is run first for code formatting -2. **dotnet format** is run second for style enforcement - -#### Formatting Workflow - -```bash -# Always format with CSharpier FIRST after editing code -dotnet csharpier . - -# Then run dotnet format to apply .editorconfig rules -dotnet format - -# Verify no changes needed -dotnet format --verify-no-changes -``` - -#### Key Formatting Rules (from .editorconfig) - -- **No `var` keyword**: Use explicit types everywhere - - ```csharp - // ✅ CORRECT - string text = "hello"; - List numbers = []; - - // ❌ WRONG - var text = "hello"; - var numbers = new List(); - ``` - -- **Indentation**: 4 spaces (not tabs) -- **Line endings**: CRLF (Windows) -- **Charset**: UTF-8 -- **Final newline**: Required -- **Trailing whitespace**: Not allowed -- **File-scoped namespaces**: Required -- **Collection expressions**: Preferred `[]` over `new List()` - -#### Pre-Commit Hook - -The Husky.Net pre-commit hook automatically runs: - -1. `dotnet csharpier .` - Code formatting -2. `dotnet format` - Style enforcement - -**Commits will be rejected if formatting fails!** - -### .NET 10 and AOT Considerations - -- The project targets .NET 10 with PublishAot enabled -- Avoid reflection where possible (not AOT-friendly) -- Use source generators instead of runtime reflection when applicable -- Be mindful of trim warnings and compatibility -- Ensure all code is AOT-compatible -- Test AOT compatibility with `dotnet publish` - -## Project Structure - -- **Utilities/**: Main library project containing utility classes - - `CommandLineEx.cs`: Command-line argument parsing utilities - - `ConsoleEx.cs`: Console interaction helpers with color support - - `Download.cs`: HTTP download utilities (sync and async) - - `Extensions.cs`: Extension methods (string compression, logger error handling) - - `FileEx.cs`: File and directory operation utilities with retry logic (sync and async) - - `FileExOptions.cs`: Configuration options for FileEx operations - - `Format.cs`: Byte size formatting utilities (binary and decimal) - - `LogOptions.cs`: Global logging configuration - - `StringCompression.cs`: String compression/decompression using Deflate (sync and async) - - `StringHistory.cs`: Bounded string history buffer - -- **Sandbox/**: Console application for testing and experimentation -- **UtilitiesTests/**: Unit tests using xUnit - -## Testing - -- Use xUnit for all tests -- Follow AAA pattern (Arrange, Act, Assert) -- Test file names should match the class being tested with "Tests" suffix -- Run tests frequently during development -- Maintain good test coverage for public APIs -- **Add tests for all new async methods** -- Consider edge cases and error conditions in tests - -## Dependencies - -- **Serilog**: Logging framework (required for LogOptions and error handling) -- **Microsoft.SourceLink.GitHub**: Source linking for debugging -- **xUnit**: Testing framework -- Keep dependencies minimal and well-justified -- Update package references to latest stable versions when appropriate - -## Common Tasks - -### Building - -Run the ".NET Build" task or use: `dotnet build` +# GitHub Copilot Instructions for Utilities Project + +## Project Overview + +This is a .NET utility library that provides generally useful C# classes and extensions. The project targets .NET 10 and includes AOT (Ahead-of-Time) compilation support for optimized runtime performance. + +## Code Style and Standards + +### General Guidelines + +- Follow C# coding conventions and .NET best practices +- Use meaningful variable and method names +- Keep methods focused and single-purpose +- **Add comprehensive XML documentation comments for ALL public APIs** (required) +- Maintain consistency with existing code style +- Follow the existing patterns in the codebase + +### Formatting Requirements + +**IMPORTANT:** This project uses **Husky.Net** pre-commit hooks that automatically enforce formatting: + +1. **CSharpier** is run first for code formatting +2. **dotnet format** is run second for style enforcement + +#### Formatting Workflow + +```bash +# Always format with CSharpier FIRST after editing code +dotnet csharpier . + +# Then run dotnet format to apply .editorconfig rules +dotnet format + +# Verify no changes needed +dotnet format --verify-no-changes +``` + +#### Key Formatting Rules (from .editorconfig) + +- **No `var` keyword**: Use explicit types everywhere + + ```csharp + // ✅ CORRECT + string text = "hello"; + List numbers = []; + + // ❌ WRONG + var text = "hello"; + var numbers = new List(); + ``` + +- **Indentation**: 4 spaces (not tabs) +- **Line endings**: CRLF (Windows) +- **Charset**: UTF-8 +- **Final newline**: Required +- **Trailing whitespace**: Not allowed +- **File-scoped namespaces**: Required +- **Collection expressions**: Preferred `[]` over `new List()` + +#### Pre-Commit Hook + +The Husky.Net pre-commit hook automatically runs: + +1. `dotnet csharpier .` - Code formatting +2. `dotnet format` - Style enforcement + +**Commits will be rejected if formatting fails!** + +### .NET 10 and AOT Considerations + +- The project targets .NET 10 with PublishAot enabled +- Avoid reflection where possible (not AOT-friendly) +- Use source generators instead of runtime reflection when applicable +- Be mindful of trim warnings and compatibility +- Ensure all code is AOT-compatible +- Test AOT compatibility with `dotnet publish` + +## Project Structure + +- **Utilities/**: Main library project containing utility classes + - `CommandLineEx.cs`: Command-line argument parsing utilities + - `ConsoleEx.cs`: Console interaction helpers with color support + - `Download.cs`: HTTP download utilities (sync and async) + - `Extensions.cs`: Extension methods (string compression, logger error handling) + - `FileEx.cs`: File and directory operation utilities with retry logic (sync and async) + - `FileExOptions.cs`: Configuration options for FileEx operations + - `Format.cs`: Byte size formatting utilities (binary and decimal) + - `LogOptions.cs`: Global logging configuration + - `StringCompression.cs`: String compression/decompression using Deflate (sync and async) + - `StringHistory.cs`: Bounded string history buffer + +- **Sandbox/**: Console application for testing and experimentation +- **UtilitiesTests/**: Unit tests using xUnit + +## Testing + +- Use xUnit for all tests +- Follow AAA pattern (Arrange, Act, Assert) +- Test file names should match the class being tested with "Tests" suffix +- Run tests frequently during development +- Maintain good test coverage for public APIs +- **Add tests for all new async methods** +- Consider edge cases and error conditions in tests + +## Dependencies + +- **Serilog**: Logging framework (required for LogOptions and error handling) +- **Microsoft.SourceLink.GitHub**: Source linking for debugging +- **xUnit**: Testing framework +- Keep dependencies minimal and well-justified +- Update package references to latest stable versions when appropriate + +## Common Tasks + +### Building + +Run the ".NET Build" task or use: `dotnet build` + +### Publishing + +Run the ".NET Publish" task or use: `dotnet publish` + +### Formatting (REQUIRED before commit) + +```bash +# Step 1: Format with CSharpier +dotnet csharpier . + +# Step 2: Apply dotnet format rules +dotnet format + +# Step 3: Verify (this is what pre-commit hook checks) +dotnet format --verify-no-changes +``` + +### Running Tests + +Use: `dotnet test` + +## Commit Guidelines + +### Pre-Commit Process (Automated by Husky.Net) + +The following happens automatically on every commit: + +1. ✅ CSharpier formats all C# files +2. ✅ dotnet format applies .editorconfig rules +3. ✅ Commit proceeds if formatting passes +4. ❌ Commit is rejected if formatting fails + +### Manual Pre-Commit Checklist + +Before committing, ensure: + +- [ ] Code formatted with CSharpier (`dotnet csharpier .`) +- [ ] Style rules applied (`dotnet format`) +- [ ] No formatting issues (`dotnet format --verify-no-changes`) +- [ ] All tests passing (`dotnet test`) +- [ ] Build successful (`dotnet build`) +- [ ] No `var` keywords used +- [ ] XML documentation complete +- [ ] Commit message is clear and descriptive + +### Commit Message Format + +Follow conventional commit format: + +```text +(): + +[optional body] + +[optional footer] +``` + +Examples: + +- `feat(download): add async download methods` +- `fix(fileex): correct boundary condition in DeleteDirectory` +- `docs(readme): update async method examples` +- `test(compression): add async compression tests` + +## Package Information + +- **Package ID**: InsaneGenius.Utilities +- **Namespace**: InsaneGenius.Utilities +- **License**: MIT +- **Repository**: +- **Target Framework**: .NET 10 +- **C# Version**: 14.0 +- **Version**: 3.5 (managed by Nerdbank.GitVersioning) + +## When Adding New Features + +1. **Consider AOT compatibility from the start** +2. **Add comprehensive XML documentation** (required for all public APIs) +3. **Create corresponding unit tests** (including async versions) +4. Update README.md if adding significant functionality +5. Ensure backward compatibility when modifying existing APIs +6. Consider performance implications +7. **Use async/await for I/O-bound operations** with proper cancellation token support +8. Handle exceptions appropriately with logging via LogOptions.Logger +9. **Follow existing patterns** (e.g., retry logic, bool return values, exception handling) +10. **Format with CSharpier before running dotnet format** + +## Code Generation Preferences + +### Modern C# Features (C# 14) + +- Prefer modern C# language features (pattern matching, records, file-scoped namespaces, etc.) +- Use nullable reference types consistently with `ArgumentNullException.ThrowIfNull()` +- Leverage expression-bodied members where appropriate +- Use collection expressions `[]` for initialization +- Use `extension` keyword for extension methods (inside static class) +- Prefer LINQ for data transformations +- Use primary constructors where appropriate + +### Async/Await Patterns + +- **Always use `ConfigureAwait(false)` in library code** +- Provide async versions of I/O-bound methods +- Use `CancellationToken` parameters (default to `default`) +- Use `await using` for async disposal +- Replace blocking calls (`.GetAwaiter().GetResult()`) with proper async +- Use `Task.Delay()` instead of `Thread.Sleep()` in async methods +- Use `Memory` and `Span` for async I/O operations + +### Input Validation + +- Use `ArgumentNullException.ThrowIfNull()` for null checks +- Validate parameters early in methods +- Document all exceptions in XML comments + +### Resource Management + +- Use `using` statements for proper disposal +- Use `await using` for async disposal +- Avoid explicit `.Close()` calls (using handles it) +- Use `leaveOpen` parameter when appropriate + +### Thread Safety + +- Use `Lazy` for thread-safe initialization +- Use `Lock` (C# 13+) instead of `object` for locks +- Avoid static mutable state +- Document thread-safety guarantees + +## Security Considerations + +- Validate all user inputs +- Use secure defaults +- Avoid hardcoding sensitive information +- Follow principle of least privilege +- Use secure random number generation when needed +- Be careful with file path manipulation + +## Performance Guidelines + +- Profile before optimizing +- Be mindful of allocations +- Use `Span` and `Memory` for performance-critical code +- Consider using object pooling for frequently allocated objects +- Use `ValueTask` for async methods that may complete synchronously +- Leverage AOT benefits for startup time and memory usage +- Avoid unnecessary string allocations +- Use `StringBuilder` for string concatenation in loops + +## Common Patterns in This Project + +### Error Handling + +```csharp +try +{ + // Operation +} +catch (IOException e) when (LogOptions.Logger.LogAndHandle(e)) +{ + // Retry or return false +} +catch (Exception e) when (LogOptions.Logger.LogAndHandle(e)) +{ + return false; +} +``` + +### Retry Logic + +- Use `Options.RetryCount` for retry attempts +- Use `Options.Cancel.IsCancellationRequested` for cancellation +- Use `Task.Delay()` for async waits +- Log retry attempts with `LogOptions.Logger.Information()` + +### Method Signatures + +- I/O methods return `bool` for success/failure +- Async methods have `Async` suffix +- Async methods include optional `CancellationToken cancellationToken = default` +- Use `out` parameters for additional return values + +### XML Documentation + +- Always include `` for all public members +- Document all `` with descriptions +- Document `` with descriptions +- Document all possible `` types +- Use `` for additional context +- Reference other types with `` + +## Anti-Patterns to Avoid + +❌ Sync-over-async: `.GetAwaiter().GetResult()`, `.Wait()`, `.Result` +❌ Missing `ConfigureAwait(false)` in library code +❌ Missing XML documentation on public APIs +❌ Not using `ArgumentNullException.ThrowIfNull()` +❌ Explicit `.Close()` calls when using `using` +❌ Missing cancellation token support in async methods +❌ Race conditions in static initialization +❌ Reflection (not AOT-compatible) +❌ Missing tests for async methods +❌ Using `var` keyword (explicit types required by .editorconfig) +❌ Forgetting to run CSharpier before dotnet format + +## File-Specific Notes + +### Download.cs + +- Uses thread-safe `Lazy` initialization +- Provides both sync and async versions +- Returns tuples from async methods for multiple values +- Uses `HttpCompletionOption.ResponseHeadersRead` for efficiency + +### FileEx.cs + +- All I/O methods have async versions +- Uses `Options` for retry configuration +- Returns `bool` for success/failure +- Supports cancellation via `Options.Cancel` and method parameter + +### StringCompression.cs + +- Supports configurable compression levels +- Has both sync and async versions +- Uses `leaveOpen` for stream management +- Proper error documentation + +### Extensions.cs + +- Uses C# 14 `extension` keyword +- Must be inside static class +- Provides extension methods for string compression and logger error handling + +## Development Workflow + +### Making Changes + +1. Edit code +2. **Run CSharpier**: `dotnet csharpier .` +3. **Run dotnet format**: `dotnet format` +4. Build: `dotnet build` +5. Test: `dotnet test` +6. Commit (Husky.Net pre-commit hook will verify formatting) + +### Before Committing + +```bash +# Format code (REQUIRED ORDER) +dotnet csharpier . +dotnet format + +# Verify +dotnet format --verify-no-changes +dotnet build +dotnet test + +# Commit +git add . +git commit -m "feat: your message" +# Husky.Net hook runs automatically +``` + +### If Pre-Commit Hook Fails + +```bash +# Hook will show formatting errors +# Re-run formatters +dotnet csharpier . +dotnet format + +# Try commit again +git commit -m "feat: your message" +``` + +## Tools Required + +- .NET 10 SDK +- CSharpier (installed as dotnet tool) +- Husky.Net (installed as dotnet tool) +- Visual Studio 2022 or VS Code with C# extension + +## EditorConfig Integration + +The project uses `.editorconfig` for style enforcement. Key rules: + +- `csharp_style_var_*` = **false** (no var keyword) +- `csharp_style_namespace_declarations` = **file_scoped** +- `csharp_prefer_system_threading_lock` = **true** +- `dotnet_style_prefer_collection_expression` = **when_types_loosely_match** + +Visual Studio and Rider automatically apply these settings. VS Code requires the EditorConfig extension. -### Publishing +## GitHub Copilot Review Runbook -Run the ".NET Publish" task or use: `dotnet publish` +Use this section for provider-specific mechanics. The expected review loop *contract* (request review on every push, verify head-SHA coverage, triage findings, reply + resolve, escalate when stuck) is defined in [AGENTS.md -> PR Review Etiquette](../AGENTS.md#pr-review-etiquette). This section only describes how to make GitHub Copilot reliably execute it. -### Formatting (REQUIRED before commit) +### Triggering and Polling -```bash -# Step 1: Format with CSharpier -dotnet csharpier . +Auto-review on push is configured (via the branch ruleset's `copilot_code_review` rule with `review_on_push: true`) but fires inconsistently in practice - treat it as best-effort, not guaranteed. After every push, **re-request a review programmatically** via the GraphQL `requestReviews` mutation, passing the Copilot reviewer's bot node id in `botIds`. This now works reliably (it previously did not - a maintainer had to click "re-request review" in the UI; the agent can now drive the loop end-to-end without that hand-off). -# Step 2: Apply dotnet format rules -dotnet format +> **The reviewer login differs by API - this is intentional, not a typo.** In **GraphQL** (`gh api graphql` and `gh pr view --json reviews`, which is GraphQL-backed) the `Bot.login` is `copilot-pull-request-reviewer` - **no `[bot]` suffix**. In the **REST** API (`gh api repos/.../issues|pulls/...`) the same account's `user.login` is `copilot-pull-request-reviewer[bot]` - **with** the suffix. Each query below uses the correct form for its API; match the API, not a single spelling, when adapting them. -# Step 3: Verify (this is what pre-commit hook checks) -dotnet format --verify-no-changes +```sh +# 1. PR node id + the Copilot reviewer's bot node id (read from any existing +# Copilot review; the reviewer login is `copilot-pull-request-reviewer`). +PR_NODE=$(gh pr view --json id --jq '.id') +BOT_ID=$(gh api graphql -f query=' +{ + repository(owner: "ptr727", name: "Utilities") { + pullRequest(number: ) { + reviews(first: 50) { nodes { author { __typename login ... on Bot { id } } } } + } + } +}' --jq '[.data.repository.pullRequest.reviews.nodes[] + | select(.author.login == "copilot-pull-request-reviewer") + | .author.id] | first') + +# 2. Re-request a Copilot review on the current head. +gh api graphql -f query=' +mutation($pr: ID!, $bot: ID!) { + requestReviews(input: { pullRequestId: $pr, botIds: [$bot], union: true }) { + pullRequest { id } + } +}' -F pr="$PR_NODE" -F bot="$BOT_ID" ``` -### Running Tests - -Use: `dotnet test` - -## Commit Guidelines - -### Pre-Commit Process (Automated by Husky.Net) - -The following happens automatically on every commit: +The bot node id is read from an existing Copilot review, so step 1 needs at least one prior review on the PR - the auto-review-on-open normally supplies the first one. If no Copilot review exists yet and auto-review didn't fire, request `Copilot` once through the GitHub PR UI to seed it, then use the mutation for every subsequent re-request. -1. ✅ CSharpier formats all C# files -2. ✅ dotnet format applies .editorconfig rules -3. ✅ Commit proceeds if formatting passes -4. ❌ Commit is rejected if formatting fails +**Do NOT post `@Copilot review` as a PR comment.** That comment triggers the Copilot *coding agent* (`copilot-swe-agent[bot]`), which makes code changes rather than posting a review. -### Manual Pre-Commit Checklist +Known non-working request paths (don't rely on them - use the `requestReviews` mutation above instead): -Before committing, ensure: +- `POST /requested_reviewers` with `reviewers=[Copilot]` can return 200 but no-op. +- `copilot-pull-request-reviewer` as a requested reviewer slug returns 422. -- [ ] Code formatted with CSharpier (`dotnet csharpier .`) -- [ ] Style rules applied (`dotnet format`) -- [ ] No formatting issues (`dotnet format --verify-no-changes`) -- [ ] All tests passing (`dotnet test`) -- [ ] Build successful (`dotnet build`) -- [ ] No `var` keywords used -- [ ] XML documentation complete -- [ ] Commit message is clear and descriptive +### Verify Review Covered Current Head -### Commit Message Format +Before merging, confirm Copilot reviewed the current PR head SHA. Copilot may respond as either a formal review (carries an exact commit SHA) or an issue comment (no SHA - use the most recent Copilot comment for manual confirmation). Check both. -Follow conventional commit format: +```sh +PR_HEAD=$(gh pr view --json headRefOid --jq '.headRefOid') -```text -(): +# 1. Formal review - exact SHA match. +gh pr view --json reviews --jq \ + '.reviews[] | select(.author.login=="copilot-pull-request-reviewer") | .commit.oid' \ + | grep -q "$PR_HEAD" && echo "covered via formal review" -[optional body] - -[optional footer] +# 2. Issue comment - show the most recent Copilot comment for manual +# confirmation. This is the REST API, so the login carries the `[bot]` suffix. +gh api repos/ptr727/Utilities/issues//comments --jq \ + '[.[] | select(.user.login=="copilot-pull-request-reviewer[bot]")] | last | {created_at, body: .body[:200]}' ``` -Examples: - -- `feat(download): add async download methods` -- `fix(fileex): correct boundary condition in DeleteDirectory` -- `docs(readme): update async method examples` -- `test(compression): add async compression tests` - -## Package Information - -- **Package ID**: InsaneGenius.Utilities -- **Namespace**: InsaneGenius.Utilities -- **License**: MIT -- **Repository**: -- **Target Framework**: .NET 10 -- **C# Version**: 14.0 -- **Version**: 3.5 (managed by Nerdbank.GitVersioning) - -## When Adding New Features +Coverage is confirmed when (1) exits 0. For issue comments (path 2), body content is the only reliable signal - `created_at` is not: `git log -1 --format=%cI` is the **commit** timestamp, not the push timestamp, so amended or rebased commits can have an earlier timestamp and an older Copilot comment could satisfy a time check even though Copilot never saw the current head. Treat path (2) as confirmed only when the comment body explicitly refers to the current changes. -1. **Consider AOT compatibility from the start** -2. **Add comprehensive XML documentation** (required for all public APIs) -3. **Create corresponding unit tests** (including async versions) -4. Update README.md if adding significant functionality -5. Ensure backward compatibility when modifying existing APIs -6. Consider performance implications -7. **Use async/await for I/O-bound operations** with proper cancellation token support -8. Handle exceptions appropriately with logging via LogOptions.Logger -9. **Follow existing patterns** (e.g., retry logic, bool return values, exception handling) -10. **Format with CSharpier before running dotnet format** +### Bounded Retry Workflow -## Code Generation Preferences +If a review did not run on the current head, retry: -### Modern C# Features (C# 14) +1. Wait briefly and check head-SHA coverage (see above). +1. Re-request the review via the `requestReviews` mutation (see "Triggering and Polling"); fall back to the GitHub PR UI only if the mutation no-ops. +1. Retry up to two more times (three total). +1. If still missing, mark review as blocked and escalate to the user/maintainer with what was attempted. -- Prefer modern C# language features (pattern matching, records, file-scoped namespaces, etc.) -- Use nullable reference types consistently with `ArgumentNullException.ThrowIfNull()` -- Leverage expression-bodied members where appropriate -- Use collection expressions `[]` for initialization -- Use `extension` keyword for extension methods (inside static class) -- Prefer LINQ for data transformations -- Use primary constructors where appropriate +### Reply and Thread Resolution Workflow -### Async/Await Patterns +List unresolved threads. Use `first: 100` with cursor-based pagination; if `hasNextPage` is true, re-run with `after: ""` to retrieve the next page: -- **Always use `ConfigureAwait(false)` in library code** -- Provide async versions of I/O-bound methods -- Use `CancellationToken` parameters (default to `default`) -- Use `await using` for async disposal -- Replace blocking calls (`.GetAwaiter().GetResult()`) with proper async -- Use `Task.Delay()` instead of `Thread.Sleep()` in async methods -- Use `Memory` and `Span` for async I/O operations - -### Input Validation - -- Use `ArgumentNullException.ThrowIfNull()` for null checks -- Validate parameters early in methods -- Document all exceptions in XML comments - -### Resource Management - -- Use `using` statements for proper disposal -- Use `await using` for async disposal -- Avoid explicit `.Close()` calls (using handles it) -- Use `leaveOpen` parameter when appropriate - -### Thread Safety - -- Use `Lazy` for thread-safe initialization -- Use `Lock` (C# 13+) instead of `object` for locks -- Avoid static mutable state -- Document thread-safety guarantees - -## Security Considerations - -- Validate all user inputs -- Use secure defaults -- Avoid hardcoding sensitive information -- Follow principle of least privilege -- Use secure random number generation when needed -- Be careful with file path manipulation - -## Performance Guidelines - -- Profile before optimizing -- Be mindful of allocations -- Use `Span` and `Memory` for performance-critical code -- Consider using object pooling for frequently allocated objects -- Use `ValueTask` for async methods that may complete synchronously -- Leverage AOT benefits for startup time and memory usage -- Avoid unnecessary string allocations -- Use `StringBuilder` for string concatenation in loops - -## Common Patterns in This Project - -### Error Handling - -```csharp -try -{ - // Operation -} -catch (IOException e) when (LogOptions.Logger.LogAndHandle(e)) +```sh +gh api graphql -f query=' { - // Retry or return false -} -catch (Exception e) when (LogOptions.Logger.LogAndHandle(e)) -{ - return false; -} + repository(owner: "ptr727", name: "Utilities") { + pullRequest(number: ) { + reviewThreads(first: 100) { + nodes { + id isResolved path + comments(first: 1) { nodes { author { login } body } } + } + pageInfo { hasNextPage endCursor } + } + } + } +}' | jq ' + .data.repository.pullRequest.reviewThreads | + (.pageInfo | "hasNextPage=\(.hasNextPage) endCursor=\(.endCursor)"), + (.nodes[] | select(.isResolved == false)) +' ``` -### Retry Logic - -- Use `Options.RetryCount` for retry attempts -- Use `Options.Cancel.IsCancellationRequested` for cancellation -- Use `Task.Delay()` for async waits -- Log retry attempts with `LogOptions.Logger.Information()` - -### Method Signatures - -- I/O methods return `bool` for success/failure -- Async methods have `Async` suffix -- Async methods include optional `CancellationToken cancellationToken = default` -- Use `out` parameters for additional return values - -### XML Documentation - -- Always include `` for all public members -- Document all `` with descriptions -- Document `` with descriptions -- Document all possible `` types -- Use `` for additional context -- Reference other types with `` - -## Anti-Patterns to Avoid - -❌ Sync-over-async: `.GetAwaiter().GetResult()`, `.Wait()`, `.Result` -❌ Missing `ConfigureAwait(false)` in library code -❌ Missing XML documentation on public APIs -❌ Not using `ArgumentNullException.ThrowIfNull()` -❌ Explicit `.Close()` calls when using `using` -❌ Missing cancellation token support in async methods -❌ Race conditions in static initialization -❌ Reflection (not AOT-compatible) -❌ Missing tests for async methods -❌ Using `var` keyword (explicit types required by .editorconfig) -❌ Forgetting to run CSharpier before dotnet format +Reply on a thread, then resolve it: -## File-Specific Notes +```sh +gh api graphql -f query=' +mutation($threadId: ID!, $body: String!) { + addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $threadId, body: $body }) { + comment { id } + } +}' -F threadId="PRRT_..." -F body="Fixed in : ." -### Download.cs - -- Uses thread-safe `Lazy` initialization -- Provides both sync and async versions -- Returns tuples from async methods for multiple values -- Uses `HttpCompletionOption.ResponseHeadersRead` for efficiency - -### FileEx.cs - -- All I/O methods have async versions -- Uses `Options` for retry configuration -- Returns `bool` for success/failure -- Supports cancellation via `Options.Cancel` and method parameter - -### StringCompression.cs - -- Supports configurable compression levels -- Has both sync and async versions -- Uses `leaveOpen` for stream management -- Proper error documentation - -### Extensions.cs - -- Uses C# 14 `extension` keyword -- Must be inside static class -- Provides extension methods for string compression and logger error handling - -## Development Workflow - -### Making Changes - -1. Edit code -2. **Run CSharpier**: `dotnet csharpier .` -3. **Run dotnet format**: `dotnet format` -4. Build: `dotnet build` -5. Test: `dotnet test` -6. Commit (Husky.Net pre-commit hook will verify formatting) - -### Before Committing - -```bash -# Format code (REQUIRED ORDER) -dotnet csharpier . -dotnet format - -# Verify -dotnet format --verify-no-changes -dotnet build -dotnet test - -# Commit -git add . -git commit -m "feat: your message" -# Husky.Net hook runs automatically -``` - -### If Pre-Commit Hook Fails - -```bash -# Hook will show formatting errors -# Re-run formatters -dotnet csharpier . -dotnet format - -# Try commit again -git commit -m "feat: your message" +gh api graphql -f query=' +mutation($threadId: ID!) { + resolveReviewThread(input: { threadId: $threadId }) { thread { id isResolved } } +}' -F threadId="PRRT_..." ``` -## Tools Required - -- .NET 10 SDK -- CSharpier (installed as dotnet tool) -- Husky.Net (installed as dotnet tool) -- Visual Studio 2022 or VS Code with C# extension +Issue-level Copilot comments (those in `issues//comments`) have no resolution action - GitHub provides no API or UI to resolve them. Reply if the finding warrants it; no resolution step is needed or possible. -## EditorConfig Integration +Reply-body conventions: -The project uses `.editorconfig` for style enforcement. Key rules: +- Accepted bug/style fix: include fixing commit SHA and a one-line summary. +- Declined style comment: cite the rule (AGENTS.md or language CODESTYLE) and the existing-tree precedent. +- Declined architecture proposal: one-sentence rationale. -- `csharp_style_var_*` = **false** (no var keyword) -- `csharp_style_namespace_declarations` = **file_scoped** -- `csharp_prefer_system_threading_lock` = **true** -- `dotnet_style_prefer_collection_expression` = **when_types_loosely_match** - -Visual Studio and Rider automatically apply these settings. VS Code requires the EditorConfig extension. +After the final push, sweep-resolve stale older threads for removed code paths. diff --git a/AGENTS.md b/AGENTS.md index 489e7474..67f5b27a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -54,6 +54,50 @@ The repo uses a **two-phase model by default**: PRs build fast, publishing is ba - Don't add `Co-Authored-By:` lines unless the developer explicitly asks. - Use US English spelling. +## PR Review Etiquette + +The repo runs a review loop on every PR: local agent iteration plus remote automated review (GitHub Copilot is the configured reviewer). Treat this as a contract regardless of which local agent authored the changes. + +### Expected Review Loop + +1. Push changes to the PR branch. +2. Re-request a review for the **current head SHA**. Auto-trigger is unreliable, so request it explicitly via the `requestReviews` GraphQL mutation (now reliable end-to-end - see the runbook); the UI is only a fallback. +3. Wait for review activity on that head. +4. Triage findings. +5. Apply fixes or write a rationale for declines. +6. Reply to each thread and resolve what was addressed. +7. Re-run the loop after every fix push until no actionable findings remain. + +`mergeStateStatus: CLEAN` only checks required statuses; it does not block on bot review comments. Drive the loop to green - review confirmed on the latest head SHA and every actionable finding closed - and then **wait for the maintainer's explicit permission to merge**. The agent does not merge on its own (consistent with "default to staging"; merging is maintainer-authorized). + +For provider-specific mechanics (how to request review, query review state, post replies, resolve threads), see the **GitHub Copilot Review Runbook** in [.github/copilot-instructions.md](./.github/copilot-instructions.md). This file owns the contract; that file owns the mechanics. + +### Triaging Review Comments + +For each comment, classify before responding: + +- **Bug** - wrong behavior, missing test coverage, or a real divergence between code and docs. Fix it. Reply with the fixing commit SHA when done. +- **Style/convention** - the comment cites a rule from this file or a language-specific style guide. Two cases: + - The cited rule matches what the existing codebase already does -> fix the offending code. + - The cited rule contradicts what's in the tree, or industry norm -> **update the rule instead of the code**. The rule is wrong, not the code. Bouncing the same code across rounds is the symptom of a wrong rule. Heuristic: three rounds on the same style category means the rule needs adjusting and the user should authorize the rule change. +- **Architectural opinion** - the comment proposes a different design ("constrain this to disabled-by-default", "move it elsewhere", "add a runtime guardrail"). This is judgement, not a bug. Surface it to the user with a recommendation; don't apply unilaterally. + +### Responding and Resolution Expectations + +Reply inline with either the fixing commit SHA (for accepted issues) or a concise rationale (for declines). Resolve review threads when addressed or intentionally declined with rationale. Issue-level comments (those at `repos/.../issues//comments` rather than tied to a specific line) have no resolution action - acknowledge with a reply if needed and move on. + +After the final push on a PR, sweep older threads from earlier rounds whose code paths no longer exist; otherwise stale unresolved markers remain in the review UI. + +### Escalating to the User + +Bring the user in when: + +- **Genuine design trade-off** surfaces (fail-open vs fail-closed, narrow vs broad refactor scope, "should we add a guardrail or trust the docstring"). Triage, recommend, ask. +- **Repeated friction** across rounds without convergence - that's the rule-needs-updating signal. Stop, summarize the pattern, and let the user authorize the rule change. +- **Architectural redesign** is requested rather than a bug fix. Surface with a recommendation; never apply unilaterally. + +Anti-pattern: don't keep flipping the code on the same style point. Flip the rule once and stick to the rule. + ## Maintainer Setup (GitHub) - **Secrets**: `NUGET_API_KEY` (NuGet.org push); `CODEGEN_APP_CLIENT_ID` + `CODEGEN_APP_PRIVATE_KEY` for the merge-bot's GitHub App token — add these to **both** the Actions and Dependabot secret stores. From 9f1bca9c400809c0e50b4ca7227e4506e65924c4 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Mon, 8 Jun 2026 09:26:16 -0700 Subject: [PATCH 3/4] Use US English 'judgment'; drop whitespace-only line in code example --- .github/copilot-instructions.md | 2 +- AGENTS.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 548c7f12..6dc2efa3 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -43,7 +43,7 @@ dotnet format --verify-no-changes // ✅ CORRECT string text = "hello"; List numbers = []; - + // ❌ WRONG var text = "hello"; var numbers = new List(); diff --git a/AGENTS.md b/AGENTS.md index 67f5b27a..c7101cb3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -80,7 +80,7 @@ For each comment, classify before responding: - **Style/convention** - the comment cites a rule from this file or a language-specific style guide. Two cases: - The cited rule matches what the existing codebase already does -> fix the offending code. - The cited rule contradicts what's in the tree, or industry norm -> **update the rule instead of the code**. The rule is wrong, not the code. Bouncing the same code across rounds is the symptom of a wrong rule. Heuristic: three rounds on the same style category means the rule needs adjusting and the user should authorize the rule change. -- **Architectural opinion** - the comment proposes a different design ("constrain this to disabled-by-default", "move it elsewhere", "add a runtime guardrail"). This is judgement, not a bug. Surface it to the user with a recommendation; don't apply unilaterally. +- **Architectural opinion** - the comment proposes a different design ("constrain this to disabled-by-default", "move it elsewhere", "add a runtime guardrail"). This is judgment, not a bug. Surface it to the user with a recommendation; don't apply unilaterally. ### Responding and Resolution Expectations From 894e8fa4242a31e4a961d0c2dfc32a5193593fe8 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Mon, 8 Jun 2026 09:26:16 -0700 Subject: [PATCH 4/4] Normalize AGENTS.md and copilot-instructions.md to CRLF per .editorconfig These files had pre-existing mixed CRLF/LF line endings; .editorconfig mandates CRLF for .md. Normalize both fully to CRLF so the carried template sections and the existing content share consistent, compliant endings. --- .github/copilot-instructions.md | 818 ++++++++++++++++---------------- 1 file changed, 409 insertions(+), 409 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 6dc2efa3..5f39aaf7 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,412 +1,412 @@ -# GitHub Copilot Instructions for Utilities Project - -## Project Overview - -This is a .NET utility library that provides generally useful C# classes and extensions. The project targets .NET 10 and includes AOT (Ahead-of-Time) compilation support for optimized runtime performance. - -## Code Style and Standards - -### General Guidelines - -- Follow C# coding conventions and .NET best practices -- Use meaningful variable and method names -- Keep methods focused and single-purpose -- **Add comprehensive XML documentation comments for ALL public APIs** (required) -- Maintain consistency with existing code style -- Follow the existing patterns in the codebase - -### Formatting Requirements - -**IMPORTANT:** This project uses **Husky.Net** pre-commit hooks that automatically enforce formatting: - -1. **CSharpier** is run first for code formatting -2. **dotnet format** is run second for style enforcement - -#### Formatting Workflow - -```bash -# Always format with CSharpier FIRST after editing code -dotnet csharpier . - -# Then run dotnet format to apply .editorconfig rules -dotnet format - -# Verify no changes needed -dotnet format --verify-no-changes -``` - -#### Key Formatting Rules (from .editorconfig) - -- **No `var` keyword**: Use explicit types everywhere - - ```csharp - // ✅ CORRECT - string text = "hello"; - List numbers = []; - - // ❌ WRONG - var text = "hello"; - var numbers = new List(); - ``` - -- **Indentation**: 4 spaces (not tabs) -- **Line endings**: CRLF (Windows) -- **Charset**: UTF-8 -- **Final newline**: Required -- **Trailing whitespace**: Not allowed -- **File-scoped namespaces**: Required -- **Collection expressions**: Preferred `[]` over `new List()` - -#### Pre-Commit Hook - -The Husky.Net pre-commit hook automatically runs: - -1. `dotnet csharpier .` - Code formatting -2. `dotnet format` - Style enforcement - -**Commits will be rejected if formatting fails!** - -### .NET 10 and AOT Considerations - -- The project targets .NET 10 with PublishAot enabled -- Avoid reflection where possible (not AOT-friendly) -- Use source generators instead of runtime reflection when applicable -- Be mindful of trim warnings and compatibility -- Ensure all code is AOT-compatible -- Test AOT compatibility with `dotnet publish` - -## Project Structure - -- **Utilities/**: Main library project containing utility classes - - `CommandLineEx.cs`: Command-line argument parsing utilities - - `ConsoleEx.cs`: Console interaction helpers with color support - - `Download.cs`: HTTP download utilities (sync and async) - - `Extensions.cs`: Extension methods (string compression, logger error handling) - - `FileEx.cs`: File and directory operation utilities with retry logic (sync and async) - - `FileExOptions.cs`: Configuration options for FileEx operations - - `Format.cs`: Byte size formatting utilities (binary and decimal) - - `LogOptions.cs`: Global logging configuration - - `StringCompression.cs`: String compression/decompression using Deflate (sync and async) - - `StringHistory.cs`: Bounded string history buffer - -- **Sandbox/**: Console application for testing and experimentation -- **UtilitiesTests/**: Unit tests using xUnit - -## Testing - -- Use xUnit for all tests -- Follow AAA pattern (Arrange, Act, Assert) -- Test file names should match the class being tested with "Tests" suffix -- Run tests frequently during development -- Maintain good test coverage for public APIs -- **Add tests for all new async methods** -- Consider edge cases and error conditions in tests - -## Dependencies - -- **Serilog**: Logging framework (required for LogOptions and error handling) -- **Microsoft.SourceLink.GitHub**: Source linking for debugging -- **xUnit**: Testing framework -- Keep dependencies minimal and well-justified -- Update package references to latest stable versions when appropriate - -## Common Tasks - -### Building - -Run the ".NET Build" task or use: `dotnet build` - -### Publishing - -Run the ".NET Publish" task or use: `dotnet publish` - -### Formatting (REQUIRED before commit) - -```bash -# Step 1: Format with CSharpier -dotnet csharpier . - -# Step 2: Apply dotnet format rules -dotnet format - -# Step 3: Verify (this is what pre-commit hook checks) -dotnet format --verify-no-changes -``` - -### Running Tests - -Use: `dotnet test` - -## Commit Guidelines - -### Pre-Commit Process (Automated by Husky.Net) - -The following happens automatically on every commit: - -1. ✅ CSharpier formats all C# files -2. ✅ dotnet format applies .editorconfig rules -3. ✅ Commit proceeds if formatting passes -4. ❌ Commit is rejected if formatting fails - -### Manual Pre-Commit Checklist - -Before committing, ensure: - -- [ ] Code formatted with CSharpier (`dotnet csharpier .`) -- [ ] Style rules applied (`dotnet format`) -- [ ] No formatting issues (`dotnet format --verify-no-changes`) -- [ ] All tests passing (`dotnet test`) -- [ ] Build successful (`dotnet build`) -- [ ] No `var` keywords used -- [ ] XML documentation complete -- [ ] Commit message is clear and descriptive - -### Commit Message Format - -Follow conventional commit format: - -```text -(): - -[optional body] - -[optional footer] -``` - -Examples: - -- `feat(download): add async download methods` -- `fix(fileex): correct boundary condition in DeleteDirectory` -- `docs(readme): update async method examples` -- `test(compression): add async compression tests` - -## Package Information - -- **Package ID**: InsaneGenius.Utilities -- **Namespace**: InsaneGenius.Utilities -- **License**: MIT -- **Repository**: -- **Target Framework**: .NET 10 -- **C# Version**: 14.0 -- **Version**: 3.5 (managed by Nerdbank.GitVersioning) - -## When Adding New Features - -1. **Consider AOT compatibility from the start** -2. **Add comprehensive XML documentation** (required for all public APIs) -3. **Create corresponding unit tests** (including async versions) -4. Update README.md if adding significant functionality -5. Ensure backward compatibility when modifying existing APIs -6. Consider performance implications -7. **Use async/await for I/O-bound operations** with proper cancellation token support -8. Handle exceptions appropriately with logging via LogOptions.Logger -9. **Follow existing patterns** (e.g., retry logic, bool return values, exception handling) -10. **Format with CSharpier before running dotnet format** - -## Code Generation Preferences - -### Modern C# Features (C# 14) - -- Prefer modern C# language features (pattern matching, records, file-scoped namespaces, etc.) -- Use nullable reference types consistently with `ArgumentNullException.ThrowIfNull()` -- Leverage expression-bodied members where appropriate -- Use collection expressions `[]` for initialization -- Use `extension` keyword for extension methods (inside static class) -- Prefer LINQ for data transformations -- Use primary constructors where appropriate - -### Async/Await Patterns - -- **Always use `ConfigureAwait(false)` in library code** -- Provide async versions of I/O-bound methods -- Use `CancellationToken` parameters (default to `default`) -- Use `await using` for async disposal -- Replace blocking calls (`.GetAwaiter().GetResult()`) with proper async -- Use `Task.Delay()` instead of `Thread.Sleep()` in async methods -- Use `Memory` and `Span` for async I/O operations - -### Input Validation - -- Use `ArgumentNullException.ThrowIfNull()` for null checks -- Validate parameters early in methods -- Document all exceptions in XML comments - -### Resource Management - -- Use `using` statements for proper disposal -- Use `await using` for async disposal -- Avoid explicit `.Close()` calls (using handles it) -- Use `leaveOpen` parameter when appropriate - -### Thread Safety - -- Use `Lazy` for thread-safe initialization -- Use `Lock` (C# 13+) instead of `object` for locks -- Avoid static mutable state -- Document thread-safety guarantees - -## Security Considerations - -- Validate all user inputs -- Use secure defaults -- Avoid hardcoding sensitive information -- Follow principle of least privilege -- Use secure random number generation when needed -- Be careful with file path manipulation - -## Performance Guidelines - -- Profile before optimizing -- Be mindful of allocations -- Use `Span` and `Memory` for performance-critical code -- Consider using object pooling for frequently allocated objects -- Use `ValueTask` for async methods that may complete synchronously -- Leverage AOT benefits for startup time and memory usage -- Avoid unnecessary string allocations -- Use `StringBuilder` for string concatenation in loops - -## Common Patterns in This Project - -### Error Handling - -```csharp -try -{ - // Operation -} -catch (IOException e) when (LogOptions.Logger.LogAndHandle(e)) -{ - // Retry or return false -} -catch (Exception e) when (LogOptions.Logger.LogAndHandle(e)) -{ - return false; -} -``` - -### Retry Logic - -- Use `Options.RetryCount` for retry attempts -- Use `Options.Cancel.IsCancellationRequested` for cancellation -- Use `Task.Delay()` for async waits -- Log retry attempts with `LogOptions.Logger.Information()` - -### Method Signatures - -- I/O methods return `bool` for success/failure -- Async methods have `Async` suffix -- Async methods include optional `CancellationToken cancellationToken = default` -- Use `out` parameters for additional return values - -### XML Documentation - -- Always include `` for all public members -- Document all `` with descriptions -- Document `` with descriptions -- Document all possible `` types -- Use `` for additional context -- Reference other types with `` - -## Anti-Patterns to Avoid - -❌ Sync-over-async: `.GetAwaiter().GetResult()`, `.Wait()`, `.Result` -❌ Missing `ConfigureAwait(false)` in library code -❌ Missing XML documentation on public APIs -❌ Not using `ArgumentNullException.ThrowIfNull()` -❌ Explicit `.Close()` calls when using `using` -❌ Missing cancellation token support in async methods -❌ Race conditions in static initialization -❌ Reflection (not AOT-compatible) -❌ Missing tests for async methods -❌ Using `var` keyword (explicit types required by .editorconfig) -❌ Forgetting to run CSharpier before dotnet format - -## File-Specific Notes - -### Download.cs - -- Uses thread-safe `Lazy` initialization -- Provides both sync and async versions -- Returns tuples from async methods for multiple values -- Uses `HttpCompletionOption.ResponseHeadersRead` for efficiency - -### FileEx.cs - -- All I/O methods have async versions -- Uses `Options` for retry configuration -- Returns `bool` for success/failure -- Supports cancellation via `Options.Cancel` and method parameter - -### StringCompression.cs - -- Supports configurable compression levels -- Has both sync and async versions -- Uses `leaveOpen` for stream management -- Proper error documentation - -### Extensions.cs - -- Uses C# 14 `extension` keyword -- Must be inside static class -- Provides extension methods for string compression and logger error handling - -## Development Workflow - -### Making Changes - -1. Edit code -2. **Run CSharpier**: `dotnet csharpier .` -3. **Run dotnet format**: `dotnet format` -4. Build: `dotnet build` -5. Test: `dotnet test` -6. Commit (Husky.Net pre-commit hook will verify formatting) - -### Before Committing - -```bash -# Format code (REQUIRED ORDER) -dotnet csharpier . -dotnet format - -# Verify -dotnet format --verify-no-changes -dotnet build -dotnet test - -# Commit -git add . -git commit -m "feat: your message" -# Husky.Net hook runs automatically -``` - -### If Pre-Commit Hook Fails - -```bash -# Hook will show formatting errors -# Re-run formatters -dotnet csharpier . -dotnet format - -# Try commit again -git commit -m "feat: your message" -``` - -## Tools Required - -- .NET 10 SDK -- CSharpier (installed as dotnet tool) -- Husky.Net (installed as dotnet tool) -- Visual Studio 2022 or VS Code with C# extension - -## EditorConfig Integration - -The project uses `.editorconfig` for style enforcement. Key rules: - -- `csharp_style_var_*` = **false** (no var keyword) -- `csharp_style_namespace_declarations` = **file_scoped** -- `csharp_prefer_system_threading_lock` = **true** -- `dotnet_style_prefer_collection_expression` = **when_types_loosely_match** - +# GitHub Copilot Instructions for Utilities Project + +## Project Overview + +This is a .NET utility library that provides generally useful C# classes and extensions. The project targets .NET 10 and includes AOT (Ahead-of-Time) compilation support for optimized runtime performance. + +## Code Style and Standards + +### General Guidelines + +- Follow C# coding conventions and .NET best practices +- Use meaningful variable and method names +- Keep methods focused and single-purpose +- **Add comprehensive XML documentation comments for ALL public APIs** (required) +- Maintain consistency with existing code style +- Follow the existing patterns in the codebase + +### Formatting Requirements + +**IMPORTANT:** This project uses **Husky.Net** pre-commit hooks that automatically enforce formatting: + +1. **CSharpier** is run first for code formatting +2. **dotnet format** is run second for style enforcement + +#### Formatting Workflow + +```bash +# Always format with CSharpier FIRST after editing code +dotnet csharpier . + +# Then run dotnet format to apply .editorconfig rules +dotnet format + +# Verify no changes needed +dotnet format --verify-no-changes +``` + +#### Key Formatting Rules (from .editorconfig) + +- **No `var` keyword**: Use explicit types everywhere + + ```csharp + // ✅ CORRECT + string text = "hello"; + List numbers = []; + + // ❌ WRONG + var text = "hello"; + var numbers = new List(); + ``` + +- **Indentation**: 4 spaces (not tabs) +- **Line endings**: CRLF (Windows) +- **Charset**: UTF-8 +- **Final newline**: Required +- **Trailing whitespace**: Not allowed +- **File-scoped namespaces**: Required +- **Collection expressions**: Preferred `[]` over `new List()` + +#### Pre-Commit Hook + +The Husky.Net pre-commit hook automatically runs: + +1. `dotnet csharpier .` - Code formatting +2. `dotnet format` - Style enforcement + +**Commits will be rejected if formatting fails!** + +### .NET 10 and AOT Considerations + +- The project targets .NET 10 with PublishAot enabled +- Avoid reflection where possible (not AOT-friendly) +- Use source generators instead of runtime reflection when applicable +- Be mindful of trim warnings and compatibility +- Ensure all code is AOT-compatible +- Test AOT compatibility with `dotnet publish` + +## Project Structure + +- **Utilities/**: Main library project containing utility classes + - `CommandLineEx.cs`: Command-line argument parsing utilities + - `ConsoleEx.cs`: Console interaction helpers with color support + - `Download.cs`: HTTP download utilities (sync and async) + - `Extensions.cs`: Extension methods (string compression, logger error handling) + - `FileEx.cs`: File and directory operation utilities with retry logic (sync and async) + - `FileExOptions.cs`: Configuration options for FileEx operations + - `Format.cs`: Byte size formatting utilities (binary and decimal) + - `LogOptions.cs`: Global logging configuration + - `StringCompression.cs`: String compression/decompression using Deflate (sync and async) + - `StringHistory.cs`: Bounded string history buffer + +- **Sandbox/**: Console application for testing and experimentation +- **UtilitiesTests/**: Unit tests using xUnit + +## Testing + +- Use xUnit for all tests +- Follow AAA pattern (Arrange, Act, Assert) +- Test file names should match the class being tested with "Tests" suffix +- Run tests frequently during development +- Maintain good test coverage for public APIs +- **Add tests for all new async methods** +- Consider edge cases and error conditions in tests + +## Dependencies + +- **Serilog**: Logging framework (required for LogOptions and error handling) +- **Microsoft.SourceLink.GitHub**: Source linking for debugging +- **xUnit**: Testing framework +- Keep dependencies minimal and well-justified +- Update package references to latest stable versions when appropriate + +## Common Tasks + +### Building + +Run the ".NET Build" task or use: `dotnet build` + +### Publishing + +Run the ".NET Publish" task or use: `dotnet publish` + +### Formatting (REQUIRED before commit) + +```bash +# Step 1: Format with CSharpier +dotnet csharpier . + +# Step 2: Apply dotnet format rules +dotnet format + +# Step 3: Verify (this is what pre-commit hook checks) +dotnet format --verify-no-changes +``` + +### Running Tests + +Use: `dotnet test` + +## Commit Guidelines + +### Pre-Commit Process (Automated by Husky.Net) + +The following happens automatically on every commit: + +1. ✅ CSharpier formats all C# files +2. ✅ dotnet format applies .editorconfig rules +3. ✅ Commit proceeds if formatting passes +4. ❌ Commit is rejected if formatting fails + +### Manual Pre-Commit Checklist + +Before committing, ensure: + +- [ ] Code formatted with CSharpier (`dotnet csharpier .`) +- [ ] Style rules applied (`dotnet format`) +- [ ] No formatting issues (`dotnet format --verify-no-changes`) +- [ ] All tests passing (`dotnet test`) +- [ ] Build successful (`dotnet build`) +- [ ] No `var` keywords used +- [ ] XML documentation complete +- [ ] Commit message is clear and descriptive + +### Commit Message Format + +Follow conventional commit format: + +```text +(): + +[optional body] + +[optional footer] +``` + +Examples: + +- `feat(download): add async download methods` +- `fix(fileex): correct boundary condition in DeleteDirectory` +- `docs(readme): update async method examples` +- `test(compression): add async compression tests` + +## Package Information + +- **Package ID**: InsaneGenius.Utilities +- **Namespace**: InsaneGenius.Utilities +- **License**: MIT +- **Repository**: +- **Target Framework**: .NET 10 +- **C# Version**: 14.0 +- **Version**: 3.5 (managed by Nerdbank.GitVersioning) + +## When Adding New Features + +1. **Consider AOT compatibility from the start** +2. **Add comprehensive XML documentation** (required for all public APIs) +3. **Create corresponding unit tests** (including async versions) +4. Update README.md if adding significant functionality +5. Ensure backward compatibility when modifying existing APIs +6. Consider performance implications +7. **Use async/await for I/O-bound operations** with proper cancellation token support +8. Handle exceptions appropriately with logging via LogOptions.Logger +9. **Follow existing patterns** (e.g., retry logic, bool return values, exception handling) +10. **Format with CSharpier before running dotnet format** + +## Code Generation Preferences + +### Modern C# Features (C# 14) + +- Prefer modern C# language features (pattern matching, records, file-scoped namespaces, etc.) +- Use nullable reference types consistently with `ArgumentNullException.ThrowIfNull()` +- Leverage expression-bodied members where appropriate +- Use collection expressions `[]` for initialization +- Use `extension` keyword for extension methods (inside static class) +- Prefer LINQ for data transformations +- Use primary constructors where appropriate + +### Async/Await Patterns + +- **Always use `ConfigureAwait(false)` in library code** +- Provide async versions of I/O-bound methods +- Use `CancellationToken` parameters (default to `default`) +- Use `await using` for async disposal +- Replace blocking calls (`.GetAwaiter().GetResult()`) with proper async +- Use `Task.Delay()` instead of `Thread.Sleep()` in async methods +- Use `Memory` and `Span` for async I/O operations + +### Input Validation + +- Use `ArgumentNullException.ThrowIfNull()` for null checks +- Validate parameters early in methods +- Document all exceptions in XML comments + +### Resource Management + +- Use `using` statements for proper disposal +- Use `await using` for async disposal +- Avoid explicit `.Close()` calls (using handles it) +- Use `leaveOpen` parameter when appropriate + +### Thread Safety + +- Use `Lazy` for thread-safe initialization +- Use `Lock` (C# 13+) instead of `object` for locks +- Avoid static mutable state +- Document thread-safety guarantees + +## Security Considerations + +- Validate all user inputs +- Use secure defaults +- Avoid hardcoding sensitive information +- Follow principle of least privilege +- Use secure random number generation when needed +- Be careful with file path manipulation + +## Performance Guidelines + +- Profile before optimizing +- Be mindful of allocations +- Use `Span` and `Memory` for performance-critical code +- Consider using object pooling for frequently allocated objects +- Use `ValueTask` for async methods that may complete synchronously +- Leverage AOT benefits for startup time and memory usage +- Avoid unnecessary string allocations +- Use `StringBuilder` for string concatenation in loops + +## Common Patterns in This Project + +### Error Handling + +```csharp +try +{ + // Operation +} +catch (IOException e) when (LogOptions.Logger.LogAndHandle(e)) +{ + // Retry or return false +} +catch (Exception e) when (LogOptions.Logger.LogAndHandle(e)) +{ + return false; +} +``` + +### Retry Logic + +- Use `Options.RetryCount` for retry attempts +- Use `Options.Cancel.IsCancellationRequested` for cancellation +- Use `Task.Delay()` for async waits +- Log retry attempts with `LogOptions.Logger.Information()` + +### Method Signatures + +- I/O methods return `bool` for success/failure +- Async methods have `Async` suffix +- Async methods include optional `CancellationToken cancellationToken = default` +- Use `out` parameters for additional return values + +### XML Documentation + +- Always include `` for all public members +- Document all `` with descriptions +- Document `` with descriptions +- Document all possible `` types +- Use `` for additional context +- Reference other types with `` + +## Anti-Patterns to Avoid + +❌ Sync-over-async: `.GetAwaiter().GetResult()`, `.Wait()`, `.Result` +❌ Missing `ConfigureAwait(false)` in library code +❌ Missing XML documentation on public APIs +❌ Not using `ArgumentNullException.ThrowIfNull()` +❌ Explicit `.Close()` calls when using `using` +❌ Missing cancellation token support in async methods +❌ Race conditions in static initialization +❌ Reflection (not AOT-compatible) +❌ Missing tests for async methods +❌ Using `var` keyword (explicit types required by .editorconfig) +❌ Forgetting to run CSharpier before dotnet format + +## File-Specific Notes + +### Download.cs + +- Uses thread-safe `Lazy` initialization +- Provides both sync and async versions +- Returns tuples from async methods for multiple values +- Uses `HttpCompletionOption.ResponseHeadersRead` for efficiency + +### FileEx.cs + +- All I/O methods have async versions +- Uses `Options` for retry configuration +- Returns `bool` for success/failure +- Supports cancellation via `Options.Cancel` and method parameter + +### StringCompression.cs + +- Supports configurable compression levels +- Has both sync and async versions +- Uses `leaveOpen` for stream management +- Proper error documentation + +### Extensions.cs + +- Uses C# 14 `extension` keyword +- Must be inside static class +- Provides extension methods for string compression and logger error handling + +## Development Workflow + +### Making Changes + +1. Edit code +2. **Run CSharpier**: `dotnet csharpier .` +3. **Run dotnet format**: `dotnet format` +4. Build: `dotnet build` +5. Test: `dotnet test` +6. Commit (Husky.Net pre-commit hook will verify formatting) + +### Before Committing + +```bash +# Format code (REQUIRED ORDER) +dotnet csharpier . +dotnet format + +# Verify +dotnet format --verify-no-changes +dotnet build +dotnet test + +# Commit +git add . +git commit -m "feat: your message" +# Husky.Net hook runs automatically +``` + +### If Pre-Commit Hook Fails + +```bash +# Hook will show formatting errors +# Re-run formatters +dotnet csharpier . +dotnet format + +# Try commit again +git commit -m "feat: your message" +``` + +## Tools Required + +- .NET 10 SDK +- CSharpier (installed as dotnet tool) +- Husky.Net (installed as dotnet tool) +- Visual Studio 2022 or VS Code with C# extension + +## EditorConfig Integration + +The project uses `.editorconfig` for style enforcement. Key rules: + +- `csharp_style_var_*` = **false** (no var keyword) +- `csharp_style_namespace_declarations` = **file_scoped** +- `csharp_prefer_system_threading_lock` = **true** +- `dotnet_style_prefer_collection_expression` = **when_types_loosely_match** + Visual Studio and Rider automatically apply these settings. VS Code requires the EditorConfig extension. ## GitHub Copilot Review Runbook