From 185484bc2e0a49129bed69103206b76cf92ce6a1 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 30 Sep 2025 00:16:59 +0200 Subject: [PATCH 1/7] =?UTF-8?q?=F0=9F=A9=B9=20[Patch]:=20Add=20Gather.ps1?= =?UTF-8?q?=20script=20for=20path=20resolution=20in=20instruction=20genera?= =?UTF-8?q?tion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/Update-Instructions.prompt.md | 388 ++++++++++++++++++ .github/prompts/framework/scripts/Gather.ps1 | 6 + 2 files changed, 394 insertions(+) create mode 100644 .github/prompts/framework/Update-Instructions.prompt.md create mode 100644 .github/prompts/framework/scripts/Gather.ps1 diff --git a/.github/prompts/framework/Update-Instructions.prompt.md b/.github/prompts/framework/Update-Instructions.prompt.md new file mode 100644 index 000000000..265fcbdc5 --- /dev/null +++ b/.github/prompts/framework/Update-Instructions.prompt.md @@ -0,0 +1,388 @@ +--- +description: Analyze codebases and generate/update Copilot instruction files that guide AI coding agents with specific, actionable code-writing guidance. +--- + +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: Generate and maintain a comprehensive instruction system that provides specific, actionable guidance for writing code. The system separates organization-level patterns (automation-managed) from project-specific implementations (manually curated). + +Execution steps: + +1. **Gather** - Run [Gather.ps1](scripts/Gather.ps1) parse outputs for: + - `$RepositoryPath` - Root repository path + - `$InstructionsPath` - Instructions directory (e.g., `$RepositoryPath/.github/prompts/organization`) + - `$OrganizationInstructionsPath` - Organization instructions directory (e.g., `$InstructionsPath/organization`) + - `$RepositoryInstructionsPath` - Repository instructions directory (e.g., `$InstructionsPath/Repository`) + + If path resolution fails, abort and instruct user to verify script availability. + +2. **Discovery and Analysis** - Perform comprehensive codebase scan: + - Identify all languages and frameworks in use (scan file extensions, imports, dependencies) + - Catalog existing instruction files and their coverage + - Discover code patterns, naming conventions, and architectural decisions from actual code + - Identify testing frameworks, build tools, and deployment patterns + - Extract project-specific patterns from README, existing code, and configuration files + + Build an internal technology inventory map (languages → files → patterns). + +3. **Content Categorization Planning** - For each discovered item, determine placement: + + **Organization Instructions** (automation-managed, `$OrganizationInstructionsPath/`): + - Universal cross-language guidelines (`main.instructions.md`): + * File organization and naming conventions + * Documentation standards and comment patterns + * Organization-wide integration patterns + * Build process and tooling workflows + + - Language-specific patterns (`{Language}/main.instructions.md`): + * Syntax formatting and code style (indentation, braces, line breaks) + * Idiomatic language patterns and best practices + * Error handling patterns with concrete examples + * Testing patterns and assertion styles + * Logging and debugging approaches + * Performance optimization patterns + + **Repository Instructions** (manually curated, `$RepositoryInstructionsPath/`): + - Project-specific context (`main.instructions.md`): + * Repository purpose and scope + * Architecture and component relationships + * Unique workflows and processes + * Project-specific overrides to organization patterns + + - Project-specific language usage (`{Language}/main.instructions.md`): + * How this project uses the language specifically + * Project-specific architectural patterns + * Concrete code examples from this repository + * Integration patterns between components + + **Categorization Rules**: + - If pattern applies across all organization projects → Organization + - If pattern is specific to this repository → Repository + - When uncertain, default to Repository (can promote to Organization later) + - Organization content must be generic enough for automation management + +4. **Generate Organization Instructions** - For each discovered language/technology: + + Create `$OrganizationInstructionsPath/main.instructions.md`: + ```yaml + --- + applyTo: "**/*" + description: "Universal code-writing guidelines for the organization" + --- + ``` + + Content must include: + - **File Organization**: How to structure new files, where to place components + - **Naming Conventions**: Specific patterns for files, functions, variables, constants + - **Documentation**: Required comment patterns, doc-string formats with examples + - **Build Integration**: How code integrates with organization build processes + + Create `$OrganizationInstructionsPath/{Language}/main.instructions.md`: + ```yaml + --- + applyTo: "**/*.{ext}" + description: "Code-writing guidelines for {Language} in organization projects" + --- + ``` + + Content must include specific, actionable rules: + - **Syntax Style**: Exact formatting rules (e.g., "Place opening brace on same line", "Use 4-space indentation") + - **Code Patterns**: Common patterns with before/after examples + - **Error Handling**: Specific error handling patterns (e.g., "Use try-catch for I/O operations", "Always include error context") + - **Testing**: Test structure, assertion patterns, mocking approaches with examples + - **Imports/Dependencies**: How to organize imports, dependency injection patterns + - **Performance**: Specific optimization patterns (e.g., "Use StringBuilder for string concatenation in loops") + + Create specialized files as needed: + - `tests.instructions.md` - Testing-specific patterns + - `classes.instructions.md` - Class design patterns + - Additional domain-specific instruction files + +5. **Generate Repository Instructions** - Create project-specific guidance: + + Create `$RepositoryInstructionsPath/main.instructions.md`: + ```yaml + --- + applyTo: "**/*" + description: "Project-specific code-writing guidance for {RepositoryName}" + --- + ``` + + Content must include: + - **Repository Purpose**: What this project does and why + - **Architecture Overview**: Component structure, data flow, integration points + - **Project-Specific Rules**: Overrides or extensions to organization patterns + - **Workflows**: Development, testing, deployment processes + - **Dependencies**: Key external dependencies and how they're used + + Create `$RepositoryInstructionsPath/{Language}/main.instructions.md`: + ```yaml + --- + applyTo: "{specific-pattern}" + description: "How to write {Language} code in this specific project" + --- + ``` + + Content must include concrete examples from the actual codebase: + - **Project Patterns**: Specific architectural patterns used (e.g., "Use Factory pattern in src/factories/") + - **Code Examples**: Real examples from the codebase showing correct patterns + - **Integration**: How components interact (e.g., "Services depend on repositories, never directly on data layer") + - **Conventions**: Project-specific naming or structure rules + +6. **Migrate Legacy Content** - If legacy instruction files exist: + - Read each legacy file completely + - Categorize content sections (organization vs repository-specific) + - Extract specific, actionable code-writing guidance + - Discard vague guidance like "write good code" - replace with specific rules + - Migrate content to appropriate new instruction files + - Verify no content loss (compare old vs new content coverage) + - DO NOT delete legacy files until migration is verified in step 7 + +7. **Validation** - Verify generated instruction system: + - **Structural Validation**: + * All instruction files have valid YAML frontmatter + * `applyTo` glob patterns are correct and non-overlapping + * File structure matches specification hierarchy + + - **Content Validation**: + * Every discovered language has organization instructions + * Repository instructions provide project-specific context + * Instructions contain specific, actionable code-writing guidance (not vague principles) + * Examples are concrete and relevant to the codebase + * No duplicate or contradictory guidance across files + + - **Coverage Validation**: + * All file types in repository are covered by appropriate instructions + * Testing, error handling, and logging patterns are documented + * Organization-vs-repository categorization is correct + + If validation fails, report specific issues and DO NOT proceed to cleanup. + +8. **Cleanup** - Only after successful validation: + - Remove legacy instruction files that were fully migrated + - Clean up temporary files or outdated patterns + - Report final instruction file structure + +9. **Report Completion** - Provide structured summary: + - List all generated instruction files with paths + - Show coverage: languages discovered vs languages documented + - Highlight any gaps or manual curation needed + - Provide statistics: number of organization files, repository files, legacy files migrated + - Suggest next actions (e.g., "Review repository instructions for accuracy") + +Behavior rules: + +- **Code-Specific Focus**: Instructions must be about *how to write code*, not general principles. Replace "Follow best practices" with "Use StringBuilder for string concatenation inside loops to avoid O(n²) performance". +- **Actionable Guidance**: Every rule must be specific enough to execute. Replace "Handle errors appropriately" with "Wrap all I/O operations in try-catch blocks with context-aware error messages". +- **Concrete Examples**: Include before/after code examples, especially for common patterns. +- **Hierarchical Override**: Repository instructions can override organization instructions - document overrides explicitly. +- **No Vagueness**: Remove generic advice like "write clean code". Specify what "clean" means in measurable terms. +- **Halt on Validation Failure**: Never proceed to cleanup if validation detects issues - report problems first. +- **Preserve Content**: During migration, err on the side of preserving too much rather than losing guidance. +- **Absolute Paths**: All file operations must use absolute paths resolved in step 1. + +Error handling: + +- **Path Resolution Failure**: Abort with instructions to verify `./scripts/Get-Paths.ps1` +- **Content Categorization Ambiguity**: Default to repository-specific, flag for manual review +- **Validation Failures**: Report specific issues with file paths and guidance for fixing +- **Legacy Migration Conflicts**: Preserve both versions in comments, flag for manual resolution +- **Missing Coverage**: Report gaps but complete generation for covered areas + +Output format: + +Provide a structured completion report: +``` +✅ Instruction System Generation Complete + +Organization Instructions Generated: +- $OrganizationInstructionsPath/main.instructions.md +- $OrganizationInstructionsPath/{Language}/main.instructions.md (per language) +- {Additional specialized files} + +Repository Instructions Generated: +- $RepositoryInstructionsPath/main.instructions.md +- $RepositoryInstructionsPath/{Language}/main.instructions.md (per language) + +Coverage Statistics: +- Languages Discovered: {count} ({list}) +- Organization Files: {count} +- Repository Files: {count} +- Legacy Files Migrated: {count} +- Legacy Files Removed: {count} + +⚠️ Manual Review Needed: +- {List any ambiguous categorizations} +- {List any missing coverage areas} +- {List any validation warnings} + +Next Actions: +- Review repository-specific instructions for accuracy +- Test instruction application on sample code generation +- Update any project-specific patterns that emerged during generation +``` + +Context for instruction generation: $ARGUMENTS + +### Template for language oriented instructions + +Here’s the ready-to-use template: + +```markdown +--- +applyTo: "**/*.{ext}" # A single string Glob pattern for applicable files +description: "Code-writing guidelines for {Language} in this organization/project/repository" +--- + +# {Language} Instructions + +## Style & Formatting +- Indentation: {n} spaces +- Max line length: {n} +- Trailing whitespace: disallowed +- Braces/blocks: {rule} +- Imports/using/order: {rule} +- Naming conventions: {rule} (classes, methods, variables, constants) + +**Example:** +```{ext} +// BEFORE: bad formatting +function foo ( ) {return 42} + +// AFTER: correct formatting +function foo() { + return 42; +} +``` + +## Project Structure + +* **Directory layout** (example tree): + +``` +src/ + services/ + models/ + tests/ +``` +* Rules for file placement (public APIs, internal modules) +* Location of configuration files + +## Patterns (Do / Don’t) + +* ✅ Prefer {X} over {Y} +* ❌ Do not {anti-pattern} + +**Example:** + +```{ext} +// BEFORE +const result = JSON.parse(fs.readFileSync(path)) + +// AFTER +const result = await loadConfig(path) +``` + +## Error Handling + +* Use `{error construct}` for {scenarios} +* Wrap I/O and network calls in try/catch +* Always include context in error messages +* Example with logging + rethrow + +## Testing + +* Testing framework: `{testFramework}` +* Test file layout and naming conventions +* Assertion style: {rule} +* Fixtures/mocks: {approach} +* Coverage floor: {percent}% + +**Example:** + +```{ext} +test("should compute total", () => { + expect(sum([1, 2, 3])).toBe(6); +}); +``` + +## Build Frameworks + +* Build tool: `{buildTool}` +* Build tasks/scripts location: `{path}` + +## CI Frameworks + +* CI integration: how builds and tests are executed in pipelines + +## Logging & Telemetry + +* Use `{logger API}` only +* Levels: DEBUG / INFO / WARN / ERROR — with examples +* Correlation IDs: required for async workflows +* No secrets/PII in logs +* Required structured fields: {list} + +## Performance + +* Optimize hot paths {rule} +* Guidelines on allocations, I/O usage +* Profiling steps & recommended tools + +**Example:** + +```{ext} +// BEFORE +let result = ""; +for (const item of items) { + result += item; +} + +// AFTER +let result = items.join(""); +``` + +## Dependencies + +* Allowed package sources: {registry} +* Pin versions using {approach} +* Dependency injection patterns: {rule} +* Banned packages: {list} + +## Documentation + +* Required doc-block style: {docStyle} +* Inline documentation example: + +```{ext} +/** + * Adds two numbers. + * @param {number} a + * @param {number} b + * @returns {number} + */ +function add(a, b) { return a + b; } +``` +* Location for usage samples (e.g., `/docs/examples/`) +* Link to ADRs or design notes if relevant + +## Snippets + +Provide canonical ready-to-use snippets for: + +* Service/module/class boilerplate +* Unit test boilerplate +* Common error-handling pattern +* Logger usage example + +## Forbidden + +* ❌ Explicitly disallow {anti-pattern} with rationale +* ❌ Avoid {package/tool} because {reason} +* Provide alternatives: ✅ “Use {X} instead of {Y}” + +``` diff --git a/.github/prompts/framework/scripts/Gather.ps1 b/.github/prompts/framework/scripts/Gather.ps1 new file mode 100644 index 000000000..5cc5e6d2c --- /dev/null +++ b/.github/prompts/framework/scripts/Gather.ps1 @@ -0,0 +1,6 @@ +[PSCustomObject]@{ + RepositoryPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../../..") + InstructionsPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../../instructions") + OrganizationInstructionsPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../../instructions/organization") + RepositoryInstructionsPath = [System.IO.Path]::GetFullPath("$PSScriptRoot/../../../instructions/Repository") +} | Format-List From 4502bfdb2a8156ea8e13dfc990cd41d4ce4c532c Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 30 Sep 2025 00:17:11 +0200 Subject: [PATCH 2/7] =?UTF-8?q?=F0=9F=A9=B9=20[Patch]:=20Enhance=20Unified?= =?UTF-8?q?=20Frontmatter=20Specification=20for=20instruction=20files=20an?= =?UTF-8?q?d=20improve=20validation=20rules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/Update-Instructions.prompt.md | 204 +++++++----------- 1 file changed, 78 insertions(+), 126 deletions(-) diff --git a/.github/prompts/framework/Update-Instructions.prompt.md b/.github/prompts/framework/Update-Instructions.prompt.md index 265fcbdc5..23053edbf 100644 --- a/.github/prompts/framework/Update-Instructions.prompt.md +++ b/.github/prompts/framework/Update-Instructions.prompt.md @@ -65,15 +65,48 @@ Execution steps: - When uncertain, default to Repository (can promote to Organization later) - Organization content must be generic enough for automation management -4. **Generate Organization Instructions** - For each discovered language/technology: - - Create `$OrganizationInstructionsPath/main.instructions.md`: + **Unified Frontmatter Specification (applies to ALL instruction files)** + Frontmatter MUST: + - Contain exactly 2 fields in this order: + 1. `description`: Single-line string describing the file's purpose + 2. `applyTo`: Single string with one or more glob patterns (comma-separated if multiple) + - Use only one YAML document block at the very top of the file (`---` ... `---`) + - Avoid inline comments or extra keys + - Represent multiple patterns in a single line, never as YAML arrays + - Maintain field order: `description` first, then `applyTo` + + Examples: + ```yaml + --- + description: "Universal code-writing guidelines for the organization" + applyTo: "**/*" + --- + ``` ```yaml - --- - applyTo: "**/*" - description: "Universal code-writing guidelines for the organization" - --- + --- + description: "PowerShell code-writing guidelines for organization projects" + applyTo: "**/*.ps1, **/*.psm1, **/*.psd1" + --- ``` + ```yaml + --- + description: "Project-specific TypeScript patterns for {RepositoryName}" + applyTo: "src/**/*.ts, scripts/**/*.ts" + --- + ``` + + Validation rules enforced later (Step 7): + - Exactly two keys present: `description`, `applyTo` + - Both values non-empty strings + - `applyTo` contains one or more glob patterns separated by commas in a single string + - No duplicate normalized glob patterns + - No table of contents headings in file content + +4. **Generate Organization Instructions** - For each discovered language/technology: + + Create `$OrganizationInstructionsPath/main.instructions.md` frontmatter using the Unified Frontmatter Specification (do not re-document rules here): + - description: "Universal code-writing guidelines for the organization" + - applyTo: `**/*` Content must include: - **File Organization**: How to structure new files, where to place components @@ -81,13 +114,9 @@ Execution steps: - **Documentation**: Required comment patterns, doc-string formats with examples - **Build Integration**: How code integrates with organization build processes - Create `$OrganizationInstructionsPath/{Language}/main.instructions.md`: - ```yaml - --- - applyTo: "**/*.{ext}" - description: "Code-writing guidelines for {Language} in organization projects" - --- - ``` + Create `$OrganizationInstructionsPath/{Language}/main.instructions.md` frontmatter (unified spec) with: + - description: "Code-writing guidelines for {Language} in organization projects" + - applyTo: `**/*.{ext}` Content must include specific, actionable rules: - **Syntax Style**: Exact formatting rules (e.g., "Place opening brace on same line", "Use 4-space indentation") @@ -104,13 +133,9 @@ Execution steps: 5. **Generate Repository Instructions** - Create project-specific guidance: - Create `$RepositoryInstructionsPath/main.instructions.md`: - ```yaml - --- - applyTo: "**/*" - description: "Project-specific code-writing guidance for {RepositoryName}" - --- - ``` + Create `$RepositoryInstructionsPath/main.instructions.md` frontmatter (unified spec) with: + - description: "Project-specific code-writing guidance for {RepositoryName}" + - applyTo: `**/*` Content must include: - **Repository Purpose**: What this project does and why @@ -119,13 +144,9 @@ Execution steps: - **Workflows**: Development, testing, deployment processes - **Dependencies**: Key external dependencies and how they're used - Create `$RepositoryInstructionsPath/{Language}/main.instructions.md`: - ```yaml - --- - applyTo: "{specific-pattern}" - description: "How to write {Language} code in this specific project" - --- - ``` + Create `$RepositoryInstructionsPath/{Language}/main.instructions.md` frontmatter (unified spec) with: + - description: "How to write {Language} code in this specific project" + - applyTo: `{specific-pattern}` Content must include concrete examples from the actual codebase: - **Project Patterns**: Specific architectural patterns used (e.g., "Use Factory pattern in src/factories/") @@ -144,8 +165,11 @@ Execution steps: 7. **Validation** - Verify generated instruction system: - **Structural Validation**: - * All instruction files have valid YAML frontmatter - * `applyTo` glob patterns are correct and non-overlapping + * All instruction files have valid YAML frontmatter per the Unified Frontmatter Specification + * `description` is a single string + * `applyTo` is a single string containing glob pattern(s) - comma-separated if multiple + * Field order is `description` first, then `applyTo` + * No table of contents sections in any instruction file * File structure matches specification hierarchy - **Content Validation**: @@ -154,6 +178,8 @@ Execution steps: * Instructions contain specific, actionable code-writing guidance (not vague principles) * Examples are concrete and relevant to the codebase * No duplicate or contradictory guidance across files + * Content is concise - no unnecessary sections or philosophical discussions + * Each section provides actionable patterns, not generic advice - **Coverage Validation**: * All file types in repository are covered by appropriate instructions @@ -176,6 +202,13 @@ Execution steps: Behavior rules: +- **Frontmatter Requirements**: Follow the Unified Frontmatter Specification (single two-key block, no inline comments, order enforced). +- **No Table of Contents**: Instruction files MUST NOT include a table of contents or navigation sections. Start directly with actionable content. +- **Conciseness Required**: Keep instructions minimal and focused. Every section must provide actionable code-writing guidance. Remove: + * Philosophical discussions or "why" explanations beyond brief context + * Redundant examples that don't add new patterns + * Generic advice that applies universally (e.g., "write clean code") + * Sections that don't apply to the specific project/language - **Code-Specific Focus**: Instructions must be about *how to write code*, not general principles. Replace "Follow best practices" with "Use StringBuilder for string concatenation inside loops to avoid O(n²) performance". - **Actionable Guidance**: Every rule must be specific enough to execute. Replace "Handle errors appropriately" with "Wrap all I/O operations in try-catch blocks with context-aware error messages". - **Concrete Examples**: Include before/after code examples, especially for common patterns. @@ -230,23 +263,21 @@ Context for instruction generation: $ARGUMENTS ### Template for language oriented instructions -Here’s the ready-to-use template: +Use this minimal template when creating a new language/project instruction file. Omit sections that are not relevant. ```markdown --- -applyTo: "**/*.{ext}" # A single string Glob pattern for applicable files description: "Code-writing guidelines for {Language} in this organization/project/repository" +applyTo: "**/*.{ext}" --- -# {Language} Instructions - ## Style & Formatting - Indentation: {n} spaces - Max line length: {n} - Trailing whitespace: disallowed - Braces/blocks: {rule} - Imports/using/order: {rule} -- Naming conventions: {rule} (classes, methods, variables, constants) +- Naming conventions: {rule} **Example:** ```{ext} @@ -255,134 +286,55 @@ function foo ( ) {return 42} // AFTER: correct formatting function foo() { - return 42; + return 42; } ``` -## Project Structure - -* **Directory layout** (example tree): - -``` -src/ - services/ - models/ - tests/ -``` -* Rules for file placement (public APIs, internal modules) -* Location of configuration files - -## Patterns (Do / Don’t) - +## Patterns (Do / Don't) * ✅ Prefer {X} over {Y} -* ❌ Do not {anti-pattern} - -**Example:** +* ❌ Avoid {anti-pattern} ```{ext} // BEFORE const result = JSON.parse(fs.readFileSync(path)) - // AFTER const result = await loadConfig(path) ``` ## Error Handling - * Use `{error construct}` for {scenarios} -* Wrap I/O and network calls in try/catch -* Always include context in error messages -* Example with logging + rethrow +* Include context in thrown errors ## Testing - -* Testing framework: `{testFramework}` -* Test file layout and naming conventions -* Assertion style: {rule} -* Fixtures/mocks: {approach} -* Coverage floor: {percent}% - -**Example:** +* Framework: `{testFramework}` +* Layout & naming conventions ```{ext} test("should compute total", () => { - expect(sum([1, 2, 3])).toBe(6); + expect(sum([1, 2, 3])).toBe(6); }); ``` -## Build Frameworks - -* Build tool: `{buildTool}` -* Build tasks/scripts location: `{path}` - -## CI Frameworks - -* CI integration: how builds and tests are executed in pipelines - -## Logging & Telemetry - -* Use `{logger API}` only -* Levels: DEBUG / INFO / WARN / ERROR — with examples -* Correlation IDs: required for async workflows -* No secrets/PII in logs -* Required structured fields: {list} - ## Performance - -* Optimize hot paths {rule} -* Guidelines on allocations, I/O usage -* Profiling steps & recommended tools - -**Example:** - ```{ext} -// BEFORE +// BEFORE (inefficient) let result = ""; -for (const item of items) { - result += item; -} - +for (const item of items) { result += item; } // AFTER let result = items.join(""); ``` -## Dependencies - -* Allowed package sources: {registry} -* Pin versions using {approach} -* Dependency injection patterns: {rule} -* Banned packages: {list} - ## Documentation - -* Required doc-block style: {docStyle} -* Inline documentation example: - ```{ext} /** * Adds two numbers. - * @param {number} a - * @param {number} b - * @returns {number} */ function add(a, b) { return a + b; } ``` -* Location for usage samples (e.g., `/docs/examples/`) -* Link to ADRs or design notes if relevant - -## Snippets - -Provide canonical ready-to-use snippets for: - -* Service/module/class boilerplate -* Unit test boilerplate -* Common error-handling pattern -* Logger usage example ## Forbidden +* ❌ {anti-pattern} → ✅ Use {preferred pattern} -* ❌ Explicitly disallow {anti-pattern} with rationale -* ❌ Avoid {package/tool} because {reason} -* Provide alternatives: ✅ “Use {X} instead of {Y}” - + ``` From 731b38aeea1981cf6e1938626809a7ae9bb23894 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 30 Sep 2025 16:58:10 +0200 Subject: [PATCH 3/7] Add comprehensive coding guidelines and instruction files for XML, YAML, and general organization practices - Introduced XML coding guidelines in `.github/instructions/organization/XML/main.instructions.md` covering formatting, ordering, patterns, validation, and forbidden practices. - Added YAML coding guidelines in `.github/instructions/organization/YAML/main.instructions.md` detailing style, file structure, patterns, error handling, and performance considerations. - Created a language mapping summary in `.github/instructions/organization/languages.instructions.md` to document discovered languages, extensions, and representative files. - Established universal code-writing guidelines in `.github/instructions/organization/main.instructions.md` outlining file organization, naming conventions, documentation standards, and testing requirements. - Updated the instruction generation prompt to include a detailed folder structure specification and execution steps for maintaining organization-level instructions. - Implemented a script to gather repository files and group them by extension in `.github/prompts/framework/scripts/Get-RepositoryFiles.ps1`. --- .../GitAttributes/main.instructions.md | 18 + .../Repository/GitIgnore/main.instructions.md | 21 + .../Repository/JSON/main.instructions.md | 23 + .../Repository/Markdown/main.instructions.md | 30 + .../PowerShell/main.instructions.md | 993 ++++++++++++++++++ .../Repository/XML/main.instructions.md | 27 + .../Repository/YAML/main.instructions.md | 41 + .../Repository/languages.instructions.md | 30 + .../Repository/main.instructions.md | 726 +++++++++++++ .../GitAttributes/main.instructions.md | 25 + .../GitIgnore/main.instructions.md | 34 + .../organization/JSON/main.instructions.md | 34 + .../Markdown/main.instructions.md | 49 + .../PowerShell/main.instructions.md | 821 +++++++++++++++ .../organization/XML/main.instructions.md | 65 ++ .../organization/YAML/main.instructions.md | 70 ++ .../organization/languages.instructions.md | 88 ++ .../organization/main.instructions.md | 135 +++ .../framework/Update-Instructions.prompt.md | 171 ++- .../framework/scripts/Get-RepositoryFiles.ps1 | 30 + 20 files changed, 3420 insertions(+), 11 deletions(-) create mode 100644 .github/instructions/Repository/GitAttributes/main.instructions.md create mode 100644 .github/instructions/Repository/GitIgnore/main.instructions.md create mode 100644 .github/instructions/Repository/JSON/main.instructions.md create mode 100644 .github/instructions/Repository/Markdown/main.instructions.md create mode 100644 .github/instructions/Repository/PowerShell/main.instructions.md create mode 100644 .github/instructions/Repository/XML/main.instructions.md create mode 100644 .github/instructions/Repository/YAML/main.instructions.md create mode 100644 .github/instructions/Repository/languages.instructions.md create mode 100644 .github/instructions/Repository/main.instructions.md create mode 100644 .github/instructions/organization/GitAttributes/main.instructions.md create mode 100644 .github/instructions/organization/GitIgnore/main.instructions.md create mode 100644 .github/instructions/organization/JSON/main.instructions.md create mode 100644 .github/instructions/organization/Markdown/main.instructions.md create mode 100644 .github/instructions/organization/PowerShell/main.instructions.md create mode 100644 .github/instructions/organization/XML/main.instructions.md create mode 100644 .github/instructions/organization/YAML/main.instructions.md create mode 100644 .github/instructions/organization/languages.instructions.md create mode 100644 .github/instructions/organization/main.instructions.md create mode 100644 .github/prompts/framework/scripts/Get-RepositoryFiles.ps1 diff --git a/.github/instructions/Repository/GitAttributes/main.instructions.md b/.github/instructions/Repository/GitAttributes/main.instructions.md new file mode 100644 index 000000000..02240fe26 --- /dev/null +++ b/.github/instructions/Repository/GitAttributes/main.instructions.md @@ -0,0 +1,18 @@ +--- +description: "How to write .gitattributes in this specific project" +applyTo: "**/.gitattributes" +--- + +## Conventions +- Ensure LF normalization for cross-platform contributors. +- Mark PowerShell as text for consistent diffs. + +## Current Baseline (append to file if missing) +``` +* text=auto eol=lf +*.ps1 text eol=lf +*.ps1xml text eol=lf +``` + +## Anti-Patterns +- ❌ Adding binary flag to text formats. diff --git a/.github/instructions/Repository/GitIgnore/main.instructions.md b/.github/instructions/Repository/GitIgnore/main.instructions.md new file mode 100644 index 000000000..d1481df85 --- /dev/null +++ b/.github/instructions/Repository/GitIgnore/main.instructions.md @@ -0,0 +1,21 @@ +--- +description: "How to write .gitignore in this specific project" +applyTo: "**/.gitignore" +--- + +## Conventions +- Only ignore artifacts not meant for source control (build outputs, local env files). +- Group related patterns with blank line separation + comment header. + +## Example Block +``` +# PowerShell +*.ps1xml + +# Local tooling +*.local.ps1 +``` + +## Anti-Patterns +- ❌ Ignoring scripts under `tools/`. +- ❌ Using wildcards that capture future source paths unintentionally. diff --git a/.github/instructions/Repository/JSON/main.instructions.md b/.github/instructions/Repository/JSON/main.instructions.md new file mode 100644 index 000000000..a369cce22 --- /dev/null +++ b/.github/instructions/Repository/JSON/main.instructions.md @@ -0,0 +1,23 @@ +--- +description: "How to write JSON code in this specific project" +applyTo: "**/*.json" +--- + +## Scope +Currently only analyzer / duplication configs. + +## Conventions +- Keep config minimal; remove unused default keys. +- Document non-obvious thresholds in adjacent README section. + +## Example +```json +{ + "threshold": 5, + "reporters": ["console", "json"] +} +``` + +## Anti-Patterns +- ❌ Adding comments (breaks strict JSON) → Document separately. +- ❌ Large unrelated reorders in same PR. diff --git a/.github/instructions/Repository/Markdown/main.instructions.md b/.github/instructions/Repository/Markdown/main.instructions.md new file mode 100644 index 000000000..c66b5ad6b --- /dev/null +++ b/.github/instructions/Repository/Markdown/main.instructions.md @@ -0,0 +1,30 @@ +--- +description: "How to write Markdown code in this specific project" +applyTo: "**/*.md" +--- + +## Project Conventions +- Primary audience: users scripting with module + contributors. +- Each example must be executable as-is (assume module imported) or include import line. + +## Sections +- README intro sequence: Badge Block → Short Value Prop → Quick Start → Features → Examples → Development. + +## Code Blocks +- Prefer `powershell` language tag. +- Multi-step examples: comment headings inside block rather than multiple blocks. + +## Example Pattern +```markdown +```powershell +# Get a repository +Get-GitHubRepository -Owner Contoso -Name Portal | Format-List * +``` +``` + +## Anti-Patterns +- ❌ Screenshots for output that can be text. +- ❌ Example without parameter explanation. + +## Overrides +- Organization rule on 120 char wrap may be exceeded for one-line commands showing multiple parameters. diff --git a/.github/instructions/Repository/PowerShell/main.instructions.md b/.github/instructions/Repository/PowerShell/main.instructions.md new file mode 100644 index 000000000..fc9fb7ad6 --- /dev/null +++ b/.github/instructions/Repository/PowerShell/main.instructions.md @@ -0,0 +1,993 @@ +--- +description: "How to write PowerShell code in this specific project" +applyTo: "**/*.ps1, **/*.psm1, **/*.psd1" +--- + +# PowerShell Code Patterns for GitHub Module + +This document provides concrete examples of how to write PowerShell code in the GitHub PowerShell module. These examples are extracted from the actual codebase and demonstrate the project-specific implementation patterns. + + +## Context Resolution Pattern + +### Implementation in Resolve-GitHubContext + +The context resolution pattern is central to the module's authentication system. Here's how it's implemented: + +```powershell +filter Resolve-GitHubContext { + [OutputType([GitHubContext])] + [CmdletBinding()] + param( + [Parameter(Mandatory, ValueFromPipeline)] + [AllowNull()] + [object] $Context, + + [Parameter()] + [bool] $Anonymous + ) + + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + Initialize-GitHubConfig + } + + process { + Write-Verbose "Context:" + $Context | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + Write-Verbose "Anonymous: [$Anonymous]" + + if ($Anonymous -or $Context -eq 'Anonymous') { + Write-Verbose 'Returning Anonymous context.' + return [GitHubContext]::new( + [pscustomobject]@{ + Name = 'Anonymous' + AuthType = 'Anonymous' + } + ) + } + + if ($Context -is [string]) { + $contextName = $Context + Write-Verbose "Getting context: [$contextName]" + $Context = Get-GitHubContext -Context $contextName + } + + if ($null -eq $Context) { + Write-Verbose 'Context is null, returning default context.' + $Context = Get-GitHubContext + } + + switch ($Context.TokenType) { + 'ghu' { + Write-Verbose 'Update GitHub User Access Token.' + $Context = Update-GitHubUserAccessToken -Context $Context -PassThru + } + 'JWT' { + Write-Verbose 'Update GitHub App JWT Token.' + $Context = Update-GitHubAppJWT -Context $Context -PassThru + } + } + + $Context + } + + end { + Write-Debug "[$stackPath] - End" + } +} +``` + +**Key Pattern Elements:** + +- **`filter` keyword**: Used instead of `function` for streaming pipeline behavior +- **`[AllowNull()]` attribute**: Permits null values for context parameter (important for defaulting) +- **String to object resolution**: Handles both string context names and GitHubContext objects +- **Token refresh logic**: Automatically updates expiring tokens based on TokenType +- **Null handling**: Returns default context when no context provided + +**Usage in Public Functions:** + +```powershell +begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + + # Resolve context - converts string names or null to GitHubContext objects + $Context = Resolve-GitHubContext -Context $Context + + # Validate authentication type for this operation + Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT +} +``` + +--- + +## API Call Pattern + +### Implementation in Invoke-GitHubAPI + +The `Invoke-GitHubAPI` function is the foundation for all REST API calls. Here's the core pattern: + +```powershell +function Invoke-GitHubAPI { + [CmdletBinding(DefaultParameterSetName = 'ApiEndpoint')] + param( + [Parameter()] + [ValidateSet('GET', 'POST', 'PUT', 'DELETE', 'PATCH')] + $Method = 'GET', + + [Parameter( + Mandatory, + ParameterSetName = 'ApiEndpoint' + )] + [string] $ApiEndpoint, + + [Parameter()] + [Alias('Query')] + [object] $Body, + + [Parameter()] + [string] $Accept = 'application/vnd.github+json; charset=utf-8', + + [Parameter()] + [object] $Context + ) + + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + $debug = $DebugPreference -eq 'Continue' + + # Resolve context with anonymous support + $Context = Resolve-GitHubContext -Context $Context -Anonymous $Anonymous + + if ($debug) { + Write-Debug 'Invoking GitHub API...' + Write-Debug 'Parent function parameters:' + Get-FunctionParameter -Scope 1 | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug 'Parameters:' + Get-FunctionParameter | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + } + } + + process { + # Resolve all settings from context + $Token = $Context.Token + $HttpVersion = Resolve-GitHubContextSetting -Name 'HttpVersion' -Value $HttpVersion -Context $Context + $ApiBaseUri = Resolve-GitHubContextSetting -Name 'ApiBaseUri' -Value $ApiBaseUri -Context $Context + $ApiVersion = Resolve-GitHubContextSetting -Name 'ApiVersion' -Value $ApiVersion -Context $Context + $RetryCount = Resolve-GitHubContextSetting -Name 'RetryCount' -Value $RetryCount -Context $Context + $RetryInterval = Resolve-GitHubContextSetting -Name 'RetryInterval' -Value $RetryInterval -Context $Context + + # Build headers + $headers = @{ + Accept = $Accept + 'X-GitHub-Api-Version' = $ApiVersion + 'User-Agent' = $script:UserAgent + } + $headers | Remove-HashtableEntry -NullOrEmptyValues + + # Build URI + if (-not $Uri) { + $Uri = New-Uri -BaseUri $ApiBaseUri -Path $ApiEndpoint -AsString + $Uri = $Uri -replace '//$', '/' + } + + # Build API call splat + $APICall = @{ + Uri = $Uri + Method = [string]$Method + Headers = $Headers + ContentType = $ContentType + InFile = $UploadFilePath + HttpVersion = [string]$HttpVersion + MaximumRetryCount = $RetryCount + RetryIntervalSec = $RetryInterval + } + $APICall | Remove-HashtableEntry -NullOrEmptyValues + + # Add authentication for non-anonymous requests + if (-not $Anonymous -and $Context.Name -ne 'Anonymous') { + $APICall['Authentication'] = 'Bearer' + $APICall['Token'] = $Token + } + + # Handle GET parameters as query string + if ($Method -eq 'GET') { + if (-not $Body) { + $Body = @{} + } + $Body['per_page'] = Resolve-GitHubContextSetting -Name 'PerPage' -Value $PerPage -Context $Context + $APICall.Uri = New-Uri -BaseUri $Uri -Query $Body -AsString + } + # Handle POST with file upload + elseif (($Method -eq 'POST') -and -not [string]::IsNullOrEmpty($UploadFilePath)) { + $APICall.Uri = New-Uri -BaseUri $Uri -Query $Body -AsString + } + # Handle body for other methods + elseif ($Body) { + if ($Body -is [hashtable]) { + $APICall.Body = $Body | ConvertTo-Json -Depth 100 + } else { + $APICall.Body = $Body + } + } + + try { + do { + $response = Invoke-WebRequest @APICall -ProgressAction 'SilentlyContinue' -Debug:$false -Verbose:$false + + # Process response headers + $headers = @{} + foreach ($item in $response.Headers.GetEnumerator()) { + $headers[$item.Key] = ($item.Value).Trim() -join ', ' + } + + # Parse content based on Content-Type + switch -Regex ($headers.'Content-Type') { + 'application/.*json' { + $results = $response.Content | ConvertFrom-Json + } + 'application/octocat-stream' { + [byte[]]$byteArray = $response.Content + $results = [System.Text.Encoding]::UTF8.GetString($byteArray) + } + default { + $results = $response.Content + } + } + + # Return structured response + [pscustomobject]@{ + Request = $APICall + Response = $results + Headers = $headers + StatusCode = $response.StatusCode + StatusDescription = $response.StatusDescription + } + + # Follow pagination + $APICall['Uri'] = $response.RelationLink.next + } while ($APICall['Uri']) + } catch { + # Detailed error handling (see Error Handling section) + $errordetails = $_.ErrorDetails | ConvertFrom-Json -AsHashtable + $errorResult = [pscustomobject]@{ + Message = $errordetails.message + Resource = $errordetails.errors.resource + Code = $errordetails.errors.code + Details = $errordetails.errors.message + Information = $errordetails.documentation_url + Status = $_.Exception.Message + StatusCode = $errordetails.status + ErrorTime = Get-Date -Format 's' + } + + # Build comprehensive exception message + $exception = @" +---------------------------------- +Context: +$($Context | Format-List | Out-String) +---------------------------------- +Request: +$([pscustomobject]$APICall | Select-Object -ExcludeProperty Body, Headers | Format-List | Out-String) +---------------------------------- +Error: +$($errorResult | Format-List | Out-String) +---------------------------------- +"@ + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.Exception]::new($exception), + 'GitHubAPIError', + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $errorResult + ) + ) + } + } + + end { + Write-Debug "[$stackPath] - End" + } +} +``` + +**Key Pattern Elements:** + +- **Setting resolution**: Use `Resolve-GitHubContextSetting` to get values from context with parameter overrides +- **Header construction**: Build headers hashtable, then remove null/empty values +- **URI construction**: Use `New-Uri` helper for consistent URI building +- **Splat pattern**: Build `$APICall` hashtable, clean it, then splat to `Invoke-WebRequest` +- **Method-specific handling**: Different logic for GET (query string), POST with upload (query string), and other methods (JSON body) +- **Pagination**: Automatic do-while loop following `RelationLink.next` +- **Response processing**: Content-Type-aware deserialization +- **Structured output**: Always return `[pscustomobject]` with Request, Response, Headers, StatusCode, StatusDescription + +--- + +## Public Function Structure + +### Complete Example from Get-GitHubRepository + +Here's the full pattern for a public function with multiple parameter sets: + +```powershell +filter Get-GitHubRepository { + <# + .SYNOPSIS + Get a repository or list of repositories + + .DESCRIPTION + Gets a repository or list of repositories based on the provided parameters. + + .EXAMPLE + Get-GitHubRepository -Owner 'octocat' -Name 'Hello-World' + + Gets the repository 'Hello-World' for the organization 'octocat'. + + .OUTPUTS + GitHubRepository + #> + [OutputType([GitHubRepository])] + [CmdletBinding(DefaultParameterSetName = 'List')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'Parameters are used in different parameter sets')] + param( + # The account owner of the repository. + [Parameter( + Mandatory, + ParameterSetName = 'ByName' + )] + [Parameter(ParameterSetName = 'List')] + [Alias('Organization', 'Username')] + [string] $Owner, + + # The name of the repository without the .git extension. + [Parameter( + Mandatory, + ParameterSetName = 'ByName', + ValueFromPipelineByPropertyName + )] + [Alias('Repository', 'Repo')] + [string] $Name, + + # The unique identifier of the repository. + [Parameter( + Mandatory, + ParameterSetName = 'ByID', + ValueFromPipelineByPropertyName + )] + [Alias('RepositoryID', 'RepoID')] + [int] $ID, + + # Properties to include in the returned object. + [Parameter()] + [string[]] $Property, + + # Additional properties to include in the returned object. + [Parameter()] + [string[]] $AdditionalProperty, + + # The context to run the command in. + [Parameter()] + [object] $Context + ) + + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + + # Resolve and validate context + $Context = Resolve-GitHubContext -Context $Context + Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT + + # Default Owner from context if not provided + if ([string]::IsNullOrEmpty($Owner)) { + $Owner = $Context.Owner + } + } + + process { + # Route to appropriate private function based on parameter set + $params = @{ + Context = $Context + } + + if ($Property) { + $params['Property'] = $Property + } + + if ($AdditionalProperty) { + $params['AdditionalProperty'] = $AdditionalProperty + } + + switch ($PSCmdlet.ParameterSetName) { + 'ByName' { + Write-Debug "Getting repository by name: [$Owner/$Name]" + $params['Owner'] = $Owner + $params['Name'] = $Name + Get-GitHubRepositoryByName @params + } + 'ByID' { + Write-Debug "Getting repository by ID: [$ID]" + $params['ID'] = $ID + Get-GitHubRepositoryByID @params + } + 'List' { + Write-Debug "Listing repositories for owner: [$Owner]" + $params['Owner'] = $Owner + Get-GitHubRepositoryList @params + } + } + } + + end { + Write-Debug "[$stackPath] - End" + } +} +``` + +**Key Pattern Elements:** + +- **`filter` keyword**: Enables streaming pipeline behavior +- **`[OutputType()]`**: Always declare output type for IntelliSense and documentation +- **`[CmdletBinding(DefaultParameterSetName = '...')]`**: Specify default parameter set +- **`[SuppressMessageAttribute]`**: Suppress false PSScriptAnalyzer warnings with justification +- **Standard aliases**: `Owner` aliased to `Organization`/`Username`, `Name` aliased to `Repository`/`Repo` +- **Context resolution in `begin` block**: Resolve and assert context once before processing pipeline +- **Parameter defaulting from context**: `if ([string]::IsNullOrEmpty($Owner)) { $Owner = $Context.Owner }` +- **Switch on parameter set**: Route to different private functions based on `$PSCmdlet.ParameterSetName` +- **Parameter splatting**: Build `$params` hashtable conditionally, then splat to private function + +--- + +## Private Function Structure + +### Complete Example from Get-GitHubRepositoryByName + +Here's the full pattern for a private function: + +```powershell +filter Get-GitHubRepositoryByName { + <# + .SYNOPSIS + Get a repository + + .DESCRIPTION + The `parent` and `source` objects are present when the repository is a fork. + `parent` is the repository this repository was forked from, `source` is the ultimate source for the network. + + .EXAMPLE + Get-GitHubRepositoryByName -Owner 'octocat' -Name 'Hello-World' + + Gets the repository 'Hello-World' for the organization 'octocat'. + + .OUTPUTS + GitHubRepository + #> + [OutputType([GitHubRepository])] + [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidLongLines', '', Justification = 'Contains a long link.')] + param( + # The account owner of the repository. The name is not case sensitive. + [Parameter(Mandatory)] + [string] $Owner, + + # The name of the repository without the .git extension. The name is not case sensitive. + [Parameter(Mandatory)] + [string] $Name, + + # Properties to include in the returned object. + [Parameter()] + [string[]] $Property = @( + 'ID', + 'NodeID' + 'Name', + 'Owner', + 'FullName', + 'Url', + 'Description', + 'CreatedAt', + 'UpdatedAt', + 'PushedAt', + 'ArchivedAt', + 'Homepage', + 'Size', + 'Language', + 'HasIssues', + 'HasProjects', + 'HasWiki', + 'HasDiscussions', + 'HasSponsorships', + 'IsArchived', + 'IsTemplate', + 'IsFork', + 'License', + 'AllowForking', + 'RequireWebCommitSignoff', + 'Topics', + 'Visibility', + 'OpenIssues', + 'OpenPullRequests', + 'Stargazers', + 'Watchers', + 'Forks', + 'DefaultBranch', + 'Permission', + 'AllowSquashMerge', + 'AllowMergeCommit', + 'AllowRebaseMerge', + 'AllowAutoMerge', + 'DeleteBranchOnMerge', + 'SuggestUpdateBranch', + 'SquashMergeCommitTitle', + 'SquashMergeCommitMessage', + 'MergeCommitTitle', + 'MergeCommitMessage', + 'TemplateRepository', + 'ForkRepository', + 'CustomProperties', + 'CloneUrl', + 'SshUrl', + 'GitUrl' + ), + + # Additional properties to include in the returned object. + [Parameter()] + [string[]] $AdditionalProperty, + + # The context to run the command in. Used to get the details for the API call. + # Can be either a string or a GitHubContext object. + [Parameter(Mandatory)] + [object] $Context + ) + + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + + # Assert context authentication type + Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT + } + + process { + # Build GraphQL field list from property names + $graphParams = @{ + PropertyList = $Property + $AdditionalProperty + PropertyToGraphQLMap = [GitHubRepository]::PropertyToGraphQLMap + } + $graphQLFields = ConvertTo-GitHubGraphQLField @graphParams + + # Call GraphQL API + $apiParams = @{ + Query = @" +query( + `$Owner: String!, + `$Name: String! +) { + repositoryOwner( + login: `$Owner + ) { + repository( + name: `$Name + ) { +$graphQLFields + } + } +} +"@ + Variables = @{ + Owner = $Owner + Name = $Name + } + Context = $Context + } + + Invoke-GitHubGraphQLQuery @apiParams | ForEach-Object { + [GitHubRepository]::new($_.repositoryOwner.repository) + } + } + + end { + Write-Debug "[$stackPath] - End" + } +} +``` + +**Key Pattern Elements:** + +- **`[Parameter(Mandatory)]` on Context**: Private functions always require context (no defaulting) +- **Default property list**: Comprehensive default array of properties to fetch +- **GraphQL field mapping**: Convert PowerShell property names to GraphQL fields using class mapping +- **Here-string for queries**: Use `@"..."@` for multi-line GraphQL queries +- **Escape dollar signs**: Use backtick before `$` in GraphQL variables: `` `$Owner `` +- **Type constructor**: Create typed object from API response: `[GitHubRepository]::new($_.repositoryOwner.repository)` +- **Pipeline support**: Use `ForEach-Object` to stream results through pipeline + +--- + +## Parameter Splatting + +### Cleaning Hashtables Before Splatting + +This module uses the `Remove-HashtableEntry` helper to clean hashtables before splatting: + +```powershell +# Build parameter hashtable conditionally +$params = @{ + Context = $Context +} + +if ($Property) { + $params['Property'] = $Property +} + +if ($AdditionalProperty) { + $params['AdditionalProperty'] = $AdditionalProperty +} + +# No need to clean - only add properties that exist +Get-GitHubRepositoryByName @params +``` + +**Alternative pattern for API calls:** + +```powershell +# Build API call hashtable with all possible parameters +$APICall = @{ + Uri = $Uri + Method = [string]$Method + Headers = $Headers + ContentType = $ContentType + InFile = $UploadFilePath + HttpVersion = [string]$HttpVersion + MaximumRetryCount = $RetryCount + RetryIntervalSec = $RetryInterval +} + +# Remove null or empty values before splatting +$APICall | Remove-HashtableEntry -NullOrEmptyValues + +Invoke-WebRequest @APICall +``` + +**Key Pattern Elements:** + +- **Conditional addition**: Only add parameters to hashtable if they have values +- **Remove-HashtableEntry**: Use `-NullOrEmptyValues` switch to clean before splatting +- **Splat with `@`**: Use `@params` not `$params` when calling function + +--- + +## Error Handling + +### Comprehensive Error Handling from Invoke-GitHubAPI + +```powershell +try { + $response = Invoke-WebRequest @APICall -ProgressAction 'SilentlyContinue' -Debug:$false -Verbose:$false + + # Process successful response... + +} catch { + $failure = $_ + + # Parse error details from API response + $errordetails = $failure.ErrorDetails | ConvertFrom-Json -AsHashtable + $errors = $errordetails.errors + + # Build structured error object + $errorResult = [pscustomobject]@{ + Message = $errordetails.message + Resource = $errors.resource + Code = $errors.code + Details = $errors.message + Information = $errordetails.documentation_url + Status = $failure.Exception.Message + StatusCode = $errordetails.status + ErrorTime = Get-Date -Format 's' + } + + # Build comprehensive exception message with context + $exception = @" + +---------------------------------- +Context: +$($Context | Format-List | Out-String) +---------------------------------- +Request: +$([pscustomobject]$APICall | Select-Object -ExcludeProperty Body, Headers | Format-List | Out-String) +---------------------------------- +Request headers: +$([pscustomobject]$APICall.Headers | Format-List | Out-String) +---------------------------------- +Request body: +$("$($APICall.Body | Out-String -Stream)".Split('\n') -split '\n') +---------------------------------- +Response headers: +$($headers | Format-List | Out-String) +---------------------------------- +Error: +$($errorResult | Format-List | Out-String) +---------------------------------- + +"@ + + # Throw terminating error with all context + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.Exception]::new($exception), + 'GitHubAPIError', + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $errorResult + ) + ) +} +``` + +**Key Pattern Elements:** + +- **Parse ErrorDetails**: GitHub API returns structured error in `$_.ErrorDetails` as JSON +- **Structured error object**: Create `[pscustomobject]` with all error properties +- **Context in error message**: Include context, request, headers, body, and response in exception +- **Here-string for formatting**: Use `@"..."@` for multi-line error message with embedded expressions +- **ThrowTerminatingError**: Use `$PSCmdlet.ThrowTerminatingError()` not `throw` for proper error records +- **Include TargetObject**: Pass `$errorResult` as TargetObject for error record + +--- + +## GraphQL Query Pattern + +### Building and Executing GraphQL Queries + +```powershell +# Build field list from properties +$graphParams = @{ + PropertyList = $Property + $AdditionalProperty + PropertyToGraphQLMap = [GitHubRepository]::PropertyToGraphQLMap +} +$graphQLFields = ConvertTo-GitHubGraphQLField @graphParams + +# Build GraphQL query with here-string +$apiParams = @{ + Query = @" +query( + `$Owner: String!, + `$Name: String! +) { + repositoryOwner( + login: `$Owner + ) { + repository( + name: `$Name + ) { +$graphQLFields + } + } +} +"@ + Variables = @{ + Owner = $Owner + Name = $Name + } + Context = $Context +} + +# Execute query and create typed objects +Invoke-GitHubGraphQLQuery @apiParams | ForEach-Object { + [GitHubRepository]::new($_.repositoryOwner.repository) +} +``` + +**Key Pattern Elements:** + +- **Property mapping**: Use class's `PropertyToGraphQLMap` to convert PowerShell names to GraphQL fields +- **Escape variables**: Use backtick before `$` in GraphQL query: `` `$Owner `` +- **Variables hashtable**: Separate query from variables for security and reusability +- **Indented field list**: Insert `$graphQLFields` with proper indentation in query +- **Type constructor**: Create typed object from nested response: `[GitHubRepository]::new($_.repositoryOwner.repository)` + +--- + +## Filter Usage + +### When to Use `filter` Instead of `function` + +This module prefers `filter` for **all** public and private functions to enable streaming pipeline behavior: + +```powershell +# ✅ CORRECT: Use filter for pipeline support +filter Get-GitHubRepository { + [CmdletBinding()] + param( + [Parameter(ValueFromPipelineByPropertyName)] + [string] $Name, + + [Parameter()] + [object] $Context + ) + + begin { + # Resolve context once + $Context = Resolve-GitHubContext -Context $Context + } + + process { + # This runs for each pipeline object + Get-GitHubRepositoryByName -Owner $Owner -Name $Name -Context $Context + } + + end { + # Cleanup if needed + } +} + +# ❌ WRONG: Don't use function unless you have a specific reason +function Get-GitHubRepository { + # This doesn't support streaming pipeline +} +``` + +**Key Pattern Elements:** + +- **`filter` = `function` + automatic `process` block**: Filter wraps entire body in `process {}` +- **Explicit blocks with filter**: Can still use `begin`/`process`/`end` with filter +- **Context in `begin`**: Resolve context in `begin` block to avoid repeating for each pipeline object +- **Stream results**: Don't collect in array - output directly to pipeline + +--- + +## Pipeline Support + +### ValueFromPipelineByPropertyName Pattern + +```powershell +filter Get-GitHubRepository { + param( + # Bind from Owner property of pipeline object + [Parameter( + Mandatory, + ParameterSetName = 'ByName' + )] + [Alias('Organization', 'Username')] + [string] $Owner, + + # Bind from Name/Repository/Repo property of pipeline object + [Parameter( + Mandatory, + ParameterSetName = 'ByName', + ValueFromPipelineByPropertyName + )] + [Alias('Repository', 'Repo')] + [string] $Name, + + # Context does NOT come from pipeline + [Parameter()] + [object] $Context + ) + + begin { + # Resolve context once before processing pipeline + $Context = Resolve-GitHubContext -Context $Context + } + + process { + # This runs for each pipeline object + # $Name is bound from pipeline object's Name/Repository/Repo property + Get-GitHubRepositoryByName -Owner $Owner -Name $Name -Context $Context + } +} +``` + +**Usage:** + +```powershell +# Pipeline array of repository names +@('repo1', 'repo2', 'repo3') | ForEach-Object { + [pscustomobject]@{ Owner = 'octocat'; Name = $_ } +} | Get-GitHubRepository + +# Pipeline from another command +Get-GitHubRepository -Owner 'octocat' | Get-GitHubRepositoryBranch +``` + +**Key Pattern Elements:** + +- **`ValueFromPipelineByPropertyName`**: Bind parameter from pipeline object property with matching name or alias +- **Aliases for binding**: Use `[Alias()]` to accept multiple property names +- **Context not from pipeline**: Context parameter should never use `ValueFromPipeline` or `ValueFromPipelineByPropertyName` +- **Resolve context in `begin`**: Only resolve context once, not for each pipeline object + +--- + +## Debug Output Pattern + +### Comprehensive Debug Logging + +```powershell +begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + $debug = $DebugPreference -eq 'Continue' + + if ($debug) { + Write-Debug 'Parent function parameters:' + Get-FunctionParameter -Scope 1 | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug 'Parameters:' + Get-FunctionParameter | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + } +} + +process { + if ($debug) { + Write-Debug '----------------------------------' + Write-Debug 'Request:' + [pscustomobject]$APICall | Select-Object -ExcludeProperty Body, Headers | Format-List | + Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug '----------------------------------' + Write-Debug 'Request headers:' + [pscustomobject]$APICall.Headers | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug '----------------------------------' + Write-Debug 'Request body:' + ($APICall.Body | Out-String).Split('\n') -split '\n' | ForEach-Object { Write-Debug $_ } + Write-Debug '----------------------------------' + } + + # ... do work ... + + if ($debug) { + Write-Debug '----------------------------------' + Write-Debug 'Response:' + $response | Select-Object -ExcludeProperty Content, Headers | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug '---------------------------' + Write-Debug 'Response headers:' + $headers | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug '---------------------------' + Write-Debug 'Response content:' + $results | ConvertTo-Json -Depth 5 -WarningAction SilentlyContinue | Out-String -Stream | ForEach-Object { + $content = $_ + $content = $content -split '\n' + $content = $content.Split('\n') + foreach ($item in $content) { + Write-Debug $item + } + } + Write-Debug '---------------------------' + } +} + +end { + Write-Debug "[$stackPath] - End" +} +``` + +**Key Pattern Elements:** + +- **Stack path**: Use `Get-PSCallStackPath` to show function call hierarchy +- **Debug preference check**: Store `$debug = $DebugPreference -eq 'Continue'` to avoid repeated checks +- **Format for readability**: Use `Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ }` +- **Separators**: Use `Write-Debug '----...'` to visually separate sections +- **Exclude large properties**: Use `-ExcludeProperty Body, Headers` when showing request object +- **Line-by-line output**: Split multi-line strings and debug each line separately +- **Begin and end markers**: Always include `Write-Debug "[$stackPath] - Start"` and `Write-Debug "[$stackPath] - End"` + +--- + +## Summary + +These patterns are extracted from the actual GitHub PowerShell module codebase. They demonstrate: + +1. **Context Resolution**: How to resolve string/null/object contexts with token refresh +2. **API Calls**: Complete pattern for REST API calls with pagination, error handling, and structured responses +3. **Public Functions**: Filter-based with parameter sets, context resolution, and routing +4. **Private Functions**: Direct API mapping with mandatory context and type constructors +5. **Parameter Splatting**: Conditional hashtable building and cleaning +6. **Error Handling**: Structured error objects with comprehensive context +7. **GraphQL Queries**: Field mapping, variable separation, and type construction +8. **Filter Usage**: Streaming pipeline behavior for all functions +9. **Pipeline Support**: Property binding with aliases and context handling +10. **Debug Output**: Comprehensive logging with stack paths and structured output + +When writing code for this module, refer to these concrete examples and follow the established patterns. diff --git a/.github/instructions/Repository/XML/main.instructions.md b/.github/instructions/Repository/XML/main.instructions.md new file mode 100644 index 000000000..b65515fc5 --- /dev/null +++ b/.github/instructions/Repository/XML/main.instructions.md @@ -0,0 +1,27 @@ +--- +description: "How to write XML code in this specific project" +applyTo: "**/*.xml, **/*.ps1xml" +--- + +## Scope +Covers PowerShell `.Format.ps1xml` and `.Types.ps1xml` for custom views & alias properties. + +## Conventions +- 2-space indentation. +- Add new type entries alphabetically by class name. +- Use alias properties for convenience accessors (e.g., `Repository` → `Name`). + +## Example Alias +```xml + + Repository + Name + +``` + +## Validation +- Import module locally to ensure no XML schema errors: `Import-Module ./src/manifest.psd1 -Force`. + +## Anti-Patterns +- ❌ Duplicate alias names. +- ❌ Out-of-order `` entries. diff --git a/.github/instructions/Repository/YAML/main.instructions.md b/.github/instructions/Repository/YAML/main.instructions.md new file mode 100644 index 000000000..505f4690f --- /dev/null +++ b/.github/instructions/Repository/YAML/main.instructions.md @@ -0,0 +1,41 @@ +--- +description: "How to write YAML code in this specific project" +applyTo: "**/*.yml, **/*.yaml" +--- + +## Project Conventions +- Workflows orchestrate module tests, lint, release. Logic lives in PowerShell scripts under `tools/` or `tests/`. +- Reusable workflows not yet adopted; prefer composite action only after >2 reuse cases. + +## Naming +- File names: `Pascal-UseCase.yml` (e.g., `Process-PSModule.yml`) +- Workflow `name:` mirrors filename (spaces allowed). + +## Permissions +- Explicit `permissions:` block in every workflow with least privileges (often `contents: read`). + +## Caching +- Cache PSModule path & PowerShell module downloads keyed by module version + OS. + +## Example Snippet +```yaml +jobs: + test: + runs-on: ubuntu-22.04 + permissions: + contents: read + steps: + - uses: actions/checkout@ + - name: Install PowerShell + uses: PowerShell/PowerShell-For-GitHub-Actions@ + - name: Run Tests + run: pwsh -File tools/utilities/Local-Testing.ps1 +``` + +## Anti-Patterns +- ❌ Embedding >30 lines script in `run:` → ✅ Move to tracked `.ps1` file. +- ❌ Using default token permissions → ✅ Declare minimal `permissions:`. +- ❌ Unpinned action versions → ✅ Pin to commit SHA. + +## Overrides (vs Org Rules) +- Allow longer step names (up to 80 chars) for clarity. diff --git a/.github/instructions/Repository/languages.instructions.md b/.github/instructions/Repository/languages.instructions.md new file mode 100644 index 000000000..e8e00342c --- /dev/null +++ b/.github/instructions/Repository/languages.instructions.md @@ -0,0 +1,30 @@ +--- +description: "Auto-generated language mapping summary" +applyTo: "**/*" +--- + +## Discovered Language Mapping (Project View) + +```json +{ + "Languages": [ + {"Language": "PowerShell", "Extensions": ["ps1","psd1","psm1"], "RepresentativeFiles": ["src/functions/public/Repositories/Get-GitHubRepository.ps1","src/manifest.psd1","tests/Repositories.Tests.ps1"], "InstructionFilesPresent": true}, + {"Language": "YAML", "Extensions": ["yml","yaml"], "RepresentativeFiles": [".github/workflows/Linter.yml",".github/dependabot.yml"], "InstructionFilesPresent": true}, + {"Language": "Markdown", "Extensions": ["md"], "RepresentativeFiles": ["README.md","CodingStandard.md"], "InstructionFilesPresent": true}, + {"Language": "JSON", "Extensions": ["json"], "RepresentativeFiles": [".github/linters/.jscpd.json"], "InstructionFilesPresent": true}, + {"Language": "XML", "Extensions": ["ps1xml","xml"], "RepresentativeFiles": ["src/formats/GitHubRepository.Format.ps1xml"], "InstructionFilesPresent": true}, + {"Language": "GitAttributes", "Extensions": ["gitattributes"], "RepresentativeFiles": [".gitattributes"], "InstructionFilesPresent": true}, + {"Language": "GitIgnore", "Extensions": ["gitignore"], "RepresentativeFiles": [".gitignore"], "InstructionFilesPresent": true} + ] +} +``` + +| Language | Repo-Specific Notes | +|----------|---------------------| +| PowerShell | Context + public/private function split is enforced (see PowerShell repository instructions). | +| YAML | Workflows must call PowerShell scripts instead of large inline `run` blocks. | +| Markdown | Include runnable examples; prefer PowerShell fenced blocks. | +| JSON | Only tooling configs currently; keep minimal & documented. | +| XML | Only format/types metadata; add new types when introducing new classes. | +| GitAttributes | Ensure LF normalization for cross-platform contributors. | +| GitIgnore | Keep patterns tight; review when adding tooling. | diff --git a/.github/instructions/Repository/main.instructions.md b/.github/instructions/Repository/main.instructions.md new file mode 100644 index 000000000..53009ed0d --- /dev/null +++ b/.github/instructions/Repository/main.instructions.md @@ -0,0 +1,726 @@ +--- +description: "Project-specific code-writing guidance for GitHub PowerShell module" +applyTo: "**/*" +--- + +# GitHub PowerShell Module Instructions + +## Repository Purpose + +The **GitHub PowerShell** module provides a PowerShell-native interface for managing and automating GitHub environments. It supports: + +- **Multiple GitHub environments**: GitHub.com, GitHub Enterprise Cloud (GHEC), and GitHub Enterprise Server (GHES) +- **GitHub Actions integration**: Context-aware execution with automatic workflow integration +- **Multiple authentication methods**: PAT, UAT, IAT, GitHub Apps +- **Pipeline-friendly commands**: Full support for PowerShell pipeline operations + +The module is designed as both a local scripting companion and a GitHub Actions workflow companion, understanding its execution context and providing appropriate defaults. + +## Architecture Overview + +### Module Structure + +``` +src/ + functions/ + public/ # Exported commands (Verb-GitHubNoun) + {ObjectType}/ # Grouped by GitHub object type (Repository, User, etc.) + private/ # Internal API wrappers and helpers + {ObjectType}/ # Mirror public structure + classes/ + public/ # GitHubRepository, GitHubUser, GitHubOwner, etc. + formats/ # Format.ps1xml for custom display + types/ # Types.ps1xml for type extensions and aliases + variables/ # Module-scope variables + loader.ps1 # Module initialization and auto-discovery + header.ps1 # Module requirements + manifest.psd1 # Module manifest + completers.ps1 # Argument completers +``` + +### Component Relationships + +``` +┌─────────────────┐ +│ Public Funcs │ (Verb-GitHubNoun) +│ - Pipeline │ - Calls Resolve-GitHubContext +│ - Context │ - Parameter validation +│ - Param sets │ - Routes to private functions +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ Private Funcs │ (Verb-GitHubNoun) +│ - No pipeline │ - Mandatory Context parameter +│ - API calls │ - Direct API endpoint mapping +│ - No defaults │ - Returns API responses +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ Invoke-GitHub- │ +│ API │ - REST API wrapper +│ - Auth handling│ - Error handling +│ - Pagination │ - Rate limiting +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ GitHub API │ +└─────────────────┘ +``` + +### Data Flow + +1. **User calls public function** → `Get-GitHubRepository -Owner 'octocat' -Name 'Hello-World'` +2. **Context resolution** → `Resolve-GitHubContext` ensures Context is `[GitHubContext]` object +3. **Parameter defaulting** → If `$Repository` missing, default from `$Context.Repo` +4. **Parameter set routing** → Switch on `$PSCmdlet.ParameterSetName` to select private function +5. **Private function call** → Pass mandatory parameters including resolved Context +6. **API invocation** → `Invoke-GitHubAPI` with method, endpoint, body +7. **Response processing** → Transform API response to typed objects (e.g., `[GitHubRepository]`) +8. **Output** → Return typed objects to pipeline + +## Project-Specific Rules + +### Function Organization + +- **Group by object type, NOT by API endpoint** + - ✅ `src/functions/public/Repositories/Get-GitHubRepository.ps1` + - ❌ `src/functions/public/Repos/Get-GitHubRepository.ps1` + +- **Object types as folder names**: + - `Repositories/` - Repository operations + - `Users/` - User operations + - `Organizations/` - Organization operations + - `Teams/` - Team operations + - `Releases/` - Release operations + - `Secrets/`, `Variables/` - GitHub Actions resources + - `Webhooks/` - Webhook operations + - `Workflows/` - GitHub Actions workflows + - `Apps/` - GitHub App operations + - `Artifacts/` - GitHub Actions artifacts + - `Environments/` - GitHub Environments + +### API Coverage Philosophy + +- **We do NOT need 100% GitHub API coverage** +- Maintain exclusion list for endpoints intentionally not covered +- Focus on commonly used operations +- Mark non-implemented endpoints as ⚠️ in coverage reports +- Examples of deliberately excluded endpoints: + - Hovercards API + - Legacy endpoints + - Rarely-used administrative operations + +### Context System + +The module uses a `GitHubContext` system to manage authentication and connection state: + +#### Context Resolution + +Every public function must: +1. Accept a `$Context` parameter of type `[object]` (allows string or GitHubContext) +2. Call `Resolve-GitHubContext -Context $Context` in `begin` block +3. Call `Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT` to validate auth + +**Example:** +```powershell +param( + [Parameter()] + [object] $Context +) + +begin { + $Context = Resolve-GitHubContext -Context $Context + Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT +} +``` + +#### Context Properties + +The resolved `[GitHubContext]` object provides: +- `$Context.Owner` - Current owner (user or organization) +- `$Context.Repo` - Current repository +- `$Context.Type` - Authentication type (PAT, UAT, IAT, APP) +- Additional context from GitHub Actions environment + +#### Parameter Defaulting from Context + +Public functions should default missing parameters from context: + +```powershell +process { + if (-not $Repository) { + $Repository = $Context.Repo + } + if (-not $Repository) { + throw "Repository not specified and not found in the context." + } + + if (-not $Owner) { + $Owner = $Context.Owner + } +} +``` + +**Private functions must NOT do this** - they receive already-resolved parameters. + +### Public vs Private Function Contract + +#### Public Functions + +- **Purpose**: User-facing API with convenience features +- **Pipeline**: Support `ValueFromPipelineByPropertyName` +- **Context**: Accept `[object] $Context`, resolve to `[GitHubContext]` +- **Parameter defaulting**: Default from context if not provided +- **Multiple parameter sets**: Route to appropriate private functions +- **Aliases**: Support common aliases on functions and parameters +- **Error handling**: User-friendly error messages + +**Structure:** +```powershell +filter Get-GitHubSomething { + [OutputType([GitHubSomething])] + [CmdletBinding(DefaultParameterSetName = 'List all')] + param( + [Parameter(Mandatory, ParameterSetName = 'Get by ID')] + [int] $ID, + + [Parameter(Mandatory, ParameterSetName = 'Get by name')] + [string] $Name, + + [Parameter()] + [object] $Context + ) + + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + $Context = Resolve-GitHubContext -Context $Context + Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT + } + + process { + $params = @{ + Context = $Context + ID = $ID + Name = $Name + } + $params | Remove-HashtableEntry -NullOrEmptyValues + + switch ($PSCmdlet.ParameterSetName) { + 'Get by ID' { + Get-GitHubSomethingByID @params + } + 'Get by name' { + Get-GitHubSomethingByName @params + } + 'List all' { + Get-GitHubSomethingList @params + } + } + } + + end { + Write-Debug "[$stackPath] - End" + } +} +``` + +#### Private Functions + +- **Purpose**: Direct API endpoint wrappers +- **Pipeline**: NO pipeline support +- **Context**: Mandatory `[GitHubContext] $Context` parameter +- **Parameters**: All required parameters are mandatory +- **No defaulting**: Caller must provide all values +- **No aliases**: No function or parameter aliases +- **One API call = One function**: Each endpoint gets its own function + +**Structure:** +```powershell +function Get-GitHubSomethingByID { + [OutputType([GitHubSomething])] + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [GitHubContext] $Context, + + [Parameter(Mandatory)] + [int] $ID + ) + + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + + $inputObject = @{ + Context = $Context + Method = 'Get' + APIEndpoint = "/something/$ID" + } + + Invoke-GitHubAPI @inputObject | ForEach-Object { + Write-Output $_.Response + } +} +``` + +### Parameter Naming Conventions + +#### Object-Oriented Naming + +Function name implies the object type, so don't repeat it in parameters: + +- ✅ `Get-GitHubRepository -ID 123` (not `-RepositoryID`) +- ✅ `Get-GitHubUser -Name 'octocat'` (not `-Username`) +- ✅ `Remove-GitHubTeam -Team 'developers'` (not `-TeamName`) + +#### Standard Aliases + +Consistently apply these aliases across all functions: + +- `$Owner` → `[Alias('Organization', 'Username')]` (never use `[Alias('org')]`) +- `$Username` → `[Alias('Login')]` where applicable +- `$Repository` → `[Alias('Repo')]` for convenience +- `$ID` → `[Alias('Id', 'id')]` if needed for compatibility + +#### API Parameter Conversion + +Transform GitHub API snake_case to PowerShell PascalCase: + +- `per_page` → `$PerPage` +- `node_id` → `$NodeID` +- `html_url` → `$Url` +- `created_at` → `$CreatedAt` +- `allow_forking` → `$AllowForking` + +### API Call Pattern + +All API calls follow this standard pattern: + +```powershell +try { + $inputObject = @{ + Context = $Context + Method = 'Post' # Use PascalCase: Get, Post, Put, Patch, Delete + APIEndpoint = "/repos/$Owner/$Repository/issues" + Body = @{ + title = $Title + body = $Body + labels = $Labels + } + } + + # Remove null values from body + $inputObject.Body | Remove-HashtableEntry -NullOrEmptyValues + + Invoke-GitHubAPI @inputObject | ForEach-Object { + Write-Output $_.Response + } +} catch { + if ($_.Exception.Response.StatusCode -eq 404) { + Write-Error "Repository '$Repository' not found for owner '$Owner'" + return + } + throw +} +``` + +**Splat order (consistent across module):** +1. `Context` +2. `Method` +3. `APIEndpoint` +4. `Body` (for POST, PUT, PATCH) + +### Class Design + +#### Base Classes + +- **GitHubNode**: Base class for objects with both `id` (database ID) and `node_id` (GraphQL ID) + - Properties: `ID` (database ID), `NodeID` (GraphQL node ID) + - All GitHub resources with these IDs should extend GitHubNode + +#### Scope Properties + +Objects that belong within a scope include properties for that scope: + +- `Enterprise` - For enterprise-scoped resources +- `Owner`/`Organization`/`Account` - For owner-scoped resources +- `Repository` - For repository-scoped resources +- `Environment` - For environment-scoped resources + +**Example:** +```powershell +class GitHubSecret : GitHubNode { + [string] $Name + [GitHubOwner] $Owner + [GitHubRepository] $Repository + [datetime] $CreatedAt + [datetime] $UpdatedAt +} +``` + +#### Convenience Properties + +Classes should have a convenience property matching the class name: + +- `GitHubRepository` has `Repository` property (alias to `Name` via types file) +- `GitHubUser` has `User` property (alias to `Login` via types file) +- `GitHubTeam` has `Team` property (alias to `Slug` via types file) + +This is done via types files (`src/types/*.Types.ps1xml`): + +```xml + + GitHubRepository + + + Repository + Name + + + +``` + +#### Property Standardization + +- **ID Properties**: + - `ID` - Primary resource ID (database ID) + - `NodeID` - GraphQL node ID + - Never use `RepositoryID`, `UserID` in class names - just `ID` + +- **Size Properties**: + - All disk size properties → `Size` in bytes + - Convert from KB/MB if needed + - Type: `[System.Nullable[uint64]]` + +- **URL Properties**: + - `Url` - Primary web URL (not `HtmlUrl`) + - `ApiUrl` - API endpoint URL if different + - Specific URLs: `Homepage`, `DocumentationUrl`, etc. + +- **Date Properties**: + - Use `[System.Nullable[datetime]]` type + - Common names: `CreatedAt`, `UpdatedAt`, `PushedAt`, `ArchivedAt` + +#### Example Class Structure + +```powershell +class GitHubRepository : GitHubNode { + # Primary identifiers + [string] $Name + [GitHubOwner] $Owner + [string] $FullName # Owner/Name format + + # URLs + [string] $Url + + # Metadata + [string] $Description + [System.Nullable[datetime]] $CreatedAt + [System.Nullable[datetime]] $UpdatedAt + [System.Nullable[uint64]] $Size + + # Features + [System.Nullable[bool]] $HasIssues + [System.Nullable[bool]] $IsPrivate + [System.Nullable[bool]] $IsArchived + + # Relationships + [GitHubLicense] $License + [string[]] $Topics +} +``` + +### Testing Patterns + +#### Test Organization + +Tests use authentication case matrix for comprehensive coverage: + +```powershell +Describe 'Feature' { + $authCases = . "$PSScriptRoot/Data/AuthCases.ps1" + + Context 'As using on ' -ForEach $authCases { + BeforeAll { + $context = Connect-GitHubAccount @connectParams -PassThru -Silent + LogGroup 'Context' { + Write-Host ($context | Format-List | Out-String) + } + } + + AfterAll { + Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount -Silent + Write-Host ('-' * 60) + } + + It 'Should perform operation' { + # Test implementation + } + } +} +``` + +#### GitHub Actions Integration + +Tests are designed to run both locally and in GitHub Actions: + +- Use `LogGroup` helper for collapsible GitHub Actions output +- Check authentication type for conditional tests +- Skip tests not applicable to certain auth types + +### GitHub Actions Integration + +#### Context Awareness + +When running in GitHub Actions, the module automatically: + +1. Imports `GITHUB_EVENT_PATH` data +2. Sets environment variables for common values +3. Detects repository, owner, workflow context +4. Provides defaults from the triggering event + +**Automatic setup in loader.ps1:** +```powershell +if ($script:IsGitHubActions) { + Write-Verbose 'Detected running on a GitHub Actions runner, preparing environment...' + $env:GITHUB_REPOSITORY_NAME = $env:GITHUB_REPOSITORY -replace '.+/' + Set-GitHubEnvironmentVariable -Name 'GITHUB_REPOSITORY_NAME' -Value $env:GITHUB_REPOSITORY_NAME + $env:GITHUB_HOST_NAME = ($env:GITHUB_SERVER_URL ?? 'github.com') -replace '^https?://' + Set-GitHubEnvironmentVariable -Name 'GITHUB_HOST_NAME' -Value $env:GITHUB_HOST_NAME + Import-GitHubEventData + Import-GitHubRunnerData +} +``` + +#### Workflow Commands + +Use module-specific functions for workflow commands: + +- `Set-GitHubOutput -Name 'result' -Value 'success'` +- `Set-GitHubEnvironmentVariable -Name 'VAR' -Value 'value'` +- `Write-GitHubNotice 'Information message'` +- `Write-GitHubWarning 'Warning message'` +- `Write-GitHubError 'Error message'` + +These use the correct workflow command syntax internally. + +### Formatting and Type Extensions + +#### Format Files (`.Format.ps1xml`) + +Define custom table and list views for display: + +```xml + + + + + GitHubRepository + + GitHubRepository + + + + + + + + + + + Name + Owner + IsPrivate + + + + + + + +``` + +#### Type Files (`.Types.ps1xml`) + +Define property aliases and computed properties: + +```xml + + + + GitHubRepository + + + Repository + Name + + + + +``` + +## Development Workflows + +### Adding a New Function + +1. Determine object type (Repository, User, Team, etc.) +2. Create public function in `src/functions/public/{ObjectType}/` +3. Create corresponding private function(s) in `src/functions/private/{ObjectType}/` +4. Add class definition if new object type in `src/classes/public/{ObjectType}/` +5. Add format file in `src/formats/` for display +6. Add type file in `src/types/` for convenience properties +7. Add tests in `tests/{Feature}.Tests.ps1` +8. Add examples in `examples/{Feature}/` + +### Function Checklist + +- [ ] Public function uses `filter` with `begin`, `process`, `end` +- [ ] All parameters have `[Parameter()]` attribute +- [ ] Context parameter is `[object]` type, resolved in `begin` +- [ ] Parameters default from context where appropriate +- [ ] Switch on parameter set name to route to private functions +- [ ] Private function has mandatory `[GitHubContext] $Context` parameter +- [ ] API call uses standard splat pattern +- [ ] Error handling with try-catch +- [ ] Comment-based help with examples +- [ ] Test coverage with auth case matrix +- [ ] No `ShouldProcess` on `Get-` commands +- [ ] Aliases applied consistently + +### Versioned Resources + +For resources with versions (like workflow files), follow this pattern: + +- **Default behavior**: Get latest version +- **Optional parameters**: + - `-Name` - Get specific version by name + - `-AllVersions` - Get all versions + +Example: `Get-GitHubWorkflow` gets the latest, `Get-GitHubWorkflow -AllVersions` lists all. + +## Key Differences from Organization Standards + +### Module-Specific Overrides + +1. **Filter over Function**: This module prefers `filter` for public functions (organization default is `function`) +2. **Context System**: Unique authentication and context management system +3. **Object Type Grouping**: Strict enforcement of grouping by object type vs endpoint +4. **Two-Tier Function Model**: Public/private split with different contracts +5. **GitHub Actions Integration**: Built-in workflow command support + +### Additional Module Patterns + +- `Get-PSCallStackPath` - Standard debug tracing +- `Remove-HashtableEntry -NullOrEmptyValues` - Clean up splatted parameters +- `LogGroup` helper for GitHub Actions output grouping +- API coverage tracking with exclusion list + +## API Coverage Philosophy + +### Intentional Exclusions + +- **We do NOT need 100% coverage of the GitHub API** +- Maintain a separate file listing endpoints you intentionally do not cover +- Coverage reports can mark excluded endpoints with ⚠️ +- Examples of commonly excluded endpoints: hovercards, rarely used features + +### Function Design Rules + +1. **One API Call = One Function**: If a single function handles multiple distinct API calls, split it into multiple functions +2. **DefaultParameterSetName Prohibition**: Do NOT declare `DefaultParameterSetName = '__AllParameterSets'` - only specify if it's actually different from the first parameter set +3. **Permissions Comment Required**: In the `begin` block, add a comment stating which permissions are required for the API call + +Example: +```powershell +begin { + # Requires: repo, read:org permissions + $Context = Resolve-GitHubContext -Context $Context + Assert-GitHubContext -Context $Context -AuthType PAT, UAT +} +``` + +## Versioned Resource Pattern + +Functions that get versioned objects return the **latest version** by default: + +- **Default behavior**: Get latest version +- **`-Name` parameter**: Get specific version by name +- **`-AllVersions` switch**: Get all versions + +Example: +```powershell +Get-GitHubWorkflow # Gets latest +Get-GitHubWorkflow -AllVersions # Gets all versions +``` + +## Class Design Rules + +### ID vs NodeID Distinction + +- **`ID` property**: The main resource identifier (GitHub's databaseID) +- **`NodeID` property**: GitHub's GraphQL node_id +- All classes using both should extend `GitHubNode` base class + +### Class Property Standards + +1. **Scope Properties**: Objects belonging inside another scope include scope properties: + - `Enterprise` - For enterprise-scoped resources + - `Owner` / `Organization` / `Account` - For organization-scoped resources + - `Repository` - For repository-scoped resources + - `Environment` - For environment-scoped resources + +2. **Convenience Property Pattern**: Classes have their class name as a property aliased to the typical command value + - Example: `GitHubRepository` class has `Repository` property (aliased via types file) with value from `Name` property + - This enables: `$repo.Repository` to get the name used in commands + +3. **Interface Consistency**: Remove properties that are purely "API wrapper" fields (e.g., raw HTTP artifacts not relevant to users) + +4. **Size Properties**: All properties referencing size on disk should: + - Be converted to bytes + - Be named `Size` + - Use type `[System.Nullable[uint64]]` + +Example class structure: +```powershell +class GitHubRepository : GitHubNode { + [string] $Name # Source for Repository alias + [GitHubOwner] $Owner # Scope property + [System.Nullable[uint64]] $Size # Size in bytes + [System.Nullable[datetime]] $CreatedAt + [System.Nullable[datetime]] $UpdatedAt + [string[]] $Topics + [GitHubLicense] $License +} +``` + +Corresponding types file entry: +```xml + + GitHubRepository + + + Repository + Name + + + +``` + +## Common Pitfalls to Avoid + +1. ❌ **Don't group by API endpoint** - Use object type folders +2. ❌ **Don't use `[Alias('org')]` on $Organization** - Use `Owner` parameter with `[Alias('Organization', 'User')]` instead +3. ❌ **Don't put defaulting logic in private functions** - Only in public functions +4. ❌ **Don't forget context resolution** - Every public function needs it +5. ❌ **Don't repeat object type in parameter names** - Function name implies it (use `Get-GitHubRepository -ID` not `-RepositoryID`) +6. ❌ **Don't use Write-Host** - Use Write-Output/Debug/Verbose (except workflow commands) +7. ❌ **Don't add ShouldProcess to Get commands** - Only for destructive operations +8. ❌ **Don't use pipeline in private functions** - Public functions only +9. ❌ **Don't forget to remove null values from body** - Use `Remove-HashtableEntry` +10. ❌ **Don't use string redundant checks** - Use `-not $Param` or validation attributes +11. ❌ **Don't create multiple API calls in one function** - Split into separate functions +12. ❌ **Don't use `DefaultParameterSetName = '__AllParameterSets'`** - Only use if actually different from first set +13. ❌ **Don't forget permissions comment in begin block** - Document required permissions diff --git a/.github/instructions/organization/GitAttributes/main.instructions.md b/.github/instructions/organization/GitAttributes/main.instructions.md new file mode 100644 index 000000000..6e95b5da0 --- /dev/null +++ b/.github/instructions/organization/GitAttributes/main.instructions.md @@ -0,0 +1,25 @@ +--- +description: "Code-writing guidelines for .gitattributes in organization projects" +applyTo: "**/.gitattributes" +--- + +## Goals +Ensure consistent line endings, diff behavior, and linguist overrides. + +## Patterns +- Normalize text: `* text=auto eol=lf` +- Enforce PowerShell scripts LF: `*.ps1 text eol=lf` +- Binary detection: mark images `*.png binary` +- Generated files (if any) can be labeled `linguist-generated=true` + +## Example +``` +* text=auto eol=lf +*.ps1 text eol=lf +*.ps1xml text eol=lf +*.png binary +``` + +## Forbidden +- ❌ Mixing CRLF & LF rules for same pattern +- ❌ Redundant identical patterns diff --git a/.github/instructions/organization/GitIgnore/main.instructions.md b/.github/instructions/organization/GitIgnore/main.instructions.md new file mode 100644 index 000000000..ca1b92f7c --- /dev/null +++ b/.github/instructions/organization/GitIgnore/main.instructions.md @@ -0,0 +1,34 @@ +--- +description: "Code-writing guidelines for .gitignore in organization projects" +applyTo: "**/.gitignore" +--- + +## Principles +Ignore build artifacts, personal tooling files, and temporary caches while tracking source & config. + +## Ordering +1. Core language / platform ignores +2. Tooling (editors, analyzers) +3. Local environment / caches +4. Exceptions (negated patterns) immediately after related block + +## Patterns +- Use trailing slash for directories (`temp/`) +- Use wildcard for extension groups (`*.log`) +- Add comments (`# Tests coverage artifacts`) + +## Example Block +``` +# PowerShell +*.ps1xml + +# Local +*.local.ps1 + +# Logs +*.log +``` + +## Forbidden +- ❌ Ignoring required source (e.g., `src/`) +- ❌ Broad `*` patterns without justification diff --git a/.github/instructions/organization/JSON/main.instructions.md b/.github/instructions/organization/JSON/main.instructions.md new file mode 100644 index 000000000..960e9a501 --- /dev/null +++ b/.github/instructions/organization/JSON/main.instructions.md @@ -0,0 +1,34 @@ +--- +description: "Code-writing guidelines for JSON in organization projects" +applyTo: "**/*.json" +--- + +## Style & Formatting +- Indentation: 2 spaces +- Trailing commas: disallowed +- Property order: stable logical grouping (meta → config → rules) to minimize diffs +- Strings: double quotes only +- Booleans: lowercase true/false +- Nulls: omit property instead of `null` unless consumer requires it + +## Patterns +- Keep comments out (JSONC not permitted) – document in adjacent README or Markdown comment above snippet. +- Break large arrays ( >10 items ) across lines, one element per line. + +## Validation +- Run through schema or tooling (e.g., jscpd config) before commit. + +## Example +```json +{ + "$schema": "https://example/schema.json", + "tool": "jscpd", + "threshold": 5, + "languages": ["javascript", "powershell"] +} +``` + +## Forbidden +- ❌ Duplicate keys +- ❌ Trailing commas +- ❌ Reordering unrelated properties in the same commit diff --git a/.github/instructions/organization/Markdown/main.instructions.md b/.github/instructions/organization/Markdown/main.instructions.md new file mode 100644 index 000000000..1617e66bf --- /dev/null +++ b/.github/instructions/organization/Markdown/main.instructions.md @@ -0,0 +1,49 @@ +--- +description: "Code-writing guidelines for Markdown in organization projects" +applyTo: "**/*.md" +--- + +## Style & Formatting +- Headings: Start at `#` for document title, never skip levels. +- Line length: wrap prose ~120 chars (soft), code blocks unwrapped. +- Lists: use `-` for unordered, `1.` for ordered (auto-increment). +- Code fences: always specify language (e.g., ```powershell, ```yaml). +- Blank lines: one blank line before/after headings, lists, and code fences. + +## Links +- Relative links for repo targets (`./path/file.md`). +- Use reference-style only when reused >= 3 times. + +## Tables +- Align with `|` separators, no trailing spaces inside cells. + +## Inline Code & Escapes +- Use backticks for parameter names / commands. + +## Examples Section Pattern +```markdown +### Example +```powershell +Get-GitHubRepository -Owner org -Name repo +``` +Short explanation. +``` + +## Bad vs Good +```markdown + +```powershell +Get-GitHubRepository +``` +No context. + + +```powershell +Get-GitHubRepository -Owner Contoso -Name Portal +``` +Returns repository object with metadata. +``` + +## Forbidden +- ❌ HTML for basic formatting → ✅ Pure Markdown. +- ❌ Mixed heading styles (`Setext` + `ATX`) → ✅ Use ATX only. diff --git a/.github/instructions/organization/PowerShell/main.instructions.md b/.github/instructions/organization/PowerShell/main.instructions.md new file mode 100644 index 000000000..f52e6a6d1 --- /dev/null +++ b/.github/instructions/organization/PowerShell/main.instructions.md @@ -0,0 +1,821 @@ +--- +description: "Code-writing guidelines for PowerShell in organization projects" +applyTo: "**/*.ps1, **/*.psd1, **/*.psm1" +--- + +# PowerShell Instructions + +## Style & Formatting + +- **Indentation**: 4 spaces (no tabs) +- **Max line length**: No hard limit, but prefer readability (PSScriptAnalyzer checks for excessively long lines) +- **Trailing whitespace**: Disallowed +- **Braces**: One True Brace Style (OTBS) + - Opening brace on same line as statement + - Closing brace on its own line +- **PowerShell Keywords**: Always lowercase + - ✅ `if`, `else`, `return`, `function`, `param`, `foreach`, `while`, `try`, `catch` + - ❌ `If`, `ELSE`, `Return`, `Function` + +**Example:** +````powershell +# BEFORE: incorrect formatting +Function Get-Data { +Return $data +} + +# AFTER: correct formatting +function Get-Data { + if ($condition) { + return $data + } +} +```` + +## Function Structure + +All functions must use this structure: + +````powershell +filter FunctionName { + <# + .SYNOPSIS + Brief description + + .DESCRIPTION + Detailed description + + .EXAMPLE + ```powershell + FunctionName -Parameter 'value' + ``` + + Description of example + #> + [OutputType([ReturnType])] + [CmdletBinding()] + param( + [Parameter()] + [string] $Parameter + ) + + begin { + # Initialization, validation, context resolution + } + + process { + # Main logic, handles pipeline + } + + end { + # Cleanup if needed + } +} +```` + +### Function Declaration Rules + +- **Filter vs Function**: Use `filter` for pipeline-enabled functions, `function` only when explicitly needed +- **Always Include**: `begin`, `process`, `end` blocks even if some are empty +- **CmdletBinding**: Always include `[CmdletBinding()]` attribute +- **OutputType**: Always declare `[OutputType([Type])]` with the actual return type + +## Parameter Guidelines + +### Parameter Declaration + +Every parameter must have explicit attributes in **this specific order**: + +1. **`[Parameter()]`** - Always present, even if empty +2. **Validation attributes** - `[ValidateNotNullOrEmpty()]`, `[ValidateSet()]`, `[ValidateRange()]`, etc. +3. **`[Alias()]`** - If applicable +4. **Type and parameter name** - `[type] $ParameterName` + +This order is enforced by PSScriptAnalyzer and ensures consistency across all functions. + +**Example:** +````powershell +param( + # The account owner + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [ValidateNotNullOrEmpty()] + [Alias('Organization', 'Username')] + [string] $Owner, + + # Optional parameter with default + [Parameter()] + [System.Nullable[int]] $PerPage, + + # Switch parameter + [Parameter()] + [switch] $Force +) +```` + +### Parameter Types + +- **Always specify types**: Never use untyped parameters +- **Nullable types**: Use `[System.Nullable[int]]` for optional numeric parameters +- **Arrays**: Specify element type: `[string[]]`, `[int[]]` +- **Objects**: Use `[object]` only when type varies; prefer specific types +- **Switches**: Use `[switch]` type, never `[bool]` + +### Parameter Naming Standards + +- **PascalCase**: All parameters use PascalCase +- **Convert from API**: Transform snake_case API parameters to PascalCase + - API: `per_page` → PowerShell: `$PerPage` + - API: `node_id` → PowerShell: `$NodeID` + - API: `html_url` → PowerShell: `$Url` + +- **Avoid Redundancy**: Don't repeat the object type in parameter names + - ✅ `Get-GitHubRepository -ID 123` (not `-RepositoryID`) + - ✅ `Get-GitHubUser -Name 'octocat'` (not `-Username`) + +- **Standard Aliases**: + - `$Owner` should have `[Alias('Organization', 'Username')]` + - `$ID` can have `[Alias('Id', 'id')]` if needed for compatibility + - `$Repository` can have `[Alias('Repo')]` for convenience + +### Parameter Sets + +- **Declare explicitly**: Use `DefaultParameterSetName` only if not the first set +- **Never use**: `DefaultParameterSetName = '__AllParameterSets'` +- **Name clearly**: Use descriptive names like `'Get a repository by name'` + +**Example:** +````powershell +[CmdletBinding(DefaultParameterSetName = 'List all repositories')] +param( + [Parameter(Mandatory, ParameterSetName = 'Get by ID')] + [int] $ID, + + [Parameter(Mandatory, ParameterSetName = 'Get by name')] + [string] $Name +) +```` + +## Patterns (Do / Don't) + +### String Validation + +````powershell +# BEFORE: verbose string checking +if ([string]::IsNullOrEmpty($Parameter)) { + throw "Parameter required" +} + +# AFTER: use built-in validation +param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $Parameter +) + +# Or simple null check +if (-not $Parameter) { + throw "Parameter required" +} +```` + +### Hashtable Cleanup + +````powershell +# BEFORE: manual null checking +if ($null -ne $Value1) { $hash.Value1 = $Value1 } +if ($null -ne $Value2) { $hash.Value2 = $Value2 } + +# AFTER: use helper function +$hash = @{ + Value1 = $Value1 + Value2 = $Value2 +} +$hash | Remove-HashtableEntry -NullOrEmptyValues +```` + +### Pipeline Support + +````powershell +# BEFORE: array collection +function Get-Items { + $results = @() + foreach ($item in $collection) { + $results += Process-Item $item + } + return $results +} + +# AFTER: streaming output +filter Get-Items { + process { + foreach ($item in $collection) { + Process-Item $item | Write-Output + } + } +} +```` + +## Error Handling + +### Try-Catch Blocks + +- Wrap all API calls in try-catch +- Include context in error messages +- Use `throw` for fatal errors +- Use `Write-Error` for non-fatal errors + +**Example:** +````powershell +try { + $result = Invoke-GitHubAPI @params + Write-Output $result.Response +} catch { + if ($_.Exception.Response.StatusCode -eq 404) { + Write-Error "Repository '$Name' not found in owner '$Owner'" + return + } + throw +} +```` + +### ShouldProcess + +- **Only for destructive operations**: `Set-*`, `Remove-*`, `New-*`, `Update-*` +- **Never for Get operations**: `Get-*` commands never use ShouldProcess +- **Implementation**: + +````powershell +[CmdletBinding(SupportsShouldProcess)] +param( + [Parameter(Mandatory)] + [string] $Name +) + +process { + if ($PSCmdlet.ShouldProcess($Name, 'Delete repository')) { + Invoke-GitHubAPI @params + } +} +```` + +## Testing + +### Test Framework + +- **Framework**: Pester 5.x +- **Test files**: `{Feature}.Tests.ps1` in `tests/` directory +- **Required version**: Declare in `#Requires` statement + +**Example:** +````powershell +#Requires -Modules @{ ModuleName = 'Pester'; RequiredVersion = '5.7.1' } + +BeforeAll { + # Setup code +} + +Describe 'Get-GitHubRepository' { + Context 'When getting by name' { + It 'Returns the repository' { + $result = Get-GitHubRepository -Owner 'octocat' -Name 'Hello-World' + $result.Name | Should -Be 'Hello-World' + } + } +} +```` + +### Assertion Patterns + +- Use Pester assertions: `Should -Be`, `Should -Not -BeNullOrEmpty`, etc. +- Test positive and negative cases +- Mock external calls appropriately + +### Test Organization + +````powershell +Describe 'Feature' { + $authCases = . "$PSScriptRoot/Data/AuthCases.ps1" + + Context 'As using ' -ForEach $authCases { + BeforeAll { + # Setup per context + } + + It 'Does something' { + # Test assertion + } + + AfterAll { + # Cleanup per context + } + } +} +```` + +## Build Frameworks + +- **Build tool**: Native PowerShell module loading +- **PSModule framework**: Custom build and release automation +- **Manifest**: All configuration in `src/manifest.psd1` + +## CI Frameworks + +- **GitHub Actions**: Primary CI/CD platform +- **Workflow files**: `.github/workflows/` +- **PSModule.yml**: Organization-wide CI configuration + +## Logging & Telemetry + +### Logging Levels + +- **Write-Debug**: Detailed diagnostic information (use `$DebugPreference`) + - Parameter values + - API endpoints being called + - Context resolution details + +- **Write-Verbose**: General progress information + - High-level operation flow + - Initialization messages + +- **Write-Warning**: Non-fatal issues + - Deprecated features + - Fallback behaviors + +- **Write-Error**: Errors that don't stop execution + - Optional operations that failed + - Validation failures with -ErrorAction Continue + +**Example:** +````powershell +begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + $Context = Resolve-GitHubContext -Context $Context + Write-Verbose "Resolved context: $($Context.Type)" +} + +process { + if ($DebugPreference -eq 'Continue') { + Write-Debug "ParamSet: [$($PSCmdlet.ParameterSetName)]" + [pscustomobject]$params | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + } +} +```` + +### GitHub Actions Integration + +For GitHub Actions workflow commands, use module-specific functions: +- `Set-GitHubOutput` - Set workflow outputs +- `Set-GitHubEnvironmentVariable` - Set environment variables +- `Write-GitHubNotice` / `Write-GitHubWarning` / `Write-GitHubError` - Workflow annotations + +## Performance + +### String Concatenation + +````powershell +# BEFORE: O(n²) string concatenation +$result = "" +foreach ($item in $items) { + $result += "$item`n" +} + +# AFTER: efficient string building +$result = $items -join "`n" +# Or for complex scenarios +$result = [System.Text.StringBuilder]::new() +foreach ($item in $items) { + [void]$result.AppendLine($item) +} +$result.ToString() +```` + +### Array Building + +````powershell +# BEFORE: expensive array growth +$results = @() +foreach ($item in $collection) { + $results += Process-Item $item +} + +# AFTER: use ArrayList or stream +$results = [System.Collections.ArrayList]@() +foreach ($item in $collection) { + [void]$results.Add((Process-Item $item)) +} + +# BETTER: stream with pipeline +filter Process-Collection { + process { + foreach ($item in $collection) { + Process-Item $item + } + } +} +```` + +### Unnecessary Variable Creation + +````powershell +# BEFORE: unnecessary intermediate variables +$apiResult = Invoke-GitHubAPI @params +$response = $apiResult.Response +Write-Output $response + +# AFTER: direct pipeline +Invoke-GitHubAPI @params | Select-Object -ExpandProperty Response +```` + +### Pipeline Output Pattern + +When extracting properties from API responses, choose between two patterns based on context: + +**Pattern 1: ForEach-Object with explicit output** (preferred for complex transformations) +````powershell +Invoke-GitHubAPI @params | ForEach-Object { + Write-Output $_.Response +} +```` + +**Pattern 2: Select-Object -ExpandProperty** (preferred for simple property extraction) +````powershell +Invoke-GitHubAPI @params | Select-Object -ExpandProperty Response +```` + +**When to use each:** +- Use **ForEach-Object** when you need to: + - Transform or process each item + - Add additional logic or filtering + - Make explicit what's being output for debugging + - Ensure proper streaming behavior + +- Use **Select-Object -ExpandProperty** when you need to: + - Simply extract a property with no transformation + - Keep the pipeline concise + - Extract from a single object (not a collection) + +## Dependencies + +### Module Requirements + +- Declare all dependencies in `src/header.ps1`: + ````powershell + #Requires -Modules @{ ModuleName = 'ModuleName'; RequiredVersion = 'x.y.z' } + ```` + +- Use specific versions, not minimum versions +- Keep dependencies minimal + +### Package Sources + +- **PowerShell Gallery**: Primary source for modules +- **Install command**: `Install-PSResource -Name ModuleName -Repository PSGallery -TrustRepository` + +## Documentation + +### Comment-Based Help + +Required sections for all public functions: + +````powershell +<# + .SYNOPSIS + One-line description. + + .DESCRIPTION + Detailed description of functionality. + + .EXAMPLE + ```powershell + Function-Name -Parameter 'value' + ``` + + Description of what this example demonstrates. + + .PARAMETER Parameter + Description of the parameter. + + .INPUTS + GitHubOwner + + .OUTPUTS + GitHubRepository + + .LINK + https://psmodule.io/ModuleName/Functions/Function-Name/ + + .NOTES + Link to official API documentation if applicable. +#> +```` + +### Documentation Conventions + +#### Examples with Triple Backticks + +**CRITICAL**: When writing `.EXAMPLE` sections in PSModule organization projects, **always use triple backticks** (` ``` `) around code blocks: + +````powershell +.EXAMPLE +```powershell +Get-GitHubRepository -Owner 'octocat' -Name 'Hello-World' +``` + +Gets the Hello-World repository from the octocat account. +```` + +**Why**: The PSModule framework automatically removes default PowerShell help fences during documentation generation. Without explicit triple backticks, code examples will not render correctly in generated documentation. + +❌ **WRONG** (will not render properly): +```powershell +.EXAMPLE +Get-GitHubRepository -Owner 'octocat' + +Gets a repository. +``` + +✅ **RIGHT** (renders correctly): +````powershell +.EXAMPLE +```powershell +Get-GitHubRepository -Owner 'octocat' +``` + +Gets a repository. +```` + +#### Inline Parameter Documentation + +Use inline comments with `///` for parameter descriptions, **not** separate `.PARAMETER` comment blocks: + +**Example:** +````powershell +param( + /// The account owner of the repository (organization or user) + [Parameter(Mandatory)] + [string] $Owner, + + /// The name of the repository + [Parameter(Mandatory)] + [string] $Name +) +```` + +This pattern keeps parameter documentation colocated with the parameter definition for better maintainability. + +#### .NOTES Section + +The `.NOTES` section should include a link to the official API documentation: + +````powershell +.NOTES +[List repositories for a user](https://docs.github.com/rest/repos/repos#list-repositories-for-a-user) +```` + +#### .LINK Section + +The `.LINK` section should list local documentation first, then official API documentation: + +````powershell +.LINK +https://psmodule.io/GitHub/Functions/Get-GitHubRepository/ + +.NOTES +[Get a repository](https://docs.github.com/rest/repos/repos#get-a-repository) +```` + +### Inline Documentation + +- Use `#` for single-line explanatory comments +- Document required permissions in `begin` block: + ````powershell + begin { + # Requires: repo scope for private repositories + } + ```` + +## Snippets + +### Public Function Boilerplate + +````powershell +filter Verb-ModuleNoun { + <# + .SYNOPSIS + Brief description + + .DESCRIPTION + Detailed description + + .EXAMPLE + ```powershell + Verb-ModuleNoun -Parameter 'value' + ``` + + Example description + #> + [OutputType([ReturnType])] + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string] $Parameter, + + [Parameter()] + [object] $Context + ) + + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + $Context = Resolve-GitHubContext -Context $Context + Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT + } + + process { + try { + $params = @{ + Context = $Context + Parameter = $Parameter + } + $params | Remove-HashtableEntry -NullOrEmptyValues + + Invoke-PrivateFunction @params + } catch { + throw + } + } + + end { + Write-Debug "[$stackPath] - End" + } +} +```` + +### Private Function Boilerplate + +````powershell +function Verb-ModulePrivateNoun { + [OutputType([ReturnType])] + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [GitHubContext] $Context, + + [Parameter(Mandatory)] + [string] $Parameter + ) + + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + + try { + $inputObject = @{ + Context = $Context + Method = 'Get' + APIEndpoint = "/endpoint" + } + + Invoke-GitHubAPI @inputObject | ForEach-Object { + Write-Output $_.Response + } + } catch { + throw + } +} +```` + +### Test Boilerplate + +````powershell +#Requires -Modules @{ ModuleName = 'Pester'; RequiredVersion = '5.7.1' } + +BeforeAll { + # Setup +} + +Describe 'Feature' { + Context 'When condition' { + BeforeAll { + # Context setup + } + + It 'Should do something' { + # Arrange + $expected = 'value' + + # Act + $result = Do-Something + + # Assert + $result | Should -Be $expected + } + + AfterAll { + # Context cleanup + } + } +} +```` + +### API Call Pattern + +When building the splat for `Invoke-GitHubAPI`, parameters must be in this specific order: + +1. **Method** - The HTTP method (PascalCase: `'Get'`, `'Post'`, `'Put'`, `'Delete'` - not `'GET'`, `'POST'`, etc.) +2. **APIEndpoint** - The API endpoint path +3. **Body** - The request body (if applicable) +4. **Context** - The GitHub context object + +**Example:** +````powershell +try { + $inputObject = @{ + Context = $Context + Method = 'Post' + APIEndpoint = "/repos/$Owner/$Repository/issues" + Body = @{ + title = $Title + body = $Body + } + } + + Invoke-GitHubAPI @inputObject | ForEach-Object { + Write-Output $_.Response + } +} catch { + if ($_.Exception.Response.StatusCode -eq 404) { + Write-Error "Resource not found: $Owner/$Repository" + return + } + throw +} +```` + +**Critical**: Method values must use PascalCase (`Post`, `Delete`, `Put`, `Get`), not uppercase (`POST`, `DELETE`, `PUT`, `GET`). + +## Forbidden + +### Anti-Patterns + +- ❌ **Untyped parameters**: Always specify parameter types + ```powershell + # WRONG + param($Parameter) + + # RIGHT + param([string] $Parameter) + ``` + +- ❌ **Uppercase PowerShell keywords**: All keywords must be lowercase + ```powershell + # WRONG + Function Get-Data { Return $data } + + # RIGHT + function Get-Data { return $data } + ``` + +- ❌ **Missing [Parameter()] attribute**: All parameters must have it + ```powershell + # WRONG + param([string] $Name) + + # RIGHT + param( + [Parameter()] + [string] $Name + ) + ``` + +- ❌ **ShouldProcess on Get commands**: Never use with Get-* functions + ```powershell + # WRONG + function Get-GitHubRepository { + [CmdletBinding(SupportsShouldProcess)] + param(...) + } + + # RIGHT + function Get-GitHubRepository { + [CmdletBinding()] + param(...) + } + ``` + +- ❌ **String redundant checks**: Use validation attributes or simple null checks + ```powershell + # WRONG + if ([string]::IsNullOrEmpty($Param)) { } + + # RIGHT + if (-not $Param) { } + # OR with validation + [ValidateNotNullOrEmpty()] + [string] $Param + ``` + +- ❌ **Write-Host**: Use Write-Output, Write-Verbose, or Write-Debug instead + - Exception: GitHub Actions workflow commands via module functions + +- ❌ **DefaultParameterSetName = '__AllParameterSets'**: Never use this diff --git a/.github/instructions/organization/XML/main.instructions.md b/.github/instructions/organization/XML/main.instructions.md new file mode 100644 index 000000000..0161e6d2e --- /dev/null +++ b/.github/instructions/organization/XML/main.instructions.md @@ -0,0 +1,65 @@ +--- +description: "Code-writing guidelines for XML in organization projects" +applyTo: "**/*.xml, **/*.ps1xml" +--- + +## Scope +Covers PowerShell metadata: `.Format.ps1xml` and `.Types.ps1xml` files. + +## Formatting +- Indentation: 2 spaces +- XML Declaration: omit unless required by consumer +- Attributes: use double quotes, one space before attribute list +- Close empty elements explicitly (``) for clarity + +## Ordering +- Types files: `` entries alphabetically by `` +- Format files: Group `` entries by related object type; inside `` maintain header → rows sequence +- Alias properties: define before ScriptProperty for the same type + +## Patterns +Alias property example: +```xml + + GitHubRepository + + + Repository + Name + + + +``` + +Format table example (abbreviated): +```xml + + GitHubRepository + + GitHubRepository + + + + + + + + + + Name + Owner + + + + + +``` + +## Validation +- Ensure all referenced properties exist on classes +- Run module import to validate formatting (PowerShell will error on malformed metadata) + +## Forbidden +- ❌ Tabs for indentation +- ❌ Unsorted `` definitions +- ❌ Duplicate `` entries diff --git a/.github/instructions/organization/YAML/main.instructions.md b/.github/instructions/organization/YAML/main.instructions.md new file mode 100644 index 000000000..bc0428685 --- /dev/null +++ b/.github/instructions/organization/YAML/main.instructions.md @@ -0,0 +1,70 @@ +--- +description: "Code-writing guidelines for YAML in organization projects" +applyTo: "**/*.yml, **/*.yaml" +--- + +## Style & Formatting +- Indentation: 2 spaces (never tabs, never 4) +- Max line length: keep under 140 chars where practical +- Keys: lowercase-hyphen-case for workflow/job/step names; camelCase for schema-defined keys +- Quoting: use double quotes only when interpolation, colon, or leading/trailing spaces present; prefer unquoted otherwise +- Booleans: lower-case `true` / `false` +- Null: use explicit `null` only when required; otherwise omit key +- Arrays: prefer block style (`- item`) on separate lines for >1 elements + +## File Structure (GitHub Workflows) +Required top order: +1. `name` +2. `on` +3. `env` (optional) +4. `permissions` (least privilege) +5. `jobs` + +Within a `job`: +1. `name` +2. `runs-on` +3. `needs` (if any) +4. `permissions` (override) +5. `env` +6. `steps` + +Within a `step` order keys: `name`, `id`, `if`, `uses` | `run`, `shell`, `env`, `with`. + +## Patterns (Do / Don't) +- ✅ Reuse actions with pinned SHAs (`actions/checkout@`) not moving tags. +- ✅ Factor repeated env into job `env:` +- ❌ Avoid long multi-line inline scripts; move to PowerShell `.ps1` in repo. +- ❌ Don't use `ubuntu-latest` for matrix; pin explicit version for stability. + +## Matrix Strategy +- Always include `fail-fast: false` +- Keep each axis <= 5 values to control build fan-out. + +## Anchors & Aliases +- Allowed only for large repetition (3+ identical blocks). Name anchors with UPPER_CASE. + +## Error Handling +- Prefer `continue-on-error: false` (default). Use `|| true` only for optional metrics steps. + +## Secrets & Security +- Never echo secrets. Mask outputs in PowerShell using `Set-GitHubOutput` patterns. +- Use minimal `permissions:` section; never rely on implicit token scopes. + +## Performance +- Use caching (`actions/cache`) keyed by tool version + lock file hash. +- Avoid redundant checkout per job. + +## Validation +- Run workflow linter locally before commit when adding new workflows. + +## Example (Good Step) +```yaml +- name: Run Unit Tests + run: pwsh -File tools/utilities/Local-Testing.ps1 + env: + PSModuleVersion: ${{ env.MODULE_VERSION }} +``` + +## Forbidden +- ❌ Unpinned 3rd-party actions → ✅ Pin by full commit SHA. +- ❌ Writing secrets to logs → ✅ Use workflow commands for outputs. diff --git a/.github/instructions/organization/languages.instructions.md b/.github/instructions/organization/languages.instructions.md new file mode 100644 index 000000000..55028cf21 --- /dev/null +++ b/.github/instructions/organization/languages.instructions.md @@ -0,0 +1,88 @@ +--- +description: "Auto-generated language mapping summary" +applyTo: "**/*" +--- + +## Discovered Language Mapping + +JSON structure (authoritative for automation): +```json +{ + "Languages": [ + { + "Language": "PowerShell", + "Extensions": ["ps1", "psd1", "psm1"], + "RepresentativeFiles": [ + "src/functions/public/Repositories/Get-GitHubRepository.ps1", + "src/manifest.psd1", + "tests/Repositories.Tests.ps1" + ], + "InstructionFilesPresent": true + }, + { + "Language": "YAML", + "Extensions": ["yml", "yaml"], + "RepresentativeFiles": [ + ".github/workflows/Linter.yml", + ".github/dependabot.yml" + ], + "InstructionFilesPresent": true + }, + { + "Language": "Markdown", + "Extensions": ["md"], + "RepresentativeFiles": [ + "README.md", + "CodingStandard.md", + "tests/README.md" + ], + "InstructionFilesPresent": true + }, + { + "Language": "JSON", + "Extensions": ["json"], + "RepresentativeFiles": [ + ".github/linters/.jscpd.json" + ], + "InstructionFilesPresent": true + }, + { + "Language": "XML", + "Extensions": ["ps1xml", "xml"], + "RepresentativeFiles": [ + "src/formats/GitHubRepository.Format.ps1xml", + "src/types/GitHubRepository.Types.ps1xml" + ], + "InstructionFilesPresent": true + }, + { + "Language": "GitAttributes", + "Extensions": ["gitattributes"], + "RepresentativeFiles": [ + ".gitattributes" + ], + "InstructionFilesPresent": true + }, + { + "Language": "GitIgnore", + "Extensions": ["gitignore"], + "RepresentativeFiles": [ + ".gitignore" + ], + "InstructionFilesPresent": true + } + ] +} +``` + +| Language | Extensions | Representative Files (subset) | Org File | Repo File | Notes | +|---------------|--------------------------------|------------------------------------------------------------------|---------|----------|-------| +| PowerShell | ps1, psd1, psm1 | src/functions/public/Repositories/Get-GitHubRepository.ps1 | Yes | Yes | Includes tests & manifest | +| YAML | yml, yaml | .github/workflows/Linter.yml | Yes | Yes | Workflows + dependabot | +| Markdown | md | README.md | Yes | Yes | Docs & tests README | +| JSON | json | .github/linters/.jscpd.json | Yes | Yes | Tooling config | +| XML | ps1xml, xml | src/formats/GitHubRepository.Format.ps1xml | Yes | Yes | PowerShell metadata | +| GitAttributes | gitattributes | .gitattributes | Yes | Yes | Text normalization rules | +| GitIgnore | gitignore | .gitignore | Yes | Yes | Ignore patterns | + +All discovered textual extensions have instruction coverage. Binary/image assets intentionally excluded from mapping. diff --git a/.github/instructions/organization/main.instructions.md b/.github/instructions/organization/main.instructions.md new file mode 100644 index 000000000..d737a1bbe --- /dev/null +++ b/.github/instructions/organization/main.instructions.md @@ -0,0 +1,135 @@ +--- +description: "Universal code-writing guidelines for the organization" +applyTo: "**/*" +--- + +# PSModule Organization Instructions + +## File Organization + +- **Module Structure**: Follow PowerShell module best practices with clear separation of concerns + - `src/` - All source code + - `tests/` - All test files + - `examples/` - Usage examples + - `tools/` - Development and utility scripts + - `icon/` - Module branding assets + +- **Source Directory Layout**: + ``` + src/ + functions/ + public/ # Exported functions + private/ # Internal helper functions + classes/ + public/ # Exported classes + formats/ # .Format.ps1xml files + types/ # .Types.ps1xml files + variables/ # Module-level variables + loader.ps1 # Module initialization + header.ps1 # Module requirements and headers + manifest.psd1 # Module manifest + completers.ps1 # Argument completers + ``` + +- **File Placement Rules**: + - One function per file, named exactly as the function (e.g., `Get-GitHubRepository.ps1`) + - One class per file, named after the class (e.g., `GitHubRepository.ps1`) + - Group related functions in folders by object type, not by API endpoint + - Test files follow naming: `{Feature}.Tests.ps1` + +## Naming Conventions + +- **Files**: PascalCase matching the function/class name exactly + - ✅ `Get-GitHubRepository.ps1` + - ❌ `get-githubrepository.ps1` + - ❌ `GetGitHubRepository.ps1` + +- **Functions**: Verb-Noun format with module prefix + - Public: `Verb-ModuleNoun` (e.g., `Get-GitHubRepository`) + - Private: `Verb-ModuleNoun` (same format, no export) + - Use approved PowerShell verbs only + +- **Parameters**: PascalCase + - ✅ `$Owner`, `$Repository`, `$PerPage` + - ❌ `$owner`, `$repo_name`, `$per_page` + +- **Variables**: camelCase for local variables, PascalCase for parameters + - ✅ `$apiEndpoint`, `$userData` + - ❌ `$APIEndpoint`, `$api_endpoint` + +- **Classes**: PascalCase with module prefix + - ✅ `GitHubRepository`, `GitHubOwner` + - ❌ `githubRepository`, `GitHub_Repository` + +- **Properties**: PascalCase + - ✅ `$repo.Name`, `$repo.FullName` + - ❌ `$repo.name`, `$repo.full_name` + +## Documentation Standards + +- **Comment-Based Help**: Required for all public functions + - Use `.SYNOPSIS`, `.DESCRIPTION`, `.PARAMETER`, `.EXAMPLE`, `.INPUTS`, `.OUTPUTS`, `.LINK` + - Place before function definition + - Always include at least one `.EXAMPLE` with fenced code blocks (```) + +- **Example Format**: + ````powershell + <# + .SYNOPSIS + Brief one-line description. + + .DESCRIPTION + Detailed description of what the function does. + + .EXAMPLE + ```powershell + Get-Something -Name 'example' + ``` + + Description of what this example does. + + .PARAMETER Name + Description of the Name parameter. + + .LINK + https://psmodule.io/ModuleName/Functions/Get-Something/ + #> + ```` + +- **Inline Comments**: Use `#` for single-line comments explaining complex logic +- **Permission Comments**: Always document required API permissions in the `begin` block + +## Build Integration + +- **Module Manifest**: Central configuration in `src/manifest.psd1` +- **Loader Pattern**: Use `src/loader.ps1` for module initialization +- **Header Requirements**: Declare module dependencies in `src/header.ps1` +- **Auto-Loading**: Functions and classes are auto-discovered and loaded + +## Code Quality Standards + +- **Linting**: All code must pass PSScriptAnalyzer rules +- **SuppressMessageAttribute**: Only use when justified with clear reasoning + ```powershell + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSAvoidUsingWriteHost', '', + Justification = 'Required for GitHub Actions output.' + )] + ``` + +- **Error Handling**: Use structured error handling (see language-specific instructions) +- **Verbose Output**: Use `Write-Debug` and `Write-Verbose` appropriately +- **Progress Indicators**: Use `Write-Progress` for long-running operations + +## Version Control + +- **Git Workflow**: Feature branches, pull requests, code reviews +- **Commit Messages**: Clear, descriptive commit messages +- **Branching**: `main` as default branch, feature branches for development + +## Testing Requirements + +- **Test Coverage**: Aim for high coverage, but 100% is not required +- **Test Organization**: Mirror source structure in tests directory +- **Test Naming**: `{Feature}.Tests.ps1` format +- **Test Framework**: See language-specific instructions for framework details diff --git a/.github/prompts/framework/Update-Instructions.prompt.md b/.github/prompts/framework/Update-Instructions.prompt.md index 23053edbf..585d3ae9d 100644 --- a/.github/prompts/framework/Update-Instructions.prompt.md +++ b/.github/prompts/framework/Update-Instructions.prompt.md @@ -10,28 +10,92 @@ $ARGUMENTS Goal: Generate and maintain a comprehensive instruction system that provides specific, actionable guidance for writing code. The system separates organization-level patterns (automation-managed) from project-specific implementations (manually curated). +### Folder Structure Specification + +The `.github/` folder contains three Copilot configuration directories, each supporting the three-tier structure: + +``` +.github/ +├─instructions/ # AI agent guidance and patterns +│ ├─enterprise/ # Enterprise-wide standard customizations +│ │ ├─main.instructions.md # Enterprise-wide style and coding guidelines +│ │ └─{Language}/ # Enterprise-wide language-specific patterns +│ │ ├─main.instructions.md # Enterprise-wide language-specific general style guides +│ │ └─{component}.instructions.md # Enterprise-wide language-specific component requirements (i.e. classes, tests) +│ ├─organization/ # Organization-wide standard customizations +│ │ ├─main.instructions.md # Organization-wide style and coding guidelines +│ │ └─{Language}/ # Organization-wide language-specific patterns +│ │ ├─main.instructions.md # Organization-wide language-specific general style guides +│ │ └─{component}.instructions.md # Organization-wide language-specific component requirements (i.e. classes, tests) +│ └─repository/ # Repository-specific overrides +│ ├─main.instructions.md # Repository-specific style and coding guidelines +│ └─{Language}/ # Repository-specific language-specific patterns +│ ├─main.instructions.md # Repository-specific language-specific general style guides +│ └─{component}.instructions.md # Repository-specific language-specific component requirements (i.e. classes, tests) +├─prompts/ # Reusable prompts +│ ├─enterprise/ # Enterprise-wide prompts +│ │ └─{prompt-files} # +│ ├─organization/ # Organization-wide prompts +│ │ └─{prompt-files} # +│ └─repository/ # Repository-specific prompts +│ └─{prompt-files} # +└─chatmodes/ # Specialized chat modes + ├─enterprise/ # Enterprise-wide chat modes + │ └─{chatmode-files} # + ├─organization/ # Organization-wide chat modes + │ └─{chatmode-files} # + └─repository/ # Repository-specific chat modes + └─{chatmode-files} # +``` + Execution steps: 1. **Gather** - Run [Gather.ps1](scripts/Gather.ps1) parse outputs for: - - `$RepositoryPath` - Root repository path - - `$InstructionsPath` - Instructions directory (e.g., `$RepositoryPath/.github/prompts/organization`) - - `$OrganizationInstructionsPath` - Organization instructions directory (e.g., `$InstructionsPath/organization`) - - `$RepositoryInstructionsPath` - Repository instructions directory (e.g., `$InstructionsPath/Repository`) + - `$RepositoryPath` - Root repository path + - `$InstructionsPath` - Instructions directory root (e.g., `$RepositoryPath/.github/instructions` or `$RepositoryPath/.github/prompts` depending on workspace layout) + - `$EnterpriseInstructionsPath` (optional) - Enterprise instructions directory (e.g., `$InstructionsPath/enterprise`). Treat as highest precedence for universal, multi-organization guidance. If not present, skip enterprise-level operations gracefully. + - `$OrganizationInstructionsPath` - Organization instructions directory (e.g., `$InstructionsPath/organization`) + - `$RepositoryInstructionsPath` - Repository instructions directory (e.g., `$InstructionsPath/repository` or `$InstructionsPath/Repository` — resolve case-insensitively, prefer existing path) - If path resolution fails, abort and instruct user to verify script availability. + If multiple candidate folders exist differing only by case, select the one with existing content; otherwise create in lowercase. All subsequent generated paths must use the resolved casing to avoid duplication. + If path resolution fails, abort and instruct user to verify script availability. 2. **Discovery and Analysis** - Perform comprehensive codebase scan: + - Obtain a flat, sorted list of all repository files by running the [Get-RepositoryFiles.ps1 -RepositoryPath $RepositoryPath](./scripts/Get-RepositoryFiles.ps1). + Treat this list as the authoritative source for building the language mapping. When mapping, simply derive: + - Extension: `[IO.Path]::GetExtension(path).ToLowerInvariant()` + - Relative path (if needed): `path.Substring($RepositoryPath.Length).TrimStart('\\','/')` + Manually ignore common binary/asset extensions (e.g. .png, .jpg, .jpeg, .gif, .ico, .lock, .exe, .dll, .pdb, .zip, .gz, .7z, .tar, .tgz, .bmp, .svg). - Identify all languages and frameworks in use (scan file extensions, imports, dependencies) - Catalog existing instruction files and their coverage - Discover code patterns, naming conventions, and architectural decisions from actual code - Identify testing frameworks, build tools, and deployment patterns - Extract project-specific patterns from README, existing code, and configuration files - - Build an internal technology inventory map (languages → files → patterns). + - Produce a canonical LANGUAGE MAPPING from file extensions → inferred language → representative sample files. + * Collect all unique file extensions excluding binary/asset types (default ignore: .png, .jpg, .gif, .ico, .lock, .exe, .dll, .pdb, .zip, .gz, .7z) + * Normalize extensions to lowercase. + * Map extensions to language buckets using rules (examples): + - .ps1, .psm1, .psd1 → PowerShell + - .ps1xml → XML (PowerShell metadata) (treat as XML specialization) + - .md → Markdown (documentation authoring) + - .yml, .yaml → YAML (CI / config) + - .json → JSON (configuration/data) + - .xml (generic) → XML + * For any unrecognized text file (heuristic: attempt to read first 512 bytes as UTF-8) create a Temporary language bucket named "Generic-{EXT}" so it is not ignored. + * Provide at least 1–3 representative relative file paths per language (prioritize files under src/ then tests/ then root). + - Build an internal technology inventory map (languages → extensions → representative files → patterns). + - Output the mapping as JSON structure for later steps (keys: Language, Extensions[], RepresentativeFiles[], InstructionFilesPresent(bool)). + + Language mapping MUST precede content generation so missing instruction files can be created deterministically. 3. **Content Categorization Planning** - For each discovered item, determine placement: - **Organization Instructions** (automation-managed, `$OrganizationInstructionsPath/`): + **Enterprise Instructions** (highest-level, automation-managed, `$EnterpriseInstructionsPath/`, OPTIONAL): + - Purpose: Hold only guidance truly universal across multiple organizations / repositories and multiple languages & tools. + - File: `$EnterpriseInstructionsPath/main.instructions.md` consolidates ONLY cross-enterprise universal rules (if enterprise layer exists). + - Language folders: Same pattern as organization if enterprise-level language-specific nuances are required (rare). Prefer promoting only when identical patterns appear in all organizations. + + **Organization Instructions** (automation-managed, `$OrganizationInstructionsPath/`): - Universal cross-language guidelines (`main.instructions.md`): * File organization and naming conventions * Documentation standards and comment patterns @@ -61,9 +125,27 @@ Execution steps: **Categorization Rules**: - If pattern applies across all organization projects → Organization + - If pattern applies across all organizations (enterprise scope) → Enterprise - If pattern is specific to this repository → Repository - When uncertain, default to Repository (can promote to Organization later) - Organization content must be generic enough for automation management + - Enterprise content must be generic enough to apply across disparate organizations and technology stacks; keep extremely small & strictly universal. + - Every discovered language (from language mapping) MUST have at minimum: + * Organization-level `{Language}/main.instructions.md` (unless intentionally excluded via an explicit ExclusionList provided by user input) + * Repository-level `{Language}/main.instructions.md` + * Enterprise-level `{Language}/main.instructions.md` ONLY if (a) enterprise path exists AND (b) language rules are materially identical across all organizations or explicitly flagged for enterprise promotion. + - If a discovered language lacks an instructions file, AUTO-GENERATE it using the minimal language template (do not wait for user confirmation). + - Provide specialized treatment rules: + * PowerShell XML metadata (.ps1xml) falls under XML, but if PowerShell instructions exist, do NOT duplicate cross-language rules—only add a short XML-specific file focusing on formatting & schema constraints. + * Markdown instructions emphasize documentation style & fenced code consistency. + * YAML instructions emphasize indentation (2 spaces), key ordering (stable where meaningful), and workflow file conventions. + * JSON instructions emphasize trailing comma prohibition, stable property ordering (if project mandates), and schema references if any. + - Include an automatically managed `languages.instructions.md` summary file at BOTH organization and repository instruction roots describing the discovered mapping (regenerate each run). Frontmatter for these summary files: + --- + description: "Auto-generated language mapping summary" + applyTo: "**/*" + --- + (No additional keys; file is overwritten each generation.) **Unified Frontmatter Specification (applies to ALL instruction files)** Frontmatter MUST: @@ -102,7 +184,25 @@ Execution steps: - No duplicate normalized glob patterns - No table of contents headings in file content -4. **Generate Organization Instructions** - For each discovered language/technology: +3.1 **Cross-Level Normalization & Promotion (Enterprise ↔ Organization ↔ Repository)** + + BEFORE generating or updating instruction file contents for steps 4–9, perform a consolidation analysis: + + - **Discovery Inputs**: Use language mapping + existing instruction files at all three levels (enterprise if present, organization, repository). + - **Similarity Detection**: For each language and for the universal `main.instructions.md` files: + * Compute a simple normalized content fingerprint (e.g., lowercase, strip whitespace & code blocks) to detect near-duplicates (>=80% similarity) across levels. + * Identify sections (by heading + first sentence) that appear verbatim or with only repository-specific nouns changed. + - **Promotion Rules**: + * If the same rule text (or semantically identical after removing repository-specific identifiers) appears in ≥ 90% of repository-level language instruction files, promote that rule to the organization-level language file; remove duplicates from repository files and leave an inline note referencing organization rule. + * If the same organization-level universal rule appears unchanged across all organizations (detected via enterprise layer presence & markers) AND enterprise path exists, promote to enterprise `main.instructions.md`; leave minimal reference lines in organization file (e.g., "(Inherited from enterprise universal guidelines)"). + * When promoting, ensure frontmatter of target file already conforms; append promoted rule under the correct section (create section if absent) maintaining concise ordering (prefer alphabetical section ordering: Style, Patterns, Error Handling, Testing, Performance, Documentation, Integration, Forbidden). + - **Demotion Rule**: If an organization-level rule is only referenced by a single repository and is repository-specific, move it down to that repository's file and replace with a note in organization file referencing repository-level specialization. + - **Conflict Handling**: If two repositories implement the same rule with conflicting specifics (e.g., indentation 2 vs 4 spaces) do NOT promote; flag in report under "Manual Review Needed". + - **Tool / Language Agnostic Rules**: Rules that do not reference a specific language, framework, directory, or file extension should live in the highest applicable level (`enterprise` if present, else `organization`). + - **Action Outcomes**: Record each promotion/demotion decision (source path, destination path, rule heading) in an internal change log used later in the completion report. + - **No Physical Moves Yet**: This step defines intended content relocation. Actual file edits occur in subsequent generation steps; ensure idempotency by basing decisions on current state + deterministic fingerprinting. + +4. **Generate Organization (and Enterprise) Instructions** - For each discovered language/technology: Create `$OrganizationInstructionsPath/main.instructions.md` frontmatter using the Unified Frontmatter Specification (do not re-document rules here): - description: "Universal code-writing guidelines for the organization" @@ -130,6 +230,16 @@ Execution steps: - `tests.instructions.md` - Testing-specific patterns - `classes.instructions.md` - Class design patterns - Additional domain-specific instruction files + - For each additional discovered language (e.g., XML, Markdown, YAML, JSON) create a FOLDER named exactly the language and place `main.instructions.md` inside it instead of using a language-prefixed filename. + * Example: `$OrganizationInstructionsPath/XML/main.instructions.md` + * Example: `$OrganizationInstructionsPath/Markdown/main.instructions.md` + * Example: `$OrganizationInstructionsPath/YAML/main.instructions.md` + * Example: `$OrganizationInstructionsPath/JSON/main.instructions.md` + - These language folders follow the same unified frontmatter rules (only description + applyTo) and MUST NOT introduce files named like `xml.main.instructions.md` (language prefix before file) — enforce folder + `main.instructions.md` pattern only. + - XML instructions: focus on structure, indentation (2 spaces unless existing convention differs), schema/element ordering, and PowerShell formatting view constraints (`.ps1xml`). + - Markdown instructions: heading hierarchy limits, fenced code block language tags, link formatting, parameter doc examples. + - YAML instructions: 2-space indentation, key ordering/stability policy, anchors/aliases usage policy, GitHub Actions workflow naming & required top-level keys. + - JSON instructions: no trailing commas, stable property ordering rules (if any), number formatting, schema reference/validation guidance, camelCase vs PascalCase conventions. 5. **Generate Repository Instructions** - Create project-specific guidance: @@ -153,6 +263,8 @@ Execution steps: - **Code Examples**: Real examples from the codebase showing correct patterns - **Integration**: How components interact (e.g., "Services depend on repositories, never directly on data layer") - **Conventions**: Project-specific naming or structure rules + - Auto-generate missing language instruction files for every language in the mapping even if currently unused for logic (e.g., only one file present) to prevent future drift. + - Where languages are configuration-only (Markdown / YAML / JSON / XML), tailor repository instructions to project-specific conventions (e.g., GitHub Actions workflow naming, documentation heading depth limits, JSON property ordering expectations, XML view schema constraints). 6. **Migrate Legacy Content** - If legacy instruction files exist: - Read each legacy file completely @@ -172,6 +284,12 @@ Execution steps: * No table of contents sections in any instruction file * File structure matches specification hierarchy + - **Cross-Level Consolidation Validation**: + * Every promoted rule MUST have been removed from its original lower-level location (except for a short reference line) to prevent duplication. + * No rule content (post-normalization) appears identically in more than one level unless intentionally overridden (override must include an explicit "Override:" prefix in the repository-level rule heading). + * Demotions are reflected only in the target repository file and removed from organization/enterprise scope. + * Enterprise file (if present) contains only universally applicable guidance (no directory or repository-specific references). + - **Content Validation**: * Every discovered language has organization instructions * Repository instructions provide project-specific context @@ -185,8 +303,33 @@ Execution steps: * All file types in repository are covered by appropriate instructions * Testing, error handling, and logging patterns are documented * Organization-vs-repository categorization is correct - - If validation fails, report specific issues and DO NOT proceed to cleanup. + * Each discovered language from mapping has both organization and repository instruction files (unless explicitly excluded and exclusion is listed in report) + * `languages.instructions.md` exists at both organization and repository instruction roots and reflects current mapping hash (recompute & compare) + + If validation fails, report specific issues and DO NOT proceed to cleanup. Include a subsection "Consolidation Issues" listing any promotion/demotion anomalies (duplicate still present, orphaned reference, conflicting style rule, unresolved conflict flagged for manual review). + +7.1 **Post-Generation Promotion Sweep (Default Elevation)** + + AFTER an initial successful validation (Step 7) but BEFORE cleanup: + + - **Objective**: Detect newly uniform rules introduced or normalized during generation/migration that now qualify for elevation to higher levels (repository → organization → enterprise). + - **Scope**: Evaluate both language-specific and universal (`main.instructions.md`) files. + - **Process**: + 1. Recompute normalized fingerprints (same normalization as Step 3.1) for each rule section across all repository-level files per language. + 2. Identify rule blocks that: + * Appear identically (≥ 90% normalized similarity) in ≥ Threshold repositories (default Threshold = 100% of repositories for that language unless fewer than 3 repositories exist; then require all) → Candidate for organization-level promotion. + * Appear identically in all organization-level language files (multi-org scenario with enterprise layer present) → Candidate for enterprise-level promotion. + 3. For candidates already present at higher level but partially diverged (minor wording differences ≤ 10% edit distance), unify by choosing the most specific variant that does NOT reference repository-specific paths; record the chosen canonical text. + 4. Insert (or update) promoted rule in higher-level file under correct section ordering. Remove full content from lower levels, leaving a single reference line: + `(Inherited from organization guidelines:
)` or `(Inherited from enterprise guidelines:
)`. + 5. Mark overrides explicitly: If a repository needs to differ, prefix the section heading with `Override:` and retain full content (do NOT delete). Overrides are excluded from promotion. + 6. Append promotion actions to consolidation change log (source, destination, rule heading, action = Promote/Skip/Override) for final reporting. + 7. Re-run Validation (Step 7 structural + cross-level checks) to ensure no duplication remains post-promotion. + - **Conflict Handling**: If two candidate blocks differ on a measurable parameter (e.g., indentation 2 vs 4) treat as conflict; DO NOT promote—flag under "Manual Review Needed". + - **Idempotency**: A no-op if no additional promotions qualify. + - **Failure Mode**: If re-validation fails after promotions, revert the last batch of promotions (retain change log entries marked Reverted) and report issues; skip cleanup. + + Proceed to Cleanup (Step 8) only after successful re-validation. 8. **Cleanup** - Only after successful validation: - Remove legacy instruction files that were fully migrated @@ -243,6 +386,10 @@ Repository Instructions Generated: Coverage Statistics: - Languages Discovered: {count} ({list}) + - Language Mapping: + | Language | Extensions | Representative Files | Org File | Repo File | Notes | + |----------|-----------|----------------------|----------|-----------|-------| + (Populate rows for each language; Notes column flags missing coverage or exclusions.) - Organization Files: {count} - Repository Files: {count} - Legacy Files Migrated: {count} @@ -257,6 +404,8 @@ Next Actions: - Review repository-specific instructions for accuracy - Test instruction application on sample code generation - Update any project-specific patterns that emerged during generation + - Confirm excluded languages (if any) are intentional; add them to a persisted ExclusionList if recurring + - Adjust auto-generated language instruction stubs with richer examples where beneficial ``` Context for instruction generation: $ARGUMENTS diff --git a/.github/prompts/framework/scripts/Get-RepositoryFiles.ps1 b/.github/prompts/framework/scripts/Get-RepositoryFiles.ps1 new file mode 100644 index 000000000..f01afeb01 --- /dev/null +++ b/.github/prompts/framework/scripts/Get-RepositoryFiles.ps1 @@ -0,0 +1,30 @@ +<# + .SYNOPSIS + Get a list of all files in a repository and group them by file extension. + + .DESCRIPTION + This script recursively retrieves all files in the specified repository root directory, + sorts them by their full path, and then groups them by file extension to provide a count of files per extension. + +#> +param( + # The root directory of the repository to analyze. + [Parameter(Mandatory)] + [string] $RepositoryPath +) + +$root = [System.IO.Path]::GetFullPath($RepositoryPath) + +@" + +Analyzing repository at: $root + +## Getting files in repository + +$((Get-ChildItem -LiteralPath $root -File -Recurse).FullName | Sort-Object | Out-String) + +## File counts by extension + +$(Get-ChildItem -Path $root -Recurse -File | Group-Object Extension | Select-Object Name, Count | Sort-Object Count -Descending | Out-String) + +"@ From a8d16cf1e36329d36216141607305c47fda0ddb3 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Tue, 30 Sep 2025 16:59:09 +0200 Subject: [PATCH 4/7] feat: Add comprehensive documentation and prompts for PSModule ecosystem - Introduced repository-specific instructions for the PSModule documentation site, detailing execution steps, behavior rules, and error handling. - Implemented a non-destructive analysis prompt to identify inconsistencies across core artifacts (`spec.md`, `plan.md`, `tasks.md`). - Created a clarification prompt to detect and reduce ambiguity in feature specifications through targeted questions. - Developed a constitution prompt to create or update the project constitution, ensuring alignment with templates and principles. - Established a framework for generating and maintaining Copilot instruction files across repositories, including a detailed implementation guide. - Added scripts for path resolution and gathering necessary paths for instruction files. - Implemented prompts for executing implementation plans, generating design artifacts, and creating actionable task lists based on available documents. --- .github/chatmodes/subfolder/cake.chatmode.md | 5 + .github/chatmodes/test.chatmode.md | 5 + .../framework/JSON/main.instructions.md | 53 +++ .../framework/Markdown.instructions.md | 60 ++++ .../framework/PowerShell.instructions.md | 61 ++++ .../PowerShell/classes.instructions.md | 94 ++++++ .../framework/PowerShell/main.instructions.md | 133 ++++++++ .../PowerShell/tests.instructions.md | 81 +++++ .../framework/XML/main.instructions.md | 54 +++ .../framework/YAML.instructions.md | 62 ++++ .../framework/YAML/actions.instructions.md | 87 +++++ .../framework/YAML/main.instructions.md | 52 +++ .../framework/YAML/workflows.instructions.md | 87 +++++ .../framework/main.instructions.md | 65 ++++ .../framework/universal.instructions.md | 57 ++++ .../repo/PowerShell/main.instructions.md | 83 +++++ .../repo/PowerShell/xml.instructions.md | 54 +++ .../instructions/repo/main.instructions.md | 71 ++++ .../repo/Markdown/main.instructions.md | 51 +++ .../repo/PowerShell/classes.instructions.md | 52 +++ .../repo/PowerShell/formats.instructions.md | 47 +++ .../PowerShell/formatting.instructions.md | 60 ++++ .../repo/PowerShell/functions.instructions.md | 46 +++ .../repo/PowerShell/main.instructions.md | 200 +++++++++++ .../private-functions.instructions.md | 71 ++++ .../public-functions.instructions.md | 52 +++ .../repo/PowerShell/tests.instructions.md | 76 +++++ .../repo/YAML/main.instructions.md | 83 +++++ .../repo/YAML/workflows.instructions.md | 49 +++ .../instructions/repo/main.instructions.md | 54 +++ .github/prompts/prompts/analyze.prompt.md | 101 ++++++ .github/prompts/prompts/clarify.prompt.md | 158 +++++++++ .../prompts/prompts/constitution.prompt.md | 73 ++++ .../framework/Update-Instructions.prompt.md | 318 ++++++++++++++++++ .../prompts/framework/scripts/Gather.ps1 | 6 + .github/prompts/prompts/implement.prompt.md | 56 +++ .github/prompts/prompts/plan.prompt.md | 43 +++ .github/prompts/prompts/specify.prompt.md | 21 ++ .github/prompts/prompts/tasks.prompt.md | 62 ++++ 39 files changed, 2843 insertions(+) create mode 100644 .github/chatmodes/subfolder/cake.chatmode.md create mode 100644 .github/chatmodes/test.chatmode.md create mode 100644 .github/instructions/instructions/framework/JSON/main.instructions.md create mode 100644 .github/instructions/instructions/framework/Markdown.instructions.md create mode 100644 .github/instructions/instructions/framework/PowerShell.instructions.md create mode 100644 .github/instructions/instructions/framework/PowerShell/classes.instructions.md create mode 100644 .github/instructions/instructions/framework/PowerShell/main.instructions.md create mode 100644 .github/instructions/instructions/framework/PowerShell/tests.instructions.md create mode 100644 .github/instructions/instructions/framework/XML/main.instructions.md create mode 100644 .github/instructions/instructions/framework/YAML.instructions.md create mode 100644 .github/instructions/instructions/framework/YAML/actions.instructions.md create mode 100644 .github/instructions/instructions/framework/YAML/main.instructions.md create mode 100644 .github/instructions/instructions/framework/YAML/workflows.instructions.md create mode 100644 .github/instructions/instructions/framework/main.instructions.md create mode 100644 .github/instructions/instructions/framework/universal.instructions.md create mode 100644 .github/instructions/instructions/instructions/repo/PowerShell/main.instructions.md create mode 100644 .github/instructions/instructions/instructions/repo/PowerShell/xml.instructions.md create mode 100644 .github/instructions/instructions/instructions/repo/main.instructions.md create mode 100644 .github/instructions/instructions/repo/Markdown/main.instructions.md create mode 100644 .github/instructions/instructions/repo/PowerShell/classes.instructions.md create mode 100644 .github/instructions/instructions/repo/PowerShell/formats.instructions.md create mode 100644 .github/instructions/instructions/repo/PowerShell/formatting.instructions.md create mode 100644 .github/instructions/instructions/repo/PowerShell/functions.instructions.md create mode 100644 .github/instructions/instructions/repo/PowerShell/main.instructions.md create mode 100644 .github/instructions/instructions/repo/PowerShell/private-functions.instructions.md create mode 100644 .github/instructions/instructions/repo/PowerShell/public-functions.instructions.md create mode 100644 .github/instructions/instructions/repo/PowerShell/tests.instructions.md create mode 100644 .github/instructions/instructions/repo/YAML/main.instructions.md create mode 100644 .github/instructions/instructions/repo/YAML/workflows.instructions.md create mode 100644 .github/instructions/instructions/repo/main.instructions.md create mode 100644 .github/prompts/prompts/analyze.prompt.md create mode 100644 .github/prompts/prompts/clarify.prompt.md create mode 100644 .github/prompts/prompts/constitution.prompt.md create mode 100644 .github/prompts/prompts/framework/Update-Instructions.prompt.md create mode 100644 .github/prompts/prompts/framework/scripts/Gather.ps1 create mode 100644 .github/prompts/prompts/implement.prompt.md create mode 100644 .github/prompts/prompts/plan.prompt.md create mode 100644 .github/prompts/prompts/specify.prompt.md create mode 100644 .github/prompts/prompts/tasks.prompt.md diff --git a/.github/chatmodes/subfolder/cake.chatmode.md b/.github/chatmodes/subfolder/cake.chatmode.md new file mode 100644 index 000000000..f53782bf2 --- /dev/null +++ b/.github/chatmodes/subfolder/cake.chatmode.md @@ -0,0 +1,5 @@ +--- +description: "Test chat mode" +--- + +This is a test diff --git a/.github/chatmodes/test.chatmode.md b/.github/chatmodes/test.chatmode.md new file mode 100644 index 000000000..f53782bf2 --- /dev/null +++ b/.github/chatmodes/test.chatmode.md @@ -0,0 +1,5 @@ +--- +description: "Test chat mode" +--- + +This is a test diff --git a/.github/instructions/instructions/framework/JSON/main.instructions.md b/.github/instructions/instructions/framework/JSON/main.instructions.md new file mode 100644 index 000000000..5e1e26d60 --- /dev/null +++ b/.github/instructions/instructions/framework/JSON/main.instructions.md @@ -0,0 +1,53 @@ +--- +applyTo: '**/*.json' +description: Framework-level JSON patterns for configuration, tooling, and data assets. +--- + +# Framework JSON Guidelines + +JSON files underpin tooling configs, API payloads, and data artifacts across PSModule repositories; these rules keep them consistent and machine-friendly. + +## Goal +- Define formatting, structure, and validation expectations so JSON remains deterministic and easy to consume across platforms. +- Prevent accidental drift in schema usage, sensitive value handling, or tooling integration. + +## Execution Steps +1. Format the document with two-space indentation, UTF-8 + LF endings, and double-quoted strings. +2. Group related keys logically and confirm casing consistency (camelCase, PascalCase, or snake_case per schema). +3. Attach schema references or validation commands when available and run them as part of your change. +4. Review arrays/objects for unnecessary nesting and ensure null handling matches consumer expectations. +5. For configuration files, document non-default overrides and keep companion README entries synchronized. + +## Behavior Rules +- **Formatting** + - Enforce two-space indentation, single trailing newline, and no trailing whitespace; limit lines to ≤ 150 characters when feasible. +- **Structure** + - Use meaningful keys, keep related keys adjacent, and avoid deep hierarchies without value. + - Represent collections with arrays and maintain consistent ordering when order matters to tooling. +- **Configuration & Tooling** + - Reference schemas (e.g., via `$schema`) or document validators; align naming conventions across similar configs. + - Capture linter/tool overrides explicitly and document the rationale. +- **Data Files** + - Include version metadata when datasets evolve, maintain type consistency, and treat `null` explicitly. + - Consider file size—compress or split data when large payloads degrade performance. +- **Security** + - Exclude secrets and personal data; rely on environment variables or vault abstractions instead. + - Guard against injection by encoding untrusted input before serialization. +- **Integration** + - Ensure CI/CD automation can parse the file; provide fallback behavior when keys are optional or new. + - Maintain backward compatibility unless a breaking change is intentional and documented. + +## Output Format +- JSON artifacts must validate against their schema (when provided), parse with standard libraries, and present deterministic key ordering if consumed by version control comparisons. +- Configuration changes should update related documentation or changelog entries describing the impact. + +## Error Handling +- Treat parse errors or schema violations as blockers; resolve before merging. +- Document any temporary schema gaps in repo instructions and plan remediation. + +## Definitions +| Term | Description | +| --- | --- | +| **Schema** | JSON Schema or similar definition that constrains permitted structure and values. | +| **Deterministic ordering** | Stable ordering of keys/arrays to minimize differences between runs or environments. | +| **Injection** | Malicious or accidental insertion of executable content via improperly sanitized JSON fields. | diff --git a/.github/instructions/instructions/framework/Markdown.instructions.md b/.github/instructions/instructions/framework/Markdown.instructions.md new file mode 100644 index 000000000..050eb41ec --- /dev/null +++ b/.github/instructions/instructions/framework/Markdown.instructions.md @@ -0,0 +1,60 @@ +--- +applyTo: '**/*.md' +description: Framework-level Markdown patterns for all PSModule documentation. +--- + +# Framework Markdown Guidelines + +PSModule documentation must stay readable in raw form, render cleanly in MkDocs and GitHub, and remain easy to diff—these rules secure that baseline. + +## Goal +- Provide cross-repository Markdown conventions that keep tone, structure, and tooling compatibility aligned. +- Minimize churn in diffs by prescribing consistent heading usage, spacing, and fenced code presentation. + +## Execution Steps +1. Check whether the page needs YAML frontmatter; add only when navigation or metadata requires it. +2. Draft content following the heading hierarchy (single H1, sequential levels) and paragraph guidance. +3. Format lists, code blocks, and tables using the rules below; ensure examples are copy-ready. +4. Validate links (prefer relative paths) and confirm the document builds under MkDocs without warnings. +5. Run Markdown linting (where available) or manually spot-check for spacing, line endings, and language tone. + +## Behavior Rules +- **Universal Standards** + - Encode as UTF-8 with LF endings, strip trailing whitespace, end with a single newline, and keep lines ≤ 150 characters when practical. +- **Front Matter** + - Include YAML front matter only when required by the build; limit keys to essentials (`title`, `description`, overrides). + - Place front matter at the top with no preceding blank lines. +- **Headings & Structure** + - Use exactly one H1 immediately after front matter. + - Step through heading levels sequentially and avoid terminal punctuation in heading text. +- **Paragraphs & Line Handling** + - Keep paragraphs as single logical lines; avoid arbitrary hard wraps except for clarity improvements. + - Don't hard-wrap inside code fences or tables; insert blank lines between paragraphs and lists as needed. +- **Lists** + - Prefer `*` for unordered items with two-space indentation for nested content. + - Leave a blank line before/after lists unless the list follows a heading directly. +- **Code Blocks** + - Always specify a fence language; use `powershell` for module examples, avoid `PS>` prompts, and trim to essential lines. + - Use inline backticks for commands or filenames. +- **Links & References** + - Use descriptive link text, relative paths for internal references, and reference-style links for reusable URLs. +- **Cross-Platform & Integration** + - Show forward-slash paths and note OS-specific nuances where needed. + - Ensure documents remain compatible with MkDocs, search indexing, and PSModule documentation navigation. +- **Style Consistency** + - Maintain consistent voice, terminology, and formatting choices across documents. + +## Output Format +- Markdown files should preview cleanly in MkDocs and GitHub, include working code samples, and respect the prescribed heading/list formatting. +- Navigation metadata (front matter, `mkdocs.yml`) must stay synchronized with document titles and hierarchy. + +## Error Handling +- When tooling or legacy pages prevent full compliance, document the exception inline and create a follow-up task to remediate. +- Treat broken links, malformed tables, or fenced code without language hints as blocking issues. + +## Definitions +| Term | Description | +| --- | --- | +| **MkDocs** | Static site generator used for PSModule documentation sites. | +| **Front matter** | Optional YAML metadata block at the top of a Markdown file controlling navigation or page settings. | +| **Reference-style link** | Markdown pattern that separates link usage from URL definition to encourage reuse (`[text][ref]` and `[ref]: url`). | diff --git a/.github/instructions/instructions/framework/PowerShell.instructions.md b/.github/instructions/instructions/framework/PowerShell.instructions.md new file mode 100644 index 000000000..609afa7f2 --- /dev/null +++ b/.github/instructions/instructions/framework/PowerShell.instructions.md @@ -0,0 +1,61 @@ +--- +applyTo: + - '**/*.ps1' + - '**/*.psm1' + - '**/*.psd1' +description: Framework-level PowerShell patterns that every PSModule repository must follow. +--- + +# Framework PowerShell Guidelines + +These rules define the baseline for all PowerShell authored within the PSModule ecosystem, regardless of repository or module specialization. + +## Goal +- Establish consistent formatting, help, and module architecture expectations for every PowerShell source file. +- Ensure code remains portable between Windows PowerShell 5.1, PowerShell 7+, and GitHub-hosted runners. + +## Execution Steps +1. Identify the PowerShell artifact you are modifying (function, script, module manifest, etc.). +2. Apply the formatting and naming rules listed below before committing. +3. Confirm comment-based help and error handling align with the guidance. +4. Run PSScriptAnalyzer (or repository equivalent) and address violations. +5. Validate module load by importing with `Import-Module -Force` in both Windows PowerShell 5.1 (when available) and PowerShell 7+. + +## Behavior Rules +- **Formatting & Encoding** + - Use UTF-8 with LF endings; trim trailing whitespace and limit lines to 150 characters. + - End each file with a single newline and avoid tabs (4-space indentation unless a repo override states otherwise). +- **Comment-Based Help** + - Provide full help for all public functions, including `.SYNOPSIS`, `.DESCRIPTION`, `.EXAMPLE`, parameter entries, and `.LINK` references where applicable. + - Keep examples executable and reflective of supported scenarios. +- **Error Handling** + - Use consistent terminating/non-terminating patterns, preferring `[ValidateNotNullOrEmpty()]` and other attributes for upfront validation. + - Emit actionable guidance and documentation links in error messages. +- **Code Quality** + - Enforce approved PowerShell verbs, strong typing, and clear parameter naming (PascalCase). + - Suppress analyzer violations only with justification and scope them narrowly. +- **Module Architecture** + - Separate public and private functions; structure modules into semantic folders. + - Utilize classes for complex data structures and ensure module initialization handles both 5.1 and 7+ environments. +- **Pipeline & Performance** + - Support streaming via `begin/process/end` blocks; avoid unnecessary array accumulation. + - Minimize per-iteration allocations and favor efficient idioms (splatting, implicit output). +- **Security** + - Protect credentials (SecureString where appropriate) and validate inputs rigorously. + - Follow least-privilege principles when invoking external services or modifying system state. + +## Output Format +- PowerShell files should import without errors, expose help accessible via `Get-Help`, and pass analyzer/test suites defined in the repository. +- Refactors must preserve module manifests, exported members, and formatting conventions. + +## Error Handling +- If a rule must be relaxed (e.g., due to legacy compatibility), document the exception in repo-specific instructions and annotate the source with justification comments. +- Treat analyzer or import failures as blocking issues; resolve before final hand-off. + +## Definitions +| Term | Description | +| --- | --- | +| **PSScriptAnalyzer** | Static analysis tool used to enforce PowerShell coding standards in PSModule projects. | +| **Comment-based help** | Inline documentation block that powers `Get-Help` output for functions and scripts. | +| **SupportsShouldProcess** | Cmdlet binding feature enabling `-WhatIf`/`-Confirm` semantics for destructive actions. | +| **Pipeline-supporting function** | A function that accepts input via `ValueFromPipeline` and processes items in the `process {}` block without array buffering. | diff --git a/.github/instructions/instructions/framework/PowerShell/classes.instructions.md b/.github/instructions/instructions/framework/PowerShell/classes.instructions.md new file mode 100644 index 000000000..086a32701 --- /dev/null +++ b/.github/instructions/instructions/framework/PowerShell/classes.instructions.md @@ -0,0 +1,94 @@ +--- +applyTo: '**/classes/**/*.{ps1,psm1}' +description: Framework-level PowerShell class patterns and object-oriented design expectations. +--- + +# Framework PowerShell Classes Guidelines + +Use this guidance when authoring or updating PowerShell classes that back PSModule modules, formatters, or API abstractions. + +## Goal +- Encourage consistent, maintainable class implementations that align with PSModule’s single-responsibility philosophy. +- Provide reusable patterns for constructors, serialization helpers, and GitHub-centric models. + +## Execution Steps +1. Determine the class’s responsibility and ensure it models a single concept (data or behavior cluster). +2. Pick PascalCase names for the class and public members, avoiding abbreviations. +3. Sketch constructors, property defaults, and helper methods, referencing the template below. +4. Implement serialization (`ToHashtable`, `FromHashtable`) and pipeline-friendly output as needed. +5. Add comment-based help (or XML documentation if required), run tests, and verify usage across modules. + +## Behavior Rules +- **Design Principles** + - Adhere to single responsibility, keep method names descriptive, and mark internal-only members as `hidden`. +- **Template Reference** + - Base new classes on this structure: + +```powershell +class ObjectName { + [string] $Property1 + [int] $Property2 + hidden [datetime] $CreatedAt + + ObjectName([string] $property1, [int] $property2) { + $this.Property1 = $property1 + $this.Property2 = $property2 + $this.CreatedAt = [datetime]::UtcNow + } + + ObjectName() { + $this.CreatedAt = [datetime]::UtcNow + } + + [string] ToString() { + "$($this.Property1): $($this.Property2)" + } + + [hashtable] ToHashtable() { + @{ + Property1 = $this.Property1 + Property2 = $this.Property2 + CreatedAt = $this.CreatedAt + } + } + + static [ObjectName] FromHashtable([hashtable] $data) { + $obj = [ObjectName]::new() + $obj.Property1 = $data.Property1 + $obj.Property2 = $data.Property2 + if ($data.ContainsKey('CreatedAt')) { + $obj.CreatedAt = $data.CreatedAt + } + return $obj + } +} +``` +- **Properties & Constructors** + - Choose precise .NET types, set defaults in constructors, and validate input parameters. + - Offer overloads or factory methods when callers may supply different shapes of data. +- **Methods & Serialization** + - Implement `ToString()` for debugging and `ToHashtable()`/`FromHashtable()` for serialization. + - Handle circular references carefully; prefer IDs or summaries over nesting entire objects. +- **Inheritance & Interfaces** + - Prefer composition; when inheriting, document base-class expectations and override virtual members deliberately. + - Implement interfaces for shared behavior across modules (e.g., `IJsonSerializable`). +- **Error Handling** + - Throw meaningful exceptions on invalid state, guard property assignments, and keep messages actionable. +- **GitHub Integration** + - When modeling GitHub API objects, store raw payloads or metadata as hidden properties and expose pipeline-friendly data. + - Surface helper methods that wrap common API operations while respecting authentication context. + +## Output Format +- Classes must compile on Windows PowerShell 5.1 and PowerShell 7+, integrate into modules without export conflicts, and be covered by targeted tests. +- Serialization helpers should round-trip sample payloads used in documentation or unit tests. + +## Error Handling +- Treat validation failures or inconsistent serialization as blocking; update tests and constructors to enforce invariants. +- Document temporary exceptions (e.g., incomplete API fields) via TODO comments with linked issues. + +## Definitions +| Term | Description | +| --- | --- | +| **Single responsibility** | Design principle where a class addresses one cohesive concern, simplifying maintenance. | +| **Hidden property** | PowerShell class member marked `hidden` to restrict exposure while keeping data accessible internally. | +| **Round-trip** | Ability for serialization/deserialization helpers to return an equivalent object without data loss. | diff --git a/.github/instructions/instructions/framework/PowerShell/main.instructions.md b/.github/instructions/instructions/framework/PowerShell/main.instructions.md new file mode 100644 index 000000000..8f6d35192 --- /dev/null +++ b/.github/instructions/instructions/framework/PowerShell/main.instructions.md @@ -0,0 +1,133 @@ +--- +applyTo: '**/*.{ps1,psm1,psd1}' +description: Framework-level PowerShell patterns governing function authoring and module layout. +--- + +# Framework PowerShell Guidelines + +This file dives deeper into day-to-day function authoring, parameter design, and module implementation details for PSModule repositories. + +## Goal +- Provide a repeatable blueprint for writing public and private functions that align with PSModule expectations. +- Ensure naming, help, pipeline behavior, and cross-platform execution are consistent across modules. + +## Execution Steps +1. Before editing, confirm the function/module purpose and choose an approved verb-noun name. +2. Apply the formatting baseline (UTF-8 with BOM when 5.1 compatibility is needed, 4-space indentation, ≤150-character lines). +3. Flesh out parameters using validation attributes and pipeline support rules, then scaffold comment-based help. +4. Implement logic with appropriate pipeline sections (`begin/process/end`), error handling, and logging hooks. +5. Run linters (`Invoke-ScriptAnalyzer`), unit tests (Pester), and import the module (`Import-Module -Force`) to verify exports and behavior. + +## Behavior Rules +- **Naming & Scope** + - Use approved `Verb-Noun` cmdlet names (PascalCase) with unambiguous nouns; avoid abbreviations. + - Parameters follow PascalCase; variables use PascalCase for module scope and camelCase for locals. + - Avoid aliases unless required for backward compatibility, and document any that remain. +- **Formatting & Encoding** + - Default to 4-space indentation, braces on the same line, and UTF-8 encoding (with BOM when Windows PowerShell 5.1 consumers demand it). + - Trim trailing whitespace and close every file with a single newline. +- **Function Structure** + - Start functions with `[CmdletBinding()]`, `[OutputType()]`, and comprehensive comment-based help. + - Use the template below as a reference implementation: + +```powershell +function Verb-Noun { + <#! + .SYNOPSIS + Brief description. + + .DESCRIPTION + Detailed description with context and usage. + + .PARAMETER Parameter + Parameter description with context and examples. + + .EXAMPLE + Verb-Noun -Parameter Value + + Description of what this example demonstrates. + + .OUTPUTS + [OutputType] - Description of output object properties. + + .NOTES + Additional context or usage notes. + #> + [OutputType([ExpectedType])] + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Parameter + ) + + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + } + + process { + try { + # Implementation + } catch { + Write-Debug "Error: $_" + throw + } + } + + end { + Write-Debug "[$stackPath] - End" + } +} +``` + +- **Parameter Design** + - Prefer `[switch]` for flags, leverage validation attributes, and support pipeline binding when natural. + - Minimize mandatory parameters when context detection can supply defaults. + - Example pattern: + +```powershell +function Set-ResourceConfiguration { + [CmdletBinding(SupportsShouldProcess)] + param( + [Parameter(Mandatory)] + [string]$Name, + [ValidateSet('Dev', 'Test', 'Prod')] + [string]$Environment = 'Dev', + [switch]$Force, + [ValidateNotNullOrEmpty()] + [string[]]$Tags + ) + process { } +} +``` +- **Pipeline & Output** + - Stream items through `process {}` without accumulating arrays; emit objects, not formatted strings. + - Provide `-PassThru` on action cmdlets when returning modified objects adds value. +- **Error Handling & Logging** + - Use `SupportsShouldProcess`/`ShouldProcess` for state changes and choose appropriate `ConfirmImpact`. + - Catch specific exceptions, rethrow with context, and leverage `Write-Error` for per-item failures. + - Integrate PSModule logging helpers (e.g., `Set-GitHubLogGroup`) when running in CI. +- **Help & Documentation** + - Include `.LINK` entries for external docs, ensure examples are runnable, and keep help synchronized with parameter definitions. +- **Cross-Platform & Performance** + - Use forward slashes in paths, test on Windows PowerShell 5.1 and PowerShell 7+, and minimize per-iteration allocations. + - Favor splatting and efficient cmdlets (`ForEach-Object -Parallel` only when concurrency justified). +- **Security** + - Handle secrets with `SecureString` or other secure abstractions, validate inputs thoroughly, and respect least privilege when calling external services. + +## Output Format +- PowerShell artifacts must import cleanly, expose accurate help via `Get-Help`, pass analyzer rules, and integrate with Pester suites defined for the module. +- Public functions should remain discoverable through module manifests and respect semantic versioning commitments. + +## Error Handling +- Treat analyzer violations, failed imports, or broken pipeline behavior as blocking; resolve or document remediation steps before merge. +- If legacy constraints require bending a rule, annotate the rationale with TODO/follow-up and capture it in repo instructions. + +## Definitions +| Term | Description | +| --- | --- | +| **Approved verbs** | List returned by `Get-Verb` denoting sanctioned PowerShell verb choices. | +| **Comment-based help** | Inline documentation that feeds `Get-Help`, covering synopsis, description, parameters, examples, outputs, and notes. | +| **SupportsShouldProcess** | CmdletBinding capability enabling `-WhatIf`/`-Confirm` support for potentially destructive operations. | +| **Splatting** | Technique of passing parameter dictionaries (`@{}`) to cmdlets for readability and reuse. | diff --git a/.github/instructions/instructions/framework/PowerShell/tests.instructions.md b/.github/instructions/instructions/framework/PowerShell/tests.instructions.md new file mode 100644 index 000000000..ca50e99c0 --- /dev/null +++ b/.github/instructions/instructions/framework/PowerShell/tests.instructions.md @@ -0,0 +1,81 @@ +--- +applyTo: '**/*.Tests.ps1,**/tests/**/*.ps1' +description: Framework-level PowerShell test patterns using Pester. +--- + +# Framework PowerShell Testing Guidelines + +Use this when writing Pester tests for PSModule modules—consistent structure keeps suites predictable and CI-friendly. + +## Goal +- Standardize test organization, naming, and setup so suites mirror production code structure. +- Ensure tests cover both happy paths and failure scenarios across local and GitHub Actions environments. + +## Execution Steps +1. Create or update a `.Tests.ps1` file that mirrors the source path (e.g., `src/functions/Public/Get-Item.ps1` → `tests/functions/Public/Get-Item.Tests.ps1`). +2. Scaffold `Describe`/`Context` blocks capturing functionality and scenario boundaries. +3. Implement Arrange-Act-Assert using the template below, adding setup/cleanup hooks as required. +4. Exercise different authentication or runtime contexts (local vs GitHub Actions) when relevant. +5. Run `Invoke-Pester` locally (and optionally in PowerShell 5.1 + 7+) to confirm the suite passes before committing. + +## Behavior Rules +- **Structure** + - Keep one primary `Describe` per function or feature and use `Context` blocks for scenario variations. + - Store test data at the top-level or within `BeforeAll`; clean up with `AfterAll/AfterEach`. +- **Template Reference** + - Base suites on this skeleton: + +```powershell +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +param() + +BeforeDiscovery { + # Discovery-time setup (mock data, environment checks) +} + +Describe 'Module-Function Tests' { + BeforeAll { + $testData = @{ + Parameter1 = 'TestValue' + Parameter2 = 42 + } + } + + Context 'When function is called with valid parameters' { + It 'Should return expected result' { + $result = Module-Function -Parameter $testData.Parameter1 + $result | Should -Be 'Expected' + } + } + + Context 'When function is called with invalid parameters' { + It 'Should throw appropriate error' { + { Module-Function -Parameter $null } | Should -Throw + } + } +} +``` +- **Assertions** + - Use precise assertions (`Should -Be`, `-Match`, `-Throw`), covering positive, negative, and pipeline behaviors. + - Validate error messages or types when they matter for consumers. +- **Context Awareness** + - Simulate GitHub Actions runs (matrix combinations, environment variables, secrets) as part of tests when feasible. + - Use `Set-GitHubLogGroup` or equivalent logging helpers to keep CI output structured. +- **Performance & Integration** + - Benchmark critical paths (e.g., repeated API calls) and include smoke tests for module import/export. + - Exercise cross-function flows and real API interactions in dedicated integration suites, gating them with tags if slow. + +## Output Format +- Test suites must pass under `Invoke-Pester`, generate NUnit/XML reports when CI expects them, and respect naming conventions so dashboards categorize results correctly. +- Document required environment variables or secrets in accompanying README/test docs. + +## Error Handling +- Treat failing assertions, leaked resources, or missing cleanup as blockers; fix before merge. +- If a scenario cannot be automated, document manual validation steps and track automation backlog items. + +## Definitions +| Term | Description | +| --- | --- | +| **Arrange-Act-Assert** | Canonical testing pattern for structuring setup, execution, and verification. | +| **BeforeDiscovery** | Pester hook executed during discovery phase, useful for locating data or skipping suites. | +| **Tagging** | Mechanism to categorize tests (e.g., `-Tag Integration`) for selective execution. | diff --git a/.github/instructions/instructions/framework/XML/main.instructions.md b/.github/instructions/instructions/framework/XML/main.instructions.md new file mode 100644 index 000000000..c26f97514 --- /dev/null +++ b/.github/instructions/instructions/framework/XML/main.instructions.md @@ -0,0 +1,54 @@ +--- +applyTo: '**/*.xml' +description: Framework-level XML patterns for configuration payloads and test outputs. +--- + +# Framework XML Guidelines + +XML remains common for tool configuration and reporting in PSModule repos; these rules ensure the documents remain parseable and cross-platform. + +## Goal +- Provide consistent encoding, structure, and schema practices for XML artifacts. +- Keep CI/CD tooling (test runners, coverage reports, deployment manifests) interoperable across environments. + +## Execution Steps +1. Start each file with an XML declaration specifying UTF-8 encoding and ensure LF line endings. +2. Organize elements under a single root, following schema or tool conventions for ordering and naming. +3. Apply consistent indentation (2 spaces recommended) and remove trailing whitespace before saving. +4. Validate the document against its schema or DTD where available; capture validation steps in comments or automation. +5. Review namespaces, links, and file paths to confirm they behave on both Windows and Unix runners. + +## Behavior Rules +- **Formatting** + - Use UTF-8 encoding, include ``, and end the file with a single newline. + - Maintain two-space indentation (unless a consuming tool enforces four) and keep lines ≤ 150 characters. +- **Structure** + - Choose meaningful element/attribute names, maintain consistent casing, and avoid unnecessary nesting. + - For configuration files, place metadata (version, description) near the top and group functional sections logically. +- **Test & Report Files** + - Follow standard formats (JUnit, JaCoCo, Pester NUnit) including timestamps, durations, and failure details. + - Preserve ordering of suites/cases when consumers rely on deterministic layouts. +- **Configuration Files** + - Comment complex sections, provide sensible defaults, and validate with schemas or XSDs when supplied. + - Keep cross-platform paths normalized (forward slashes) and document platform-specific requirements. +- **Security** + - Disable or avoid external entities (XXE) unless explicitly required and vetted. + - Escape special characters properly and sanitize untrusted input before serialization. +- **Integration** + - Ensure files load in standard processors (System.Xml, xmllint) and surface informative errors if parsing fails. + - Maintain backward compatibility or note breaking changes in release notes when altering structure. + +## Output Format +- XML artifacts should validate successfully, open in standard viewers without warnings, and align with the consuming tool's expectations (e.g., GitHub integration, Azure DevOps reports). +- Test outputs must include full failure context (message, stack trace) for troubleshooting. + +## Error Handling +- Treat validation or parsing failures as blockers; fix structure or schema references before merge. +- Document exceptions (e.g., third-party schema bugs) in repo instructions and plan remediation. + +## Definitions +| Term | Description | +| --- | --- | +| **XML declaration** | First line specifying version and encoding (e.g., ``). | +| **XXE** | XML External Entity processing; potential security vulnerability when untrusted entities are loaded. | +| **JUnit format** | Widely used XML schema for test results consumed by CI dashboards. | diff --git a/.github/instructions/instructions/framework/YAML.instructions.md b/.github/instructions/instructions/framework/YAML.instructions.md new file mode 100644 index 000000000..b29011d64 --- /dev/null +++ b/.github/instructions/instructions/framework/YAML.instructions.md @@ -0,0 +1,62 @@ +--- +applyTo: '**/*.{yml,yaml}' +description: Framework-level YAML patterns covering workflows, actions, and shared configuration. +--- + +# Framework YAML Guidelines + +Every PSModule YAML file—from GitHub workflows to documentation config—must remain readable, lintable, and secure across repositories. + +## Goal +- Define universal YAML formatting, naming, and validation practices that keep automation predictable and diff-friendly. +- Outline GitHub Actions specific patterns so reusable actions and workflows share a common contract. + +## Execution Steps +1. Confirm the file name follows lowercase-hyphenated conventions (e.g., `ci-build.yml`). +2. Apply formatting rules (UTF-8 + LF, two-space indent, single trailing newline) and organise sections logically. +3. Validate schema compatibility (yaml-language-server directive or dedicated validator) and resolve warnings. +4. For Actions/Workflows, ensure inputs, outputs, permissions, and branding align with PSModule expectations. +5. Re-run linting or workflow dry-runs after modifications to guarantee correctness. + +## Behavior Rules +- **Formatting & Structure** + - Use UTF-8 with LF endings, no tabs, two-space indentation, and keep lines ≤ 150 characters. + - Group related keys, separate sections with a single blank line, and maintain metadata keys (`name`, `description`, etc.) first. +- **Naming & Organization** + - Choose descriptive file names (`deployment-config.yml`, `mkdocs.yml`) and avoid generic labels. + - Keep nesting shallow when possible; prefer flattened keys for clarity. +- **Scalars & Quoting** + - Use plain scalars when safe and quote values that could be misinterpreted (`on`, `yes`, `007`, values starting with special characters). + - Prefer double quotes; leverage `|` and `>` block styles intentionally to control wrapping. +- **Lists & Sequences** + - Align dashes with the parent key indentation; use block lists for multi-line content and concise inline lists only for short sequences (< 80 chars). +- **Schemas & Validation** + - Include `# yaml-language-server` schema hints when supported (GitHub workflow, Dependabot, etc.). + - Treat schema or linter errors as blockers—update content or schema references instead of suppressing. +- **Comments** + - Start comments with a space (`# Explanation`), keep them current, and format TODOs as `# TODO(owner): summary`. +- **Security & Secrets** + - Never commit actual secrets; reference them via `${{ secrets.NAME }}` or other vault abstractions. + - Redact examples with placeholders and pin external actions to specific versions. +- **GitHub Actions Patterns** + - Supply metadata (name, description, author, branding) for composite actions and define explicit input/output contracts. + - Structure repositories with `scripts/` and `tests/` folders, provide CI logging (`pwsh` with `Set-GitHubLogGroup`), and support Windows/Linux/macOS runners. +- **Workflows & Config** + - Use descriptive workflow/job names, matrix strategies for multi-environment coverage, artifacts for cross-job data, and caching where beneficial. + - Document options in accompanying README files and keep PSModule helper integrations consistent. + +## Output Format +- YAML artifacts should lint cleanly, load under the documented schema, and, for workflows/actions, execute successfully in a dry run or targeted test. +- Action READMEs and repository docs must describe inputs/outputs exactly as defined in YAML. + +## Error Handling +- If a conflicting third-party requirement forces deviation (e.g., different indentation), capture the reason in repo instructions and cite upstream constraints in comments. +- Fail fast on schema validation, permission mismatches, or unpinned external dependencies. + +## Definitions +| Term | Description | +| --- | --- | +| **Composite action** | GitHub Action defined in YAML that orchestrates steps (often PowerShell) for reuse across repositories. | +| **Schema hint** | `yaml-language-server` directive that points editors to JSON schemas for validation and IntelliSense. | +| **Matrix strategy** | GitHub Actions feature allowing jobs to run across permutations of inputs (OS, PowerShell version, etc.). | +| **Pinned action** | External action referenced with a full version (`@v1.2.3` or commit SHA) to prevent supply-chain drift. | diff --git a/.github/instructions/instructions/framework/YAML/actions.instructions.md b/.github/instructions/instructions/framework/YAML/actions.instructions.md new file mode 100644 index 000000000..e05c99833 --- /dev/null +++ b/.github/instructions/instructions/framework/YAML/actions.instructions.md @@ -0,0 +1,87 @@ +--- +applyTo: + - '**/action.yml' + - '**/action.yaml' +description: Framework-level GitHub Actions definition patterns for PSModule composite actions. +--- + +# Framework GitHub Actions Guidelines + +Composite actions standardize how PSModule scripts are packaged for reuse; this guidance keeps inputs, outputs, and branding consistent. + +## Goal +- Provide a reusable action scaffold that integrates seamlessly with PSModule helpers and CI pipelines. +- Enforce naming, branding, and security patterns so actions remain trustworthy across repositories. + +## Execution Steps +1. Start from the PSModule composite template below and adjust metadata (name, description, icon) to match the action’s purpose. +2. Define inputs/outputs with descriptive names, defaults, and documentation; ensure they map cleanly to environment variables. +3. Reference `PSModule/Install-PSModuleHelpers` (pinned version) before invoking repository scripts. +4. Add simulation steps or tests that verify the action works on Windows, Linux, and macOS runners. +5. Update accompanying README files with the same inputs/outputs and usage examples. + +## Behavior Rules +- **Template Reference** + +```yaml +name: ActionName (by PSModule) +description: Clear summary of the action’s purpose. +author: PSModule +branding: + icon: package + color: gray-dark + +inputs: + Name: + description: Name of the resource to process. + required: false + WorkingDirectory: + description: Directory in which the script runs. + required: false + default: '.' + +outputs: + OutputName: + description: Description of the output. + +runs: + using: composite + steps: + - name: Install PSModule helpers + uses: PSModule/Install-PSModuleHelpers@v1 + + - name: Execute action + shell: pwsh + working-directory: ${{ inputs.WorkingDirectory }} + env: + PSMODULE_ACTIONNAME_INPUT_Name: ${{ inputs.Name }} + run: | + & "${{ github.action_path }}/scripts/main.ps1" +``` +- **Inputs & Outputs** + - Use PascalCase input keys, provide defaults when safe, and mark required inputs sparingly. + - Document outputs with structure details (type, example values) and consume `::set-output` alternatives (environment files) as required by GitHub updates. +- **Environment Variable Mapping** + - Map each input to an environment variable named `PSMODULE__INPUT_` to keep scripts consistent. +- **Branding & Naming** + - Append “(by PSModule)” to action names, use `gray-dark` branding, and pick Feather icons matching purpose. +- **Dependencies & Scripts** + - Pin helper and third-party actions to explicit versions; call scripts via `${{ github.action_path }}` to support relative resources. +- **Security** + - Never embed secrets; expect callers to pass them via `${{ secrets.NAME }}` and validate inputs. + - Restrict permissions in the consuming workflow (document recommended settings). + +## Output Format +- Action metadata must parse via `act`/GitHub validators, include README documentation, and provide working sample workflows. +- Steps should emit structured logs suitable for CI grouping and set outputs using the GitHub environment file mechanism. + +## Error Handling +- Fail the action with clear messages when required inputs are missing or operations error; bubble PowerShell exceptions with context. +- Document known limitations or platform restrictions in the README and TODO comments. + +## Definitions +| Term | Description | +| --- | --- | +| **Composite action** | YAML-defined action bundling multiple steps for reuse. | +| **Environment file** | GitHub Actions mechanism for setting outputs, env vars, and path modifications (`$GITHUB_OUTPUT`, `$GITHUB_ENV`). | +| **Feather icon** | Icon set used for GitHub Action branding (`branding.icon`). | diff --git a/.github/instructions/instructions/framework/YAML/main.instructions.md b/.github/instructions/instructions/framework/YAML/main.instructions.md new file mode 100644 index 000000000..754079977 --- /dev/null +++ b/.github/instructions/instructions/framework/YAML/main.instructions.md @@ -0,0 +1,52 @@ +--- +applyTo: + - '**/*.yml' + - '**/*.yaml' +description: Framework-level YAML practices for shared configuration components. +--- + +# Framework YAML Guidelines + +This file complements the top-level YAML guidance with patterns for generic configuration files that aren’t specific to workflows or composite actions. + +## Goal +- Keep YAML configuration readable, validated, and modular across PSModule repositories. +- Ensure environmental integration (local + CI) without duplicating workflow-specific rules. + +## Execution Steps +1. Name the file with lowercase hyphenated words reflecting purpose (`mkdocs.yml`, `settings-release.yml`). +2. Format with UTF-8, LF endings, two-space indentation, and a single trailing newline. +3. Organize logical sections with metadata first, then functional blocks separated by a single blank line. +4. Insert schema hints or validation commands if the consuming tool supports them and run the validation. +5. Update accompanying documentation or comments to explain complex settings. + +## Behavior Rules +- **Structure & Formatting** + - Group related keys, avoid excessive nesting, and keep key names descriptive but succinct. + - Maintain deterministic key ordering to reduce diff churn. +- **Comments & Documentation** + - Use comments sparingly for non-obvious behavior; format TODOs as `# TODO(owner): summary`. + - Document default values and overrides near their definitions. +- **Environment Integration** + - Leverage environment variables or templating features for values that differ between environments. + - Validate configuration under Windows, Linux, and macOS contexts when applicable. +- **Versioning & Compatibility** + - Track schema versions or tool version compatibility; note breaking changes in release notes. + - Prefer additive changes over destructive reorganizations to preserve backward compatibility. +- **Security** + - Keep secrets out of configuration files; reference secure stores or placeholder tokens. + - Validate external inputs before interpolation to prevent injection. + +## Output Format +- Configuration YAML must parse without warnings in the target tool, and any generated artifacts (site configs, templates) should remain functional. +- Documented defaults and overrides should stay synchronized with README or docs references. + +## Error Handling +- Treat schema validation failures or parse exceptions as blockers—resolve before delivery. +- If a third-party tool enforces conflicting formatting, document the exception and scope it narrowly. + +## Definitions +| Term | Description | +| --- | --- | +| **Deterministic ordering** | Keeping keys in a consistent order for predictable diffs and tooling consumption. | +| **Schema hint** | Metadata (comment or key) pointing a YAML file to a validation schema. | diff --git a/.github/instructions/instructions/framework/YAML/workflows.instructions.md b/.github/instructions/instructions/framework/YAML/workflows.instructions.md new file mode 100644 index 000000000..765709a16 --- /dev/null +++ b/.github/instructions/instructions/framework/YAML/workflows.instructions.md @@ -0,0 +1,87 @@ +--- +applyTo: + - '**/.github/workflows/*.yml' + - '**/.github/workflows/*.yaml' +description: Framework-level GitHub workflow patterns for CI/CD. +--- + +# Framework GitHub Workflows Guidelines + +Workflows orchestrate PSModule automation; keeping them consistent ensures reliable CI/CD across repositories. + +## Goal +- Provide a canonical workflow skeleton, key ordering, and permission strategy. +- Balance robustness, security, and performance for GitHub-hosted and self-hosted runners. + +## Execution Steps +1. Base new workflows on the template below, adjusting triggers, permissions, and concurrency to fit the scenario. +2. Define jobs with descriptive names, `needs` dependencies, and matrix strategies where cross-platform coverage is required. +3. Use pinned action versions, PSModule helpers, and scripts stored in `.github/scripts` for multi-line logic. +4. Tune caching, artifacts, and secrets management for the workload. +5. Test via `act` or selective dispatch events before merging, monitoring logs for unexpected warnings. + +## Behavior Rules +- **Template Reference** + +```yaml +name: Workflow Name +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + job-name: + name: Job Display Name + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PowerShell + uses: PSModule/Setup-PowerShell@v1 + + - name: Execute Action + uses: PSModule/ActionName@v1 +``` +- **Key Ordering & Structure** + - Order top-level keys as `name`, `on`, `permissions`, `env`, `defaults`, `concurrency`, `jobs`; separate sections with single blank lines. +- **Permissions & Security** + - Grant least privilege (read-only by default); escalate at job level when necessary and document justification. + - Never output secrets; rely on `${{ secrets.NAME }}` or OIDC tokens for external auth. +- **Scripts & Steps** + - Keep inline scripts short; move longer logic to `.github/scripts` and reference with `run: pwsh` and path. + - Pin all third-party actions to versions/tags (avoid `@main`). +- **Job Design** + - Use `needs` for ordering, matrices for OS/PowerShell coverage, and descriptive `name` values for readability. + - Incorporate caching (PowerShell modules, npm/pip dependencies) and artifact handling with explicit retention. +- **Error Handling & Resilience** + - Avoid `continue-on-error` except for non-blocking tasks; include cleanup steps and actionable failure messages. +- **Performance** + - Cancel superseded runs via concurrency, minimize redundant jobs, and leverage parallelization judiciously to control usage. +- **PSModule Integration** + - Use PSModule helper actions consistently, align logging (`Set-GitHubLogGroup`), and ensure modules import with required versions. + +## Output Format +- Workflows must pass YAML validation, run successfully on target runners, and emit structured logs/artifacts consumable by maintainers. +- Document triggers, environment assumptions, and required secrets in repository README or `.github/workflows/README.md`. + +## Error Handling +- Treat failed steps as blocking unless explicitly non-critical; surface failure causes clearly (e.g., `Write-Error` with guidance in PowerShell steps). +- For flaky upstream services, add retries with exponential backoff and log references to tracking issues. + +## Definitions +| Term | Description | +| --- | --- | +| **Concurrency group** | GitHub Actions feature preventing overlapping runs based on workflow/ref keys. | +| **Matrix** | Job expansion technique for running the same steps across environments. | +| **OIDC token** | OpenID Connect token issued by GitHub for requesting cloud credentials without stored secrets. | diff --git a/.github/instructions/instructions/framework/main.instructions.md b/.github/instructions/instructions/framework/main.instructions.md new file mode 100644 index 000000000..71866f583 --- /dev/null +++ b/.github/instructions/instructions/framework/main.instructions.md @@ -0,0 +1,65 @@ +--- +applyTo: '**/*' +description: Universal cross-language guidelines for PSModule framework development. +--- + +# PSModule Framework Guidelines + +Universal patterns and conventions that apply across all programming languages and file types within the PSModule ecosystem. + +## Goal +- Establish consistent development patterns across all PSModule repositories +- Provide foundational guidelines that language-specific instructions build upon +- Ensure compatibility with PSModule build processes, tooling, and ecosystem integration + +## Execution Steps +1. Review target file type and apply language-specific instructions in addition to these universal guidelines +2. Follow PSModule naming conventions and file organization patterns +3. Integrate with established build processes, authentication patterns, and logging standards +4. Validate changes against both local tooling and CI automation +5. Document new patterns for potential promotion to framework-level guidance + +## Behavior Rules +- **Naming Conventions** + - Use PascalCase for public functions, types, and exported members + - Use kebab-case for file names and directory structures + - Use descriptive, intention-revealing names that align with domain terminology +- **File Organization** + - Maintain consistent directory structures across repositories + - Separate public/private components clearly + - Group related functionality logically +- **Documentation Standards** + - All public APIs must have complete documentation + - Include examples that demonstrate real-world usage patterns + - Link to related documentation and external references +- **Build Integration** + - Support cross-platform compatibility (Windows, Linux, macOS) + - Integrate with existing PSModule build and deployment pipelines + - Ensure compatibility with automated testing and validation processes +- **Authentication & Security** + - Use PSModule authentication abstractions for external service integration + - Follow principle of least privilege for permissions and access + - Implement proper error handling for authentication failures +- **Logging & Observability** + - Use structured logging with LogGroup for consistency + - Include appropriate level of detail for debugging and monitoring + - Support pipeline-friendly output formats + +## Output Format +- All artifacts must be compatible with PSModule tooling and build processes +- Follow established patterns for the target file type +- Maintain consistency with existing repository structure and conventions + +## Error Handling +- Provide clear, actionable error messages +- Include context for debugging and resolution +- Follow established patterns for error handling in the target technology +- Document known issues and workarounds + +## Definitions +| Term | Description | +| --- | --- | +| **PSModule ecosystem** | Collection of PowerShell modules, documentation, and tooling following consistent patterns | +| **LogGroup** | Structured logging system used across PSModule repositories | +| **Cross-platform compatibility** | Support for Windows, Linux, and macOS operating systems | +| **Authentication abstractions** | Standardized patterns for handling external service authentication | diff --git a/.github/instructions/instructions/framework/universal.instructions.md b/.github/instructions/instructions/framework/universal.instructions.md new file mode 100644 index 000000000..50f08cf7f --- /dev/null +++ b/.github/instructions/instructions/framework/universal.instructions.md @@ -0,0 +1,57 @@ +--- +applyTo: '**/*' +description: Universal framework patterns for every PSModule repository. +--- + +# PSModule Framework Guidelines + +These cross-language expectations keep every PSModule component consistent, pipeline-friendly, and ready for automation across local and CI environments. + +## Goal +- Provide a single source of truth for ecosystem-wide authoring, organization, and integration requirements. +- Ensure code, docs, and automation remain portable between Windows PowerShell 5.1, PowerShell 7+, and GitHub Actions runners. + +## Execution Steps +1. Map the change you are making to the relevant PSModule component (module code, docs, automation, etc.). +2. Review the framework behavior rules below and identify which ones influence your task. +3. Apply those rules while editing, validating encoding/line-endings and structural conventions before finalizing changes. +4. Confirm your update keeps PSModule tooling (Pester, MkDocs, GitHub Actions) functional by running targeted validations. +5. Document any notable deviations so repository instructions can extend or override these defaults if required. + +## Behavior Rules +- **Architecture & Convention** + - Favor modular design with single-responsibility components and semantic folders (`src/`, `tests/`, `docs/`). + - Prefer convention over configuration to minimize bespoke setup. + - Keep all deliverables cross-platform and context-aware; auto-detect CI/CD environments when possible. +- **Authoring Standards** + - Encode files as UTF-8 with LF endings, no BOM unless a language-specific rule demands it. + - Remove trailing whitespace, keep line length ≤ 150 characters, and end files with a single newline. + - Write in English and use forward slashes (`/`) for paths. +- **Workflow Integration** + - Develop and test PowerShell modules with `Import-Module ... -Force` and validate via `Invoke-Pester`. + - Maintain semantic versioning and automated release flows through GitHub Actions. + - Provide current documentation and ensure examples execute as written. +- **Error Handling & Resilience** + - Prefer attribute-based validation (for example `[ValidateNotNullOrEmpty()]`) over manual checks. + - Emit actionable error messages that link to resolution guidance when available. + - Use standard PowerShell error surfaces (`throw`, `Write-Error`) while preserving pipeline semantics. +- **Ecosystem Integration** + - Honour PSModule authentication abstractions and shared environment variables (`GITHUB_*`, `PSMODULE_*`). + - Produce logs compatible with CI viewers (e.g., `LogGroup` sections) and consider cross-repo dependencies. + +## Output Format +- Deliverables must retain the repository's semantic structure, respect encoding/line-ending rules, and include runnable examples or tests where prescribed. +- Documentation updates should remain MkDocs-compatible with intact navigation metadata. + +## Error Handling +- When a rule cannot be satisfied (e.g., tooling limitation), document the deviation in repo-specific instructions and surface a warning in pull-request notes. +- Fail fast on structural violations (incorrect encoding, missing newline, or inconsistent naming) and correct them before submission. + +## Definitions +| Term | Description | +| --- | --- | +| **PSModule ecosystem** | Collection of interoperable PowerShell modules, docs, and automation maintained under the PSModule banner. | +| **Pipeline-friendly** | Design that supports streaming input/output via the PowerShell pipeline without forcing array materialization. | +| **Context awareness** | Ability to detect execution environment (local PowerShell, GitHub Actions, enterprise runners) and adjust behavior automatically. | +| **LogGroup** | Structured logging helper used to create collapsible sections in CI/CD output. | +| **Semantic folders** | Conventional top-level directories (`src`, `tests`, `docs`, etc.) shared across PSModule repositories. | diff --git a/.github/instructions/instructions/instructions/repo/PowerShell/main.instructions.md b/.github/instructions/instructions/instructions/repo/PowerShell/main.instructions.md new file mode 100644 index 000000000..25da6b551 --- /dev/null +++ b/.github/instructions/instructions/instructions/repo/PowerShell/main.instructions.md @@ -0,0 +1,83 @@ +--- +applyTo: "**/*.{ps1,psm1,psd1}" +description: GitHub module PowerShell specific patterns and conventions. +--- + +# GitHub Module PowerShell Instructions + +## Function Structure Pattern +All public functions in this module follow this specific pattern: + +### Authentication Context Pattern +```powershell +function Get-GitHubExample { + [CmdletBinding()] + param( + [Parameter()] + [object] $Context + ) + + begin { + # Always start public functions with context resolution and validation + $Context = Resolve-GitHubContext -Context $Context + Assert-GitHubContext -Context $Context + + # Include permission requirements comment + # Requires permissions: repository:read + } + + process { + # Implementation with consistent API call pattern + $splat = @{ + Method = 'GET' + APIEndpoint = '/repos/{owner}/{repo}/example' + Body = $Body + Context = $Context + } + Invoke-GitHubAPI @splat | ForEach-Object { Write-Output $_.Response } + } +} +``` + +## Parameter Conventions +- Use PascalCase for all parameters +- Always include `[Parameter()]` attribute (even if empty) +- Use `[ValidateNotNullOrEmpty()]` instead of string null checks +- Owner parameters: `[Alias('User','Organization')]` +- Repository parameters: `[Alias('Repo')]` if needed +- Context parameter: `[object] $Context` for all public functions + +## Authentication Context +- **Public functions**: Handle context resolution and validation with `Resolve-GitHubContext` and `Assert-GitHubContext` +- **Private functions**: Expect resolved `[GitHubContext] $Context` parameter +- Support all auth types: PAT, UAT, IAT, GitHub App JWT + +## API Call Patterns +- Always splat API calls with consistent parameter order: Method, APIEndpoint, Body, Context +- Use PascalCase for HTTP methods: `Get`, `Post`, `Put`, `Delete`, `Patch` +- Handle pipeline output with `ForEach-Object { Write-Output $_.Response }` +- Include permission requirements in function begin block + +## Naming Standards +- **Public functions**: `Verb-GitHubNoun` (e.g., `Get-GitHubRepository`) with aliases where appropriate +- **Private functions**: Same pattern but NO aliases +- **Classes**: `GitHubObjectType` (e.g., `GitHubRepository`) +- Object-oriented parameter naming (don't repeat function context in parameter names) + +## Error Handling & Validation +- Use `[ValidateNotNullOrEmpty()]` instead of string null checks +- Throw meaningful errors when required parameters missing from context +- Follow PowerShell error handling patterns +- Provide actionable error messages that guide users to solutions +- Include relevant GitHub API documentation links in error messages + +## GitHub Actions Integration +- Always consider whether code is running in GitHub Actions environment +- Respect existing authentication context before establishing new connections +- Support environment variable-based configuration +- Handle both local development and CI/CD scenarios + +## Reference Implementation +- See `src/functions/public/Auth/Connect-GitHubAccount.ps1` for authentication patterns +- See `src/functions/public/Repositories/` for standard function structure +- Follow patterns documented in `CodingStandard.md` diff --git a/.github/instructions/instructions/instructions/repo/PowerShell/xml.instructions.md b/.github/instructions/instructions/instructions/repo/PowerShell/xml.instructions.md new file mode 100644 index 000000000..f034c2991 --- /dev/null +++ b/.github/instructions/instructions/instructions/repo/PowerShell/xml.instructions.md @@ -0,0 +1,54 @@ +--- +applyTo: "**/*.{xml,ps1xml}" +--- + +# PowerShell XML Configuration Guidelines + +## Format Definition Files +- Use proper PowerShell formatting XML schema +- Define table views with appropriate column widths +- Consider terminal display constraints +- Include meaningful headers and labels + +## Type Definition Files +- Define property aliases for common alternate names +- Add script properties for calculated values +- Maintain consistency with class definitions +- Use proper PowerShell types XML schema + +## Common Patterns +```xml + + + + GitHubRepository-Table + + GitHubRepository + + + + + + 25 + + + + + + + Name + + + + + + + + +``` + +## File Organization +- One XML file per GitHub object type +- Match file names to corresponding classes +- Place format files in `src/formats/` +- Place type files in `src/types/` \ No newline at end of file diff --git a/.github/instructions/instructions/instructions/repo/main.instructions.md b/.github/instructions/instructions/instructions/repo/main.instructions.md new file mode 100644 index 000000000..72b6d2176 --- /dev/null +++ b/.github/instructions/instructions/instructions/repo/main.instructions.md @@ -0,0 +1,71 @@ +--- +applyTo: "**/*" +description: GitHub PowerShell Module specific patterns and conventions. +--- + +# GitHub Module Repository Instructions + +> **Context**: This is the GitHub PowerShell module repository - a comprehensive PowerShell wrapper for the GitHub API. + +## Repository Architecture + +### Module Structure +- `src/functions/public/` - User-facing cmdlets organized by GitHub object type (Repositories, Issues, etc.) +- `src/functions/private/` - Internal helper functions +- `src/classes/public/` - PowerShell classes for GitHub objects (GitHubRepository, GitHubUser, etc.) +- `src/formats/` - Custom formatting for display output +- `src/types/` - Type extensions and aliases + +### Core Design Patterns +- **Object-oriented organization**: Functions grouped by GitHub entity type, not API endpoints +- **Context-aware**: Automatically detects GitHub Actions environment and loads context +- **Pipeline-friendly**: All public functions support pipeline input where appropriate +- **Authentication abstraction**: Single connection model supporting multiple auth types + +## Development Workflows + +### Function Creation +Use the utility script to scaffold new functions: +```powershell +.\tools\utilities\New-Function.ps1 -Path "Repositories" -Method "GET" +``` + +### Local Development +```powershell +# Import module for testing +Import-Module ./src/GitHub.psm1 -Force + +# Connect and test +Connect-GitHubAccount +Get-GitHubRepository -Owner PSModule -Name GitHub +``` + +### Testing +```powershell +# Run all tests +pwsh -Command "Invoke-Pester" + +# Run specific test suite +pwsh -Command "Invoke-Pester -Path tests/Repositories.Tests.ps1" +``` + +## File Organization Rules +- Group by GitHub object type (Repository, Issue, etc.), not by API operation +- One API endpoint = one function +- Public functions in `public/ObjectType/`, private in `private/ObjectType/` +- Classes mirror the object hierarchy from GitHub API + +## Key Integration Points +- **GitHub Actions**: Auto-detects runner environment, imports event data +- **Azure Key Vault**: Supports GitHub App key storage in Key Vault +- **PSModule Framework**: Follows PSModule.io conventions and build system +- **Pester Testing**: Comprehensive test suite with auth context scenarios + +## Module Capabilities +This module serves as both a local automation tool and a GitHub Actions companion, with special handling for CI/CD scenarios and enterprise environments. + +### Supported Features +- Multiple authentication methods (PATs, GitHub Apps, OAuth, device flow) +- GitHub Actions context awareness +- Enterprise GitHub (GHES, GHEC) support +- Pipeline-friendly cmdlets with standard PowerShell conventions diff --git a/.github/instructions/instructions/repo/Markdown/main.instructions.md b/.github/instructions/instructions/repo/Markdown/main.instructions.md new file mode 100644 index 000000000..ea0e9beb6 --- /dev/null +++ b/.github/instructions/instructions/repo/Markdown/main.instructions.md @@ -0,0 +1,51 @@ +--- +applyTo: '**/*.md' +description: Markdown standards specific to the PSModule documentation site and sample content. +--- + +# Repository Markdown Guidelines + +These rules extend the framework Markdown guidance with MkDocs-specific navigation, PSModule voice, and example requirements. + +## Goal +- Keep documentation pages, blog posts, and READMEs consistent with PSModule brand and site structure. +- Ensure examples remain executable against current modules and workflows. + +## Execution Steps +1. Confirm the document’s placement in the MkDocs navigation and update `mkdocs.yml` if structure changes. +2. Start the page with a descriptive H1, then follow the heading hierarchy established for the site section. +3. Embed runnable examples (PowerShell or YAML) using fenced code blocks with language annotations. +4. Add cross-links to relevant PSModule modules, actions, or dictionary entries using relative references defined in `includes/links.md`. +5. Run `mkdocs serve` (or build) locally to verify navigation, formatting, and link integrity. + +## Behavior Rules +- **Document Structure** + - Provide a table of contents when sections exceed three H2 headings. + - Close with related links, references, or next steps to support discoverability. +- **README Standards** + - Summarize purpose, installation, usage, contribution, and license; keep them aligned with module repositories. +- **Code Blocks & Samples** + - Use ```powershell, ```yaml, or appropriate language hints; show inputs and expected outputs when relevant. + - Prefer examples that authenticate via PSModule helpers or demonstrate GitHub Actions integration. +- **Linking** + - Use descriptive link text, relative paths for internal content, and shared link definitions from `includes/links.md` when possible. +- **Formatting** + - Bold key terms sparingly, use inline code for commands/filenames, blockquotes for callouts, and tables for structured data. +- **Integration** + - Align content with MkDocs metadata/frontmatter conventions and ensure navigation breadcrumbs remain accurate. +- **Examples & Scenarios** + - Cover common use cases, include error-handling guidance, and highlight PSModule best practices. + +## Output Format +- Markdown pages must render cleanly in MkDocs, appear in navigation, and reference any updated assets (images, includes). +- Example code should be copy/paste ready and validated against current module versions. + +## Error Handling +- Treat broken links, missing includes, or failing MkDocs builds as blocking issues. +- Document temporary discrepancies (e.g., pending module release) with visible callouts and follow-up tasks. + +## Definitions +| Term | Description | +| --- | --- | +| **MkDocs navigation** | Menu structure defined in `mkdocs.yml` that controls page placement. | +| **Dictionary entries** | Shared glossary pages under `docs/Dictionary/` referenced throughout the site. | diff --git a/.github/instructions/instructions/repo/PowerShell/classes.instructions.md b/.github/instructions/instructions/repo/PowerShell/classes.instructions.md new file mode 100644 index 000000000..0941df40f --- /dev/null +++ b/.github/instructions/instructions/repo/PowerShell/classes.instructions.md @@ -0,0 +1,52 @@ +--- +applyTo: '**/classes/**/*.ps1' +description: Repository-specific PowerShell class patterns for GitHub objects. +--- + +# GitHub Module Class Guidelines + +## GitHub Object Class Structure +- One class per file, named after the GitHub resource type +- Use PascalCase for all public properties and methods +- Extend `GitHubNode` for objects with database/node IDs from GitHub API + +## GitHub-Specific Property Standards +- `ID` property for GitHub database ID (aliased to `DatabaseID` via types) +- `NodeID` property for GitHub GraphQL node ID +- Size properties in bytes, named `Size` (GitHub API standard) +- Include GitHub scope properties (Enterprise, Owner, Repository, etc.) + +## GitHub Class Aliases +- Add class name as alias property pointing to primary identifier +- Example: `GitHubRepository` has `Repository` property pointing to `Name` +- Use types files (`.Types.ps1xml`) for property aliases matching GitHub API + +## GitHub Object Documentation +- Include comprehensive property descriptions matching GitHub API documentation +- Document expected value formats and examples from GitHub API responses +- Reference official GitHub API documentation for complex properties +- Include permission requirements and availability information + +## GitHub API Integration +- Properties should match GitHub API response field names where possible +- Handle GitHub API timestamp formats and convert to PowerShell DateTime +- Support both REST API and GraphQL API response patterns +- Include calculated properties for derived GitHub data + +## Inheritance Hierarchy for GitHub Objects +- Use `GitHubNode` base class for objects with ID and URL properties +- Implement common GitHub interfaces consistently across related classes +- Override methods only when necessary for GitHub-specific behavior +- Maintain serialization compatibility with GitHub API responses + +## JSON Integration for GitHub API +- Ensure classes serialize/deserialize correctly with GitHub API responses +- Use appropriate property names that match GitHub API field names +- Handle null values gracefully during GitHub API deserialization +- Support round-trip serialization for GitHub API data + +## Reference Implementation Files +- See `src/classes/public/GitHubNode.ps1` for base class pattern +- See `src/classes/public/Repositories/` for GitHub repository object examples +- Check `src/types/` for GitHub-specific property alias patterns +- Reference GitHub API documentation for object structure diff --git a/.github/instructions/instructions/repo/PowerShell/formats.instructions.md b/.github/instructions/instructions/repo/PowerShell/formats.instructions.md new file mode 100644 index 000000000..fa594b3c5 --- /dev/null +++ b/.github/instructions/instructions/repo/PowerShell/formats.instructions.md @@ -0,0 +1,47 @@ +--- +applyTo: '**/src/formats/**/*.Format.ps1xml' +description: PowerShell formatting files for custom display of objects. +--- + +# PowerShell Format Guidelines + +## File Structure +- XML format files define how objects appear in console output +- Name format: `ObjectType.Format.ps1xml` (e.g., `GitHubRepository.Format.ps1xml`) +- Include both table and list views where appropriate + +## Table Views +- Use descriptive column headers with `