@cratis/pi 0.0.1 → 2.0.2
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/LICENSE +21 -0
- package/README.md +18 -37
- package/package/corpus/agents/backend-developer.md +125 -0
- package/package/corpus/agents/code-reviewer.md +165 -0
- package/package/corpus/agents/coordinator.md +163 -0
- package/package/corpus/agents/frontend-developer.md +246 -0
- package/package/corpus/agents/orchestrator.md +196 -0
- package/package/corpus/agents/performance-reviewer.md +109 -0
- package/package/corpus/agents/planner.md +145 -0
- package/package/corpus/agents/repository-investigation-reviewer.md +45 -0
- package/package/corpus/agents/repository-investigator.md +50 -0
- package/package/corpus/agents/security-reviewer.md +118 -0
- package/package/corpus/agents/slice-implementer.md +59 -0
- package/package/corpus/agents/spec-writer.md +149 -0
- package/package/corpus/harnesses/pi/extensions/cratis-hooks/index.ts +213 -0
- package/package/corpus/harnesses/pi/extensions/cratis-rules/index.ts +27 -0
- package/package/corpus/harnesses/pi/extensions/package.json +4 -0
- package/package/corpus/harnesses/pi/extensions/subagent/agents.ts +167 -0
- package/package/corpus/harnesses/pi/extensions/subagent/index.ts +352 -0
- package/package/corpus/hooks/README.md +434 -0
- package/package/corpus/hooks/agent-stop.md +49 -0
- package/package/corpus/hooks/pre-commit.md +47 -0
- package/package/corpus/hooks/scripts/cratis-guard-writes.sh +87 -0
- package/package/corpus/hooks/scripts/cratis-nuget-pins.txt +11 -0
- package/package/corpus/hooks/scripts/cratis-pattern-scan.sh +197 -0
- package/package/corpus/hooks/scripts/cratis-patterns.json +84 -0
- package/package/corpus/hooks/scripts/cratis-quality-gate.sh +219 -0
- package/package/corpus/hooks/scripts/hook-lib.sh +152 -0
- package/package/corpus/hooks/scripts/quality-gates.json +261 -0
- package/package/corpus/hooks/scripts/type-references-allowlist.txt +71 -0
- package/package/corpus/hooks/scripts/validate-package-imports.sh +166 -0
- package/package/corpus/hooks/scripts/validate-package-subpaths.sh +120 -0
- package/package/corpus/hooks/scripts/validate-type-references.sh +308 -0
- package/package/corpus/hooks/settings.template.json +40 -0
- package/package/corpus/prompts/add-business-rule.prompt.md +22 -0
- package/package/corpus/prompts/add-concept.prompt.md +17 -0
- package/package/corpus/prompts/add-ef-migration.prompt.md +24 -0
- package/package/corpus/prompts/add-projection.prompt.md +20 -0
- package/package/corpus/prompts/add-reactor.prompt.md +22 -0
- package/package/corpus/prompts/add-reducer.prompt.md +20 -0
- package/package/corpus/prompts/audit-hooks.prompt.md +15 -0
- package/package/corpus/prompts/check-doc-drift.prompt.md +21 -0
- package/package/corpus/prompts/code-review.prompt.md +9 -0
- package/package/corpus/prompts/new-feature.prompt.md +9 -0
- package/package/corpus/prompts/new-vertical-slice.prompt.md +18 -0
- package/package/corpus/prompts/review-pr.prompt.md +35 -0
- package/package/corpus/prompts/review-skill.prompt.md +16 -0
- package/package/corpus/prompts/scaffold-feature.prompt.md +16 -0
- package/package/corpus/prompts/ship-changes.prompt.md +20 -0
- package/package/corpus/prompts/verify-ai-setup.prompt.md +19 -0
- package/package/corpus/prompts/write-documentation.prompt.md +21 -0
- package/package/corpus/prompts/write-specs.prompt.md +22 -0
- package/package/corpus/rules/capability-is-not-authority.md +31 -0
- package/package/corpus/rules/code-quality.csharp.md +91 -0
- package/package/corpus/rules/code-quality.md +82 -0
- package/package/corpus/rules/code-quality.typescript.md +89 -0
- package/package/corpus/rules/components.md +207 -0
- package/package/corpus/rules/concepts.md +115 -0
- package/package/corpus/rules/csharp.md +269 -0
- package/package/corpus/rules/dialogs.md +264 -0
- package/package/corpus/rules/documentation-structure-and-formatting.md +148 -0
- package/package/corpus/rules/documentation.md +90 -0
- package/package/corpus/rules/editing-cratis-docs.md +69 -0
- package/package/corpus/rules/efcore.md +235 -0
- package/package/corpus/rules/efcore.specs.md +44 -0
- package/package/corpus/rules/exit-codes-and-wrappers.md +33 -0
- package/package/corpus/rules/framework.md +52 -0
- package/package/corpus/rules/frontend-quality.md +59 -0
- package/package/corpus/rules/frontend-testing.md +126 -0
- package/package/corpus/rules/general.md +305 -0
- package/package/corpus/rules/git-commits.md +138 -0
- package/package/corpus/rules/github-actions.md +92 -0
- package/package/corpus/rules/glossary.md +61 -0
- package/package/corpus/rules/guards-and-fuses.md +45 -0
- package/package/corpus/rules/local-work-artifacts.md +33 -0
- package/package/corpus/rules/managing-ai-rules.md +40 -0
- package/package/corpus/rules/orleans.md +50 -0
- package/package/corpus/rules/pull-requests.md +78 -0
- package/package/corpus/rules/react.md +195 -0
- package/package/corpus/rules/reactors.md +238 -0
- package/package/corpus/rules/rtk.md +37 -0
- package/package/corpus/rules/specs.csharp.md +139 -0
- package/package/corpus/rules/specs.md +132 -0
- package/package/corpus/rules/specs.scenarios.csharp.md +172 -0
- package/package/corpus/rules/specs.typescript.md +139 -0
- package/package/corpus/rules/storybook.md +84 -0
- package/package/corpus/rules/terminal-commands.md +19 -0
- package/package/corpus/rules/typescript.md +149 -0
- package/package/corpus/rules/verification-discipline.md +21 -0
- package/package/corpus/rules/vertical-slices.md +338 -0
- package/package/corpus/rules/web-fetching.md +11 -0
- package/package/corpus/rules/writing-correct-examples.md +35 -0
- package/package/corpus/rules/writing-cratis-docs.md +70 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/LICENSE +2 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/SKILL.md +131 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/references/code-style.md +187 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/references/domain-philosophy.md +91 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/references/exceptions-logging-and-di.md +223 -0
- package/package/corpus/skills/cratis-engineering-decision-record/LICENSE +2 -0
- package/package/corpus/skills/cratis-engineering-decision-record/SKILL.md +133 -0
- package/package/corpus/skills/cratis-engineering-decision-record/references/record-format.md +107 -0
- package/package/corpus/skills/cratis-engineering-docs-authoring/LICENSE +2 -0
- package/package/corpus/skills/cratis-engineering-docs-authoring/SKILL.md +86 -0
- package/package/corpus/skills/cratis-engineering-docs-authoring/references/site-format.md +46 -0
- package/package/corpus/skills/cratis-engineering-effect-boundaries/LICENSE +2 -0
- package/package/corpus/skills/cratis-engineering-effect-boundaries/SKILL.md +130 -0
- package/package/corpus/skills/cratis-engineering-effect-boundaries/references/failure-archetypes.md +133 -0
- package/package/corpus/skills/cratis-fundamentals-concept/verification.json +8 -0
- package/package/profile-catalog.json +821 -0
- package/package.json +35 -13
- package/src/index.ts +80 -0
- /package/{skills → package/corpus/skills}/cratis-application-react-specifications/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-application-react-specifications/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-application-slice-specifications/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-application-slice-specifications/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/authentication.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/authorization.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/frontend.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/local-development.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command/references/command-result.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command/references/handler-shapes.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command/references/proxy-generation.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command/references/read-model-injection.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command-execution/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command-execution/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command-validation/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-command-validation/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-ef-core-migration/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-ef-core-migration/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-observable-query-http/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-observable-query-http/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-query-paging/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-query-paging/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-react-page/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-react-page/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/data-page.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/data-tables.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/dialogs.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/mvvm.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-arc-react-page/references/queries-and-commands.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-cli-operations/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-cli-operations/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-dotnet/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-dotnet/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-elixir/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-elixir/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-kotlin/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-kotlin/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-typescript/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-client-typescript/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-compliance/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-compliance/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-constraints/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-constraints/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-modeling/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-modeling/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-specifications/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-specifications/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-type-migration/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-event-type-migration/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-mcp-inspection/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-mcp-inspection/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-mcp-inspection/references/blocked-tools.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-mcp-inspection/references/observational-tools.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-multi-tenancy/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-multi-tenancy/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-projection/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-projection/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-projection/references/fluent-builder.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-projection/references/model-bound-attributes.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-reactor/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-reactor/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-read-model/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-read-model/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-read-model/references/queries.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-read-model-specifications/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-read-model-specifications/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-reducer/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-reducer/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-web-workbench/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-chronicle-web-workbench/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-cli-terminal-workbench/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-cli-terminal-workbench/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-code-review/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-code-review/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-accessibility/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-accessibility/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-schema-editor/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-schema-editor/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-stepper-command-dialog/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-stepper-command-dialog/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-styling/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-styling/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-toolbar/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-components-toolbar/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-documentation-writing/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-documentation-writing/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-event-model-diagram/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-event-model-diagram/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-fundamentals-concept/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-fundamentals-concept/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-fundamentals-type-discovery/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-fundamentals-type-discovery/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-governed-release-methodology/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-governed-release-methodology/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-lens-browser-extension/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-lens-browser-extension/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-performance-review/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-performance-review/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-screenplay-model-authoring/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-screenplay-model-authoring/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-security-review/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-security-review/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-specification-by-example/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-specification-by-example/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-csharp/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-csharp/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-csharp/references/application-scenarios.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-csharp/references/csharp-patterns.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-csharp/references/integration-specs.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-typescript/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-typescript/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-specifications-typescript/references/typescript-patterns.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-stage-rendering-and-sandbox/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-stage-rendering-and-sandbox/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-studio-mcp-safety-guidance/LICENSE +0 -0
- /package/{skills → package/corpus/skills}/cratis-studio-mcp-safety-guidance/SKILL.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-studio-mcp-safety-guidance/references/blocked-tools.md +0 -0
- /package/{skills → package/corpus/skills}/cratis-studio-mcp-safety-guidance/references/observational-tools.md +0 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": [
|
|
3
|
+
"Quality gates run by the Stop hook (cratis-quality-gate.sh).",
|
|
4
|
+
"Data, not code: a consuming repository overrides this file (or points CRATIS_HOOKS_GATES at",
|
|
5
|
+
"its own) without touching the script. Gates run in order and stop at the first failure.",
|
|
6
|
+
"",
|
|
7
|
+
"A gate runs only when the working tree contains a change matching one of its \"changed\" globs,",
|
|
8
|
+
"AND every entry in \"requires\" is satisfied, AND its project was discovered. Any of those",
|
|
9
|
+
"unsatisfied makes the gate a NO-OP with a message on stderr \u2014 that is how a repo that has no",
|
|
10
|
+
".NET, no frontend, or no source at all stays quiet.",
|
|
11
|
+
"",
|
|
12
|
+
"Gate fields:",
|
|
13
|
+
" id stable identifier, shown in failure output",
|
|
14
|
+
" description one line, shown in the dry-run plan",
|
|
15
|
+
" changed globs (repo-relative) that must match a changed file for the gate to run",
|
|
16
|
+
" excludeChanged globs that never count as a relevant change",
|
|
17
|
+
" requires.commands executables that must be on PATH",
|
|
18
|
+
" requires.paths repo-relative literal paths that must all exist (no globbing)",
|
|
19
|
+
" workingDirectory repo-relative directory the command runs in",
|
|
20
|
+
" workingDirectoryFrom globs identifying the project file this gate builds; the command runs",
|
|
21
|
+
" in that file's directory, and a repository holding none makes the gate a",
|
|
22
|
+
" NO-OP. Globs are tried in order, so the list states a preference. Ignored",
|
|
23
|
+
" when workingDirectory is set.",
|
|
24
|
+
" command argv array \u2014 executed directly, never through a shell (no eval)",
|
|
25
|
+
"",
|
|
26
|
+
"No gate names a product's solution, package or source root \u2014 a default that does would",
|
|
27
|
+
"silently no-op in every repository except the one it was written for. The .NET and frontend",
|
|
28
|
+
"gates discover the repository's own solution/package instead, so they activate unchanged in",
|
|
29
|
+
"an application repository, a framework repository, and (as nothing to run) in a corpus-only",
|
|
30
|
+
"repository such as this one, which carries no .NET or Node project at all.",
|
|
31
|
+
"",
|
|
32
|
+
"The override model, in order of increasing force: a repository sets workingDirectory to pin",
|
|
33
|
+
"one of several candidate projects; or it drops its own quality-gates.json in place of this",
|
|
34
|
+
"file; or it points CRATIS_HOOKS_GATES at a file anywhere. None of them requires a script fork.",
|
|
35
|
+
"",
|
|
36
|
+
"Commands are pinned to .cratis/ai/rules/general.md 'Quality Gates' and .cratis/ai/hooks/agent-stop.md.",
|
|
37
|
+
"Note the Release build passes -p:CratisProxiesOutputPath= per general.md so the proxy",
|
|
38
|
+
"generator does not re-run and touch already-correct generated files. That property is the",
|
|
39
|
+
"only gate: the generator's target is Condition=\"'$(CratisProxiesOutputPath)' != ''\", and",
|
|
40
|
+
"there is no DisableProxyGenerator property \u2014 MSBuild accepts unknown -p: names silently,",
|
|
41
|
+
"so passing one looks effective and does nothing. The Debug gate deliberately omits the",
|
|
42
|
+
"override because general.md makes Debug the canonical proxy-regeneration trigger. A",
|
|
43
|
+
"consuming repository's own CI should split Debug and Release the same way."
|
|
44
|
+
],
|
|
45
|
+
"enabled": true,
|
|
46
|
+
"failFast": true,
|
|
47
|
+
"maxOutputLines": 60,
|
|
48
|
+
"gates": [
|
|
49
|
+
{
|
|
50
|
+
"id": "backend-build-debug",
|
|
51
|
+
"description": "dotnet build (Debug) \u2014 compiles #if DEBUG spec code and regenerates the TypeScript proxies",
|
|
52
|
+
"changed": [
|
|
53
|
+
"**/*.cs",
|
|
54
|
+
"**/*.csproj",
|
|
55
|
+
"**/*.slnx",
|
|
56
|
+
"**/*.sln",
|
|
57
|
+
"**/*.props",
|
|
58
|
+
"**/*.targets"
|
|
59
|
+
],
|
|
60
|
+
"excludeChanged": [
|
|
61
|
+
"**/obj/**",
|
|
62
|
+
"**/bin/**"
|
|
63
|
+
],
|
|
64
|
+
"requires": {
|
|
65
|
+
"commands": [
|
|
66
|
+
"dotnet"
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
"workingDirectoryFrom": [
|
|
70
|
+
"*.slnx",
|
|
71
|
+
"*.sln",
|
|
72
|
+
"**/*.slnx",
|
|
73
|
+
"**/*.sln"
|
|
74
|
+
],
|
|
75
|
+
"command": [
|
|
76
|
+
"dotnet",
|
|
77
|
+
"build",
|
|
78
|
+
"--configuration",
|
|
79
|
+
"Debug"
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"id": "backend-specs",
|
|
84
|
+
"description": "dotnet test (Debug, --no-build) \u2014 zero failures",
|
|
85
|
+
"changed": [
|
|
86
|
+
"**/*.cs",
|
|
87
|
+
"**/*.csproj",
|
|
88
|
+
"**/*.slnx",
|
|
89
|
+
"**/*.sln"
|
|
90
|
+
],
|
|
91
|
+
"excludeChanged": [
|
|
92
|
+
"**/obj/**",
|
|
93
|
+
"**/bin/**"
|
|
94
|
+
],
|
|
95
|
+
"requires": {
|
|
96
|
+
"commands": [
|
|
97
|
+
"dotnet"
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
"workingDirectoryFrom": [
|
|
101
|
+
"*.slnx",
|
|
102
|
+
"*.sln",
|
|
103
|
+
"**/*.slnx",
|
|
104
|
+
"**/*.sln"
|
|
105
|
+
],
|
|
106
|
+
"command": [
|
|
107
|
+
"dotnet",
|
|
108
|
+
"test",
|
|
109
|
+
"--configuration",
|
|
110
|
+
"Debug",
|
|
111
|
+
"--no-build"
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"id": "backend-build-release",
|
|
116
|
+
"description": "dotnet build (Release) \u2014 build-only check, proxy generation skipped (general.md)",
|
|
117
|
+
"changed": [
|
|
118
|
+
"**/*.cs",
|
|
119
|
+
"**/*.csproj",
|
|
120
|
+
"**/*.slnx",
|
|
121
|
+
"**/*.sln",
|
|
122
|
+
"**/*.props",
|
|
123
|
+
"**/*.targets"
|
|
124
|
+
],
|
|
125
|
+
"excludeChanged": [
|
|
126
|
+
"**/obj/**",
|
|
127
|
+
"**/bin/**"
|
|
128
|
+
],
|
|
129
|
+
"requires": {
|
|
130
|
+
"commands": [
|
|
131
|
+
"dotnet"
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
"workingDirectoryFrom": [
|
|
135
|
+
"*.slnx",
|
|
136
|
+
"*.sln",
|
|
137
|
+
"**/*.slnx",
|
|
138
|
+
"**/*.sln"
|
|
139
|
+
],
|
|
140
|
+
"command": [
|
|
141
|
+
"dotnet",
|
|
142
|
+
"build",
|
|
143
|
+
"--configuration",
|
|
144
|
+
"Release",
|
|
145
|
+
"-p:CratisProxiesOutputPath="
|
|
146
|
+
]
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"id": "frontend-lint",
|
|
150
|
+
"description": "yarn lint:ci \u2014 zero errors",
|
|
151
|
+
"changed": [
|
|
152
|
+
"**/*.ts",
|
|
153
|
+
"**/*.tsx"
|
|
154
|
+
],
|
|
155
|
+
"excludeChanged": [
|
|
156
|
+
"**/node_modules/**",
|
|
157
|
+
"**/wwwroot/**",
|
|
158
|
+
"**/dist/**",
|
|
159
|
+
"**/obj/**",
|
|
160
|
+
"**/bin/**"
|
|
161
|
+
],
|
|
162
|
+
"requires": {
|
|
163
|
+
"commands": [
|
|
164
|
+
"yarn"
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
"workingDirectoryFrom": [
|
|
168
|
+
"package.json",
|
|
169
|
+
"**/package.json"
|
|
170
|
+
],
|
|
171
|
+
"command": [
|
|
172
|
+
"yarn",
|
|
173
|
+
"lint:ci"
|
|
174
|
+
]
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"id": "frontend-compile",
|
|
178
|
+
"description": "yarn g:compile \u2014 zero TypeScript errors",
|
|
179
|
+
"changed": [
|
|
180
|
+
"**/*.ts",
|
|
181
|
+
"**/*.tsx"
|
|
182
|
+
],
|
|
183
|
+
"excludeChanged": [
|
|
184
|
+
"**/node_modules/**",
|
|
185
|
+
"**/wwwroot/**",
|
|
186
|
+
"**/dist/**",
|
|
187
|
+
"**/obj/**",
|
|
188
|
+
"**/bin/**"
|
|
189
|
+
],
|
|
190
|
+
"requires": {
|
|
191
|
+
"commands": [
|
|
192
|
+
"yarn"
|
|
193
|
+
]
|
|
194
|
+
},
|
|
195
|
+
"workingDirectoryFrom": [
|
|
196
|
+
"package.json",
|
|
197
|
+
"**/package.json"
|
|
198
|
+
],
|
|
199
|
+
"command": [
|
|
200
|
+
"yarn",
|
|
201
|
+
"g:compile"
|
|
202
|
+
]
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"id": "frontend-compile-specs",
|
|
206
|
+
"description": "yarn g:compile:specs \u2014 zero TypeScript errors in specs",
|
|
207
|
+
"changed": [
|
|
208
|
+
"**/*.ts",
|
|
209
|
+
"**/*.tsx"
|
|
210
|
+
],
|
|
211
|
+
"excludeChanged": [
|
|
212
|
+
"**/node_modules/**",
|
|
213
|
+
"**/wwwroot/**",
|
|
214
|
+
"**/dist/**",
|
|
215
|
+
"**/obj/**",
|
|
216
|
+
"**/bin/**"
|
|
217
|
+
],
|
|
218
|
+
"requires": {
|
|
219
|
+
"commands": [
|
|
220
|
+
"yarn"
|
|
221
|
+
]
|
|
222
|
+
},
|
|
223
|
+
"workingDirectoryFrom": [
|
|
224
|
+
"package.json",
|
|
225
|
+
"**/package.json"
|
|
226
|
+
],
|
|
227
|
+
"command": [
|
|
228
|
+
"yarn",
|
|
229
|
+
"g:compile:specs"
|
|
230
|
+
]
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"id": "frontend-specs",
|
|
234
|
+
"description": "yarn test \u2014 zero failures",
|
|
235
|
+
"changed": [
|
|
236
|
+
"**/*.ts",
|
|
237
|
+
"**/*.tsx"
|
|
238
|
+
],
|
|
239
|
+
"excludeChanged": [
|
|
240
|
+
"**/node_modules/**",
|
|
241
|
+
"**/wwwroot/**",
|
|
242
|
+
"**/dist/**",
|
|
243
|
+
"**/obj/**",
|
|
244
|
+
"**/bin/**"
|
|
245
|
+
],
|
|
246
|
+
"requires": {
|
|
247
|
+
"commands": [
|
|
248
|
+
"yarn"
|
|
249
|
+
]
|
|
250
|
+
},
|
|
251
|
+
"workingDirectoryFrom": [
|
|
252
|
+
"package.json",
|
|
253
|
+
"**/package.json"
|
|
254
|
+
],
|
|
255
|
+
"command": [
|
|
256
|
+
"yarn",
|
|
257
|
+
"test"
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
]
|
|
261
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Names validate-type-references.sh must never warn about.
|
|
2
|
+
#
|
|
3
|
+
# The Tier 3 index is built from the XML documentation of the Cratis NuGet packages the consuming
|
|
4
|
+
# repository pins, and from what the corpus and that repository's own `Source/**/*.cs` declare for
|
|
5
|
+
# themselves. (`Cratis/AI` itself is a corpus with no C# source, so only the corpus half
|
|
6
|
+
# contributes here and the package half is skipped in silence.) Everything below is a
|
|
7
|
+
# name that is genuinely real and genuinely outside that index — a .NET attribute from an ecosystem
|
|
8
|
+
# whose package ships no XML docs, a Chronicle *Kernel* internal that is in no client package, or a
|
|
9
|
+
# TypeScript API in a repository whose index only covers .NET assemblies.
|
|
10
|
+
#
|
|
11
|
+
# One name per line, `#` starts a comment, blank lines ignored. Every entry states *why* it is here:
|
|
12
|
+
# an entry nobody can justify is an entry that should have been a fix instead. An entry is wrong the
|
|
13
|
+
# moment its name becomes resolvable — the guard silently stops checking it — so prefer widening the
|
|
14
|
+
# index over adding a line here whenever that is possible.
|
|
15
|
+
#
|
|
16
|
+
# Add the bare name, not the `Attribute` suffix; the guard resolves both spellings.
|
|
17
|
+
|
|
18
|
+
# --- ASP.NET Core, from the Microsoft.AspNetCore.App shared framework ------------------------------
|
|
19
|
+
# The shared framework is a ref pack, and ref packs carry no XML documentation, so no amount of
|
|
20
|
+
# indexing NuGet reaches these. The corpus names them to say what NOT to write (`[Route]` on a
|
|
21
|
+
# model-bound query) or in non-Cratis illustrations.
|
|
22
|
+
FromServices
|
|
23
|
+
HttpGet
|
|
24
|
+
HttpPost
|
|
25
|
+
FromBody
|
|
26
|
+
|
|
27
|
+
# --- System.ComponentModel.DataAnnotations, from the base class library ---------------------------
|
|
28
|
+
# Same reason: BCL, ref pack, no XML docs. Cited when contrasting DataAnnotations validation with
|
|
29
|
+
# Arc's `CommandValidator<T>` / `ConceptValidator<T>`.
|
|
30
|
+
Required
|
|
31
|
+
Range
|
|
32
|
+
MaxLength
|
|
33
|
+
|
|
34
|
+
# --- Microsoft.Extensions.* -----------------------------------------------------------------------
|
|
35
|
+
# `[LoggerMessage]` is the logging source generator's attribute; `[FromKeyedServices]` is keyed DI.
|
|
36
|
+
# Both are real; the packages that carry them are transitive here, so their XML docs are not indexed.
|
|
37
|
+
LoggerMessage
|
|
38
|
+
FromKeyedServices
|
|
39
|
+
|
|
40
|
+
# --- Microsoft Orleans ----------------------------------------------------------------------------
|
|
41
|
+
# `orleans.md` is framework-profile guidance for the Chronicle Kernel's grains. The pinned Orleans
|
|
42
|
+
# packages ship no XML documentation, so `[Alias]` and `[StorageProvider]` cannot be resolved.
|
|
43
|
+
Alias
|
|
44
|
+
StorageProvider
|
|
45
|
+
|
|
46
|
+
# --- Cratis Chronicle Kernel ----------------------------------------------------------------------
|
|
47
|
+
# `WellKnown` is a Kernel static class (`WellKnown.MeterName`), used by the framework-profile
|
|
48
|
+
# add-traces skill. Kernel internals are not published in any client package, so nothing this guard
|
|
49
|
+
# can read knows about them.
|
|
50
|
+
WellKnown
|
|
51
|
+
|
|
52
|
+
# --- xUnit -----------------------------------------------------------------------------------------
|
|
53
|
+
# `[Fact]` is xUnit's test attribute. The pinned Cratis packages never reference the test framework,
|
|
54
|
+
# so its attributes cannot appear in this guard's index no matter how wide the pin list grows.
|
|
55
|
+
Fact
|
|
56
|
+
|
|
57
|
+
# --- The `cratis` CLI -----------------------------------------------------------------------------
|
|
58
|
+
# `[CliCommand]` and `[CliExample]` are `Cratis.Cli.Registration` attributes — verified present as
|
|
59
|
+
# `T:Cratis.Cli.Registration.CliCommandAttribute` / `...CliExampleAttribute` in the Cratis/cli build
|
|
60
|
+
# output. The CLI is a dotnet tool from a separate repository, not a package any application pins,
|
|
61
|
+
# so no Directory.Packages.props entry can ever bring it into the index.
|
|
62
|
+
CliCommand
|
|
63
|
+
CliExample
|
|
64
|
+
|
|
65
|
+
# --- Cratis TypeScript ----------------------------------------------------------------------------
|
|
66
|
+
# `JsonSerializer` is `@cratis/fundamentals`' TypeScript serializer, referenced from the frontend
|
|
67
|
+
# identity guidance. `ObservableQuery` is the TypeScript class the Arc proxy generator emits for
|
|
68
|
+
# `ISubject<T>` query return types. This guard indexes .NET assemblies only; named TypeScript
|
|
69
|
+
# imports are Tier 2's question, and a bare TypeScript class name is nobody's.
|
|
70
|
+
JsonSerializer
|
|
71
|
+
ObservableQuery
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Tier 2 of the package drift guard. Where validate-package-subpaths.sh asks whether a module
|
|
3
|
+
# specifier resolves, this asks whether the *names* imported through it exist: for every
|
|
4
|
+
# `import { A, B } from '@cratis/<pkg>/<subpath>'` the AI corpus writes, it WARNS about each
|
|
5
|
+
# identifier that appears nowhere in the installed package's `.d.ts` tree.
|
|
6
|
+
#
|
|
7
|
+
# It exists because a subpath that resolves says nothing about what is behind it. `Toaster`,
|
|
8
|
+
# `toastCommandResult`, `PasswordField`, `RatingField` and friends are real APIs of
|
|
9
|
+
# @cratis/components 3.0.0 and absent from 2.6.1; Tier 1 caught the three *subpaths* that moved with
|
|
10
|
+
# them, and the names themselves were only ever found by a human reading package internals.
|
|
11
|
+
#
|
|
12
|
+
# WARN, never fail, and silent when it cannot judge — for the identical reasons spelled out at the
|
|
13
|
+
# top of validate-package-subpaths.sh. A miss is exact; the conclusion drawn from it is not.
|
|
14
|
+
#
|
|
15
|
+
# Deliberately permissive, because a false warning is worse than a missed one. A name counts as
|
|
16
|
+
# present when it appears as a *word anywhere* in the package's `.d.ts` closure — not only in an
|
|
17
|
+
# export position — and the closure follows `export ... from '<other-package>'` re-exports one level
|
|
18
|
+
# out. That admits a name that is merely referenced by the types (an imported PrimeReact symbol, a
|
|
19
|
+
# name in a doc comment) and it still flags every one of the twelve 3.0.0 names above.
|
|
20
|
+
#
|
|
21
|
+
# Portable: bash 3.2 + grep + sed + awk, with `jq` as the one accepted dependency (absent -> silent
|
|
22
|
+
# no-op, per the hook design constraints in ../README.md).
|
|
23
|
+
#
|
|
24
|
+
# Usage: validate-package-imports.sh [root ...] # default roots: .cratis/ai/rules .cratis/ai/skills .cratis/ai/agents .cratis/ai/prompts
|
|
25
|
+
# CRATIS_HOOKS_IMPORT_REPORT=1 ... # also print every binding and its resolved status
|
|
26
|
+
set -euo pipefail
|
|
27
|
+
|
|
28
|
+
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
|
|
29
|
+
cd "$root"
|
|
30
|
+
|
|
31
|
+
warn() { printf 'ai-corpus warn: %s\n' "$1" >&2; }
|
|
32
|
+
# `if`, not `A && B || C`: the trailing `|| true` was there to keep a disabled report
|
|
33
|
+
# from failing the caller, but it also swallowed a real printf failure, and shellcheck
|
|
34
|
+
# flags the shape (SC2015) for exactly that reason.
|
|
35
|
+
report() {
|
|
36
|
+
if [[ "${CRATIS_HOOKS_IMPORT_REPORT:-0}" == "1" ]]; then printf 'ai-corpus import: %s\n' "$1" >&2; fi
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
40
|
+
[[ -d node_modules/@cratis ]] || exit 0
|
|
41
|
+
|
|
42
|
+
# `.cratis/ai/hooks` is deliberately not a default root: this file and ../README.md name deliberately-bogus
|
|
43
|
+
# identifiers as examples, and a guard that reports its own documentation is a guard people switch off.
|
|
44
|
+
if [[ $# -gt 0 ]]; then roots=("$@"); else roots=(.cratis/ai/rules .cratis/ai/skills .cratis/ai/agents .cratis/ai/prompts); fi
|
|
45
|
+
scan=()
|
|
46
|
+
for d in "${roots[@]}"; do [[ -d "$d" ]] && scan+=("$d"); done
|
|
47
|
+
[[ "${#scan[@]}" -gt 0 ]] || exit 0
|
|
48
|
+
|
|
49
|
+
# Same clearing rule, and the same generosity, as Tier 1: a line that carries a version alongside the
|
|
50
|
+
# name has declared the skew on purpose (`(**≥ 3.0.0**)`), so it is not drift.
|
|
51
|
+
version_re='[0-9]+\.[0-9x]+|≥|>='
|
|
52
|
+
|
|
53
|
+
q="'"
|
|
54
|
+
|
|
55
|
+
# One awk pass over the corpus emits `file <TAB> line <TAB> package <TAB> specifier <TAB> name` for
|
|
56
|
+
# every named binding of an `@cratis/*` import — single-line and brace-on-its-own-line forms alike,
|
|
57
|
+
# `import type`, `A as B` (the *imported* name is what has to exist), and trailing `//` comments.
|
|
58
|
+
# Anything that does not lex as a plain identifier is dropped rather than guessed at. The quote
|
|
59
|
+
# character arrives as `-v q` so the program itself never has to contain one.
|
|
60
|
+
extract="$(cat <<'AWK'
|
|
61
|
+
FNR == 1 { n = 0; buf = "" }
|
|
62
|
+
{
|
|
63
|
+
# A blank line, or a second `import`, ends an unterminated buffer rather than being glued onto
|
|
64
|
+
# it. Without that, a stray `import {` in prose swallows the real statement below it and the
|
|
65
|
+
# names get read off one block while the specifier is read off another — which invents an
|
|
66
|
+
# identifier that resolves nowhere, i.e. exactly the false positive this guard must not produce.
|
|
67
|
+
if (n > 0 && ($0 ~ /^[[:space:]]*$/ || $0 ~ /^[[:space:]]*import[[:space:]]/)) { n = 0; buf = "" }
|
|
68
|
+
if (n == 0) {
|
|
69
|
+
if ($0 !~ /^[[:space:]]*import[[:space:]]/) next
|
|
70
|
+
if (index($0, "{") == 0) next
|
|
71
|
+
start = FNR; buf = ""
|
|
72
|
+
}
|
|
73
|
+
line = $0
|
|
74
|
+
sub(/\/\/.*$/, "", line)
|
|
75
|
+
buf = buf " " line
|
|
76
|
+
n++
|
|
77
|
+
if (line ~ ("from[[:space:]]*[" q "\"]") || n > 40) { emit(); n = 0; buf = "" }
|
|
78
|
+
}
|
|
79
|
+
function emit( spec, pkg, names, parts, count, i, name) {
|
|
80
|
+
if (match(buf, "from[[:space:]]*[" q "\"][^" q "\"]+[" q "\"]") == 0) return
|
|
81
|
+
spec = substr(buf, RSTART, RLENGTH)
|
|
82
|
+
sub("^from[[:space:]]*[" q "\"]", "", spec)
|
|
83
|
+
sub("[" q "\"]$", "", spec)
|
|
84
|
+
if (spec !~ /^@cratis\//) return
|
|
85
|
+
pkg = substr(spec, 9)
|
|
86
|
+
sub(/\/.*$/, "", pkg)
|
|
87
|
+
if (match(buf, /\{[^}]*\}/) == 0) return
|
|
88
|
+
names = substr(buf, RSTART + 1, RLENGTH - 2)
|
|
89
|
+
count = split(names, parts, ",")
|
|
90
|
+
for (i = 1; i <= count; i++) {
|
|
91
|
+
name = parts[i]
|
|
92
|
+
sub(/^[[:space:]]+/, "", name); sub(/[[:space:]]+$/, "", name)
|
|
93
|
+
sub(/^type[[:space:]]+/, "", name)
|
|
94
|
+
sub(/[[:space:]]+as[[:space:]].*$/, "", name)
|
|
95
|
+
sub(/[[:space:]]+$/, "", name)
|
|
96
|
+
if (name !~ /^[A-Za-z_$][A-Za-z0-9_$]*$/) continue
|
|
97
|
+
printf "%s\t%d\t%s\t%s\t%s\n", FILENAME, start, pkg, spec, name
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
AWK
|
|
101
|
+
)"
|
|
102
|
+
|
|
103
|
+
files=()
|
|
104
|
+
while IFS= read -r f; do files+=("$f"); done < <(find "${scan[@]}" -type f 2>/dev/null | LC_ALL=C sort)
|
|
105
|
+
[[ "${#files[@]}" -gt 0 ]] || exit 0
|
|
106
|
+
|
|
107
|
+
bindings="$(awk -v q="$q" "$extract" "${files[@]}" 2>/dev/null || true)"
|
|
108
|
+
[[ -n "$bindings" ]] || exit 0
|
|
109
|
+
|
|
110
|
+
# Every `.d.ts` reachable from a package: its own tree, plus one level out through re-exports to
|
|
111
|
+
# another installed package (`export { Messenger } from '@cratis/arc/messaging'`). Intra-package
|
|
112
|
+
# barrels (`export * from './X'`) need no following — the whole tree is read either way.
|
|
113
|
+
closure_dirs() {
|
|
114
|
+
local pkgdir="node_modules/@cratis/$1" dep
|
|
115
|
+
printf '%s\n' "$pkgdir"
|
|
116
|
+
grep -rhE "^[[:space:]]*export[[:space:]][^;]*[[:space:]]from[[:space:]]*[\"$q]" "$pkgdir" --include='*.d.ts' 2>/dev/null \
|
|
117
|
+
| sed -E "s/.*[\"$q]([^\"$q]*)[\"$q].*/\1/" \
|
|
118
|
+
| grep -v '^\.' \
|
|
119
|
+
| sed -E 's#^(@[^/]+/[^/]+|[^@/][^/]*).*#\1#' \
|
|
120
|
+
| LC_ALL=C sort -u \
|
|
121
|
+
| while IFS= read -r dep; do
|
|
122
|
+
[[ -n "$dep" && -d "node_modules/$dep" && "node_modules/$dep" != "$pkgdir" ]] && printf 'node_modules/%s\n' "$dep"
|
|
123
|
+
done
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# Sorted by package so each closure is read exactly once.
|
|
127
|
+
printf '%s\n' "$bindings" | cut -f3,5 | LC_ALL=C sort -u | while IFS="$(printf '\t')" read -r pkg name; do
|
|
128
|
+
[[ -n "$pkg" && -n "$name" ]] || continue
|
|
129
|
+
|
|
130
|
+
if [[ "${current:-}" != "$pkg" ]]; then
|
|
131
|
+
current="$pkg"; tokens=""; version="?"
|
|
132
|
+
if [[ -f "node_modules/@cratis/$pkg/package.json" ]]; then
|
|
133
|
+
version="$(jq -r '.version // "?"' "node_modules/@cratis/$pkg/package.json" 2>/dev/null || printf '?')"
|
|
134
|
+
dirs=()
|
|
135
|
+
while IFS= read -r d; do [[ -n "$d" ]] && dirs+=("$d"); done < <(closure_dirs "$pkg")
|
|
136
|
+
if [[ "${#dirs[@]}" -gt 0 ]]; then
|
|
137
|
+
tokens="$(grep -rhoE '[A-Za-z_$][A-Za-z0-9_$]*' "${dirs[@]}" --include='*.d.ts' 2>/dev/null | LC_ALL=C sort -u || true)"
|
|
138
|
+
fi
|
|
139
|
+
fi
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
# No package, or a package that ships no type declarations: nothing authoritative to compare
|
|
143
|
+
# against, so it is not a finding.
|
|
144
|
+
if [[ -z "$tokens" ]]; then report "$name — skipped (@cratis/$pkg has no installed type declarations)"; continue; fi
|
|
145
|
+
# A here-string, never `printf … | grep -q`: under `pipefail` an early-exiting `grep -q` closes
|
|
146
|
+
# the pipe, `printf` dies of SIGPIPE with 141, and the pipeline reports failure even though the
|
|
147
|
+
# name matched. On a token list this size that is not a rare race — it is every time, and it
|
|
148
|
+
# manufactures false positives, which is the one thing this guard must not do.
|
|
149
|
+
if LC_ALL=C grep -qxF -- "$name" <<<"$tokens"; then report "$name — yes (@cratis/$pkg $version)"; continue; fi
|
|
150
|
+
|
|
151
|
+
# Qualification is judged per (file, name) exactly as in Tier 1: the corpus states a version
|
|
152
|
+
# requirement once in prose and then writes the import unqualified in a fenced block below it,
|
|
153
|
+
# so any line in the file that mentions the name and carries a version clears the file.
|
|
154
|
+
# One warning per file — the first binding — so a page that repeats an example is not a flood.
|
|
155
|
+
printf '%s\n' "$bindings" | awk -F'\t' -v p="$pkg" -v n="$name" \
|
|
156
|
+
'$3 == p && $5 == n { print $1 "\t" $2 "\t" $4 }' \
|
|
157
|
+
| LC_ALL=C sort -t"$(printf '\t')" -k1,1 -k2,2n -u | awk -F'\t' '!seen[$1]++' \
|
|
158
|
+
| while IFS="$(printf '\t')" read -r file line spec; do
|
|
159
|
+
hits="$(grep -nwF -- "$name" "$file" 2>/dev/null || true)"
|
|
160
|
+
if [[ -n "$hits" ]] && grep -qE "$version_re" <<<"$hits"; then
|
|
161
|
+
report "$name — missing from @cratis/$pkg $version but version-qualified in $file"
|
|
162
|
+
continue
|
|
163
|
+
fi
|
|
164
|
+
warn "$file:$line: '$name' imported from '$spec' is not declared anywhere in the installed @cratis/$pkg $version — fix the name, or mark the line with the version it needs (e.g. '(≥ 3.0.0)')"
|
|
165
|
+
done
|
|
166
|
+
done
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Resolves every `@cratis/<package>/<subpath>` the AI corpus references against the `exports` map of
|
|
3
|
+
# the package actually installed under node_modules, and WARNS about each one that cannot resolve.
|
|
4
|
+
#
|
|
5
|
+
# WARN, never fail — deliberately. The exports map is exact, but the *conclusion* drawn from a miss
|
|
6
|
+
# is not: "the corpus documents an API that does not exist" and "this repository is pinned behind the
|
|
7
|
+
# version the corpus documents" produce the identical observation. This script is propagated to every
|
|
8
|
+
# Cratis repository and runs in the `ai-corpus` CI job, which checks out the tree and installs
|
|
9
|
+
# nothing — so a fatal verdict would either be a permanent no-op there or turn a repo red for its own
|
|
10
|
+
# dependency pin. A warning is the honest signal: look at this line, decide which of the two it is.
|
|
11
|
+
#
|
|
12
|
+
# Not installed is not a finding. A missing node_modules, a missing @cratis scope, a package this
|
|
13
|
+
# repository does not depend on, and a package published without an `exports` map are all skipped
|
|
14
|
+
# silently — there is nothing authoritative to compare against.
|
|
15
|
+
#
|
|
16
|
+
# Portable: bash 3.2 + grep + sed, with `jq` as the one accepted dependency (absent -> silent no-op,
|
|
17
|
+
# per the hook design constraints in ../README.md).
|
|
18
|
+
#
|
|
19
|
+
# Usage: validate-package-subpaths.sh [root ...] # default roots: .cratis/ai/rules .cratis/ai/skills .cratis/ai/agents .cratis/ai/prompts
|
|
20
|
+
# CRATIS_HOOKS_SUBPATH_REPORT=1 ... # also print every reference and its resolved status
|
|
21
|
+
set -euo pipefail
|
|
22
|
+
|
|
23
|
+
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
|
|
24
|
+
cd "$root"
|
|
25
|
+
|
|
26
|
+
warn() { printf 'ai-corpus warn: %s\n' "$1" >&2; }
|
|
27
|
+
# `if`, not `A && B || C`: the trailing `|| true` was there to keep a disabled report
|
|
28
|
+
# from failing the caller, but it also swallowed a real printf failure, and shellcheck
|
|
29
|
+
# flags the shape (SC2015) for exactly that reason.
|
|
30
|
+
report() {
|
|
31
|
+
if [[ "${CRATIS_HOOKS_SUBPATH_REPORT:-0}" == "1" ]]; then printf 'ai-corpus subpath: %s\n' "$1" >&2; fi
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Tier 3 over the same roots: whether the .NET types the corpus names in prose and in C# positions
|
|
35
|
+
# exist at all. Invoked exactly like Tier 2 at the bottom of this file — tested with -f, not -x, and
|
|
36
|
+
# run through `bash`, so a checkout that lost the exec bit does not silently drop the guard — but
|
|
37
|
+
# necessarily *above* the two gates below, because it reads a NuGet cache rather than node_modules
|
|
38
|
+
# and must still run in a repository that has no frontend and no `jq`.
|
|
39
|
+
types="$(dirname "${BASH_SOURCE[0]}")/validate-type-references.sh"
|
|
40
|
+
if [[ -f "$types" ]]; then bash "$types" "$@" || true; fi
|
|
41
|
+
|
|
42
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
43
|
+
[[ -d node_modules/@cratis ]] || exit 0
|
|
44
|
+
|
|
45
|
+
# `.cratis/ai/hooks` is deliberately not a default root: this file and ../README.md name deliberately-bogus
|
|
46
|
+
# subpaths as examples, and a guard that reports its own documentation is a guard people switch off.
|
|
47
|
+
if [[ $# -gt 0 ]]; then roots=("$@"); else roots=(.cratis/ai/rules .cratis/ai/skills .cratis/ai/agents .cratis/ai/prompts); fi
|
|
48
|
+
scan=()
|
|
49
|
+
for d in "${roots[@]}"; do [[ -d "$d" ]] && scan+=("$d"); done
|
|
50
|
+
[[ "${#scan[@]}" -gt 0 ]] || exit 0
|
|
51
|
+
|
|
52
|
+
# A line that carries a version alongside the reference has already declared the skew on purpose
|
|
53
|
+
# (`(**≥ 3.0.0**)`), so it is not drift. Kept deliberately generous — a dotted version, an `N.x`, or
|
|
54
|
+
# either inequality spelling qualifies — because the cost of a missed warning is one stale line while
|
|
55
|
+
# the cost of a false one is noise on exactly the lines someone just fixed correctly.
|
|
56
|
+
version_re='[0-9]+\.[0-9x]+|≥|>='
|
|
57
|
+
|
|
58
|
+
# `node_modules/@cratis/...` in the corpus is a filesystem path (a "look in the .d.ts" pointer), not a
|
|
59
|
+
# module specifier. Capture the prefix so it can be dropped rather than mis-parsed as a subpath.
|
|
60
|
+
refs="$(grep -rhoE '(node_modules/)?@cratis/[A-Za-z0-9._-]+(/[A-Za-z0-9._-]+)+' "${scan[@]}" 2>/dev/null \
|
|
61
|
+
| grep -v '^node_modules/' | sed -E 's/[.-]+$//' | LC_ALL=C sort -u || true)"
|
|
62
|
+
[[ -n "$refs" ]] || exit 0
|
|
63
|
+
|
|
64
|
+
# Prints yes | no | unknown for "./<subpath>" against a package.json's exports map. `unknown` covers
|
|
65
|
+
# every shape that carries no authoritative subpath list; only `no` is ever reported.
|
|
66
|
+
resolves() {
|
|
67
|
+
jq -r --arg s "./$2" '
|
|
68
|
+
def matches($key): if ($key | contains("*"))
|
|
69
|
+
then ($key | split("*")) as $p
|
|
70
|
+
| if ($p | length) == 2 then ($s | startswith($p[0])) and ($s | endswith($p[1])) else true end
|
|
71
|
+
else $key == $s end;
|
|
72
|
+
(.exports // null) as $e
|
|
73
|
+
| if $e == null then "unknown"
|
|
74
|
+
elif ($e | type) == "string" then "no"
|
|
75
|
+
elif ($e | type) != "object" then "unknown"
|
|
76
|
+
else ($e | keys) as $k
|
|
77
|
+
| if ([$k[] | startswith(".")] | any) == false then "no"
|
|
78
|
+
elif ([$k[] | select(matches(.))] | length) > 0 then "yes"
|
|
79
|
+
else "no" end
|
|
80
|
+
end' "$1" 2>/dev/null || printf 'unknown'
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
printf '%s\n' "$refs" | while IFS= read -r ref; do
|
|
84
|
+
[[ -n "$ref" ]] || continue
|
|
85
|
+
rest="${ref#@cratis/}"; pkg="${rest%%/*}"; sub="${rest#*/}"
|
|
86
|
+
manifest="node_modules/@cratis/$pkg/package.json"
|
|
87
|
+
if [[ ! -f "$manifest" ]]; then report "$ref — skipped (@cratis/$pkg not installed)"; continue; fi
|
|
88
|
+
status="$(resolves "$manifest" "$sub")"
|
|
89
|
+
version="$(jq -r '.version // "?"' "$manifest" 2>/dev/null || printf '?')"
|
|
90
|
+
if [[ "$status" != "no" ]]; then report "$ref — $status (@cratis/$pkg $version)"; continue; fi
|
|
91
|
+
|
|
92
|
+
# Bounded so `.../CommandForm` does not answer for `.../CommandForm/fields`. Qualification is
|
|
93
|
+
# judged per (file, reference): the corpus states a subpath's version requirement once and may
|
|
94
|
+
# then mention it again unqualified in the same file, so any qualified line clears the file.
|
|
95
|
+
bounded="($(printf '%s' "$ref" | sed 's/\./\\./g'))([^A-Za-z0-9._/-]|\$)"
|
|
96
|
+
{ grep -rlE "$bounded" "${scan[@]}" 2>/dev/null || true; } | LC_ALL=C sort | while IFS= read -r file; do
|
|
97
|
+
# `grep -n` prefixes each hit with `<line>:`, which carries no dot and so cannot itself look
|
|
98
|
+
# like a version. Lines where the reference is part of a node_modules path are dropped again
|
|
99
|
+
# here — the extraction pass already ignores them, and they are not module specifiers.
|
|
100
|
+
hits="$(grep -nE "$bounded" "$file" 2>/dev/null | grep -vF "node_modules/$ref" || true)"
|
|
101
|
+
[[ -n "$hits" ]] || continue
|
|
102
|
+
# A here-string, never `printf … | grep -q`: an early-exiting `grep -q` closes the pipe,
|
|
103
|
+
# `printf` dies of SIGPIPE with 141, and under `pipefail` the pipeline reports failure even
|
|
104
|
+
# though the version matched — turning a correctly-qualified line into a warning.
|
|
105
|
+
if grep -qE "$version_re" <<<"$hits"; then
|
|
106
|
+
report "$ref — missing from @cratis/$pkg $version but version-qualified in $file"
|
|
107
|
+
continue
|
|
108
|
+
fi
|
|
109
|
+
line="$(printf '%s\n' "$hits" | head -1 | cut -d: -f1)"
|
|
110
|
+
warn "$file:$line: '$ref' is not in the exports map of the installed @cratis/$pkg $version — fix the reference, or mark the line with the version it needs (e.g. '(≥ 3.0.0)')"
|
|
111
|
+
done
|
|
112
|
+
done
|
|
113
|
+
|
|
114
|
+
# Tier 2 over the same roots: a subpath that resolves says nothing about the *names* imported
|
|
115
|
+
# through it. Kept in its own script — a different question, a different corpus extraction and a
|
|
116
|
+
# different report variable — and invoked from here so the single call site in validate-ai-setup.sh
|
|
117
|
+
# gets both. Tested with -f, not -x, and run through `bash`: a checkout that lost the exec bit must
|
|
118
|
+
# not silently drop the guard.
|
|
119
|
+
imports="$(dirname "${BASH_SOURCE[0]}")/validate-package-imports.sh"
|
|
120
|
+
if [[ -f "$imports" ]]; then bash "$imports" "$@" || true; fi
|