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
34 changes: 34 additions & 0 deletions .agents/agents/sdk-consumer-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# sdk-consumer-setup (generic agent spec)

## Goal

Help a consuming repository adopt or troubleshoot `Purview.DotNetProjectSdk` correctly, without breaking existing build behaviour.

## Workflow

1. Confirm the SDK is imported in `Directory.Build.props`/`Directory.Build.targets` via
`<Import Sdk="Purview.DotNetProjectSdk" Project="Sdk.props" />` and the matching `Sdk.targets` import.
2. Check pre-import bootstrap properties are set **before** the `Sdk.props` import when they must affect
evaluation: `NamespacePrefix`, `UsePackageJsonVersion`, `RootPackageJson`.
3. If version resolution looks wrong, verify `package.json` discovery: explicit `RootPackageJson`, then CI
variables, `.git` root, or a nearby `package.json`. `UsePackageJsonVersion=Strict` fails fast instead of
silently skipping resolution.
4. If the bundled `.agents/**` content isn't appearing in the repo root, check `EnableAgentFolderInPackage`
(default `true`) and `AgentPackDestinationFolder` (default `.agents`) — the copy runs before build via
`EnsureAgentFolderInPackageTarget`.
5. For test-framework or project-shape questions, confirm the project follows repo naming and placement
conventions the SDK expects, rather than introducing bespoke structure.
6. Re-run `dotnet build` (or the repo's canonical build command) after each configuration change to confirm
the fix.

## Constraints

- Prefer minimal, targeted property changes over broad `Directory.Build.props` rewrites.
- Do not disable `PurviewAutoSdkPack` or `EnableAgentFolderInPackage` unless the consumer explicitly asks to
opt out.
- Do not duplicate SDK-managed properties in individual project files unless the scenario is intentionally
project-specific.

## Related skill

See `../skills/sdk-configuration-reference/SKILL.md` for the full property reference.
55 changes: 55 additions & 0 deletions .agents/agents/source-generator-framework-writer.agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
name: Source Generator Framework Writer
description: "Specialist for Purview.SourceGeneratorFramework generation code using CodeWriter and XmlCodeWriter-style XML doc extensions; ideal for creating or refactoring generator emitters."
tools:
[
"search/codebase",
"edit/editFiles",
"search",
"execute/getTerminalOutput",
"execute/runInTerminal",
"read/terminalLastCommand",
"read/terminalSelection",
"execute/createAndRunTask",
"execute/runTask",
"read/getTaskOutput",
"vscodeTasks/createAndRunTask",
"vscodeTasks/getTaskOutput",
"vscodeTasks/runTask",
]
---

You are a specialist for `Purview.SourceGeneratorFramework` emitter authoring.

## Primary objective

Produce clear, deterministic, maintainable source-generator emission code using `CodeWriter` and XML extension helpers from `XmlCommentWriter`.

## Must-follow rules

1. Prefer structured declaration APIs over handwritten declaration strings.
2. Prefer XML helper extensions (`XmlSummary`, `XmlParam`, etc.) over raw `///` output.
3. Keep `CodeWriter` instances output-scoped; never cache in incremental provider state.
4. Preserve semantic behavior while modernizing implementation style.
5. Keep edits minimal and localized to emitter concerns.

## Refactoring posture

When modernizing legacy code:

- Replace manual indentation/braces with scope APIs.
- Replace signature text with declaration option records.
- Replace ad-hoc XML tags with helper APIs.
- Preserve diagnostics and emitted symbol names.

## Quality gates

- Build/tests pass for impacted projects.
- No scope leaks when materializing generated source.
- Generated artifacts remain deterministic and reviewable.

## Skill routing

When relevant, first load and apply:

- `source-generator-codewriter-modernization`
52 changes: 52 additions & 0 deletions .agents/prompts/refactor-source-generator-to-codewriter.prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
agent: ask
description: "Refactor a legacy source generator emitter from string/StringBuilder to CodeWriter + XmlCodeWriter-style XML extensions with behavior parity."
---

You are modernizing a source generator implementation in this repository.

## Inputs

- Target file(s): `${input:targetFiles:Path(s) to emitter file(s)}`
- Generator type name: `${input:generatorName:Generator class name}`
- Generator version: `${input:generatorVersion:Version string (for generated attributes/header)}`
- Keep output byte-identical where possible: `${input:preserveFormatting:true|false}`

## Task

Refactor the selected legacy emitter implementation from manual `string` / `StringBuilder` output construction to `CodeWriter` and XML documentation extension helpers from `XmlCommentWriter` (XmlCodeWriter-style API usage).

### Requirements

1. Use structured declaration APIs where applicable:
- `WriteClass/WriteStruct/WriteRecordClass/WriteInterface/WriteEnum`
- `WriteMethod`, `WriteProperty`, `WriteField`, `WriteConstructor`
2. Use XML helper extensions instead of raw `///` composition:
- `XmlSummary`, `XmlParam`, `XmlReturn`, `XmlRemarks`, `XmlCode` or `XmlCodeBlock`
3. Use `TypeReferenceOptions` when type text becomes complex (nullability, generics, arrays).
4. Ensure writer lifetime is output-scoped (`generationContext.CreateCodeWriter()` inside callback).
5. Preserve behavior, diagnostics, and generated names.
6. Keep changes minimal and focused; do not reformat unrelated logic.

### Migration strategy

- Identify emitter phases: header, namespace, type declarations, member declarations.
- Replace indentation/braces with scoped APIs.
- Replace signature strings with declaration options.
- Replace XML comments with XmlCommentWriter extension methods.
- Keep semantic equivalence; call out any intentional deltas.

### Verification

- Run relevant tests.
- Confirm generated files still compile.
- Confirm no `CodeWriter` scope leaks (`OpenScopeCount == 0` when materialized).

### Output format

Return:

1. Files changed
2. Why each change was necessary
3. Risks/behavior differences (if any)
4. Verification performed
26 changes: 26 additions & 0 deletions .agents/prompts/sdk-diagnose-agent-folder-copy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# sdk-diagnose-agent-folder-copy (generic prompt spec)

Diagnose why the bundled `.agents/**` folder from `Purview.DotNetProjectSdk` did not appear at the expected
destination in a consuming repository.

## Required behaviour

1. Confirm the NuGet package actually contains `.agents/**` content (inspect the `.nupkg` if available).
2. Confirm the consuming project is packable/buildable and imports the SDK via
`Sdk.props`/`Sdk.targets`, since the copy runs in `EnsureAgentFolderInPackageTarget` before build.
3. Check `EnableAgentFolderInPackage` is not set to `false` anywhere in the build (project file,
`Directory.Build.props`, or command-line `-p:` overrides).
4. Confirm the destination folder: default is `.agents` at the repo root, overridable per-build with
`-p:AgentPackDestinationFolder=<folder>`.
5. Verify repo-root discovery succeeded: explicit `RepoRoot`, then a nearby `AGENTS.md`, then source-control
root metadata.
6. Re-run the build and confirm the destination folder now contains the copied files (including the
generated `.gitignore` for skill/prompt/agent subfolders).

## Suggested output

- A short root-cause explanation (missing import, disabled flag, wrong destination override, or repo-root
discovery miss).
- The exact command used to reproduce/verify the fix (for example
`dotnet build <project> -p:AgentPackDestinationFolder=<folder>`).
- Confirmation that the expected files exist at the resolved destination path.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Ignore all files
*

# Don't ignore directories, so Git can traverse them
!*/

# Keep this file
!.gitignore
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
-->
<RoslynCompilerVersion>4.13.0</RoslynCompilerVersion>
<RoslynAnalyzersVersion>[5.6.0,)</RoslynAnalyzersVersion>
<TUnitVersion>[1.65.0,)</TUnitVersion>
<PurviewSourceGenFramework>[1.0.0-prerelease.23,)</PurviewSourceGenFramework>
<TUnitVersion>[1.65.68,)</TUnitVersion>
<PurviewSourceGenFramework>[1.0.0-prerelease.25,)</PurviewSourceGenFramework>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Bogus" Version="[35.6.5,)" />
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"allowPrerelease": false
},
"msbuild-sdks": {
"Purview.DotNetProjectSdk": "1.0.0-prerelease.39"
"Purview.DotNetProjectSdk": "1.0.0-prerelease.40"
},
"test": {
"runner": "Microsoft.Testing.Platform"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,10 @@ public static bool IsNamedType(ITypeSymbol type, string fullyQualifiedMetadataNa
&& string.Equals(namedType.ToDisplayString(), fullyQualifiedMetadataName, StringComparison.Ordinal);
}

public static bool HasAttribute(IEnumerable<AttributeData> attributes, TypeValueObject attribute) =>
public static bool HasAttribute(IEnumerable<AttributeData> attributes, TypeIdentity attribute) =>
attributes.Any(attribute.Equals);

public static bool IsOrImplements(ITypeSymbol type, TypeValueObject interfaceType)
public static bool IsOrImplements(ITypeSymbol type, TypeIdentity interfaceType)
{
var unwrapped = StripNullableAnnotations(type);
return TypeHelpers.IsNamedType(unwrapped, interfaceType.MetadataFullName)
Expand Down
37 changes: 26 additions & 11 deletions src/src/SourceGenerators/Helpers/AttributeGenHelper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Microsoft.CodeAnalysis.Text;

using ZodSharp.SourceGenerators.Models;

namespace ZodSharp.SourceGenerators.Helpers;
Expand All @@ -13,7 +12,7 @@ static class AttributeGenHelper

static SourceText ZodSchemaAttribute()
{
CodeWriter writer = new(typeof(ZodSchemaGenerator).FullName, AssemblyInfo.Version);
CodeWriter writer = new(GenerationSettings.Create<ZodSchemaGenerator>());

writer.WriteAutoGeneratedHeader().WriteFileScopedNamespace(TypeLibrary.ZodSchemaAttribute);

Expand All @@ -32,17 +31,24 @@ static SourceText ZodSchemaAttribute()
"If not specified, uses \"{ClassName}Schema\"."
)
.WriteProperty(
new(nameof(ZodSchemaAttributeData.SchemaName), PurviewTypeLibrary.System.String.AsTypeReference().Nullable())
new(
nameof(ZodSchemaAttributeData.SchemaName),
PurviewTypeLibrary.System.String.AsTypeReference().Nullable(),
TypeDeclarationAccessibility.Public
)
{
IsInitOnly = true,
}
);

body.XmlSummary("Whether to generate a static Validate method.", "Default is true.")
.WriteProperty(
new(nameof(ZodSchemaAttributeData.GenerateValidateMethod), PurviewTypeLibrary.System.Boolean)
new(
nameof(ZodSchemaAttributeData.GenerateValidateMethod),
PurviewTypeLibrary.System.Boolean,
TypeDeclarationAccessibility.Public
)
{
Accessibility = TypeDeclarationAccessibility.Public,
IsInitOnly = true,
Initializer = "true",
}
Expand All @@ -53,9 +59,12 @@ static SourceText ZodSchemaAttribute()
$"Default is true."
)
.WriteProperty(
new(nameof(ZodSchemaAttributeData.GenerateParseMethod), PurviewTypeLibrary.System.Boolean)
new(
nameof(ZodSchemaAttributeData.GenerateParseMethod),
PurviewTypeLibrary.System.Boolean,
TypeDeclarationAccessibility.Public
)
{
Accessibility = TypeDeclarationAccessibility.Public,
IsInitOnly = true,
Initializer = "true",
}
Expand All @@ -66,9 +75,12 @@ static SourceText ZodSchemaAttribute()
$"Default is true."
)
.WriteProperty(
new(nameof(ZodSchemaAttributeData.EnableComposition), PurviewTypeLibrary.System.Boolean)
new(
nameof(ZodSchemaAttributeData.EnableComposition),
PurviewTypeLibrary.System.Boolean,
TypeDeclarationAccessibility.Public
)
{
Accessibility = TypeDeclarationAccessibility.Public,
IsInitOnly = true,
Initializer = "true",
}
Expand All @@ -83,9 +95,12 @@ static SourceText ZodSchemaAttribute()
"No diagnostic is reported when the default name has no matching method."
)
.WriteProperty(
new(nameof(ZodSchemaAttributeData.CustomValidationMethodName), PurviewTypeLibrary.System.String.AsTypeReference().Nullable())
new(
nameof(ZodSchemaAttributeData.CustomValidationMethodName),
PurviewTypeLibrary.System.String.AsTypeReference().Nullable(),
TypeDeclarationAccessibility.Public
)
{
Accessibility = TypeDeclarationAccessibility.Public,
IsInitOnly = true,
}
);
Expand Down
4 changes: 2 additions & 2 deletions src/src/SourceGenerators/Helpers/CodeGenHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ string errorMessage
"));",
bodyWriter =>
{
bodyWriter.Write(errorCode.Surround()).WriteLine(",");
bodyWriter.Write(errorMessage.Surround()).WriteLine(",");
bodyWriter.Write(Quote(errorCode)).WriteLine(",");
bodyWriter.Write(Quote(errorMessage)).WriteLine(",");
bodyWriter.WriteLine($"new[] {{ \"{propertyName}\" }}");
}
);
Expand Down
Loading
Loading