Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

## [0.3.4] - 2026-04-24

This is a patch release that hardens the .NET scaffold guidance around hidden asset recovery, making incomplete `npx skills add` installs easier to diagnose and repair before generation continues. The validator and repo docs now reinforce the same upstream-verification and manual-restore rule so local cache or packaging mismatches do not get mistaken for template defects.
This is a patch release that hardens the .NET scaffold guidance around hidden asset recovery and makes `git-keep-a-changelog` safer in yolo/auto mode. Incomplete `npx skills add` installs now pivot immediately to an upstream restore path driven by the shared asset manifest, while the changelog skill treats yolo/auto as an explicit full-autonomy mode instead of asking for scope confirmation.

### Changed

- Clarified `dotnet-new-app-slnx` and `dotnet-new-lib-slnx` so missing required or dot-prefixed files in an installed skill copy are treated as a local install mismatch first, with explicit upstream verification and manual restoration before continuing,
- Aligned the app and library variant references, `AGENTS.md`, and `README.md` with the same hidden-asset recovery rule so incomplete `npx skills add` copies get repaired consistently across the repo.
- Clarified `dotnet-new-app-slnx` and `dotnet-new-lib-slnx` so missing required or dot-prefixed files in an installed skill copy immediately trigger the manifest-driven restore path from upstream before generation continues,
- Aligned the app and library variant references, `AGENTS.md`, and `README.md` with the same hidden-asset recovery rule so incomplete `npx skills add` copies get repaired consistently across the repo,
- Expanded `git-keep-a-changelog` with an explicit yolo/auto mode that skips the pending-change confirmation gate and folds staged, unstaged, and untracked worktree changes into the draft automatically.

### Fixed

- Tightened `scripts/validate-skill-templates.ps1` so it now asserts the hidden-asset recovery wording in both scaffold references, keeping the validator synchronized with the documented install-fallback behavior.
- Tightened `scripts/validate-skill-templates.ps1` so it now asserts the shared hidden-asset recovery wording in both scaffold references, keeping the validator synchronized with the documented install-fallback behavior,
- Added validator coverage for the new manifest-driven restore guidance and the yolo/auto changelog bypass contract.

## [0.3.3] - 2026-03-25

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ npx skills add https://github.com/codebeltnet/agentic --skill skill-creator-agno
npx skills add https://github.com/codebeltnet/agentic --skill markdown-illustrator
npx skills add https://github.com/codebeltnet/agentic --skill trunk-first-repo
npx skills add https://github.com/codebeltnet/agentic --skill dotnet-strong-name-signing
npx skills add https://github.com/codebeltnet/agentic --skill dotnet-new-app-slnx
npx skills add https://github.com/codebeltnet/agentic --skill dotnet-new-lib-slnx
# npx skills add https://github.com/codebeltnet/agentic --skill another-skill
```

Expand Down
2 changes: 1 addition & 1 deletion scripts/validate-skill-templates.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ Add-ValidationResult -Results $results -Name 'App skill documents web-family App
Assert-Contains -Name 'dotnet-new-app-slnx/SKILL.md' -Content $skill -Needle 'expect MinVer to report a placeholder pre-release version such as `0.0.0-alpha.0` until the user initializes git and adds a version tag'
Assert-Contains -Name 'dotnet-new-app-slnx/SKILL.md' -Content $skill -Needle 'The solution file must be named `{SOLUTION_NAME}.slnx`, not `{REPO_SLUG}.slnx` and not any lowercased variant.'
Assert-Contains -Name 'dotnet-new-app-slnx/SKILL.md' -Content $skill -Needle 'The `.slnx` file is required even for single-host scaffolds.'
Assert-Contains -Name 'dotnet-new-app-slnx/SKILL.md' -Content $skill -Needle 'Every file from `assets/shared/` exists in the generated repo with the same relative path'
Assert-Contains -Name 'dotnet-new-app-slnx/SKILL.md' -Content $skill -Needle 'Every file listed in `assets/shared.manifest.json` exists in the generated repo at its declared relative path'
Assert-Contains -Name 'dotnet-new-app-slnx/SKILL.md' -Content $skill -Needle 'No generated app or test `.csproj` file introduces `<TargetFramework>`'
Assert-Contains -Name 'dotnet-new-app-slnx/SKILL.md' -Content $skill -Needle '{AppType} = Web'
Assert-Contains -Name 'dotnet-new-app-slnx/SKILL.md' -Content $skill -Needle '{AppType} = Api'
Expand Down
22 changes: 17 additions & 5 deletions skills/dotnet-new-app-slnx/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ description: >

# .NET Application Solution Setup (Codebelt Conventions)

## Upstream Source

| Field | Value |
|-------|-------|
| **Repo** | `https://github.com/codebeltnet/agentic` |
| **Branch** | `main` |
| **Shared assets root** | `skills/dotnet-new-app-slnx/assets/shared` |
| **Raw base URL** | `https://raw.githubusercontent.com/codebeltnet/agentic/main/skills/dotnet-new-app-slnx/assets/shared` |
| **Asset manifest** | `assets/shared.manifest.json` |

