feat(llms): add cli tools for llms#7461
Conversation
Reviewer's GuideIntroduce an LlmsDocsGenerator tooling project that analyzes BootstrapBlazor components with Roslyn to generate llms*.txt markdown docs for AI code agents, ship the generated files with the server app, and integrate generation/freshness checks into Docker, CI build, and documentation, along with a few minor comment/typo fixes in existing components. Sequence diagram for CI documentation freshness checksequenceDiagram
actor Dev as Developer
participant GH as GitHubActions_build.yml
participant CLI as LlmsDocsGenerator_Program
participant DG as DocsGenerator
participant CA as ComponentAnalyzer
Dev->>GH: Push changes / open PR
GH->>GH: Run job Check LLM Documentation
GH->>CLI: dotnet run --project tools/LlmsDocsGenerator -- --check
CLI->>DG: new DocsGenerator(outputPath)
CLI->>DG: CheckAsync()
DG->>CA: AnalyzeAllComponentsAsync()
CA-->>DG: List<ComponentInfo>
DG->>DG: CategorizeComponents(components)
DG->>DG: Read llms.txt and llms-*.txt timestamps
DG->>DG: Compare with component LastModified times
alt any doc missing or stale
DG-->>CLI: return false
CLI-->>GH: exit code 1
GH-->>Dev: Mark workflow as failed
else docs up-to-date
DG-->>CLI: return true
CLI-->>GH: exit code 0
GH-->>Dev: Workflow succeeds
end
Class diagram for LlmsDocsGenerator core typesclassDiagram
class DocsGenerator {
- string _outputPath
- string _sourcePath
- ComponentAnalyzer _analyzer
- MarkdownBuilder _markdownBuilder
+ DocsGenerator(string outputPath)
+ Task GenerateAllAsync()
+ Task GenerateIndexAsync()
+ Task GenerateComponentAsync(string componentName)
+ Task~bool~ CheckAsync()
- string FindSourcePath()
- Dictionary~string, List~ComponentInfo~~ CategorizeComponents(List~ComponentInfo~ components)
- string GetComponentCategory(string componentName)
- string GetCategoryFileName(string category)
- Task GenerateCategoryDocAsync(string category, List~ComponentInfo~ components)
}
class ComponentAnalyzer {
- string _sourcePath
- string _componentsPath
- string _samplesPath
+ ComponentAnalyzer(string sourcePath)
+ Task~List~ComponentInfo~~ AnalyzeAllComponentsAsync()
+ Task~ComponentInfo?~ AnalyzeComponentAsync(string componentName)
- Task~ComponentInfo?~ AnalyzeFileAsync(string filePath)
- string GetClassName(ClassDeclarationSyntax classDeclaration)
- string GetFullClassName(ClassDeclarationSyntax classDeclaration, SyntaxNode root)
- List~string~ GetTypeParameters(ClassDeclarationSyntax classDeclaration)
- string? GetBaseClass(ClassDeclarationSyntax classDeclaration)
- List~ParameterInfo~ ExtractParameters(ClassDeclarationSyntax classDeclaration)
- List~MethodInfo~ ExtractPublicMethods(ClassDeclarationSyntax classDeclaration)
- string? ExtractXmlSummary(SyntaxNode node)
- string CleanXmlComment(string comment)
- string? GetDefaultValue(PropertyDeclarationSyntax property)
- string SimplifyDefaultValue(string value)
- string SimplifyTypeName(string typeName)
- bool HasAttribute(MemberDeclarationSyntax member, string attributeName)
- string? GetObsoleteMessage(PropertyDeclarationSyntax property)
- string GetRelativePath(string fullPath)
- string? FindSamplePath(string componentName)
}
class MarkdownBuilder {
- const string GitHubBaseUrl
- StringBuilder _sb
+ string BuildIndex(Dictionary~string, List~ComponentInfo~~ categorizedComponents)
+ string BuildCategoryDoc(string category, List~ComponentInfo~ components)
+ string BuildComponentDoc(ComponentInfo component)
+ string BuildParameterTable(List~ParameterInfo~ parameters)
- void BuildComponentSection(ComponentInfo component, bool includeHeader)
- string EscapeMarkdownCell(string text)
}
class ComponentInfo {
+ string Name
+ string FullName
+ string? Summary
+ List~string~ TypeParameters
+ List~ParameterInfo~ Parameters
+ List~MethodInfo~ PublicMethods
+ string? BaseClass
+ string SourcePath
+ DateTime LastModified
+ string? SamplePath
}
class ParameterInfo {
+ string Name
+ string Type
+ string? DefaultValue
+ string? Description
+ bool IsRequired
+ bool IsObsolete
+ string? ObsoleteMessage
+ bool IsEventCallback
}
class MethodInfo {
+ string Name
+ string ReturnType
+ List~(string Type, string Name)~ Parameters
+ string? Description
+ bool IsJSInvokable
}
DocsGenerator --> ComponentAnalyzer
DocsGenerator --> MarkdownBuilder
DocsGenerator --> ComponentInfo
ComponentAnalyzer --> ComponentInfo
ComponentInfo --> ParameterInfo
ComponentInfo --> MethodInfo
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Thanks for your PR, @ice6. Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
There was a problem hiding this comment.
Hey - I've found 6 issues, and left some high level feedback:
- The
SimplifyTypeNamemethod inComponentAnalyzerdoes a series of string replacements (e.g.,Nullable<→ empty and>→?) that will corrupt generic type names (e.g.,List<int>orDictionary<string,int?>), so consider parsing types more structurally (e.g., via Roslyn or a small parser) instead of naïve string replacement to preserve accurate type information in the generated docs. - The Dockerfile uses
mcr.microsoft.com/dotnet/sdk:10.0while the CI workflows set updotnet-version: 8.0.x; it would be safer to align these versions (or parameterize them) so the documentation generator and build run against the same SDK and avoid version-specific inconsistencies.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `SimplifyTypeName` method in `ComponentAnalyzer` does a series of string replacements (e.g., `Nullable<` → empty and `>` → `?`) that will corrupt generic type names (e.g., `List<int>` or `Dictionary<string,int?>`), so consider parsing types more structurally (e.g., via Roslyn or a small parser) instead of naïve string replacement to preserve accurate type information in the generated docs.
- The Dockerfile uses `mcr.microsoft.com/dotnet/sdk:10.0` while the CI workflows set up `dotnet-version: 8.0.x`; it would be safer to align these versions (or parameterize them) so the documentation generator and build run against the same SDK and avoid version-specific inconsistencies.
## Individual Comments
### Comment 1
<location> `tools/LlmsDocsGenerator/ComponentAnalyzer.cs:315-324` </location>
<code_context>
+ return value;
+ }
+
+ private string SimplifyTypeName(string typeName)
+ {
+ // Simplify common type names
+ return typeName
+ .Replace("System.", "")
+ .Replace("Collections.Generic.", "")
+ .Replace("Threading.Tasks.", "")
+ .Replace("Nullable<", "")
+ .Replace(">", "?")
+ .Replace("Int32", "int")
+ .Replace("Int64", "long")
+ .Replace("Boolean", "bool")
+ .Replace("String", "string")
+ .Replace("Object", "object");
+ }
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Type simplification replaces all '>' with '?' which corrupts most generic type names.
`Replace("Nullable<", "").Replace(">", "?")` will incorrectly rewrite all generic types (e.g. `List<int>` → `List<int?`, `Task<string>` → `Task<string?`) by dropping the closing `>` and adding a nullable marker. This produces incorrect signatures in the generated docs.
If the goal is only to map `Nullable<T>` to `T?`, use a more targeted transformation (e.g. regex or explicit parsing of `Nullable<...>`) instead of a global `>` replacement.
</issue_to_address>
### Comment 2
<location> `tools/LlmsDocsGenerator/ComponentAnalyzer.cs:136-143` </location>
<code_context>
+ }
+ }
+
+ private string GetClassName(ClassDeclarationSyntax classDeclaration)
+ {
+ var name = classDeclaration.Identifier.Text;
+
+ // Remove generic part for display name
+ if (classDeclaration.TypeParameterList != null)
+ {
+ return name;
+ }
+
</code_context>
<issue_to_address>
**suggestion:** GetClassName contains an unnecessary conditional and does not match its own comment about generics.
The implementation always returns `name` and never strips the generic arity (e.g. `Table<TItem>`), so it contradicts the comment. The `TypeParameterList` check is therefore dead code. Please either implement the intended normalization (removing the generic suffix) or delete the misleading comment and redundant conditional.
Suggested implementation:
```csharp
private string GetClassName(ClassDeclarationSyntax classDeclaration)
{
return classDeclaration.Identifier.Text;
}
```
If the existing method body in your file differs slightly (e.g., different spacing or missing the final `return name;`), adjust the SEARCH section to match the exact current implementation of `GetClassName` before applying this replacement.
</issue_to_address>
### Comment 3
<location> `tools/LlmsDocsGenerator/DocsGenerator.cs:124-133` </location>
<code_context>
+ public async Task<bool> CheckAsync()
</code_context>
<issue_to_address>
**suggestion (bug_risk):** CheckAsync validates staleness for category files but not for the main index file.
In `CheckAsync`, you only verify that `llms.txt` exists, whereas you compare timestamps for each category file. As a result, the index can be older than its component files while `--check` still succeeds. Consider adding a last-write comparison for `llms.txt` against all component sources so CI reliably enforces freshness.
</issue_to_address>
### Comment 4
<location> `src/BootstrapBlazor.Server/wwwroot/llmstxt/llms-other.txt:1023` </location>
<code_context>
+
+## DateTimeFilter
+
+DataTime filter component
+
+### Parameters
</code_context>
<issue_to_address>
**issue (typo):** Spelling typo: “DataTime” should be “DateTime”.
Please also update the description to “DateTime filter component” so it matches the component name and .NET type.
```suggestion
DateTime filter component
```
</issue_to_address>
### Comment 5
<location> `src/BootstrapBlazor.Server/wwwroot/llmstxt/llms-button.txt:78` </location>
<code_context>
+| LoadingIcon | `string?` | - | 获得/设置 正在加载动画图标 默认为 fa-solid fa-spin fa-spinner |
+| OnClickWithoutRender | `Func<Task??` | - | 获得/设置 OnClick 事件不刷新父组件 |
+| Size | `Size` | - | 获得/设置 Size 大小 |
+| StopPropagation | `bool` | - | 获得/设置 点击时间是否向上传播 默认 false |
+| Text | `string?` | - | 获得/设置 显示文字 |
+| OnClick | `EventCallback<MouseEventArgs?` | - | 获得/设置 OnClick 事件 |
</code_context>
<issue_to_address>
**issue (typo):** Chinese description likely should say “点击事件” instead of “点击时间”.
Similarly for `StopPropagation`, “点击时间是否向上传播” should be “点击事件是否向上传播” to correctly describe whether the click event is propagated upward.
Suggested implementation:
```
| StopPropagation | `bool` | - | 获得/设置 点击事件是否向上传播 默认 false |
```
在该文件及相关文档中全局搜索“点击时间”,并统一替换为“点击事件”,以确保描述一致且正确。如果有其他属性或事件说明中出现相同的错别字,也需要一并修改。
</issue_to_address>
### Comment 6
<location> `src/BootstrapBlazor.Server/wwwroot/llmstxt/llms-button.txt:421` </location>
<code_context>
+| IsDisabled | `bool` | - | 获得/设置 是否禁用 默认为 false |
+| Items | `IEnumerable<SelectedItem??` | - | 获得/设置 展开项集合 |
+| Offset | `float` | 8 | 获得/设置 弹窗偏移量 默认 8px |
+| Placement | `Placement` | - | 获得/设置 按钮颜色 |
+| ShowHeader | `bool` | - | 获得/设置 是否显示标题 默认 false 不显示 |
+| Size | `Size` | - | 获得/设置 Size 大小 |
</code_context>
<issue_to_address>
**issue (typo):** Description for `Placement` appears to be copied from a color property.
For `SlideButton`, the `Placement` parameter is documented as “获得/设置 按钮颜色”, which doesn’t align with its name or type. Please update the description to match its actual purpose (likely popup/button placement, e.g., slider content position).
```suggestion
| Placement | `Placement` | - | 获得/设置 弹窗/内容显示位置 |
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7461 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 749 749
Lines 32912 32912
Branches 4573 4573
=========================================
Hits 32912 32912
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Hey - I've found 8 issues, and left some high level feedback:
- In
DocsGenerator.CheckAsyncyou callcomponents.SelectMany(c => c.SourceFiles)butComponentInfodoes not expose aSourceFilesproperty (onlySourcePath/LastModified), so this either won’t compile or is dead code and should be aligned with the currentComponentInfoshape. - In
ComponentAnalyzer.AnalyzeAllComponentsAsyncyou analyze both*.razor.csand*Base.csand always take the first class in each file, which can lead to duplicateComponentInfoentries for the same logical component (e.g.,TableandTableBase); consider deduplicating byFullNameor making the base-class analysis opt-in. - The Dockerfile uses
mcr.microsoft.com/dotnet/sdk:10.0while the GitHub workflows install.NET 8.0.x; it would be good to align these versions (or add a comment explaining the intentional difference) to avoid unexpected build/runtime discrepancies.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `DocsGenerator.CheckAsync` you call `components.SelectMany(c => c.SourceFiles)` but `ComponentInfo` does not expose a `SourceFiles` property (only `SourcePath`/`LastModified`), so this either won’t compile or is dead code and should be aligned with the current `ComponentInfo` shape.
- In `ComponentAnalyzer.AnalyzeAllComponentsAsync` you analyze both `*.razor.cs` and `*Base.cs` and always take the first class in each file, which can lead to duplicate `ComponentInfo` entries for the same logical component (e.g., `Table` and `TableBase`); consider deduplicating by `FullName` or making the base-class analysis opt-in.
- The Dockerfile uses `mcr.microsoft.com/dotnet/sdk:10.0` while the GitHub workflows install `.NET 8.0.x`; it would be good to align these versions (or add a comment explaining the intentional difference) to avoid unexpected build/runtime discrepancies.
## Individual Comments
### Comment 1
<location> `tools/LlmsDocsGenerator/DocsGenerator.cs:142-143` </location>
<code_context>
+ var indexLastWrite = File.GetLastWriteTimeUtc(indexPath);
+
+ // compute the most recent component source timestamp:
+ var newestComponentWrite = components
+ .SelectMany(c => c.SourceFiles)
+ .Select(File.GetLastWriteTimeUtc)
+ .DefaultIfEmpty(indexLastWrite)
</code_context>
<issue_to_address>
**issue (bug_risk):** DocsGenerator.CheckAsync references a non-existent ComponentInfo.SourceFiles property, which will fail to compile.
`ComponentInfo` in this change set only exposes `SourcePath` and `LastModified`, so `components.SelectMany(c => c.SourceFiles)` will not compile. Either add and populate a `SourceFiles` collection on `ComponentInfo` (via `ComponentAnalyzer`), or rework this freshness check to rely on existing properties (e.g., `components.Max(c => c.LastModified)`).
</issue_to_address>
### Comment 2
<location> `tools/LlmsDocsGenerator/DocsGenerator.cs:96-98` </location>
<code_context>
+
+ private async Task GenerateIndexAsync(Dictionary<string, List<ComponentInfo>> categorized)
+ {
+ var indexPath = Path.Combine(_outputPath, "llms.txt");
+ var content = _markdownBuilder.BuildIndex(categorized);
+ await File.WriteAllTextAsync(indexPath, content);
+ Console.WriteLine($"Generated: {indexPath}");
+ }
</code_context>
<issue_to_address>
**issue:** Output directory is not ensured before writing files, which may fail if the directory does not exist.
If `_outputPath` (or its parent dirs) don’t exist, `File.WriteAllTextAsync` will throw—likely in fresh clones or CI. Please ensure the directory is created once (e.g., in the `DocsGenerator` ctor or before the first write) with `Directory.CreateDirectory(_outputPath);`, and apply the same handling to the other write locations (category and per-component docs).
</issue_to_address>
### Comment 3
<location> `tools/LlmsDocsGenerator/MarkdownBuilder.cs:248-257` </location>
<code_context>
+ _sb.AppendLine();
+ }
+
+ // Parameters table
+ if (component.Parameters.Count > 0)
+ {
+ _sb.AppendLine("### Parameters");
+ _sb.AppendLine();
+ _sb.AppendLine("<!-- AUTO-GENERATED-PARAMETERS-START -->");
+ _sb.AppendLine();
+ _sb.AppendLine("| Parameter | Type | Default | Description |");
+ _sb.AppendLine("|-----------|------|---------|-------------|");
+
+ // Sort: required first, then events, then alphabetically
+ var sortedParams = component.Parameters
+ .OrderByDescending(p => p.IsRequired)
+ .ThenBy(p => p.IsEventCallback)
</code_context>
<issue_to_address>
**suggestion:** EventCallback parameters are listed in both the general parameter table and the dedicated Event Callbacks table.
Since you already render a dedicated `Event Callbacks` section from `component.Parameters.Where(p => p.IsEventCallback)`, including those items in the main `Parameters` table duplicates information and adds noise. Consider filtering out `IsEventCallback` entries from the main table so callbacks are documented only in their own section.
Suggested implementation:
```csharp
// Parameters table (excluding EventCallback parameters; those are documented separately)
var nonEventParams = component.Parameters
.Where(p => !p.IsEventCallback)
.ToList();
if (nonEventParams.Count > 0)
{
_sb.AppendLine("### Parameters");
_sb.AppendLine();
_sb.AppendLine("<!-- AUTO-GENERATED-PARAMETERS-START -->");
_sb.AppendLine();
_sb.AppendLine("| Parameter | Type | Default | Description |");
_sb.AppendLine("|-----------|------|---------|-------------|");
// Sort: required first, then alphabetically
var sortedParams = nonEventParams
.OrderByDescending(p => p.IsRequired)
.ThenBy(p => p.Name);
foreach (var param in sortedParams)
{
var required = param.IsRequired ? " **[Required]**" : "";
var description = EscapeMarkdownCell(param.Description ?? "") + required;
var defaultVal = param.DefaultValue ?? "-";
var type = EscapeMarkdownCell(param.Type);
_sb.AppendLine($"| {param.Name} | `{type}` | {defaultVal} | {description} |");
```
1. Ensure `using System.Linq;` is present at the top of `MarkdownBuilder.cs` so that `Where`, `OrderByDescending`, and `ThenBy` compile correctly.
2. No other changes should be required, assuming the existing Event Callbacks section already uses `component.Parameters.Where(p => p.IsEventCallback)` as mentioned in your comment.
</issue_to_address>
### Comment 4
<location> `src/BootstrapBlazor.Server/Dockerfile:19-20` </location>
<code_context>
-WORKDIR "src/BootstrapBlazor.Server"
+# Generate LLM documentation
+WORKDIR /tools/LlmsDocsGenerator
+RUN dotnet run
+
+WORKDIR "/src/BootstrapBlazor.Server"
</code_context>
<issue_to_address>
**issue (bug_risk):** In the Docker build, the generator’s default output path likely doesn’t align with the desired wwwroot/llmstxt folder.
Because `Program.cs` resolves the default `--output` relative to the current directory, running `dotnet run` from `/tools/LlmsDocsGenerator` writes to `/tools/LlmsDocsGenerator/src/BootstrapBlazor.Server/wwwroot/llmstxt`, not `/src/BootstrapBlazor.Server/wwwroot/llmstxt` where the app serves static files. To align paths, either invoke the tool from the repo root (e.g., `WORKDIR /` then `dotnet run --project tools/LlmsDocsGenerator`) or pass `--output /src/BootstrapBlazor.Server/wwwroot/llmstxt` explicitly.
</issue_to_address>
### Comment 5
<location> `src/BootstrapBlazor.Server/Dockerfile:18-20` </location>
<code_context>
-WORKDIR "src/BootstrapBlazor.Server"
+# Generate LLM documentation
+WORKDIR /tools/LlmsDocsGenerator
+RUN dotnet run
+
+WORKDIR "/src/BootstrapBlazor.Server"
</code_context>
<issue_to_address>
**suggestion (performance):** Using plain `dotnet run` in the Docker build stage may slow builds due to implicit restore/build in Debug configuration.
Here `dotnet run` triggers an extra restore/build (usually Debug) on top of the later `dotnet build/publish`, which slows image builds and adds unnecessary artifacts to this layer. Prefer either `dotnet build` followed by `dotnet run --configuration Release --no-build`, or publishing the tool once and running the published DLL to avoid duplicate compilation.
```suggestion
# Generate LLM documentation
WORKDIR /tools/LlmsDocsGenerator
# Build once in Release and run without rebuilding to avoid duplicate restore/build work
RUN dotnet build --configuration Release
RUN dotnet run --configuration Release --no-build
```
</issue_to_address>
### Comment 6
<location> `src/BootstrapBlazor.Server/wwwroot/llmstxt/llms-other.txt:286` </location>
<code_context>
+| CanPreviewCallback | `Func<UploadFile, bool>?` | - | 获得/设置 是否允许预览回调方法 默认 null |
+| DeleteIcon | `string?` | - | 获得/设置 删除图标 |
+| Height | `int` | 100 | 获得/设置 文件预览框高度 |
+| InvalidStatusIcon | `string?` | - | 获得/设置 状态正常图标 |
+| IsCircle | `bool` | - | 获得/设置 是否圆形图片框 Avatar 模式时生效 默认为 false |
+| IsUploadButtonAtFirst | `bool` | - | 获得/设置 继续上传按钮是否在列表前 默认 false |
</code_context>
<issue_to_address>
**issue (typo):** The `InvalidStatusIcon` description appears to be a copy-paste error.
For `AvatarUpload`, the `InvalidStatusIcon` (“无效状态图标”) description currently says “状态正常图标”, which contradicts the property name. Please update it to reflect the invalid state (for example: “获得/设置 状态无效图标”) and align with other `InvalidStatusIcon` descriptions.
```suggestion
| InvalidStatusIcon | `string?` | - | 获得/设置 状态无效图标 |
```
</issue_to_address>
### Comment 7
<location> `src/BootstrapBlazor.Server/wwwroot/llmstxt/llms-other.txt:951-952` </location>
<code_context>
+| FirstDayOfWeek | `DayOfWeek` | DayOfWeek.Sunday | 获得/设置 星期第一天 默认 <see cref="DayOfWeek.Sunday"/> |
+| MaxValue | `DateTime?` | - | 获得/设置 当前日期最大值 |
+| MinValue | `DateTime?` | - | 获得/设置 当前日期最小值 |
+| NextMonthIcon | `string?` | - | 获得/设置 上一年图标 |
+| NextYearIcon | `string?` | - | 获得/设置 上一年图标 |
+| NowButtonText | `string?` | - | 获得/设置 此刻按钮文字 |
+| OnClear | `Func<Task>?` | - | 获得/设置 清空按钮回调委托 |
</code_context>
<issue_to_address>
**issue (typo):** `NextMonthIcon` and `NextYearIcon` descriptions reference the wrong period (previous year).
Recommend updating the Chinese documentation to describe these as the icons for the next month/year (e.g., “获得/设置 下一月图标” and “获得/设置 下一年图标”) to stay consistent with the `Next*` naming and avoid confusion with the `Previous*` icons.
Suggested implementation:
```
| NextMonthIcon | `string?` | - | 获得/设置 下一月图标 |
```
```
| NextYearIcon | `string?` | - | 获得/设置 下一年图标 |
```
</issue_to_address>
### Comment 8
<location> `src/BootstrapBlazor.Server/wwwroot/llmstxt/llms-other.txt:1175` </location>
<code_context>
+| LookupService | `ILookupService?` | - | <inheritdoc/> |
+| LookupServiceData | `object?` | - | <inheritdoc/> |
+| LookupServiceKey | `string?` | - | <inheritdoc/> |
+| LookupStringComparison | `stringComparison` | StringComparison.OrdinalIgnoreCase | <inheritdoc/> |
+| ShowTooltip | `bool` | - | 获得/设置 是否显示 Tooltip 多用于标签文字过长导致裁减时使用 默认 false 不显示 |
+| TypeResolver | `Func<Assembly?, string, bool, Type?>?` | - | 获得/设置 类型解析回调方法 组件泛型为 Array 时内部调用 |
</code_context>
<issue_to_address>
**issue (typo):** The `StringComparison` type is misspelled with a lowercase `s` in multiple parameter tables.
This is written as `stringComparison`, but the correct .NET type is `StringComparison` (capital `S`). The same issue appears in other parameter tables (e.g., `MultiFilter.StringComparison`, `SelectBase.StringComparison`, and `llms-select.txt` for `Select.StringComparison`). Please update all occurrences for accuracy and consistency.
Suggested implementation:
```
| LookupStringComparison | `StringComparison` | StringComparison.OrdinalIgnoreCase | <inheritdoc/> |
```
Based on your review comment, similar fixes are needed in other parameter tables:
1. In the documentation entries for:
- `MultiFilter.StringComparison`
- `SelectBase.StringComparison`
ensure the Type column uses ``StringComparison`` (capital S) instead of ``stringComparison``.
2. In `src/BootstrapBlazor.Server/wwwroot/llmstxt/llms-select.txt`, locate the row for `Select.StringComparison` and change the Type column from ``stringComparison`` to ``StringComparison``.
The pattern to search for in those files is typically a table row like:
`| *StringComparison* | \`stringComparison\` | ... | ... |`
which should be updated to:
`| *StringComparison* | \`StringComparison\` | ... | ... |`.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
临时使用 build 事件调试成功后使用发布事件
# Conflicts: # tools/LlmsDocsGenerator/LlmsDocsGenerator.csproj
There was a problem hiding this comment.
We encountered an error and are unable to review this PR. We have been notified and are working to fix it.
You can try again by commenting this pull request with @sourcery-ai review, or contact us for help.
There was a problem hiding this comment.
Pull request overview
This pull request introduces an automated LLM documentation generation system for BootstrapBlazor components. The system uses a new CLI tool (bootstrapblazor.llmsdocsgenerator) to analyze components and generate LLM-friendly Markdown documentation that can be consumed by AI code assistants.
Key Changes:
- Added a new .NET tool (
bootstrapblazor.llmsdocsgeneratorv10.0.0) for generating LLM documentation - Integrated documentation generation into the build process via MSBuild targets
- Configured git to ignore generated documentation and code agent directories
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/BootstrapBlazor.Server/Directory.Build.targets |
Adds MSBuild target that runs LLM docs generator after every build |
dotnet-tools.json |
Registers the new bootstrapblazor.llmsdocsgenerator tool (v10.0.0) |
.gitignore |
Excludes generated LLM documentation files and code agent directories from version control |
exclusion.dic |
Adds "llms" to the spell-check exclusion dictionary |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -0,0 +1,9 @@ | |||
| <Project> | |||
|
|
|||
| <Target Name="LLMs" AfterTargets="Build" Condition="'$(TargetFramework)' == '$(RunTargetFramework)'"> | |||
There was a problem hiding this comment.
The LLMs target runs after every build when the TargetFramework matches RunTargetFramework. This means the documentation generation will execute on every build, even for incremental builds during development. Consider adding a condition to skip this in development environments or making it opt-in via a build property (e.g., Condition="'$(GenerateLlmsDocs)' == 'true'"), to avoid impacting build performance during routine development.
| <Target Name="LLMs" AfterTargets="Build" Condition="'$(TargetFramework)' == '$(RunTargetFramework)'"> | |
| <Target Name="LLMs" AfterTargets="Build" Condition="'$(TargetFramework)' == '$(RunTargetFramework)' and '$(GenerateLlmsDocs)' == 'true'"> |
| <Target Name="LLMs" AfterTargets="Build" Condition="'$(TargetFramework)' == '$(RunTargetFramework)'"> | ||
| <Message Text="LLMs documentation generating ..." Importance="high"></Message> | ||
| <Exec Command="dotnet tool restore"></Exec> |
There was a problem hiding this comment.
Running "dotnet tool restore" on every build can slow down the build process unnecessarily. Consider checking if the tool is already available before restoring, or move the tool restore to a separate target that only runs when needed. Alternatively, document that developers should run "dotnet tool restore" manually before building.
| <Target Name="LLMs" AfterTargets="Build" Condition="'$(TargetFramework)' == '$(RunTargetFramework)'"> | |
| <Message Text="LLMs documentation generating ..." Importance="high"></Message> | |
| <Exec Command="dotnet tool restore"></Exec> | |
| <PropertyGroup> | |
| <!-- Set to true to restore local .NET tools explicitly when needed --> | |
| <RestoreDotNetTools>false</RestoreDotNetTools> | |
| </PropertyGroup> | |
| <Target Name="RestoreDotNetToolsTarget" Condition="'$(RestoreDotNetTools)' == 'true'"> | |
| <Exec Command="dotnet tool restore"></Exec> | |
| </Target> | |
| <Target Name="LLMs" AfterTargets="Build" Condition="'$(TargetFramework)' == '$(RunTargetFramework)'"> | |
| <Message Text="LLMs documentation generating ..." Importance="high"></Message> |
| <Target Name="LLMs" AfterTargets="Build" Condition="'$(TargetFramework)' == '$(RunTargetFramework)'"> | ||
| <Message Text="LLMs documentation generating ..." Importance="high"></Message> | ||
| <Exec Command="dotnet tool restore"></Exec> | ||
| <Exec Command="dotnet llms-docs --root=$(MSBuildThisFileDirectory) --debug"></Exec> |
There was a problem hiding this comment.
The --debug flag appears to be included in the production build target. Consider making this conditional based on the build configuration (e.g., only include --debug when Configuration is Debug) to avoid unnecessary debug output in release builds.
| <Exec Command="dotnet llms-docs --root=$(MSBuildThisFileDirectory) --debug"></Exec> | |
| <Exec Command="dotnet llms-docs --root=$(MSBuildThisFileDirectory) --debug" Condition="'$(Configuration)' == 'Debug'"></Exec> | |
| <Exec Command="dotnet llms-docs --root=$(MSBuildThisFileDirectory)" Condition="'$(Configuration)' != 'Debug'"></Exec> |
Link issues
fixes #7400
Summary By Copilot
Regression?
Risk
Verification
Packaging changes reviewed?
☑️ Self Check before Merge
Summary by Sourcery
Add an automated LLM-focused documentation generation system for BootstrapBlazor components and integrate it into docs, build, and Docker workflows.
New Features:
Enhancements:
Build:
CI:
Documentation: