@phuc1403/musketeer 0.12.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALLATION.md +1 -1
- package/README.md +2 -2
- package/manifest.json +4 -3
- package/package.json +1 -1
- package/template/.claude/hooks/inject-ubiquitous-language.cjs +2 -2
- package/template/.claude/skills/domain-expert/SKILL.md +76 -0
- package/template/.claude/skills/logical-components/SKILL.md +165 -94
- package/template/.claude/skills/logical-components/assets/component-diagram-template.html +104 -0
- package/template/.claude/skills/logical-components/references/component-analyst-prompt.md +88 -0
- package/template/.claude/skills/logical-components/references/component-level-detection.md +95 -0
- package/template/.claude/skills/logical-components/references/logical-component-concepts.md +92 -0
- package/template/.claude/skills/logical-components/scripts/build-component-model.cjs +120 -0
- package/template/.claude/skills/logical-components/scripts/index-declared-symbols.cjs +106 -0
- package/template/.claude/skills/logical-components/scripts/lib/coupling-metrics.cjs +48 -0
- package/template/.claude/skills/logical-components/scripts/lib/declared-symbol-patterns.cjs +57 -0
- package/template/.claude/skills/logical-components/scripts/lib/html-sections.cjs +91 -0
- package/template/.claude/skills/logical-components/scripts/lib/mermaid-graph.cjs +55 -0
- package/template/.claude/skills/logical-components/scripts/lib/read-json-input.cjs +109 -0
- package/template/.claude/skills/logical-components/scripts/lib/repo-walk.cjs +61 -0
- package/template/.claude/skills/logical-components/scripts/lib/verify-claims.cjs +159 -0
- package/template/.claude/skills/logical-components/scripts/render-component-diagram-html.cjs +112 -0
- package/template/.claude/skills/logical-components/scripts/scan-folder-tree.cjs +88 -0
- package/template/.claude/skills/logical-components/scripts/select-stale-components.cjs +129 -0
- package/template/.claude/skills/logical-components/.gitattributes +0 -2
- package/template/.claude/skills/logical-components/references/component-classifier-prompt.md +0 -53
- package/template/.claude/skills/logical-components/references/responsibility-agent-prompt.md +0 -67
- package/template/.claude/skills/logical-components/references/responsibility-verifier-prompt.md +0 -39
- package/template/.claude/skills/logical-components/scripts/Directory.Build.props +0 -7
- package/template/.claude/skills/logical-components/scripts/Directory.Build.rsp +0 -1
- package/template/.claude/skills/logical-components/scripts/Directory.Build.targets +0 -6
- package/template/.claude/skills/logical-components/scripts/Directory.Packages.props +0 -8
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/BatchPlanning.cs +0 -70
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/BatchesCommand.cs +0 -47
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/CandidatesCommand.cs +0 -98
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/CitableSource.cs +0 -54
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/Citation.cs +0 -37
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/ClassifyBatchesCommand.cs +0 -86
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/ClassifyMergeCommand.cs +0 -219
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/ComponentDiscovery.cs +0 -180
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/ComponentRule.cs +0 -130
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/ComponentsFile.cs +0 -101
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/CouplingResolver.cs +0 -251
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/DisplayName.cs +0 -14
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/EdgeExtraction.cs +0 -111
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/ExtractCommand.cs +0 -87
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/ExtractConfig.cs +0 -79
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/LogicalComponents.csproj +0 -15
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/MarkdownCodeSpans.cs +0 -72
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/MarkdownRenderer.cs +0 -78
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/MemberUseResolution.cs +0 -272
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/MemberUses.cs +0 -231
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/PlainText.cs +0 -55
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/PortResolution.cs +0 -75
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/Program.cs +0 -27
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/ProjectFilters.cs +0 -77
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/PruneCommand.cs +0 -164
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/RenderCommand.cs +0 -234
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/ResponsibilityBatches.cs +0 -84
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/ResponsibilityValidation.cs +0 -197
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/SeamDispatch.cs +0 -115
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/SourceCodeLines.cs +0 -79
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/SymbolNames.cs +0 -53
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/TypeMap.cs +0 -106
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/WalkRoots.cs +0 -63
- package/template/.claude/skills/logical-components/scripts/LogicalComponents/WorkspaceLoader.cs +0 -297
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/AssignIdsTests.cs +0 -41
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/BatchPlanningTests.cs +0 -90
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/BatchesCommandTests.cs +0 -37
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/CandidatesCommandTests.cs +0 -59
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/CitationTests.cs +0 -42
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/ClassifyCommandsTests.cs +0 -249
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/ComponentDiscoveryTests.cs +0 -253
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/CouplingAppExtractionTests.cs +0 -188
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/CouplingAppWorkspace.cs +0 -30
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/CouplingExtractionTests.cs +0 -174
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/DisplayNameTests.cs +0 -17
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/ExtractConfigTests.cs +0 -72
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/FixturePaths.cs +0 -36
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/LogicalComponents.Tests.csproj +0 -23
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/MarkdownRendererTests.cs +0 -93
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/ProjectFiltersTests.cs +0 -69
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/PruneCommandTests.cs +0 -149
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/RenderCommandTests.cs +0 -225
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/RenderGoldenTests.cs +0 -34
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/RenderTestData.cs +0 -55
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/ResponsibilityValidationTests.cs +0 -315
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/SampleAppWorkspace.cs +0 -27
- package/template/.claude/skills/logical-components/scripts/LogicalComponents.Tests/WorkspaceLoaderTests.cs +0 -266
- package/template/.claude/skills/logical-components/scripts/NuGet.config +0 -17
- package/template/.claude/skills/logical-components/scripts/fixtures/BrokenApp/src/Broken.Application/Broken.Application.csproj +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/BrokenApp/src/Broken.Application/Broken.cs +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/CompositionRootApp/CompositionRootApp.slnx +0 -5
- package/template/.claude/skills/logical-components/scripts/fixtures/CompositionRootApp/src/Root.Application/HostBuilderExtensions.cs +0 -15
- package/template/.claude/skills/logical-components/scripts/fixtures/CompositionRootApp/src/Root.Application/HostBuilderShims.cs +0 -25
- package/template/.claude/skills/logical-components/scripts/fixtures/CompositionRootApp/src/Root.Application/NativeInterop.cs +0 -17
- package/template/.claude/skills/logical-components/scripts/fixtures/CompositionRootApp/src/Root.Application/Root.Application.csproj +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/CompositionRootApp/src/Root.Application/RootAutofacModule.cs +0 -11
- package/template/.claude/skills/logical-components/scripts/fixtures/CompositionRootApp/src/Root.Application/RootGreeter.cs +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/CouplingApp/logical-components.json +0 -48
- package/template/.claude/skills/logical-components/scripts/fixtures/CouplingApp/src/CouplingApp.Application/AbstractMemberCases.cs +0 -27
- package/template/.claude/skills/logical-components/scripts/fixtures/CouplingApp/src/CouplingApp.Application/AttributeCases.cs +0 -23
- package/template/.claude/skills/logical-components/scripts/fixtures/CouplingApp/src/CouplingApp.Application/CouplingApp.Application.csproj +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/CouplingApp/src/CouplingApp.Application/GenericBaseCases.cs +0 -14
- package/template/.claude/skills/logical-components/scripts/fixtures/CouplingApp/src/CouplingApp.Application/GenericSeamBindingCases.cs +0 -59
- package/template/.claude/skills/logical-components/scripts/fixtures/CouplingApp/src/CouplingApp.Application/ImplicitCallCases.cs +0 -230
- package/template/.claude/skills/logical-components/scripts/fixtures/CouplingApp/src/CouplingApp.Application/PartialInheritCases.First.cs +0 -11
- package/template/.claude/skills/logical-components/scripts/fixtures/CouplingApp/src/CouplingApp.Application/PartialInheritCases.Second.cs +0 -14
- package/template/.claude/skills/logical-components/scripts/fixtures/CouplingApp/src/CouplingApp.Application/StackOverflowCases.cs +0 -14
- package/template/.claude/skills/logical-components/scripts/fixtures/CouplingApp/src/CouplingApp.Application/ViaDisambiguationCases.cs +0 -36
- package/template/.claude/skills/logical-components/scripts/fixtures/MultiTargetApp/MultiTargetApp.slnx +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/MultiTargetApp/src/Legacy.FSharp/Legacy.FSharp.fsproj +0 -8
- package/template/.claude/skills/logical-components/scripts/fixtures/MultiTargetApp/src/Legacy.FSharp/Library.fs +0 -5
- package/template/.claude/skills/logical-components/scripts/fixtures/MultiTargetApp/src/Multi.Application/Multi.Application.csproj +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/MultiTargetApp/src/Multi.Application/Scheduling.cs +0 -14
- package/template/.claude/skills/logical-components/scripts/fixtures/MultiTargetApp/src/Multi.Infrastructure/Multi.Infrastructure.csproj +0 -12
- package/template/.claude/skills/logical-components/scripts/fixtures/MultiTargetApp/src/Multi.Infrastructure/SchedulingClient.cs +0 -12
- package/template/.claude/skills/logical-components/scripts/fixtures/NoSolutionApp/src/Solo.Application/Solo.Application.csproj +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/NoSolutionApp/src/Solo.Application/SoloGreeting.cs +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/NoSolutionApp/src/Solo.Application.Tests/FakeGreeting.cs +0 -6
- package/template/.claude/skills/logical-components/scripts/fixtures/NoSolutionApp/src/Solo.Application.Tests/Solo.Application.Tests.csproj +0 -11
- package/template/.claude/skills/logical-components/scripts/fixtures/ObjOutputApp/ObjOutputApp.slnx +0 -5
- package/template/.claude/skills/logical-components/scripts/fixtures/ObjOutputApp/src/Obj.Application/Greeting.cs +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/ObjOutputApp/src/Obj.Application/Obj.Application.csproj +0 -14
- package/template/.claude/skills/logical-components/scripts/fixtures/RenderGolden/components.json +0 -67
- package/template/.claude/skills/logical-components/scripts/fixtures/RenderGolden/expected.md +0 -28
- package/template/.claude/skills/logical-components/scripts/fixtures/RenderGolden/repo/src/App/OrderAcceptance.cs +0 -16
- package/template/.claude/skills/logical-components/scripts/fixtures/RenderGolden/repo/src/Infra/OrderQueue.cs +0 -10
- package/template/.claude/skills/logical-components/scripts/fixtures/RenderGolden/resp/batch-01.json +0 -18
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/SampleApp.slnx +0 -10
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/logical-components.json +0 -78
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/CandidateCases.cs +0 -37
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/CouplingCases.cs +0 -33
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/DependencyInjection.cs +0 -18
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/EdgeCases.cs +0 -63
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/LoudNotifying.Base.cs +0 -5
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/LoudNotifying.cs +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/OrderAcceptance.cs +0 -17
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/OrderOptions.cs +0 -9
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/OrderRejectedException.cs +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/OrderRequest.cs +0 -4
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/OrderRunning.cs +0 -18
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/OrderValidation.cs +0 -6
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/Ports.cs +0 -19
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/PriceRules.cs +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/ReceiptPrinting.Totals.cs +0 -6
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/ReceiptPrinting.cs +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/ReviewCases.cs +0 -107
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/ReviewFixCases.cs +0 -143
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/RunClock.cs +0 -12
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/Sample.Application.csproj +0 -10
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Application/SeeThroughCases.cs +0 -89
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Billing.Application/OrderRunning.cs +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Billing.Application/Sample.Billing.Application.csproj +0 -11
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Infrastructure/ForwardedNotifier.cs +0 -8
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Infrastructure/InfrastructureDependencyInjection.cs +0 -16
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Infrastructure/Mailer.cs +0 -6
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Infrastructure/OrderQueue.cs +0 -11
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Infrastructure/QueueEntry.cs +0 -4
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Infrastructure/QueueStore.cs +0 -8
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Infrastructure/ReviewCases.cs +0 -35
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Infrastructure/ReviewFixCases.cs +0 -13
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Infrastructure/Sample.Infrastructure.csproj +0 -13
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Infrastructure/SeeThroughCases.cs +0 -18
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/src/Sample.Infrastructure/UnreachedPlumbing.cs +0 -7
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/tests/Sample.Tests/FakeOrderQueue.cs +0 -11
- package/template/.claude/skills/logical-components/scripts/fixtures/SampleApp/tests/Sample.Tests/Sample.Tests.csproj +0 -11
- package/template/.claude/skills/logical-components/scripts/fixtures/SharedOutsideRepo/SharedClock.cs +0 -7
- package/template/.claude/skills/logical-components/scripts/global.json +0 -5
- package/template/.claude/skills/logical-components/scripts/smoke-sample.sh +0 -26
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
using Microsoft.CodeAnalysis;
|
|
2
|
-
using Microsoft.CodeAnalysis.CSharp;
|
|
3
|
-
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
4
|
-
|
|
5
|
-
namespace LogicalComponents;
|
|
6
|
-
|
|
7
|
-
/// <summary>
|
|
8
|
-
/// Reads what a piece of code uses, as members: <c>new</c> (the constructor an object creation, a <c>: this(...)</c> /
|
|
9
|
-
/// <c>: base(...)</c> initializer or a primary-constructor base call runs), <c>static</c> (a static member, an extension
|
|
10
|
-
/// method or C# 14 extension member, a user-defined operator or conversion) and <c>call</c> (an instance member used on
|
|
11
|
-
/// any receiver, an indexer or conditional-access indexer, the enumerator, deconstruction and <c>MoveNext</c>/<c>Current</c>
|
|
12
|
-
/// a <c>foreach</c> runs, the <c>Add</c> a collection initializer element runs, a deconstruction assignment's
|
|
13
|
-
/// <c>Deconstruct</c>, and the <c>GetAwaiter</c>/<c>IsCompleted</c>/<c>GetResult</c> an <c>await</c> runs). A property or
|
|
14
|
-
/// indexer use is the accessor that actually runs: a read is the getter, an assignment the setter, a compound
|
|
15
|
-
/// assignment or increment both. Attribute names are not uses: an attribute is metadata the compiler reads, not code the
|
|
16
|
-
/// annotated member runs. Only citable source is read, so generator output never contributes. What a use couples to is
|
|
17
|
-
/// decided by <see cref="MemberUseResolution"/>.
|
|
18
|
-
/// </summary>
|
|
19
|
-
public static class MemberUses
|
|
20
|
-
{
|
|
21
|
-
/// <summary>One use of <see cref="Member"/>, at a repo-relative file and 1-based line.</summary>
|
|
22
|
-
public sealed record MemberUse(ISymbol Member, string Kind, string File, int Line);
|
|
23
|
-
|
|
24
|
-
/// <summary>
|
|
25
|
-
/// Every member use inside the given syntax nodes, in source order. Nodes outside the repo, and nodes inside an
|
|
26
|
-
/// attribute list, are skipped.
|
|
27
|
-
/// </summary>
|
|
28
|
-
public static async Task<List<MemberUse>> ReadAsync(
|
|
29
|
-
IEnumerable<SyntaxNode> roots, LoadedWorkspace loaded, CancellationToken cancellationToken)
|
|
30
|
-
{
|
|
31
|
-
var uses = new List<MemberUse>();
|
|
32
|
-
foreach (var root in roots)
|
|
33
|
-
{
|
|
34
|
-
var (kind, file) = CitableSource.Classify(loaded, root.SyntaxTree);
|
|
35
|
-
if (kind != CitableSource.Kind.InRepo || loaded.Solution.GetDocument(root.SyntaxTree) is not { } document)
|
|
36
|
-
{
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
var model = await document.GetSemanticModelAsync(cancellationToken)
|
|
41
|
-
?? throw new InvalidOperationException($"No semantic model for {file}");
|
|
42
|
-
|
|
43
|
-
// An attribute name resolves to its constructor, but applying an attribute is not running code: never
|
|
44
|
-
// descend into an attribute list at all, so nothing inside one is ever read as a use.
|
|
45
|
-
foreach (var node in root.DescendantNodesAndSelf(descendant => descendant is not AttributeListSyntax))
|
|
46
|
-
{
|
|
47
|
-
var line = node.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
|
|
48
|
-
uses.AddRange(Uses(node, model, cancellationToken).Select(u => new MemberUse(u.Member, u.Kind, file, line)));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return uses;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
private static IEnumerable<(ISymbol Member, string Kind)> Uses(SyntaxNode node, SemanticModel model, CancellationToken token)
|
|
56
|
-
{
|
|
57
|
-
ISymbol? Symbol() => model.GetSymbolInfo(node, token).Symbol;
|
|
58
|
-
(ISymbol, string) AsUse(ISymbol member) => (member, member.IsStatic ? EdgeExtraction.Static : EdgeExtraction.Call);
|
|
59
|
-
|
|
60
|
-
switch (node)
|
|
61
|
-
{
|
|
62
|
-
case BaseObjectCreationExpressionSyntax or ConstructorInitializerSyntax or PrimaryConstructorBaseTypeSyntax:
|
|
63
|
-
if (Symbol() is IMethodSymbol constructor)
|
|
64
|
-
{
|
|
65
|
-
yield return (constructor, EdgeExtraction.New);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
break;
|
|
69
|
-
case SimpleNameSyntax name when !IsInsideNameOf(name, model):
|
|
70
|
-
foreach (var use in Classify(Symbol(), name))
|
|
71
|
-
{
|
|
72
|
-
yield return use;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
break;
|
|
76
|
-
case ElementAccessExpressionSyntax or ElementBindingExpressionSyntax when Symbol() is IPropertySymbol indexer:
|
|
77
|
-
foreach (var accessor in AccessorsThatRun(indexer, node))
|
|
78
|
-
{
|
|
79
|
-
yield return (accessor, EdgeExtraction.Call);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
break;
|
|
83
|
-
case BinaryExpressionSyntax or PrefixUnaryExpressionSyntax or PostfixUnaryExpressionSyntax
|
|
84
|
-
or CastExpressionSyntax or AssignmentExpressionSyntax
|
|
85
|
-
when Symbol() is IMethodSymbol { MethodKind: MethodKind.UserDefinedOperator or MethodKind.Conversion } op:
|
|
86
|
-
yield return (op, EdgeExtraction.Static);
|
|
87
|
-
break;
|
|
88
|
-
// A deconstruction assignment, `(a, b) = point;`: tried before the operator-assignment case above, but only
|
|
89
|
-
// claims the node when it is actually a deconstruction, so a plain or compound assignment still falls
|
|
90
|
-
// through to it.
|
|
91
|
-
case AssignmentExpressionSyntax assignment when DeconstructMethods(model.GetDeconstructionInfo(assignment)).ToList()
|
|
92
|
-
is { Count: > 0 } assignmentDeconstructs:
|
|
93
|
-
foreach (var method in assignmentDeconstructs)
|
|
94
|
-
{
|
|
95
|
-
yield return AsUse(method);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
break;
|
|
99
|
-
case CommonForEachStatementSyntax loop:
|
|
100
|
-
var info = model.GetForEachStatementInfo(loop);
|
|
101
|
-
foreach (var member in new ISymbol?[] { info.GetEnumeratorMethod, info.MoveNextMethod, info.CurrentProperty?.GetMethod })
|
|
102
|
-
{
|
|
103
|
-
if (member is not null)
|
|
104
|
-
{
|
|
105
|
-
yield return AsUse(member);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// foreach (var (a, b) in pairs): the per-element deconstruction, same as an explicit assignment's.
|
|
110
|
-
if (loop is ForEachVariableStatementSyntax deconstructingLoop)
|
|
111
|
-
{
|
|
112
|
-
foreach (var method in DeconstructMethods(model.GetDeconstructionInfo(deconstructingLoop)))
|
|
113
|
-
{
|
|
114
|
-
yield return AsUse(method);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
break;
|
|
119
|
-
case InitializerExpressionSyntax initializer when initializer.IsKind(SyntaxKind.CollectionInitializerExpression):
|
|
120
|
-
foreach (var element in initializer.Expressions)
|
|
121
|
-
{
|
|
122
|
-
if (model.GetCollectionInitializerSymbolInfo(element).Symbol is IMethodSymbol add)
|
|
123
|
-
{
|
|
124
|
-
yield return AsUse(add);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
break;
|
|
129
|
-
case AwaitExpressionSyntax awaitExpression:
|
|
130
|
-
var awaited = model.GetAwaitExpressionInfo(awaitExpression);
|
|
131
|
-
foreach (var member in new ISymbol?[] { awaited.GetAwaiterMethod, awaited.IsCompletedProperty?.GetMethod, awaited.GetResultMethod })
|
|
132
|
-
{
|
|
133
|
-
if (member is not null)
|
|
134
|
-
{
|
|
135
|
-
yield return AsUse(member);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/// <summary>The Deconstruct methods a deconstruction runs, including any nested tuple/pattern's own.</summary>
|
|
144
|
-
private static IEnumerable<IMethodSymbol> DeconstructMethods(DeconstructionInfo info)
|
|
145
|
-
{
|
|
146
|
-
if (info.Method is { } method)
|
|
147
|
-
{
|
|
148
|
-
yield return method;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
foreach (var nested in info.Nested)
|
|
152
|
-
{
|
|
153
|
-
foreach (var method2 in DeconstructMethods(nested))
|
|
154
|
-
{
|
|
155
|
-
yield return method2;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/// <summary>
|
|
161
|
-
/// A symbol a name refers to, as uses: static or extension members are <c>static</c> (a classic extension method is
|
|
162
|
-
/// seen as its unreduced static form), other members are <c>call</c>, a property is the accessors that run. Locals,
|
|
163
|
-
/// parameters, types, namespaces and consts (copied into the caller at compile time) are not uses.
|
|
164
|
-
/// </summary>
|
|
165
|
-
private static IEnumerable<(ISymbol, string)> Classify(ISymbol? symbol, SyntaxNode usage)
|
|
166
|
-
{
|
|
167
|
-
var kind = symbol is not null && (symbol.IsStatic || symbol.ContainingType is { IsExtension: true })
|
|
168
|
-
? EdgeExtraction.Static
|
|
169
|
-
: EdgeExtraction.Call;
|
|
170
|
-
switch (symbol)
|
|
171
|
-
{
|
|
172
|
-
case IFieldSymbol { IsConst: true }:
|
|
173
|
-
case IMethodSymbol { MethodKind: MethodKind.LocalFunction or MethodKind.AnonymousFunction }:
|
|
174
|
-
break;
|
|
175
|
-
case IMethodSymbol { ReducedFrom: { } extension }:
|
|
176
|
-
yield return (extension, EdgeExtraction.Static);
|
|
177
|
-
break;
|
|
178
|
-
case IPropertySymbol property:
|
|
179
|
-
foreach (var accessor in AccessorsThatRun(property, usage))
|
|
180
|
-
{
|
|
181
|
-
yield return (accessor, kind);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
break;
|
|
185
|
-
case IMethodSymbol or IFieldSymbol or IEventSymbol:
|
|
186
|
-
yield return (symbol, kind);
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/// <summary>
|
|
192
|
-
/// The accessors a property or indexer use runs, from where it stands: the target of <c>=</c> (including an object
|
|
193
|
-
/// initializer) runs the setter; a compound assignment, <c>++</c>/<c>--</c>, or a <c>ref</c>/<c>out</c> argument runs
|
|
194
|
-
/// both; anything else is a read and runs the getter. An accessor the property does not have runs nothing.
|
|
195
|
-
/// </summary>
|
|
196
|
-
private static IEnumerable<IMethodSymbol> AccessorsThatRun(IPropertySymbol property, SyntaxNode usage)
|
|
197
|
-
{
|
|
198
|
-
// The whole access expression: `a.B` for the name B, `a?.B` for a conditional access, the name itself otherwise.
|
|
199
|
-
var access = usage.Parent switch
|
|
200
|
-
{
|
|
201
|
-
MemberAccessExpressionSyntax member when member.Name == usage => member,
|
|
202
|
-
MemberBindingExpressionSyntax binding => (SyntaxNode)binding,
|
|
203
|
-
_ => usage,
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
var (reads, writes) = access.Parent switch
|
|
207
|
-
{
|
|
208
|
-
AssignmentExpressionSyntax assignment when assignment.Left == access =>
|
|
209
|
-
(!assignment.IsKind(SyntaxKind.SimpleAssignmentExpression), true),
|
|
210
|
-
PrefixUnaryExpressionSyntax or PostfixUnaryExpressionSyntax => (true, true),
|
|
211
|
-
ArgumentSyntax { RefKindKeyword.RawKind: not 0 } => (true, true),
|
|
212
|
-
_ => (true, false),
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
if (reads && property.GetMethod is { } getter)
|
|
216
|
-
{
|
|
217
|
-
yield return getter;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (writes && property.SetMethod is { } setter)
|
|
221
|
-
{
|
|
222
|
-
yield return setter;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// nameof(Foo.Bar) mentions a member without using it.
|
|
227
|
-
private static bool IsInsideNameOf(SyntaxNode node, SemanticModel model) =>
|
|
228
|
-
node.Ancestors().OfType<InvocationExpressionSyntax>().Any(invocation =>
|
|
229
|
-
invocation.Expression is IdentifierNameSyntax { Identifier.Text: "nameof" }
|
|
230
|
-
&& model.GetSymbolInfo(invocation).Symbol is null);
|
|
231
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
using System.Globalization;
|
|
2
|
-
using System.Text;
|
|
3
|
-
|
|
4
|
-
namespace LogicalComponents;
|
|
5
|
-
|
|
6
|
-
/// <summary>
|
|
7
|
-
/// The unicode rule shared by every short string an agent hands back that is stored verbatim or shown at a review
|
|
8
|
-
/// gate: a classify verdict's <c>reason</c> (<see cref="ClassifyMergeCommand"/>) and a responsibility's <c>text</c>
|
|
9
|
-
/// (<see cref="ResponsibilityValidation"/>). Neither may contain a character that could hide something from a
|
|
10
|
-
/// reviewer or a renderer - a bidi override, a zero-width character, or one that fakes a line break.
|
|
11
|
-
/// </summary>
|
|
12
|
-
public static class PlainText
|
|
13
|
-
{
|
|
14
|
-
/// <summary>
|
|
15
|
-
/// True for text containing a Unicode category that must never appear in plain text stored or shown verbatim:
|
|
16
|
-
/// control (Cc), format (Cf - includes bidi overrides like U+202E and zero-width characters), line/paragraph
|
|
17
|
-
/// separators (Zl/Zp - could fake a second line), private-use (Co), or a lone surrogate (Cs).
|
|
18
|
-
/// </summary>
|
|
19
|
-
public static bool HasDisallowedUnicode(string text)
|
|
20
|
-
{
|
|
21
|
-
for (var i = 0; i < text.Length; i++)
|
|
22
|
-
{
|
|
23
|
-
var c = text[i];
|
|
24
|
-
if (char.IsHighSurrogate(c))
|
|
25
|
-
{
|
|
26
|
-
if (i + 1 >= text.Length || !char.IsLowSurrogate(text[i + 1]))
|
|
27
|
-
{
|
|
28
|
-
return true; // lone high surrogate
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (IsBannedCategory(Rune.GetUnicodeCategory(new Rune(c, text[i + 1]))))
|
|
32
|
-
{
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
i++;
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (char.IsLowSurrogate(c) || IsDisallowed(c))
|
|
41
|
-
{
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/// <summary>True for one UTF-16 code unit in a category <see cref="HasDisallowedUnicode"/> rejects.</summary>
|
|
50
|
-
public static bool IsDisallowed(char c) => IsBannedCategory(CharUnicodeInfo.GetUnicodeCategory(c));
|
|
51
|
-
|
|
52
|
-
private static bool IsBannedCategory(UnicodeCategory category) => category is UnicodeCategory.Control
|
|
53
|
-
or UnicodeCategory.Format or UnicodeCategory.LineSeparator or UnicodeCategory.ParagraphSeparator
|
|
54
|
-
or UnicodeCategory.PrivateUse or UnicodeCategory.Surrogate;
|
|
55
|
-
}
|
package/template/.claude/skills/logical-components/scripts/LogicalComponents/PortResolution.cs
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
using Microsoft.CodeAnalysis;
|
|
2
|
-
using Microsoft.CodeAnalysis.FindSymbols;
|
|
3
|
-
|
|
4
|
-
namespace LogicalComponents;
|
|
5
|
-
|
|
6
|
-
/// <summary>
|
|
7
|
-
/// Maps a seam (an interface or abstract class) to the concrete classes behind it in the loaded solution. Test
|
|
8
|
-
/// projects were removed at load time, so test doubles never appear. Asking the type system rather than parsing DI
|
|
9
|
-
/// registrations covers every registration style (instance, factory, forwarding) at once.
|
|
10
|
-
/// A constructed generic seam (<c>IHandler<Order></c>) resolves only to classes implementing exactly that construction,
|
|
11
|
-
/// or an open generic one (<c>LoggingHandler<T> : IHandler<T></c>). Results are cached per construction and sorted.
|
|
12
|
-
/// </summary>
|
|
13
|
-
public sealed class PortResolution(Solution solution)
|
|
14
|
-
{
|
|
15
|
-
private readonly Dictionary<string, IReadOnlyList<INamedTypeSymbol>> _cache = new(StringComparer.Ordinal);
|
|
16
|
-
|
|
17
|
-
public async Task<IReadOnlyList<INamedTypeSymbol>> ImplementationsAsync(
|
|
18
|
-
INamedTypeSymbol seam, CancellationToken cancellationToken)
|
|
19
|
-
{
|
|
20
|
-
var key = Display(seam);
|
|
21
|
-
if (_cache.TryGetValue(key, out var cached))
|
|
22
|
-
{
|
|
23
|
-
return cached;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
var definition = seam.OriginalDefinition;
|
|
27
|
-
IEnumerable<INamedTypeSymbol> found = definition.TypeKind == TypeKind.Interface
|
|
28
|
-
? (await SymbolFinder.FindImplementationsAsync(definition, solution, cancellationToken: cancellationToken))
|
|
29
|
-
.OfType<INamedTypeSymbol>()
|
|
30
|
-
: await SymbolFinder.FindDerivedClassesAsync(definition, solution, transitive: true, cancellationToken: cancellationToken);
|
|
31
|
-
|
|
32
|
-
var implementations = found
|
|
33
|
-
.Where(t => t.TypeKind == TypeKind.Class && !t.IsAbstract)
|
|
34
|
-
.Select(t => t.OriginalDefinition)
|
|
35
|
-
.Where(t => ImplementsConstruction(t, seam))
|
|
36
|
-
.DistinctBy(SymbolNames.FullName)
|
|
37
|
-
.OrderBy(SymbolNames.FullName, StringComparer.Ordinal)
|
|
38
|
-
.ToList();
|
|
39
|
-
_cache[key] = implementations;
|
|
40
|
-
return implementations;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
private static bool ImplementsConstruction(INamedTypeSymbol implementation, INamedTypeSymbol seam)
|
|
44
|
-
{
|
|
45
|
-
if (!seam.IsGenericType || seam.TypeArguments.All(a => a is ITypeParameterSymbol))
|
|
46
|
-
{
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
var definition = SymbolNames.FullName(seam.OriginalDefinition);
|
|
51
|
-
var wanted = Display(seam);
|
|
52
|
-
return Supertypes(implementation, seam.TypeKind).Any(s =>
|
|
53
|
-
SymbolNames.FullName(s.OriginalDefinition) == definition
|
|
54
|
-
&& (Display(s) == wanted || s.TypeArguments.Any(a => a is ITypeParameterSymbol)));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
private static IEnumerable<INamedTypeSymbol> Supertypes(INamedTypeSymbol type, TypeKind seamKind)
|
|
58
|
-
{
|
|
59
|
-
if (seamKind == TypeKind.Interface)
|
|
60
|
-
{
|
|
61
|
-
return type.AllInterfaces;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
var bases = new List<INamedTypeSymbol>();
|
|
65
|
-
for (var current = type.BaseType; current is not null; current = current.BaseType)
|
|
66
|
-
{
|
|
67
|
-
bases.Add(current);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return bases;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Compared as text: symbols of the same type seen from two projects' compilations are not always the same instance.
|
|
74
|
-
private static string Display(ITypeSymbol type) => type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
|
75
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
using LogicalComponents;
|
|
2
|
-
|
|
3
|
-
// Entry point: dispatches to one command per pipeline step. Anything unknown prints usage and exits 1.
|
|
4
|
-
// candidates, extract and render report on stderr (their artifact is a file); the others report on stdout.
|
|
5
|
-
return args switch
|
|
6
|
-
{
|
|
7
|
-
["candidates", .. var rest] => await CandidatesCommand.RunAsync(rest, Console.Error),
|
|
8
|
-
["classify-batches", .. var rest] => await ClassifyBatchesCommand.RunAsync(rest, Console.Out),
|
|
9
|
-
["classify-merge", .. var rest] => await ClassifyMergeCommand.RunAsync(rest, Console.Out),
|
|
10
|
-
["extract", .. var rest] => await ExtractCommand.RunAsync(rest, Console.Error),
|
|
11
|
-
["batches", .. var rest] => await BatchesCommand.RunAsync(rest, Console.Out),
|
|
12
|
-
["prune", .. var rest] => await PruneCommand.RunAsync(rest, Console.Out),
|
|
13
|
-
["render", .. var rest] => await RenderCommand.RunAsync(rest, Console.Error),
|
|
14
|
-
_ => PrintUsage(),
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
static int PrintUsage()
|
|
18
|
-
{
|
|
19
|
-
Console.Error.WriteLine(CandidatesCommand.Usage);
|
|
20
|
-
Console.Error.WriteLine(ClassifyBatchesCommand.Usage);
|
|
21
|
-
Console.Error.WriteLine(ClassifyMergeCommand.Usage);
|
|
22
|
-
Console.Error.WriteLine(ExtractCommand.Usage);
|
|
23
|
-
Console.Error.WriteLine(BatchesCommand.Usage);
|
|
24
|
-
Console.Error.WriteLine(PruneCommand.Usage);
|
|
25
|
-
Console.Error.WriteLine(RenderCommand.Usage);
|
|
26
|
-
return 1;
|
|
27
|
-
}
|
package/template/.claude/skills/logical-components/scripts/LogicalComponents/ProjectFilters.cs
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
using System.Text.RegularExpressions;
|
|
2
|
-
|
|
3
|
-
namespace LogicalComponents;
|
|
4
|
-
|
|
5
|
-
/// <summary>
|
|
6
|
-
/// Pure rules for which loaded projects the extractor keeps: no test projects, and one project per
|
|
7
|
-
/// multi-targeted csproj. Kept free of Roslyn types so they are unit-testable from plain strings.
|
|
8
|
-
/// </summary>
|
|
9
|
-
public static partial class ProjectFilters
|
|
10
|
-
{
|
|
11
|
-
/// <summary>
|
|
12
|
-
/// A test project lives under a <c>tests</c>/<c>test</c> folder, is named like one
|
|
13
|
-
/// (<c>.Tests</c>, <c>.UnitTests</c>, <c>.IntegrationTests</c>, <c>.Test</c>, <c>.Specs</c>), or its project
|
|
14
|
-
/// file carries a test marker (test SDK, <c>IsTestProject</c>, or a test-framework package).
|
|
15
|
-
/// Test-support libraries such as <c>Foo.Testing</c> are deliberately kept: nothing here can tell them apart
|
|
16
|
-
/// from source, and their types can be excluded through config.
|
|
17
|
-
/// </summary>
|
|
18
|
-
public static bool IsTestProject(string relativePath, string projectFileText)
|
|
19
|
-
{
|
|
20
|
-
var segments = relativePath.Split('/', '\\');
|
|
21
|
-
if (segments.Any(s => s.Equals("tests", StringComparison.OrdinalIgnoreCase)
|
|
22
|
-
|| s.Equals("test", StringComparison.OrdinalIgnoreCase)))
|
|
23
|
-
{
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return TestProjectName().IsMatch(Path.GetFileNameWithoutExtension(relativePath))
|
|
28
|
-
|| TestProjectMarker().IsMatch(projectFileText);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/// <summary>
|
|
32
|
-
/// Orders the target framework in a loaded multi-target project name such as <c>Foo(net10.0)</c>.
|
|
33
|
-
/// Families rank .NET Framework < netstandard < netcoreapp < net5+; versions order within a family.
|
|
34
|
-
/// </summary>
|
|
35
|
-
public static (int Family, Version Version) TargetFrameworkRank(string loadedProjectName)
|
|
36
|
-
{
|
|
37
|
-
var match = TargetFrameworkSuffix().Match(loadedProjectName);
|
|
38
|
-
if (!match.Success)
|
|
39
|
-
{
|
|
40
|
-
return (0, new Version(0, 0));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Drop an OS suffix: net9.0-windows ranks as net9.0.
|
|
44
|
-
var tfm = match.Groups["tfm"].Value.ToLowerInvariant().Split('-')[0];
|
|
45
|
-
return tfm switch
|
|
46
|
-
{
|
|
47
|
-
_ when tfm.StartsWith("netstandard") => (1, ParseVersion(tfm["netstandard".Length..])),
|
|
48
|
-
_ when tfm.StartsWith("netcoreapp") => (2, ParseVersion(tfm["netcoreapp".Length..])),
|
|
49
|
-
_ when tfm.Contains('.') => (3, ParseVersion(tfm["net".Length..])),
|
|
50
|
-
// .NET Framework monikers pack the version into digits: net472 is 4.7.2.
|
|
51
|
-
_ => (0, ParseVersion(string.Join('.', tfm["net".Length..].ToCharArray()))),
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/// <summary><c>Foo(net10.0)</c> becomes <c>Foo</c>; a name without a framework suffix is returned as-is.</summary>
|
|
56
|
-
public static string StripTargetFramework(string loadedProjectName)
|
|
57
|
-
{
|
|
58
|
-
var match = TargetFrameworkSuffix().Match(loadedProjectName);
|
|
59
|
-
return match.Success ? match.Groups["name"].Value : loadedProjectName;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
private static Version ParseVersion(string text) =>
|
|
63
|
-
Version.TryParse(text.Contains('.') ? text : text + ".0", out var version) ? version : new Version(0, 0);
|
|
64
|
-
|
|
65
|
-
// Segment-based and case-sensitive on purpose: "Foo.Contest", "Latest.Feeds", "Net.SpeedTest" and
|
|
66
|
-
// "Experiments.ABTest" must not look like test projects; only these exact last-segment suffixes do.
|
|
67
|
-
[GeneratedRegex(@"\.(Test|Tests|UnitTests|IntegrationTests|FunctionalTests|Spec|Specs)$")]
|
|
68
|
-
private static partial Regex TestProjectName();
|
|
69
|
-
|
|
70
|
-
[GeneratedRegex(
|
|
71
|
-
@"<IsTestProject>\s*true\s*</IsTestProject>|Sdk=""MSTest\.Sdk|Include=""(Microsoft\.NET\.Test\.Sdk|xunit|xunit\.v3|xunit\.core|xunit\.v3\.core|NUnit|MSTest|MSTest\.TestFramework|Microsoft\.Testing\.Platform)""",
|
|
72
|
-
RegexOptions.IgnoreCase)]
|
|
73
|
-
private static partial Regex TestProjectMarker();
|
|
74
|
-
|
|
75
|
-
[GeneratedRegex(@"^(?<name>.+)\((?<tfm>net[^)]*)\)$")]
|
|
76
|
-
private static partial Regex TargetFrameworkSuffix();
|
|
77
|
-
}
|
package/template/.claude/skills/logical-components/scripts/LogicalComponents/PruneCommand.cs
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
using System.Text.Json;
|
|
2
|
-
|
|
3
|
-
namespace LogicalComponents;
|
|
4
|
-
|
|
5
|
-
/// <summary>
|
|
6
|
-
/// <c>prune --resp <dir> --verify <dir> --out <dir></c>: applies the verifier agents' verdicts. For every
|
|
7
|
-
/// <c>resp/batch-NN.json</c> it reads <c>verify/batch-NN.json</c> — <c>{ "<id>": [ { "item": 1, "supported": true,
|
|
8
|
-
/// "reason": "..." } ] }</c> — and writes the batch without its unsupported items to <c>--out</c>. The agents' files are
|
|
9
|
-
/// never edited, so pruning is repeatable. Every item needs exactly one verdict: an unjudged claim is not verified, a
|
|
10
|
-
/// null verdict entry or one missing <c>supported</c> is malformed (never silently "not supported"), so any gap or
|
|
11
|
-
/// malformed entry fails the step (exit 3) before anything is written. Exit codes: 0 written, 1 usage, 3 verdicts
|
|
12
|
-
/// incomplete or malformed.
|
|
13
|
-
/// </summary>
|
|
14
|
-
public static class PruneCommand
|
|
15
|
-
{
|
|
16
|
-
public const string Usage = "usage: LogicalComponents prune --resp <dir> --verify <dir> --out <dir>";
|
|
17
|
-
|
|
18
|
-
public sealed record Verdict(int Item, bool Supported, string? Reason);
|
|
19
|
-
|
|
20
|
-
// Supported is nullable only for reading: a verifier file missing it, or a null array entry, is malformed input
|
|
21
|
-
// (exit 3) rather than a silent "not supported". Verdict above keeps Supported non-nullable for every other use,
|
|
22
|
-
// once ReadVerdicts has confirmed it is actually there.
|
|
23
|
-
private sealed record RawVerdict(int Item, bool? Supported, string? Reason);
|
|
24
|
-
|
|
25
|
-
private static readonly JsonSerializerOptions ReadOptions = new()
|
|
26
|
-
{
|
|
27
|
-
PropertyNameCaseInsensitive = true,
|
|
28
|
-
ReadCommentHandling = JsonCommentHandling.Skip,
|
|
29
|
-
AllowTrailingCommas = true,
|
|
30
|
-
AllowDuplicateProperties = false,
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
public static async Task<int> RunAsync(string[] args, TextWriter output)
|
|
34
|
-
{
|
|
35
|
-
if (args is not ["--resp", var respDir, "--verify", var verifyDir, "--out", var outDir]
|
|
36
|
-
|| !Directory.Exists(respDir) || !Directory.Exists(verifyDir))
|
|
37
|
-
{
|
|
38
|
-
await output.WriteLineAsync(Usage);
|
|
39
|
-
return 1;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
var problems = new List<string>();
|
|
43
|
-
var pruned = new List<(string File, SortedDictionary<string, List<Responsibility>> Kept)>();
|
|
44
|
-
var removed = new List<string>();
|
|
45
|
-
var emptied = new List<string>();
|
|
46
|
-
|
|
47
|
-
foreach (var respFile in Directory.EnumerateFiles(respDir, "batch-*.json").Order(StringComparer.Ordinal))
|
|
48
|
-
{
|
|
49
|
-
var name = Path.GetFileName(respFile);
|
|
50
|
-
var batch = ResponsibilityBatches.Load([respFile], ReadOptions);
|
|
51
|
-
problems.AddRange(batch.Problems);
|
|
52
|
-
var verdicts = ReadVerdicts(Path.Combine(verifyDir, name), name, problems);
|
|
53
|
-
if (verdicts is null)
|
|
54
|
-
{
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
var kept = new SortedDictionary<string, List<Responsibility>>(StringComparer.Ordinal);
|
|
59
|
-
foreach (var (id, items) in batch.Responsibilities.OrderBy(r => r.Key, StringComparer.Ordinal))
|
|
60
|
-
{
|
|
61
|
-
var judged = verdicts.GetValueOrDefault(id) ?? [];
|
|
62
|
-
problems.AddRange(judged.Where(v => v.Item < 1 || v.Item > items.Count)
|
|
63
|
-
.Select(v => $"{id}[{v.Item}]: verdict for an item that does not exist ({name})"));
|
|
64
|
-
|
|
65
|
-
kept[id] = [];
|
|
66
|
-
for (var item = 1; item <= items.Count; item++)
|
|
67
|
-
{
|
|
68
|
-
var forItem = judged.Where(v => v.Item == item).ToList();
|
|
69
|
-
if (forItem.Count != 1)
|
|
70
|
-
{
|
|
71
|
-
problems.Add($"{id}[{item}]: {(forItem.Count == 0 ? "no verdict" : "more than one verdict")} ({name})");
|
|
72
|
-
}
|
|
73
|
-
else if (forItem[0].Supported)
|
|
74
|
-
{
|
|
75
|
-
kept[id].Add(items[item - 1]);
|
|
76
|
-
}
|
|
77
|
-
else
|
|
78
|
-
{
|
|
79
|
-
removed.Add($"removed {id}[{item}]: {forItem[0].Reason ?? "(no reason given)"}");
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (items.Count > 0 && kept[id].Count == 0)
|
|
84
|
-
{
|
|
85
|
-
emptied.Add($"empty {id}: every responsibility was unsupported");
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
problems.AddRange(verdicts.Keys.Where(id => !batch.Responsibilities.ContainsKey(id))
|
|
90
|
-
.Order(StringComparer.Ordinal)
|
|
91
|
-
.Select(id => $"{id}: verdicts for a component the batch does not answer ({name})"));
|
|
92
|
-
pruned.Add((name, kept));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (problems.Count > 0)
|
|
96
|
-
{
|
|
97
|
-
foreach (var problem in problems)
|
|
98
|
-
{
|
|
99
|
-
await output.WriteLineAsync(problem);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return 3;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
Directory.CreateDirectory(outDir);
|
|
106
|
-
foreach (var (file, kept) in pruned)
|
|
107
|
-
{
|
|
108
|
-
await File.WriteAllBytesAsync(Path.Combine(outDir, file), ComponentsFile.ToJson(kept));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
foreach (var line in removed.Concat(emptied))
|
|
112
|
-
{
|
|
113
|
-
await output.WriteLineAsync(line);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
await output.WriteLineAsync($"{removed.Count} removed, {emptied.Count} empty");
|
|
117
|
-
return 0;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
private static Dictionary<string, List<Verdict>>? ReadVerdicts(string path, string name, List<string> problems)
|
|
121
|
-
{
|
|
122
|
-
if (!File.Exists(path))
|
|
123
|
-
{
|
|
124
|
-
problems.Add($"{name}: no verdict file");
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
Dictionary<string, List<RawVerdict?>?>? raw;
|
|
129
|
-
try
|
|
130
|
-
{
|
|
131
|
-
raw = JsonSerializer.Deserialize<Dictionary<string, List<RawVerdict?>?>>(File.ReadAllText(path), ReadOptions);
|
|
132
|
-
}
|
|
133
|
-
catch (Exception e) when (e is IOException or UnauthorizedAccessException or JsonException)
|
|
134
|
-
{
|
|
135
|
-
problems.Add($"{name}: not a valid verdict file ({e.Message})");
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
var clean = new Dictionary<string, List<Verdict>>(StringComparer.Ordinal);
|
|
140
|
-
foreach (var (id, items) in raw ?? [])
|
|
141
|
-
{
|
|
142
|
-
var kept = new List<Verdict>();
|
|
143
|
-
foreach (var verdict in items ?? [])
|
|
144
|
-
{
|
|
145
|
-
if (verdict is null)
|
|
146
|
-
{
|
|
147
|
-
problems.Add($"{id}: a null verdict entry ({name})");
|
|
148
|
-
}
|
|
149
|
-
else if (verdict.Supported is not { } supported)
|
|
150
|
-
{
|
|
151
|
-
problems.Add($"{id}[{verdict.Item}]: verdict is missing 'supported' ({name})");
|
|
152
|
-
}
|
|
153
|
-
else
|
|
154
|
-
{
|
|
155
|
-
kept.Add(new Verdict(verdict.Item, supported, verdict.Reason));
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
clean[id] = kept;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return clean;
|
|
163
|
-
}
|
|
164
|
-
}
|