关于 template-authoring
帮助开发者从现有项目创建并验证自定义 dotnet new 模板的技能,也可用于编写模板配置和 NuGet 打包。它强调保留源项目的构建设置与包元数据,并要求创建的模板经过本地实例化和构建验证。
主要功能
- 01从现有项目生成模板配置
- 02验证 template.json
- 03保留项目与包设置
- 04配置参数、条件内容和后续操作
- 05安装并构建生成的项目
适用场景
- 01将 .csproj 项目制作成可复用模板
- 02发布前检查模板配置
- 03制作 NuGet 模板包
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dotnet/ skills --skill template-authoring安装后在 Claude Code 中输入/template-authoring确认已加载。
详细介绍
模板创作
此技能可帮助 Agent 创建并验证自定义 dotnet new 模板。它会引导用户从现有项目引导创建模板,并在发布前检查 template.json 文件的创作问题。
适用场景
- 用户想从现有的 .csproj 创建可复用模板
- 用户想在发布前验证正在编写的 template.json
- 用户从头开始设置
.template.config/template.json - 用户想将模板打包后分发到 NuGet
不适用场景
- 用户想查找或使用现有模板——转至
template-discovery或template-instantiation - 用户遇到与模板创作无关的 MSBuild 问题——转至
dotnet-msbuild插件
输入
| 输入 | 必需性 | 描述 |
|---|---|---|
| 源项目路径 | 创建时需要 | 要用作模板源的 .csproj 路径 |
| template.json 路径 | 验证时需要 | 要验证的现有 template.json 路径 |
| 模板名称 | 创建时需要 | 模板的易读名称 |
| 短名称 | 建议提供 | 用于 dotnet new <shortname> 的短名称 |
工作流
会影响答案的规则
严格使用以下结构;不要虚构其他模板功能中的字段。
交付所请求的内容。 当用户说“展示”“编写”或“给我内容”时,即使你也已将其写入磁盘,也要在最终回复中提供完整的 JSON/XML。绝不要用编辑此技能的 SKILL.md 或插件文档来代替编写用户的模板。只有在用户请求修改文件,且目标模板/项目已存在时,才创建或修改模板文件。
| 需求 | 正确结构 | 绝不可使用 |
|---|---|---|
.csproj 中的条件 XML | 使用 XML 注释(例如 和)包围完整元素 | 单独的 #if 行,这会导致 XML 无效 |
| 还原生成的项目 | 使用还原操作 210D431B-A78B-4D2F-B762-4ED3E3EA9025;使用 primaryOutputs,或使用包含源模板路径/通配符的 args.files | 在还原操作中使用 executable 等运行脚本字段 |
| 限定为 SDK 宿主 | 使用 host 约束,其 args 是包含 { "hostname": "dotnetcli" } 的数组;其可选的 version 用于限制宿主/CLI 版本 | 当需求明确是限制当前 SDK 版本时使用宿主 version,使用无效的宿主 ID dotnet-cli,或使用无关的 pattern / value 字段 |
| 限制当前 SDK 版本 | 使用 sdk-version 约束,并在 args 中填写 NuGet 版本/范围字符串 | 除非模板确实需要,否则使用特定于机器的精确补丁版本 |
| 保留 CPM | 让生成的 PackageReference 项不指定版本,并在模板可独立使用时一并打包所属的 Directory.Packages.props | 添加内联 Version 属性 |
| 打包模板 | 使用包含 <PackageType>Template</PackageType> 的打包项目,并将模板内容打包到 content/ 下 | 只描述目录结构,却不展示用户要求的项目文件 |
步骤 1:从现有项目创建模板
分析源 .csproj,并创建 .template.config/template.json:
- 默认将源项目复制到专用的模板源目录中,并保留原始项目不变。只有当用户明确要求采用该布局时,才在原位置修改项目。
- 在模板源目录中创建
.template.config。 - 生成
template.json,包含identity(反向 DNS 格式)、name、shortName、sourceName(用于替换的项目名称)、classifications和tags。 - 保留源项目中的以下内容——通用的
dotnet new模板经常会在这些方面出错,因此请逐项确认它们均从原始.csproj沿用:- SDK 类型 —
Microsoft.NET.Sdk、Microsoft.NET.Sdk.Web、Microsoft.NET.Sdk.Worker等。 - 分析器/包引用元数据 —
PrivateAssets、IncludeAssets、ExcludeAssets OutputType和其他关键属性 —TreatWarningsAsErrors、Nullable、LangVersion- 参与 CPM — 存在
Directory.Packages.props时,不使用内联Version属性 - 自定义构建属性/目标和
Directory.Build.props约定 - 仓库约定 — 文件夹布局、命名、
global.jsonSDK 固定版本
- SDK 类型 —
最小示例:
{
"$schema": "http://json.schemastore.org/template",
"author": "MyOrg",
"classifications": ["Library"],
"identity": "MyOrg.Templates.MyLib",
"name": "My Library Template",
"shortName": "mylib",
"sourceName": "MyLib",
"tags": { "language": "C#", "type": "project" }
}
必需输出——不要只提供最简存根。 为实际源项目编写完整的 .template.config/template.json,然后简短确认已保留约定,让用户知道没有内容被悄悄遗漏。沿用这些内容正是此技能的全部价值;通用的 dotnet new 模板若丢失这些内容,编写模板还不如手写存根。
源 .csproj 设置 | 是否沿用? | 方式 |
|---|---|---|
SDK(Microsoft.NET.Sdk.*) | ✅ | 模板内容中的 .csproj 使用相同 SDK |
TreatWarningsAsErrors / Nullable / LangVersion | ✅ | 在模板 .csproj 中原样保留 |
PackageReference PrivateAssets / IncludeAssets / ExcludeAssets | ✅ | 在每个引用上保留元数据 |
CPM(存在 Directory.Packages.props) | ✅ | ManagePackageVersionsCentrally 保持启用,且不生成内联 Version 属性 |
对有意省略的任何一行标记为 ⚠️,并说明原因——绝不能含糊带过。
步骤 2:验证 template.json
使用 template-validation 技能验证生成的 template.json(该技能涵盖全部规则——必需字段、identity 格式、保留的 shortName 冲突、参数数据类型、后置操作、约束和标签)。
检查内容概要:
- 必需字段 — 必须包含
identity、name和shortName。 - ShortName 冲突 — 避免使用与
dotnet new子命令冲突的名称。请从已安装 SDK 的dotnet new --help中Commands:部分读取权威列表,不要将其硬编码(列表可能随版本变化);当前 SDK 的示例包括install、uninstall、update、list、search、details、create。当dotnet new <name>被解析为同名子命令时,就会发生冲突。顶层dotnet动词(如build、run、test和publish)不会冲突。运行dotnet new list,确认该名称尚未被占用。 - 参数、后置操作、标签 — 完整规则(包括有效的数据类型列表)请参阅 template-validation。
步骤 3:完善模板
根据验证结果和用户需求:
-
添加参数,并设置合适的类型(string、bool、choice)、默认值和描述
-
添加条件内容,使用文件类型支持的有效语法。在 XML 中,使用 XML 注释包裹模板指令,而不要直接使用预处理器指令:
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" /> -
配置后置操作,用于添加解决方案、还原或运行自定义脚本
-
设置约束,限制模板支持的 SDK 或工作负载
-
添加分类和标签,便于发现
对于还原后置操作,如果已知项目路径,优先使用 primaryOutputs:
"primaryOutputs": [{ "path": "MyProject.csproj" }],
"postActions": [{
"description": "Restore NuGet packages.",
"manualInstructions": [{ "text": "Run 'dotnet restore'." }],
"actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
"continueOnError": true
}]
如果需要使用 args.files,其路径会在重命名前与源模板进行匹配,例如 "files": ["**/*.csproj"]。请说明这一区别。
步骤 4:在本地测试模板
对于基于现有项目创建模板的请求,此步骤是必需的,而非可选项:
安装编写好的模板,运行试运行,将模板实例化到临时输出文件夹中,并构建生成的项目。报告每项观察到的结果;仅检查 template.json 并不能证明可复用模板有效。
dotnet new install ./path/to/template/root
dotnet new mylib --name TestProject --dry-run
dotnet new mylib --name TestProject --output ./test-output
dotnet build ./test-output/TestProject
需要打包时,请提供完整的打包项目,而不只是目录树:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>Contoso.ProjectTemplates</PackageId>
<PackageType>Template</PackageType>
<TargetFramework>net8.0</TargetFramework>
<IncludeBuildOutput>false</IncludeBuildOutput>
<NoWarn>$(NoWarn);NU5128</NoWarn>
</PropertyGroup>
<ItemGroup>
<Compile Remove="**\*" />
<Content Include="templates\**\*" Pack="true" PackagePath="content\" />
</ItemGroup>
</Project>
打包项目的目标框架仅适用于仅包含内容的打包项目;它不会更改 templates/ 中项目的目标框架。除非打包项目本身使用了较新的构建功能,否则应优先选择广泛可用且受支持的框架。
对于独立的 CPM 模板,打包后的 Directory.Packages.props 必须包含 <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>,并且每个没有版本号的 PackageReference 都必须有匹配的 PackageVersion。将 props 文件保留在预期生成的存储库根目录;不要在项目附近放置重复文件,以免改变查找行为。
验证
-
template.json通过手动验证,且无错误 - 模板标识和 shortName 唯一且有意义
- 所有参数都有描述和合适的默认值
- 模板可以成功安装、试运行和实例化
- 创建的项目可通过
dotnet build顺利构建 - 条件内容在所有参数组合下都能生成正确输出
- XML 模板指令已用 XML 注释包裹,且生成的项目可正常解析
- 主机约束使用
args[].hostname;还原操作使用primaryOutputs或args.files
常见问题
| 问题 | 解决方案 |
|---|---|
| 标识格式问题 | 使用反向 DNS 格式(例如 MyOrg.Templates.WebApi)。避免使用空格或特殊字符。 |
| ShortName 与 CLI 命令冲突 | 避免使用与 dotnet new 子命令相同的名称;从 dotnet new --help 获取当前命令列表,不要将其硬编码(示例仅供说明:install、uninstall、update、list、search、details、create)。build/run/test/publish 等顶级动词可以使用。运行 dotnet new list,查看该名称是否已被占用。 |
| 缺少参数描述 | 每个参数都应设置 description 和 displayName,以便用户发现。 |
| 未测试所有参数组合 | 使用不同的参数值运行 dotnet new <template> --dry-run,验证条件内容是否正常工作。 |
| 在模板中硬编码版本 | 使用 sourceName 替换项目名称,并考虑将框架版本参数化。 |
| 未设置分类 | 添加适当的 classifications(例如 ["Web", "API"]),便于发现模板。 |
| 重用来自其他约束或后置操作的字段 | 遵循确切的架构:使用 host.args[].hostname,还原操作使用 args.files,而不是运行脚本的字段。 |
更多信息
- dotnet new 的自定义模板 — 官方编写指南
- template.json 参考 — 完整的架构参考
- 模板引擎 Wiki — 模板引擎内部机制