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
4 changes: 2 additions & 2 deletions .agents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ Codex-only UI metadata and explicit-invocation policy. Claude needs thin skill w
`disable-model-invocation` policy lives in `SKILL.md` frontmatter.

The hook adapters differ because Claude and Codex use different payload and response contracts. Both delegate
all substantive behavior to the same scripts: `scripts/tidy-cs.ps1` and `scripts/public-api-guard.ps1`.
all substantive behavior to the same scripts: `scripts/tidy-code.ps1` and `scripts/public-api-guard.ps1`.

The tidy hook runs the **default scope only** — CSharpier, under a second. Code style and member ordering are
not run on every edit: `dotnet format style` needs MSBuild and ReSharper loads the whole solution, and neither
belongs on the critical path of a single edit. All three are build errors, and `scripts/preflight.ps1` runs
`tidy-cs.ps1 -Scope all` before a commit, so nothing reaches a pull request untidied.
`tidy-code.ps1 -Scope all` before a commit, so nothing reaches a pull request untidied.

When changing behavior, edit the canonical file. Keep only required names, descriptions, policies, tool/model
settings, and reference instructions in tool-specific files.
2 changes: 1 addition & 1 deletion .agents/references/code-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ explicit field and the compiler's capture field are one field, not two.
## When a tool surprises you

- **`cleanupcode` re-indents the content of raw string literals and CSharpier puts it back.** Neither is
idempotent alone; the pair is. Always let CSharpier run last — `tidy-cs.ps1` does.
idempotent alone; the pair is. Always let CSharpier run last — `tidy-code.ps1` does.
- **Explicit interface implementations sort first — except events.** StyleCop counts an explicit property,
indexer or method as public, so each of those has its own "Explicit interface …" entry in the file layout
putting it at the front of its group. An explicit **event** is counted as private, so it stays where
Expand Down
8 changes: 4 additions & 4 deletions .claude/hooks/tidy-cs.ps1 → .claude/hooks/tidy-code.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PostToolUse hook: format an edited C# file with CSharpier.
#
# The logic itself lives in scripts/tidy-cs.ps1, so that Codex's hook and a human run exactly the same
# The logic itself lives in scripts/tidy-code.ps1, so that Codex's hook and a human run exactly the same
# thing. This file is only the hook wiring: read the tool payload off stdin, pull the edited path out of
# it, and delegate.
#
Expand All @@ -21,9 +21,9 @@ try {
if ([System.IO.Path]::GetExtension($filePath) -ne '.cs') { exit 0 }
if (-not (Test-Path -LiteralPath $filePath)) { exit 0 }

$script = Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) 'scripts/tidy-cs.ps1'
$script = Join-Path (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) 'scripts/tidy-code.ps1'
if (-not (Test-Path -LiteralPath $script)) {
Write-Output "tidy-cs hook: scripts/tidy-cs.ps1 not found at $script"
Write-Output "tidy-code hook: scripts/tidy-code.ps1 not found at $script"
exit 0
}

Expand All @@ -36,7 +36,7 @@ try {
}
}
catch {
Write-Output "tidy-cs hook error: $($_.Exception.Message)"
Write-Output "tidy-code hook error: $($_.Exception.Message)"
}