This metadata is the single source of truth for restoring any file the installer may have dropped. Use it immediately — do not spend cycles confirming absence multiple ways first.

Scaffold new .NET standalone application solutions following the codebeltnet engineering conventions — the same pattern used across [codebeltnet](https://github.com/codebeltnet). Produces a fully wired solution with CI pipeline, centralized build config, semantic versioning, code quality tooling, and proper folder structure.

> **CRITICAL:** All application projects **must** use the `Codebelt.Bootstrapper.*` framework — never vanilla `WebApplication.CreateBuilder()` or raw `Host.CreateDefaultBuilder()`. The bootstrapper provides a uniform, convention-driven `Program.cs` (and `Startup.cs` for classic hosting). The asset templates in `assets/app/` already wire this up correctly — **always copy from templates, never write Program.cs from scratch**.
Expand Down Expand Up @@ -137,11 +149,11 @@ After writing `Directory.Packages.props`, re-check the generated versions agains
Generate files in this order:

### 1. Copy shared templates
Copy every file from `assets/shared/` to the project root, preserving directory structure. Treat the **current working directory** as that project root. Apply placeholder substitution (Step 4) to all file contents during the copy.
Copy every file from `assets/shared/` to the project root, preserving directory structure. Do **not** copy `assets/shared.manifest.json` — it is a skill-internal file used only for asset validation and restoration, and must never appear in a generated solution. Treat the **current working directory** as that project root. Apply placeholder substitution (Step 4) to all file contents during the copy.

Do this as a recursive, dotfile-aware copy. Hidden folders and files under `assets/shared/` are part of the scaffold and must not be skipped. In particular, copy `assets/shared/.bot/README.md` as a real file in the generated repo; do not replace it with a synthetic `.gitkeep` or placeholder note.

If the installed skill copy omits any required file or dot-prefixed path during a `npx skills add` copy, do not treat that as success or as a template defect. Verify the generated tree against the upstream repository contents, then manually restore the missing files directly from the repository source tree, preserving the same relative paths, before continuing. That includes hidden assets such as `.bot/`, `.github/`, and any other `.`-prefixed directories under `assets/shared/`.
**Asset mismatch policy — pivot immediately to upstream.** The `npx skills add` installer silently strips dot-prefixed entries (`.bot/`, `.github/`, `.editorconfig`, `.gitattributes`, `.gitignore`). Do not spend time re-proving what is absent. The moment any entry from `assets/shared.manifest.json` is missing from the installed skill copy, run `scripts/restore-missing-shared-assets.ps1` to fetch every missing file directly from the upstream repository in one step, then continue. If PowerShell is unavailable, use the raw base URL in the **Upstream Source** table above to download each missing file manually. If upstream fetch fails, halt and report — do not substitute placeholders.

Do not selectively copy only "key" shared files. The intended output includes the complete shared asset inventory, including `.gitignore`, `.gitattributes`, `AGENTS.md`, `CHANGELOG.md`, `.github/`, and `.bot/`, in addition to the build and package-management files.

Expand Down Expand Up @@ -180,8 +192,8 @@ After generating, verify:

- [ ] `.slnx` references all generated src/ and test/ projects
- [ ] The generated solution filename is `{SOLUTION_NAME}.slnx` with the original user-facing casing preserved
- [ ] Every file from `assets/shared/` exists in the generated repo with the same relative path, including dotfiles and dotfolders such as `.gitignore`, `.gitattributes`, `.bot/README.md`, and `.github/*`
- [ ] If a `npx skills add` install omitted any dot-prefixed files, manually restore them from the same repository commit before generation continues
- [ ] Every file listed in `assets/shared.manifest.json` exists in the generated repo at its declared relative path (this covers all dotfiles and dotfolders)
- [ ] If any manifest entry was absent from the installed skill copy, `scripts/restore-missing-shared-assets.ps1` was run (or files fetched manually from the upstream raw URL) — not diagnosed iteratively
- [ ] `Directory.Packages.props` lists all `<PackageReference>` packages used in the solution (including host-type-specific packages)
- [ ] `Directory.Packages.props` contains concrete version numbers with no unresolved `*_VERSION` placeholders
- [ ] No generated `.csproj` file or `Directory.Build.props` contains ad-hoc inline `Version=` attributes for packages that are supposed to be centrally managed by `Directory.Packages.props`
Expand All @@ -194,7 +206,7 @@ After generating, verify:
- [ ] `.bot/` folder exists and is listed in `.gitignore`
- [ ] `.bot/README.md` exists in the generated repo and came from the shared asset template
- [ ] `testenvironments.json` uses the major-tag `codebeltnet/ubuntu-testrunner:{major}` convention for the selected target framework
- [ ] If a `npx skills add` install omitted any required files, verify the upstream repository contents and manually restore the missing files directly from the repository source tree before generation continues
- [ ] No manifest entries were silently skipped; if the restore script reported failures, generation was halted rather than continuing with incomplete shared assets
- [ ] Correct hosting pattern files generated (`Program.cs` only for Minimal, `Program.cs` + `Startup.cs` for Startup)
- [ ] `Web API` is the default `web_variant` when the user asked for a generic `Web` app
- [ ] `Empty Web` uses the `Web` suffix, `Web API` uses `Api`, `MVC` uses `Mvc`, and `Web App / Razor` uses `WebApp`
Expand Down
25 changes: 25 additions & 0 deletions skills/dotnet-new-app-slnx/assets/shared.manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"description": "Authoritative inventory of all required shared scaffold assets, including dot-prefixed files and folders. Used to detect installer truncation and drive restoration from upstream.",
"upstream": {
"repo": "https://github.com/codebeltnet/agentic",
"branch": "main",
"root": "skills/dotnet-new-app-slnx/assets/shared"
},
"files": [
".bot/README.md",
".editorconfig",
".gitattributes",
".github/CODE_OF_CONDUCT.md",
".github/CONTRIBUTING.md",
".github/copilot-instructions.md",
".github/dependabot.yml",
".github/workflows/ci-pipeline.yml",
".gitignore",
"AGENTS.md",
"CHANGELOG.md",
"Directory.Build.targets",
"Directory.Packages.props",
"README.md",
"testenvironments.json"
]
}
105 changes: 105 additions & 0 deletions skills/dotnet-new-app-slnx/scripts/restore-missing-shared-assets.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<#
.SYNOPSIS
Detects missing shared scaffold assets (including dotfiles) and restores them
directly from the upstream repository.

