Guidelines for AI coding agents working in the Clippit repository. Clippit is a .NET library providing OpenXml PowerTools for Word, Excel, and PowerPoint.
| Task | Command |
|---|---|
| Full build pipeline | ./build.sh (Unix) or .\build.cmd (Windows) |
| Build only | dotnet build Clippit.slnx -c Release |
| Restore tools | dotnet tool restore |
| Lint check | dotnet csharpier check . |
| Lint fix | dotnet csharpier format . |
| Test all | dotnet test --project Clippit.Tests/ |
| Single test | dotnet test --project Clippit.Tests/ --treenode-filter "/*/*/*/MethodName**" |
| Tests in class | dotnet test --project Clippit.Tests/ --treenode-filter "/*/*/ClassName/**" |
| Check outdated deps | dotnet outdated |
The full build script (build.sh/build.cmd) calls dotnet fsi build.fsx -- -p build,
which runs these stages in order:
dotnet tool restoreanddotnet restoredotnet csharpier check .(formatting check — build fails if violated)dotnet cleanand cleanbin/- Generate
Clippit/Properties/AssemblyInfo.g.cs(version fromCHANGELOG.md) dotnet build Clippit.slnx -c Releasedotnet test --solution Clippit.slnxdotnet pack Clippit/Clippit.csproj -o bin/
Always run dotnet csharpier format . before committing. A pre-commit hook (Husky.NET)
runs CSharpier on staged .cs files automatically.
Tests use TUnit with the Microsoft.Testing.Platform runner (configured in global.json).
The test project targets net10.0 only. The --treenode-filter flag uses TUnit's tree
filter syntax — the pattern /*/*/*/MethodName** matches any test method starting with
MethodName across all assemblies/namespaces/classes.
Clippit/ Main library (targets net10.0)
Word/ Word/DOCX: DocumentBuilder, DocumentAssembler, WmlComparer, HtmlConverter
Assembler/ DocumentAssembler helpers (templates, extensions)
PowerPoint/ PPTX: PresentationBuilder, Fluent API
Excel/ XLSX: SpreadsheetWriter, SmlDataRetriever
Html/ HTML-to-WML conversion (CssParser, CssApplier)
Comparer/ WmlComparer (partial classes split across ~26 files by concern)
Core/ PowerToolsBlock, StronglyTypedBlock
Internal/ ColorParser, TextReplacer, Relationships
Properties/ AssemblyInfo.cs (InternalsVisibleTo), AssemblyInfo.g.cs (generated)
Clippit.Cli/ CLI tool (targets net10.0)
Commands/Pptx/Split/ `pptx split` command and result payload
Commands/Pptx/Build/ `pptx build init` / `pptx build run`, manifests, sections
Commands/Pptx/Verify/ `pptx verify` validation command and result payload
Commands/Version/ `version` command and root `--version` payload
Infrastructure/ Output format, structured errors, exit codes
Clippit.Tests/ Test project (targets net10.0)
Word/ PowerPoint/ Excel/ Html/ Common/ Mirror library structure
Cli/ CLI integration and AOT smoke tests
*/Samples/ Sample/integration tests
TestFiles/ Test data (.docx, .pptx, .xlsx)
docs/schemas/ JSON schemas for CLI manifests/results
Directory.Build.props Shared MSBuild props (nullable, implicit usings, lang version)
.editorconfig Code style rules
- 4 spaces for C#; 2 spaces for XML/JSON/scripts
- Max line length: 120 characters
- CSharpier handles all formatting — do not fight it
- UTF-8 encoding (with BOM for
.csfiles) - Always insert final newline; trim trailing whitespace
System.*usings first, then all others- No blank lines between using groups
- Implicit usings are enabled — do not add explicit usings for
System,System.Collections.Generic,System.Linq,System.Threading.Tasks, etc.
- Prefer
varfor all local variable declarations - Use language keywords (
int,string,bool) over framework types (Int32,String) - Nullable reference types are enabled — annotate nullability, avoid
null!suppression
| Symbol | Convention | Example |
|---|---|---|
| Types, methods, properties | PascalCase | DocumentBuilder, GetMetrics() |
| Constants, enum members | PascalCase | MaxRetries, Landscape |
| Local functions | PascalCase | ProcessElement() |
| Private static fields | s_ + camelCase |
s_tempDir, s_maxId |
| Private instance fields | _ + camelCase |
_package, _validator |
| Locals, parameters | camelCase | sourceDoc, partUri |
Avoid this. qualification for field/property/method access.
- Allman style: newline before every opening brace
- Always use braces for
if/else/for/foreach/while/usingblocks - Expression bodies for properties, indexers, and accessors
- Block bodies for methods, constructors, and operators
Prefer modern C# idioms wherever applicable:
- Object and collection initializers
- Null coalescing (
??) and null propagation (?.) - Pattern matching (
is,switchexpressions) overas+ null check is null/is not nullover== null/!= null- Primary constructors for simple exception/record types
- Collection expressions (
[]) for empty collections usingdeclarations (without braces) where scope allows
AnalysisModeisAllbut warnings are not treated as errors- Use project-specific exceptions:
OpenXmlPowerToolsException,PowerToolsDocumentException,InvalidOpenXmlDocumentException,DocumentBuilderException,PresentationBuilderException - Guard clauses: prefer
ArgumentNullException.ThrowIfNull(param)for null checks - No logging framework — the library does not log; tests use
Console.WriteLine
- File-scoped namespaces preferred for new code (
namespace Clippit.Word;) - Some legacy files use block-scoped namespaces — either style is acceptable
-
Testing framework: TUnit — use
[Test]and[Arguments]attributes -
Assertions are async and fluent:
await Assert.That(errors).IsEmpty(); await Assert.That(value).IsEqualTo(expected); await Assert.That(collection).HasCount(5); await Assert.That(() => action).Throws<OpenXmlPowerToolsException>();
- All test classes inherit from
TestsBase(providesTempDir,Validate(), helpers) - Test data lives in
TestFiles/— reference vianew DirectoryInfo("../../../../TestFiles/") - Test output goes to
temp/directory (created lazily byTestsBase.TempDir) - CLI tests use
CliTestRunner.CreateTempDirectory()under the OS temp directory instead ofTestsBase.TempDirbecauseTestsBasemay wipe repotemp/during parallel test runs. - Organize tests to mirror library structure (
Word/,PowerPoint/, etc.) - Name tests with a prefix code and descriptive name:
DB001_DocumentBuilderKeepSections
- Always validate OpenXml output with
Validate()fromTestsBase - Parameterized tests: stack multiple
[Arguments]on a single[Test]method - Synchronous tests return
void; async tests returnTask - Bug fix PRs must include test case(s) that reproduce the issue and verify the fix
- Partial classes:
WmlCompareris split across ~26 files by concern (e.g.,WmlComparer.Public.Methods.Compare.cs,WmlComparer.Private.Methods.Hashing.cs) - Extension methods:
PtOpenXmlExtensions,PtExtensionsextend OpenXml SDK types - CLI output discipline: success payloads go to stdout (compact JSON when
--format jsonor stdout is piped, text otherwise); command errors always go to stderr as compact JSON with a symboliccode. - CLI validation:
pptx verifycombinesOpenXmlValidatorschema checks withRelationshipValidatordangling relationship checks; invalid-but-readable decks return a result on stdout and exit code4. - XNamespace constants:
W,WP,M,MC, etc. inPtOpenXmlUtil.csdefine all XML namespace constants used throughout the library - PPTX memory model: large-deck workflows should open source presentations with
OpenSettings { AutoSave = false }, copy slides throughFluentPresentationBuilder, then release per-slide XML caches. For source slides callRemoveAnnotations<XDocument>()andUnloadRootElement()after copying. For destination slides that were mutated directly, callPutXDocument()andRemoveAnnotations<XDocument>(). Dispose in this order: fluent builder, presentation document, stream; save sections after builder disposal and before disposing the destination document. - Versioning: version is derived from
CHANGELOG.mdby the build script - No new dependencies without clear justification — the project values minimal deps