exit 0
2 changes: 1 addition & 1 deletion .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"hooks": [
{
"type": "command",
"command": "pwsh -NoProfile -NonInteractive -File \"$CLAUDE_PROJECT_DIR/.claude/hooks/tidy-cs.ps1\"",
"command": "pwsh -NoProfile -NonInteractive -File \"$CLAUDE_PROJECT_DIR/.claude/hooks/tidy-code.ps1\"",
"timeout": 60
},
{
Expand Down
2 changes: 1 addition & 1 deletion .codex/hooks.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"hooks": [
{
"type": "command",
"command": "pwsh -NoProfile -NonInteractive -Command \"& (Join-Path (git rev-parse --show-toplevel) '.codex/hooks/tidy-cs.ps1')\"",
"command": "pwsh -NoProfile -NonInteractive -Command \"& (Join-Path (git rev-parse --show-toplevel) '.codex/hooks/tidy-code.ps1')\"",
"statusMessage": "Formatting changed C# files",
"timeout": 120
},
Expand Down
8 changes: 4 additions & 4 deletions .codex/hooks/tidy-cs.ps1 → .codex/hooks/tidy-code.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Codex PostToolUse hook: format the C# files an edit just touched, with CSharpier.
#
# The logic itself lives in scripts/tidy-cs.ps1, which Claude Code's hook runs too. This file is only
# The logic itself lives in scripts/tidy-code.ps1, which Claude Code's hook runs too. This file is only
# the hook wiring.
#
# Why it does not read a path out of the payload: for a file edit Codex reports tool_name "apply_patch" and
Expand Down Expand Up @@ -45,9 +45,9 @@ try {
$repositoryRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
}

$script = Join-Path $repositoryRoot 'scripts/tidy-cs.ps1'
$script = Join-Path $repositoryRoot 'scripts/tidy-code.ps1'
if (-not (Test-Path -LiteralPath $script)) {
Write-HookResult -AdditionalContext "tidy-cs hook: scripts/tidy-cs.ps1 not found at $script"
Write-HookResult -AdditionalContext "tidy-code hook: scripts/tidy-code.ps1 not found at $script"
exit 0
}

Expand All @@ -63,7 +63,7 @@ try {
}
}
catch {
Write-HookResult -AdditionalContext "tidy-cs hook error: $($_.Exception.Message)"
Write-HookResult -AdditionalContext "tidy-code hook error: $($_.Exception.Message)"
}

exit 0
11 changes: 6 additions & 5 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ root = true
# ======================================================================================================
# Every file
#
# Line endings are deliberately NOT set here. .gitattributes owns them: it normalizes to LF in the
# repository and checks each file out with the ending that file type needs. An end_of_line here would
# fight that on Windows.
# Line endings are LF everywhere, on every OS. .gitattributes stores LF and checks out LF - that is the
# guarantee, and it holds whatever core.autocrlf a contributor has. end_of_line below is what makes the
# editors and formatters write LF too, so the working tree never drifts.
# ======================================================================================================

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 4
insert_final_newline = true
Expand All @@ -36,7 +37,7 @@ trim_trailing_whitespace = false
# NewStyleCop.Analyzers member ordering (checking only). Fixed by ReSharper, see the ordering
# section further down.
#
# Run scripts/tidy-cs.ps1 to apply all three.
# Run scripts/tidy-code.ps1 to apply all three.
# ======================================================================================================

[*.cs]
Expand Down Expand Up @@ -221,7 +222,7 @@ dotnet_diagnostic.RCS1037.severity = none
# Adding a StyleCop rule to this repository means adding it explicitly below.
#
# StyleCop can only REPORT a wrong order - its ordering code fix is disabled upstream. ReSharper does the
# fixing: in Rider through Code Cleanup, and on the command line through scripts/tidy-cs.ps1. So the
# fixing: in Rider through Code Cleanup, and on the command line through scripts/tidy-code.ps1. So the
# order is defined twice, and the two definitions have to stay in step:
# stylecop.json -> what is checked
# DbConnectionPlus.slnx.DotSettings -> what is applied
Expand Down
50 changes: 15 additions & 35 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,36 +1,16 @@
# Auto-detect text files, normalize to LF in the repository.
* text=auto
# Every text file is LF, in the repository and in the working tree, on every OS.
# Git auto-detects text vs. binary; the rules below are the exceptions.
#
# The explicit eol= overrides core.autocrlf and core.eol, so a contributor needs no git configuration
# and a wrong one cannot change what lands in the repository. CI fails if a file is ever stored
# otherwise.
* text=auto eol=lf

# Force specific line endings where it matters.
*.cs text
*.csproj text eol=crlf
*.slnx text eol=crlf
*.props text eol=crlf
*.targets text eol=crlf
*.config text eol=crlf
*.resx text eol=crlf
*.editorconfig text eol=crlf
*.DotSettings text eol=crlf
*.json text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.md text eol=lf
*.properties text eol=lf

# PowerShell is the repository's scripting language and runs on both hosts; CRLF is what pwsh writes on
# Windows, which is where these are edited.
*.ps1 text eol=crlf

# Windows batch files require CRLF; shell scripts require LF.
*.cmd text eol=crlf
*.bat text eol=crlf
*.sh text eol=lf

# Binary files - never touch.
*.png binary
*.jpg binary
*.ico binary
*.dll binary
*.exe binary
*.snk binary
*.db binary
# Binary files - never convert. text=auto already detects most of these; the list makes it certain.
*.png binary
*.jpg binary
*.gif binary
*.ico binary
*.snk binary
*.dll binary
*.exe binary
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
- [ ] Public API changes are declared in the affected `PublicAPI.Unshipped.txt` (`scripts/update-public-api.ps1`).
- [ ] XML docs and `README.md` updated for public API changes.
- [ ] `CHANGELOG.md` updated, and the version in `src/Directory.Build.props` bumped if this release-bound change needs it.
- [ ] Style, formatting and member ordering applied (`pwsh -File scripts/tidy-cs.ps1 -Scope all`, which
- [ ] Style, formatting and member ordering applied (`pwsh -File scripts/tidy-code.ps1 -Scope all`, which
`preflight.ps1` also runs).
- [ ] Branch name follows [Conventional Branch](https://conventionalbranch.org/): `<type>/issue-<issue#>-<slug>`.
17 changes: 15 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
# Enforce the style, formatting and member ordering the repo ships tooling for. A rule that is not
# enforced in CI drifts with the first external pull request.
#
# This runs scripts/tidy-cs.ps1, the same entry point a developer and both AI agents use, so CI cannot
# This runs scripts/tidy-code.ps1, the same entry point a developer and both AI agents use, so CI cannot
# disagree with what the tooling produces locally. -Check -Scope all tidies the checkout for real and
# then asks whether anything changed: ReSharper has no check mode, and neither it nor CSharpier is
# idempotent alone, so comparing before against after is the only honest question. Writing to the
Expand Down Expand Up @@ -112,6 +112,19 @@ jobs:

echo "All ${count} revision(s) resolve and are reachable."

# .gitattributes only applies when a file is ADDED. A file committed past it, or a rule added
# after the file, stays wrong forever and nothing complains - and commits created server-side on GitHub,
# such as a bot's, bypass the filter entirely. Renormalizing and asking whether anything changed
# is the exact question, and it needs no toolchain.
- name: Verify line endings are normalized
run: |
git add --renormalize .
if ! git diff --cached --quiet; then
git --no-pager diff --cached --name-only
echo "::error title=Line endings::The files above are not stored as .gitattributes requires. Run 'git add --renormalize .' and commit the result."
exit 1
fi

- name: Setup .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
Expand All @@ -135,7 +148,7 @@ jobs:
run: dotnet tool restore

- name: Verify style, formatting and member ordering
run: pwsh -NoProfile -NonInteractive -File scripts/tidy-cs.ps1 -Scope all -Check
run: pwsh -NoProfile -NonInteractive -File scripts/tidy-code.ps1 -Scope all -Check

# The check above only says that something is untidy. This says what.
- name: Show the changes that would fix it
Expand Down
13 changes: 8 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ methods on `DbConnection`, with per-database dialect support from pluggable adap
| `docs/` | docfx config, the site landing page and implementation plans. |
| `.agents/`, `.codex/`, `.claude/` | Canonical skills and references, plus each tool's agent metadata and hook wiring. |
| `.github/workflows/` | `ci.yml` (lint → build/test → package + docs → package-consumption gates → publish), `codeql.yml`, `dependency-review.yml`. |
| `scripts/` | The commands you type: `preflight`, `verify-package-aot`, `benchmarks`, `update-public-api`, `clean-build-artifacts`, `extract-release-notes`. Plus `tidy-cs` and `public-api-guard`, which the editor hooks run for you. |
| `scripts/` | The commands you type: `preflight`, `verify-package-aot`, `benchmarks`, `update-public-api`, `clean-build-artifacts`, `extract-release-notes`. Plus `tidy-code` and `public-api-guard`, which the editor hooks run for you. |

The solution file is `DbConnectionPlus.slnx` (XML `.slnx`, not `.sln`). **New projects must be added to it.**

Expand Down Expand Up @@ -118,9 +118,9 @@ something surprising: [the code-style reference](.agents/references/code-style.m
One entry point applies all of it, in three scopes:

```bash
pwsh -File scripts/tidy-cs.ps1 # ~1s formatting, on the files git reports as changed
pwsh -File scripts/tidy-cs.ps1 -Scope style # ~15s + the code-style fixers
pwsh -File scripts/tidy-cs.ps1 -Scope all # ~3min + member ordering, whole solution
pwsh -File scripts/tidy-code.ps1 # ~1s formatting, on the files git reports as changed
pwsh -File scripts/tidy-code.ps1 -Scope style # ~15s + the code-style fixers
pwsh -File scripts/tidy-code.ps1 -Scope all # ~3min + member ordering, whole solution
```

**Before you commit, run `-Scope all`** — or `scripts/preflight.ps1`, which does it for you. That is the only scope
Expand Down Expand Up @@ -174,6 +174,9 @@ constraints must not be broken:
[the `commit` skill](.agents/skills/commit/SKILL.md). `CHANGELOG.md` follows
[Keep a Changelog](https://keepachangelog.com/); versioning is SemVer.
- Pull request process: [CONTRIBUTING.md](CONTRIBUTING.md#pull-request-process).
- Line endings are LF everywhere; `.gitattributes` and `.editorconfig` enforce this and CI verifies it.
Never hand-convert line endings, and never compare a multi-line source literal against
`Environment.NewLine` - the literal carries the file's bytes, `Environment.NewLine` carries the host's.

### Releases

Expand All @@ -189,7 +192,7 @@ Two skills and two review agents are checked in, each under a Codex and a Claude

Claude Code (`.claude/settings.json`) and Codex (`.codex/hooks.json`) fire the same two PostToolUse hooks —
formatting and the public-API reminder — and both delegate to `scripts/`. Codex needs those hooks trusted once per
clone (`/hooks`); until then nothing fires and you run `scripts/tidy-cs.ps1` yourself.
clone (`/hooks`); until then nothing fires and you run `scripts/tidy-code.ps1` yourself.

Reusable skills and reference material belong in `.agents/`, executable checks in `scripts/`, and only metadata and
hook wiring in `.codex/` and `.claude/`. Never fork a procedure or a check into a tool-specific copy — both
Expand Down
27 changes: 24 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ Please note we have a code of conduct, please follow it in all your interactions
pwsh -File scripts/preflight.ps1
```
It rewrites files — review what it changed and include it in your commit. To run just the tidying:
`pwsh -File scripts/tidy-cs.ps1 -Scope all`.
`pwsh -File scripts/tidy-code.ps1 -Scope all`.

`TreatWarningsAsErrors` is on for **every** project, so the build is also the style, member-ordering,
trim-analyzer and public-API gate, and `CSharpier.MsBuild` makes an unformatted file a build error too.
The build never rewrites your files — it fails and names them; `scripts/tidy-cs.ps1` is what fixes them.
The build never rewrites your files — it fails and names them; `scripts/tidy-code.ps1` is what fixes them.
**Never suppress an `IL2xxx` warning to get a green build** — it is the only
build-time evidence that the trimming annotations are complete.
4. If you touched a reflection path, also run the Native AOT gate. Nothing else in the repository can see
Expand Down Expand Up @@ -57,7 +57,7 @@ dotnet tool restore
git config blame.ignoreRevsFile .git-blame-ignore-revs
```

The first installs CSharpier, the ReSharper command line tools and docfx, which `scripts/tidy-cs.ps1` and the
The first installs CSharpier, the ReSharper command line tools and docfx, which `scripts/tidy-code.ps1` and the
documentation build need. The second makes `git blame` skip the commits listed in `.git-blame-ignore-revs`,
which reformatted and reordered the whole repository, so blame points at whoever wrote the logic rather than
at the tool that moved it. GitHub already does this on its own.
Expand All @@ -66,6 +66,27 @@ If you use Rider, two settings make the tooling invisible: install the **CSharpi
Settings | Tools | CSharpier | Run on Save, and use the shared **ReorderMembers** cleanup profile (from
`DbConnectionPlus.slnx.DotSettings`) when you want members put back in order.

## Line endings

Every text file is LF, in the repository and in the working tree, on every OS. `.gitattributes`
enforces this whatever your `core.autocrlf` is set to, so there is nothing to configure, and CI fails
if a wrongly stored file lands anyway.

`.editorconfig` also asks editors and formatters to write LF. If one does not, git still stores LF, but
`git status` lists the file as modified while `git diff` shows nothing. Run
`pwsh -File scripts/tidy-code.ps1` to fix it, or `git checkout -- <path>`.

If you have set `git config core.safecrlf true`, git refuses to add such a file with "CRLF would be
replaced by LF". Run the tidy script first, or use `core.safecrlf warn`.

To refresh a clone made before this policy (commit or stash your changes first - the second command
discards uncommitted work):

```shell
git rm -r --cached . -q
git reset --hard
```

## Releasing

Releases are cut by CI from a pushed tag; nothing is packed or pushed by hand. The versioning scheme is
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
Check, not format. Left at its default the package REWRITES source files during the build, which
turns building into editing: the file on disk stops being the file you asked the compiler to
compile. With Check the build fails instead and names the file, and fixing it stays a deliberate
act - scripts/tidy-cs.ps1, or the editor hook that already runs on every agent edit.
act - scripts/tidy-code.ps1, or the editor hook that already runs on every agent edit.

CSharpier_Bypass=true skips it, for the rare case of needing a build out of a tree mid-edit.
-->
Expand Down
4 changes: 2 additions & 2 deletions scripts/preflight.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ $repositoryRoot = Split-Path -Parent $PSScriptRoot
$solution = Join-Path $repositoryRoot 'DbConnectionPlus.slnx'
$unitTests = Join-Path $repositoryRoot 'tests/DbConnectionPlus.UnitTests/DbConnectionPlus.UnitTests.csproj'
$publicApiGuard = Join-Path $repositoryRoot 'scripts/public-api-guard.ps1'
$tidy = Join-Path $repositoryRoot 'scripts/tidy-cs.ps1'
$tidy = Join-Path $repositoryRoot 'scripts/tidy-code.ps1'

$failures = New-Object System.Collections.Generic.List[String]

Expand Down Expand Up @@ -93,7 +93,7 @@ if (-not $SkipTidy) {
& pwsh -NoProfile -NonInteractive -File $tidy -Scope all
if ($LASTEXITCODE -ne 0) {
$failures.Add('tidy')
Write-Output 'FAIL - tidy-cs could not finish. Run `dotnet tool restore` if the tools are missing.'
Write-Output 'FAIL - tidy-code could not finish. Run `dotnet tool restore` if the tools are missing.'
}
else {
$dirtyAfter = @(& git -C $repositoryRoot diff --name-only -- '*.cs' 2>$null | Where-Object { $_ })
Expand Down
Loading
Loading