.DESCRIPTION
Reads assets/shared.manifest.json, checks each required path relative to the
skill root, and downloads any missing file from the authoritative GitHub source.
Exits with code 1 if upstream fetch fails for any file.

.PARAMETER SkillRoot
Absolute path to the installed skill directory (parent of assets/).
Defaults to the directory containing this script's parent.

.PARAMETER DryRun
Report missing files without downloading them.

.EXAMPLE
# Restore missing files into the installed skill copy
scripts/restore-missing-shared-assets.ps1

# Preview what is missing without restoring
scripts/restore-missing-shared-assets.ps1 -DryRun
#>
[CmdletBinding()]
param(
[string] $SkillRoot = (Split-Path -Parent $PSScriptRoot),
[switch] $DryRun
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$manifestPath = Join-Path $SkillRoot 'assets/shared.manifest.json'
if (-not (Test-Path $manifestPath)) {
Write-Error "Manifest not found at: $manifestPath"
exit 1
}

$manifest = Get-Content $manifestPath -Raw | ConvertFrom-Json
$repoUrl = $manifest.upstream.repo # e.g. https://github.com/codebeltnet/agentic
$branch = $manifest.upstream.branch # e.g. main
$remoteRoot = $manifest.upstream.root # e.g. skills/dotnet-new-app-slnx/assets/shared
$localRoot = Join-Path $SkillRoot 'assets/shared'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore app hidden assets outside assets/shared

The restore script only checks and repairs assets/shared, but the app scaffold also depends on hidden files under assets/app (for example assets/app/.github/workflows/ci-pipeline.yml is required by skills/dotnet-new-app-slnx/SKILL.md Step 5). If the installer drops dot-prefixed paths, this script will report success while that required app workflow file is still missing, leading to incomplete or incorrect app scaffolds.

Useful? React with 👍 / 👎.


# Build raw-content base URL
$rawBase = $repoUrl -replace 'https://github.com', 'https://raw.githubusercontent.com'
$rawBase = "$rawBase/$branch/$remoteRoot"

$missing = [System.Collections.Generic.List[string]]::new()
$restored = [System.Collections.Generic.List[string]]::new()
$failed = [System.Collections.Generic.List[string]]::new()

foreach ($file in $manifest.files) {
$localPath = Join-Path $localRoot $file
if (-not (Test-Path $localPath)) {
$missing.Add($file)
}
}

if ($missing.Count -eq 0) {
Write-Host "✅ All shared assets present — nothing to restore." -ForegroundColor Green
exit 0
}

Write-Host "⚠️ Missing shared assets ($($missing.Count)):" -ForegroundColor Yellow
$missing | ForEach-Object { Write-Host " - $_" }

if ($DryRun) {
Write-Host "`n[DryRun] No files downloaded." -ForegroundColor Cyan
exit 0
}
Comment on lines +69 to +72
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 -DryRun always exits 0, even when files are missing

When the script is invoked with -DryRun and missing files are detected, it exits with code 0. Any CI pipeline or automated check that calls this script with -DryRun to audit completeness will never detect the gap from the exit code alone — it would need to parse stdout for the warning text. Exiting with code 1 when files are missing (even in dry-run mode) makes the script safe to use in -WhatIf-style pipeline steps.

Suggested change
if ($DryRun) {
Write-Host "`n[DryRun] No files downloaded." -ForegroundColor Cyan
exit 0
}
if ($DryRun) {
Write-Host "`n[DryRun] No files downloaded." -ForegroundColor Cyan
if ($missing.Count -gt 0) { exit 1 }
exit 0
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: skills/dotnet-new-app-slnx/scripts/restore-missing-shared-assets.ps1
Line: 69-72

Comment:
**`-DryRun` always exits 0, even when files are missing**

When the script is invoked with `-DryRun` and missing files are detected, it exits with code `0`. Any CI pipeline or automated check that calls this script with `-DryRun` to audit completeness will never detect the gap from the exit code alone — it would need to parse stdout for the warning text. Exiting with code `1` when files are missing (even in dry-run mode) makes the script safe to use in `-WhatIf`-style pipeline steps.

```suggestion
if ($DryRun) {
    Write-Host "`n[DryRun] No files downloaded." -ForegroundColor Cyan
    if ($missing.Count -gt 0) { exit 1 }
    exit 0
}
```

How can I resolve this? If you propose a fix, please make it concise.


Write-Host "`nRestoring from upstream: $repoUrl (branch: $branch)" -ForegroundColor Cyan

foreach ($file in $missing) {
$url = "$rawBase/$file"
$dest = Join-Path $localRoot $file
$destDir = Split-Path $dest -Parent

if (-not (Test-Path $destDir)) {
New-Item -ItemType Directory -Path $destDir -Force | Out-Null
}

try {
Write-Host " ↓ $file" -NoNewline
Invoke-WebRequest -Uri $url -OutFile $dest -UseBasicParsing
$restored.Add($file)
Write-Host " ✓" -ForegroundColor Green
}
catch {
$failed.Add($file)
Write-Host " ✗ ($($_.Exception.Message))" -ForegroundColor Red
}
}

if ($restored.Count -gt 0) {
Write-Host "`n✅ Restored $($restored.Count) file(s)." -ForegroundColor Green
}

if ($failed.Count -gt 0) {
Write-Host "❌ Failed to restore $($failed.Count) file(s):" -ForegroundColor Red
$failed | ForEach-Object { Write-Host " - $_" }
exit 1
}
Loading
Loading