@ontrails/warden 0.2.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/CHANGELOG.md +1556 -0
- package/README.md +200 -0
- package/bin/warden.ts +51 -0
- package/package.json +51 -0
- package/src/adapter-check.ts +136 -0
- package/src/cli.ts +2159 -0
- package/src/command.ts +1167 -0
- package/src/config.ts +193 -0
- package/src/draft.ts +22 -0
- package/src/drift.ts +260 -0
- package/src/fix.ts +126 -0
- package/src/formatters.ts +178 -0
- package/src/guide.ts +245 -0
- package/src/index.ts +295 -0
- package/src/project-context.ts +446 -0
- package/src/project-rules.ts +290 -0
- package/src/regrade-history.ts +188 -0
- package/src/regrade-receipt.ts +484 -0
- package/src/resolve.ts +531 -0
- package/src/rules/activation-orphan.ts +97 -0
- package/src/rules/captured-kernel.ts +375 -0
- package/src/rules/circular-refs.ts +150 -0
- package/src/rules/cli-command-route-coherence.ts +177 -0
- package/src/rules/composes-declarations.ts +839 -0
- package/src/rules/context-no-surface-types.ts +214 -0
- package/src/rules/dead-internal-trail.ts +161 -0
- package/src/rules/dead-public-trail.ts +258 -0
- package/src/rules/draft-file-marking.ts +155 -0
- package/src/rules/draft-visible-debt.ts +83 -0
- package/src/rules/duplicate-exported-symbol.ts +172 -0
- package/src/rules/duplicate-public-contract.ts +137 -0
- package/src/rules/entity-exists.ts +254 -0
- package/src/rules/entity-ids.ts +15 -0
- package/src/rules/error-mapping-completeness.ts +290 -0
- package/src/rules/example-valid.ts +395 -0
- package/src/rules/fires-declarations.ts +740 -0
- package/src/rules/governed-symbol-residue.ts +535 -0
- package/src/rules/governed-vocabulary-permutation-watch.ts +77 -0
- package/src/rules/implementation-returns-result.ts +1733 -0
- package/src/rules/incomplete-accessor-for-standard-op.ts +272 -0
- package/src/rules/incomplete-crud.ts +583 -0
- package/src/rules/index.ts +328 -0
- package/src/rules/intent-propagation.ts +125 -0
- package/src/rules/layer-field-name-drift.ts +102 -0
- package/src/rules/library-render-coherence.ts +97 -0
- package/src/rules/metadata.ts +929 -0
- package/src/rules/missing-reconcile.ts +97 -0
- package/src/rules/missing-visibility.ts +111 -0
- package/src/rules/no-destructured-compose.ts +196 -0
- package/src/rules/no-dev-permit-in-source.ts +99 -0
- package/src/rules/no-direct-implementation-call.ts +52 -0
- package/src/rules/no-legacy-cli-alias-export.ts +247 -0
- package/src/rules/no-legacy-layer-imports.ts +211 -0
- package/src/rules/no-native-error-result.ts +118 -0
- package/src/rules/no-redundant-result-error-wrap.ts +382 -0
- package/src/rules/no-retired-cross-vocabulary.ts +203 -0
- package/src/rules/no-sync-result-assumption.ts +1199 -0
- package/src/rules/no-throw-in-detour-recover.ts +225 -0
- package/src/rules/no-throw-in-implementation.ts +48 -0
- package/src/rules/no-top-level-surface.ts +371 -0
- package/src/rules/on-references-exist.ts +194 -0
- package/src/rules/orphaned-signal.ts +149 -0
- package/src/rules/owner-render-parity.ts +143 -0
- package/src/rules/permit-governance.ts +25 -0
- package/src/rules/prefer-schema-inference.ts +141 -0
- package/src/rules/public-export-example-coverage.ts +561 -0
- package/src/rules/public-internal-deep-imports.ts +454 -0
- package/src/rules/public-output-schema.ts +29 -0
- package/src/rules/public-union-output-discriminants.ts +150 -0
- package/src/rules/read-intent-fires.ts +188 -0
- package/src/rules/reference-exists.ts +97 -0
- package/src/rules/registry-names.ts +171 -0
- package/src/rules/resolved-import-boundary.ts +146 -0
- package/src/rules/resource-declarations.ts +697 -0
- package/src/rules/resource-exists.ts +181 -0
- package/src/rules/resource-id-grammar.ts +65 -0
- package/src/rules/resource-mock-coverage.ts +115 -0
- package/src/rules/retired-vocabulary.ts +1407 -0
- package/src/rules/scan.ts +59 -0
- package/src/rules/scheduled-destroy-intent.ts +44 -0
- package/src/rules/signal-graph-coaching.ts +220 -0
- package/src/rules/source/composition.ts +165 -0
- package/src/rules/source/drafts.ts +164 -0
- package/src/rules/source/entities.ts +618 -0
- package/src/rules/source/pragmas.ts +45 -0
- package/src/rules/source/resources.ts +64 -0
- package/src/rules/source/signals.ts +397 -0
- package/src/rules/source/stores.ts +310 -0
- package/src/rules/specs.ts +388 -0
- package/src/rules/static-resource-accessor-preference.ts +654 -0
- package/src/rules/structure.ts +234 -0
- package/src/rules/surface-overlay-coherence.ts +262 -0
- package/src/rules/surface-trailhead-coherence.ts +366 -0
- package/src/rules/trail-fork-coaching.ts +625 -0
- package/src/rules/trail-versioning-source.ts +1076 -0
- package/src/rules/trail-versioning-topo.ts +172 -0
- package/src/rules/trailhead-override-divergence.ts +356 -0
- package/src/rules/types.ts +464 -0
- package/src/rules/unmaterialized-activation-source.ts +85 -0
- package/src/rules/unreachable-detour-shadowing.ts +343 -0
- package/src/rules/valid-describe-refs.ts +224 -0
- package/src/rules/valid-detour-contract.ts +78 -0
- package/src/rules/warden-export-symmetry.ts +540 -0
- package/src/rules/warden-rules-use-ast.ts +1109 -0
- package/src/rules/webhook-route-collision.ts +306 -0
- package/src/rules/workspace-lock-ownership.ts +34 -0
- package/src/trails/activation-orphan.trail.ts +84 -0
- package/src/trails/captured-kernel.trail.ts +108 -0
- package/src/trails/circular-refs.trail.ts +29 -0
- package/src/trails/cli-command-route-coherence.trail.ts +47 -0
- package/src/trails/composes-declarations.trail.ts +22 -0
- package/src/trails/context-no-surface-types.trail.ts +21 -0
- package/src/trails/dead-internal-trail.trail.ts +26 -0
- package/src/trails/dead-public-trail.trail.ts +31 -0
- package/src/trails/deprecation-without-guidance.trail.ts +21 -0
- package/src/trails/draft-file-marking.trail.ts +16 -0
- package/src/trails/draft-visible-debt.trail.ts +16 -0
- package/src/trails/duplicate-exported-symbol.trail.ts +48 -0
- package/src/trails/duplicate-public-contract.trail.ts +47 -0
- package/src/trails/entity-exists.trail.ts +21 -0
- package/src/trails/error-mapping-completeness.trail.ts +30 -0
- package/src/trails/example-valid.trail.ts +25 -0
- package/src/trails/fires-declarations.trail.ts +23 -0
- package/src/trails/fork-without-preserved-implementation.trail.ts +31 -0
- package/src/trails/governed-symbol-residue.trail.ts +24 -0
- package/src/trails/governed-vocabulary-permutation-watch.trail.ts +16 -0
- package/src/trails/implementation-returns-result.trail.ts +20 -0
- package/src/trails/incomplete-accessor-for-standard-op.trail.ts +76 -0
- package/src/trails/incomplete-crud.trail.ts +39 -0
- package/src/trails/index.ts +91 -0
- package/src/trails/intent-propagation.trail.ts +30 -0
- package/src/trails/layer-field-name-drift.trail.ts +39 -0
- package/src/trails/library-render-coherence.trail.ts +43 -0
- package/src/trails/marker-schema-unsupported.trail.ts +23 -0
- package/src/trails/missing-reconcile.trail.ts +33 -0
- package/src/trails/missing-visibility.trail.ts +22 -0
- package/src/trails/no-destructured-compose.trail.ts +44 -0
- package/src/trails/no-dev-permit-in-source.trail.ts +16 -0
- package/src/trails/no-direct-implementation-call.trail.ts +16 -0
- package/src/trails/no-legacy-cli-alias-export.trail.ts +41 -0
- package/src/trails/no-legacy-layer-imports.trail.ts +41 -0
- package/src/trails/no-native-error-result.trail.ts +18 -0
- package/src/trails/no-redundant-result-error-wrap.trail.ts +55 -0
- package/src/trails/no-retired-cross-vocabulary.trail.ts +42 -0
- package/src/trails/no-sync-result-assumption.trail.ts +19 -0
- package/src/trails/no-throw-in-detour-recover.trail.ts +24 -0
- package/src/trails/no-throw-in-implementation.trail.ts +20 -0
- package/src/trails/no-top-level-surface.trail.ts +43 -0
- package/src/trails/on-references-exist.trail.ts +21 -0
- package/src/trails/orphaned-signal.trail.ts +36 -0
- package/src/trails/owner-render-parity.trail.ts +26 -0
- package/src/trails/pending-force.trail.ts +21 -0
- package/src/trails/permit-governance.trail.ts +51 -0
- package/src/trails/prefer-schema-inference.trail.ts +21 -0
- package/src/trails/public-export-example-coverage.trail.ts +16 -0
- package/src/trails/public-internal-deep-imports.trail.ts +94 -0
- package/src/trails/public-output-schema.trail.ts +55 -0
- package/src/trails/public-union-output-discriminants.trail.ts +33 -0
- package/src/trails/read-intent-fires.trail.ts +20 -0
- package/src/trails/reference-exists.trail.ts +25 -0
- package/src/trails/resolved-import-boundary.trail.ts +109 -0
- package/src/trails/resource-declarations.trail.ts +25 -0
- package/src/trails/resource-exists.trail.ts +27 -0
- package/src/trails/resource-id-grammar.trail.ts +39 -0
- package/src/trails/resource-mock-coverage.trail.ts +40 -0
- package/src/trails/run.ts +205 -0
- package/src/trails/scheduled-destroy-intent.trail.ts +56 -0
- package/src/trails/schema.ts +295 -0
- package/src/trails/signal-graph-coaching.trail.ts +77 -0
- package/src/trails/static-resource-accessor-preference.trail.ts +25 -0
- package/src/trails/surface-overlay-coherence.trail.ts +24 -0
- package/src/trails/surface-trailhead-coherence.trail.ts +25 -0
- package/src/trails/topo.ts +6 -0
- package/src/trails/trail-fork-coaching.trail.ts +42 -0
- package/src/trails/trailhead-override-divergence.trail.ts +47 -0
- package/src/trails/unmaterialized-activation-source.trail.ts +72 -0
- package/src/trails/unreachable-detour-shadowing.trail.ts +45 -0
- package/src/trails/valid-describe-refs.trail.ts +18 -0
- package/src/trails/valid-detour-contract.trail.ts +71 -0
- package/src/trails/version-gap.trail.ts +35 -0
- package/src/trails/version-pinned-compose.trail.ts +23 -0
- package/src/trails/version-without-examples.trail.ts +38 -0
- package/src/trails/warden-export-symmetry.trail.ts +16 -0
- package/src/trails/warden-rules-use-ast.trail.ts +64 -0
- package/src/trails/webhook-route-collision.trail.ts +50 -0
- package/src/trails/workspace-lock-ownership.trail.ts +16 -0
- package/src/trails/wrap-rule.ts +269 -0
- package/src/workspaces.ts +264 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI-oriented formatters for warden reports.
|
|
3
|
+
*
|
|
4
|
+
* Each formatter takes a `WardenReport` and produces output suited to a
|
|
5
|
+
* specific CI environment: GitHub Actions annotations, structured JSON,
|
|
6
|
+
* or a concise markdown summary.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { WardenReport } from './cli.js';
|
|
10
|
+
import { staleDriftMessage } from './drift.js';
|
|
11
|
+
import type { WardenGuidanceLink, WardenSeverity } from './rules/types.js';
|
|
12
|
+
|
|
13
|
+
/** Map warden severity to GitHub Actions annotation level. */
|
|
14
|
+
const ghLevel: Record<WardenSeverity, string> = {
|
|
15
|
+
error: 'error',
|
|
16
|
+
warn: 'warning',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Produce GitHub Actions workflow command annotations, one per diagnostic.
|
|
21
|
+
*
|
|
22
|
+
* Severity mapping: `error` to `::error`, `warn` to `::warning`.
|
|
23
|
+
* Drift staleness or established-export blocking is emitted as a single
|
|
24
|
+
* `::error` annotation when detected.
|
|
25
|
+
*/
|
|
26
|
+
export const formatGitHubAnnotations = (report: WardenReport): string => {
|
|
27
|
+
const lines: string[] = [];
|
|
28
|
+
|
|
29
|
+
for (const d of report.diagnostics) {
|
|
30
|
+
const level = ghLevel[d.severity];
|
|
31
|
+
lines.push(
|
|
32
|
+
`::${level} file=${d.filePath},line=${String(d.line)}::${d.rule}: ${d.message}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (report.drift?.blockedReason !== undefined) {
|
|
37
|
+
lines.push(`::error::drift: ${report.drift.blockedReason}`);
|
|
38
|
+
} else if (report.drift?.stale) {
|
|
39
|
+
lines.push(`::error::drift: ${staleDriftMessage(report.drift)}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return lines.join('\n');
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Produce a structured JSON string from the report.
|
|
47
|
+
*
|
|
48
|
+
* Includes a `summary` object with error, warning, and suggestion counts
|
|
49
|
+
* for easy consumption by downstream tooling.
|
|
50
|
+
*/
|
|
51
|
+
export const formatJson = (report: WardenReport): string => {
|
|
52
|
+
const summary = {
|
|
53
|
+
errors: report.errorCount,
|
|
54
|
+
suggestions: 0,
|
|
55
|
+
warnings: report.warnCount,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return JSON.stringify(
|
|
59
|
+
{
|
|
60
|
+
diagnostics: report.diagnostics,
|
|
61
|
+
drift: report.drift,
|
|
62
|
+
fixes: report.fixes,
|
|
63
|
+
passed: report.passed,
|
|
64
|
+
summary,
|
|
65
|
+
},
|
|
66
|
+
null,
|
|
67
|
+
2
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const formatGuidanceLink = (link: WardenGuidanceLink): string => {
|
|
72
|
+
if (link.path !== undefined) {
|
|
73
|
+
return `[${link.label}](${link.path})`;
|
|
74
|
+
}
|
|
75
|
+
if (link.url !== undefined) {
|
|
76
|
+
return `[${link.label}](${link.url})`;
|
|
77
|
+
}
|
|
78
|
+
return link.label;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/** Format diagnostic guidance as indented markdown lines. */
|
|
82
|
+
const diagnosticGuidanceLines = (
|
|
83
|
+
d: WardenReport['diagnostics'][number]
|
|
84
|
+
): readonly string[] => {
|
|
85
|
+
const { guidance } = d;
|
|
86
|
+
if (guidance === undefined) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const lines = [` - Next: ${guidance.summary}`];
|
|
91
|
+
if (guidance.steps !== undefined && guidance.steps.length > 0) {
|
|
92
|
+
lines.push(
|
|
93
|
+
` - Steps: ${guidance.steps
|
|
94
|
+
.map((step, index) => `${String(index + 1)}. ${step}`)
|
|
95
|
+
.join(' ')}`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
if (guidance.docs !== undefined && guidance.docs.length > 0) {
|
|
99
|
+
lines.push(` - Docs: ${guidance.docs.map(formatGuidanceLink).join(', ')}`);
|
|
100
|
+
}
|
|
101
|
+
if (guidance.commands !== undefined && guidance.commands.length > 0) {
|
|
102
|
+
lines.push(
|
|
103
|
+
` - Commands: ${guidance.commands.map((cmd) => `\`${cmd}\``).join(', ')}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
if (guidance.relatedRules !== undefined && guidance.relatedRules.length > 0) {
|
|
107
|
+
lines.push(
|
|
108
|
+
` - Related: ${guidance.relatedRules.map((rule) => `\`${rule}\``).join(', ')}`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
return lines;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/** Format a diagnostic as markdown lines. */
|
|
115
|
+
const diagnosticLines = (
|
|
116
|
+
d: WardenReport['diagnostics'][number]
|
|
117
|
+
): readonly string[] => [
|
|
118
|
+
`- \`${d.filePath}:${String(d.line)}\` — ${d.rule}: ${d.message}`,
|
|
119
|
+
...diagnosticGuidanceLines(d),
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
/** Render a severity group as a headed markdown section, or empty array. */
|
|
123
|
+
const severitySection = (
|
|
124
|
+
heading: string,
|
|
125
|
+
diagnostics: WardenReport['diagnostics']
|
|
126
|
+
): readonly string[] => {
|
|
127
|
+
if (diagnostics.length === 0) {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
return ['', `### ${heading}`, ...diagnostics.flatMap(diagnosticLines)];
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/** Render a drift section if stale, otherwise empty array. */
|
|
134
|
+
const driftSection = (drift: WardenReport['drift']): readonly string[] => {
|
|
135
|
+
if (drift?.blockedReason !== undefined) {
|
|
136
|
+
return [
|
|
137
|
+
'',
|
|
138
|
+
'### Drift',
|
|
139
|
+
`- established exports are blocked: ${drift.blockedReason}`,
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!drift?.stale) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
return ['', '### Drift', `- ${staleDriftMessage(drift)}`];
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/** Render safe-fix counts when a fix pass was requested. */
|
|
150
|
+
const fixSummaryLine = (fixes: WardenReport['fixes']): readonly string[] => {
|
|
151
|
+
if (fixes === undefined) {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
return [
|
|
155
|
+
`**Fixes:** ${String(fixes.applied)} applied, ${String(fixes.filesChanged)} files changed, ${String(fixes.skipped)} skipped`,
|
|
156
|
+
];
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Produce a concise markdown summary suitable for a GitHub job summary or PR comment.
|
|
161
|
+
*
|
|
162
|
+
* Groups diagnostics by severity and includes drift status when relevant.
|
|
163
|
+
*/
|
|
164
|
+
export const formatSummary = (report: WardenReport): string => {
|
|
165
|
+
const result = report.passed ? 'PASS' : 'FAIL';
|
|
166
|
+
const errors = report.diagnostics.filter((d) => d.severity === 'error');
|
|
167
|
+
const warnings = report.diagnostics.filter((d) => d.severity === 'warn');
|
|
168
|
+
|
|
169
|
+
return [
|
|
170
|
+
'## Warden Report',
|
|
171
|
+
'',
|
|
172
|
+
`**Result: ${result}** | ${String(report.errorCount)} errors, ${String(report.warnCount)} warnings`,
|
|
173
|
+
...fixSummaryLine(report.fixes),
|
|
174
|
+
...severitySection('Errors', errors),
|
|
175
|
+
...severitySection('Warnings', warnings),
|
|
176
|
+
...driftSection(report.drift),
|
|
177
|
+
].join('\n');
|
|
178
|
+
};
|
package/src/guide.ts
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
WardenFixCapability,
|
|
3
|
+
WardenGuidance,
|
|
4
|
+
WardenGuidanceLink,
|
|
5
|
+
WardenRuleConcern,
|
|
6
|
+
WardenRuleLifecycle,
|
|
7
|
+
WardenRuleScope,
|
|
8
|
+
WardenRuleTier,
|
|
9
|
+
WardenSeverity,
|
|
10
|
+
} from './rules/index.js';
|
|
11
|
+
import {
|
|
12
|
+
listWardenRuleMetadata,
|
|
13
|
+
wardenRules,
|
|
14
|
+
wardenTopoRules,
|
|
15
|
+
} from './rules/index.js';
|
|
16
|
+
import type { WardenDepth } from './config.js';
|
|
17
|
+
|
|
18
|
+
export const wardenGuideFormatValues = [
|
|
19
|
+
'markdown',
|
|
20
|
+
'agent-json',
|
|
21
|
+
'manifest',
|
|
22
|
+
] as const;
|
|
23
|
+
|
|
24
|
+
export type WardenGuideFormat = (typeof wardenGuideFormatValues)[number];
|
|
25
|
+
|
|
26
|
+
export interface WardenRuleGuideEntry {
|
|
27
|
+
readonly concern: WardenRuleConcern;
|
|
28
|
+
readonly depth: WardenDepth;
|
|
29
|
+
readonly description: string;
|
|
30
|
+
readonly docs: readonly WardenGuidanceLink[];
|
|
31
|
+
readonly fix?: WardenFixCapability | undefined;
|
|
32
|
+
readonly guidance?: WardenGuidance | undefined;
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly invariant: string;
|
|
35
|
+
readonly lifecycle: WardenRuleLifecycle;
|
|
36
|
+
readonly scope: WardenRuleScope;
|
|
37
|
+
readonly severity: WardenSeverity;
|
|
38
|
+
readonly tier: WardenRuleTier;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface WardenGuideManifest {
|
|
42
|
+
readonly generatedFrom: {
|
|
43
|
+
readonly package: '@ontrails/warden';
|
|
44
|
+
readonly registries: readonly ['wardenRules', 'wardenTopoRules'];
|
|
45
|
+
readonly source: 'builtin-rule-metadata';
|
|
46
|
+
};
|
|
47
|
+
readonly kind: 'trails-warden-guide-manifest';
|
|
48
|
+
readonly ruleCount: number;
|
|
49
|
+
readonly rules: readonly WardenRuleGuideEntry[];
|
|
50
|
+
readonly version: 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface WardenAgentRuleGuide {
|
|
54
|
+
readonly appliesAt: {
|
|
55
|
+
readonly depth: WardenDepth;
|
|
56
|
+
readonly scope: WardenRuleScope;
|
|
57
|
+
readonly tier: WardenRuleTier;
|
|
58
|
+
};
|
|
59
|
+
readonly concern: WardenRuleConcern;
|
|
60
|
+
readonly fix?: WardenFixCapability | undefined;
|
|
61
|
+
readonly guidance?: WardenGuidance | undefined;
|
|
62
|
+
readonly id: string;
|
|
63
|
+
readonly invariant: string;
|
|
64
|
+
readonly severity: WardenSeverity;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface WardenAgentGuide {
|
|
68
|
+
readonly instructions: readonly string[];
|
|
69
|
+
readonly kind: 'trails-warden-agent-guide';
|
|
70
|
+
readonly rules: readonly WardenAgentRuleGuide[];
|
|
71
|
+
readonly version: 1;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const lookupRule = (id: string) =>
|
|
75
|
+
wardenRules.get(id) ?? wardenTopoRules.get(id);
|
|
76
|
+
|
|
77
|
+
const compareRuleEntries = (
|
|
78
|
+
[left]: readonly [string, unknown],
|
|
79
|
+
[right]: readonly [string, unknown]
|
|
80
|
+
): number => left.localeCompare(right);
|
|
81
|
+
|
|
82
|
+
export const buildWardenGuideManifest = (): WardenGuideManifest => {
|
|
83
|
+
const rules = listWardenRuleMetadata()
|
|
84
|
+
.toSorted(compareRuleEntries)
|
|
85
|
+
.map(([id, metadata]) => {
|
|
86
|
+
const rule = lookupRule(id);
|
|
87
|
+
const docs = metadata.guidance?.docs ?? [];
|
|
88
|
+
return {
|
|
89
|
+
concern: metadata.concern,
|
|
90
|
+
depth: metadata.depth,
|
|
91
|
+
description: rule?.description ?? '',
|
|
92
|
+
docs,
|
|
93
|
+
fix: metadata.fix,
|
|
94
|
+
guidance: metadata.guidance,
|
|
95
|
+
id,
|
|
96
|
+
invariant: metadata.invariant,
|
|
97
|
+
lifecycle: metadata.lifecycle,
|
|
98
|
+
scope: metadata.scope,
|
|
99
|
+
severity: rule?.severity ?? 'warn',
|
|
100
|
+
tier: metadata.tier,
|
|
101
|
+
} satisfies WardenRuleGuideEntry;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
generatedFrom: {
|
|
106
|
+
package: '@ontrails/warden',
|
|
107
|
+
registries: ['wardenRules', 'wardenTopoRules'],
|
|
108
|
+
source: 'builtin-rule-metadata',
|
|
109
|
+
},
|
|
110
|
+
kind: 'trails-warden-guide-manifest',
|
|
111
|
+
ruleCount: rules.length,
|
|
112
|
+
rules,
|
|
113
|
+
version: 1,
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const formatGuideLink = (link: WardenGuidanceLink): string => {
|
|
118
|
+
if (link.path) {
|
|
119
|
+
return `[${link.label}](${link.path})`;
|
|
120
|
+
}
|
|
121
|
+
if (link.url) {
|
|
122
|
+
return `[${link.label}](${link.url})`;
|
|
123
|
+
}
|
|
124
|
+
return link.label;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const renderOptionalList = (
|
|
128
|
+
lines: string[],
|
|
129
|
+
label: string,
|
|
130
|
+
items: readonly string[] | undefined
|
|
131
|
+
): void => {
|
|
132
|
+
if (items === undefined || items.length === 0) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
lines.push(`- ${label}:`);
|
|
137
|
+
for (const [index, item] of items.entries()) {
|
|
138
|
+
lines.push(` ${index + 1}. ${item}`);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const renderRuleMarkdown = (rule: WardenRuleGuideEntry): readonly string[] => {
|
|
143
|
+
const lines = [
|
|
144
|
+
`### \`${rule.id}\``,
|
|
145
|
+
'',
|
|
146
|
+
`- Severity: \`${rule.severity}\``,
|
|
147
|
+
`- Concern: \`${rule.concern}\``,
|
|
148
|
+
`- Depth: \`${rule.depth}\``,
|
|
149
|
+
`- Tier: \`${rule.tier}\``,
|
|
150
|
+
`- Scope: \`${rule.scope}\``,
|
|
151
|
+
`- Lifecycle: \`${rule.lifecycle.state}\``,
|
|
152
|
+
`- Invariant: ${rule.invariant}`,
|
|
153
|
+
`- Description: ${rule.description}`,
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
if (rule.lifecycle.retireWhen) {
|
|
157
|
+
lines.push(`- Retire when: ${rule.lifecycle.retireWhen}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (rule.fix) {
|
|
161
|
+
lines.push(
|
|
162
|
+
`- Fix: \`${rule.fix.class}\` (${rule.fix.safety === 'safe' ? 'safe, applied by `warden --fix`' : 'review-required'})`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (rule.guidance) {
|
|
167
|
+
lines.push('', `Guidance: ${rule.guidance.summary}`);
|
|
168
|
+
renderOptionalList(lines, 'Steps', rule.guidance.steps);
|
|
169
|
+
renderOptionalList(lines, 'Commands', rule.guidance.commands);
|
|
170
|
+
if (rule.docs.length > 0) {
|
|
171
|
+
lines.push(`- Docs: ${rule.docs.map(formatGuideLink).join(', ')}`);
|
|
172
|
+
}
|
|
173
|
+
if (rule.guidance.relatedRules && rule.guidance.relatedRules.length > 0) {
|
|
174
|
+
lines.push(
|
|
175
|
+
`- Related rules: ${rule.guidance.relatedRules.map((id) => `\`${id}\``).join(', ')}`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return lines;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export const formatWardenGuideMarkdown = (
|
|
184
|
+
manifest: WardenGuideManifest
|
|
185
|
+
): string => {
|
|
186
|
+
const lines = [
|
|
187
|
+
'# Trails Warden Guide',
|
|
188
|
+
'',
|
|
189
|
+
'Generated from `@ontrails/warden` built-in rule metadata and live rule registries.',
|
|
190
|
+
'',
|
|
191
|
+
'## Summary',
|
|
192
|
+
'',
|
|
193
|
+
`- Rules: ${manifest.ruleCount}`,
|
|
194
|
+
`- Guided rules: ${manifest.rules.filter((rule) => rule.guidance).length}`,
|
|
195
|
+
'',
|
|
196
|
+
'## Rules',
|
|
197
|
+
'',
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
for (const rule of manifest.rules) {
|
|
201
|
+
lines.push(...renderRuleMarkdown(rule), '');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return lines.join('\n').trimEnd();
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
export const buildWardenAgentGuide = (
|
|
208
|
+
manifest: WardenGuideManifest
|
|
209
|
+
): WardenAgentGuide => ({
|
|
210
|
+
instructions: [
|
|
211
|
+
'Treat Warden rules as enforceable Trails doctrine when working in this repository.',
|
|
212
|
+
'Prefer the rule guidance summary and ordered steps over diagnostic prose when deciding how to remediate a finding.',
|
|
213
|
+
'When guidance is absent, use the invariant, concern, tier, and scope as classification metadata rather than inventing a rule-specific fix.',
|
|
214
|
+
],
|
|
215
|
+
kind: 'trails-warden-agent-guide',
|
|
216
|
+
rules: manifest.rules.map((rule) => ({
|
|
217
|
+
appliesAt: {
|
|
218
|
+
depth: rule.depth,
|
|
219
|
+
scope: rule.scope,
|
|
220
|
+
tier: rule.tier,
|
|
221
|
+
},
|
|
222
|
+
concern: rule.concern,
|
|
223
|
+
fix: rule.fix,
|
|
224
|
+
guidance: rule.guidance,
|
|
225
|
+
id: rule.id,
|
|
226
|
+
invariant: rule.invariant,
|
|
227
|
+
severity: rule.severity,
|
|
228
|
+
})),
|
|
229
|
+
version: 1,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
export const formatWardenGuide = (
|
|
233
|
+
manifest: WardenGuideManifest,
|
|
234
|
+
format: WardenGuideFormat
|
|
235
|
+
): string => {
|
|
236
|
+
if (format === 'markdown') {
|
|
237
|
+
return formatWardenGuideMarkdown(manifest);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (format === 'agent-json') {
|
|
241
|
+
return JSON.stringify(buildWardenAgentGuide(manifest), null, 2);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return JSON.stringify(manifest, null, 2);
|
|
245
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Warden - Governance package for Trails.
|
|
3
|
+
*
|
|
4
|
+
* Provides lint rules, drift detection, and a CLI runner to enforce
|
|
5
|
+
* contract-first discipline at development time.
|
|
6
|
+
*
|
|
7
|
+
* Package: `@ontrails/warden`
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Rule types
|
|
11
|
+
export type {
|
|
12
|
+
BuiltinWardenRuleName,
|
|
13
|
+
GovernedVocabularyHistoryEvidence,
|
|
14
|
+
GovernedVocabularyHistoryFormJudgment,
|
|
15
|
+
GovernedVocabularyHistoryIssue,
|
|
16
|
+
ProjectAwareWardenRule,
|
|
17
|
+
ProjectContext,
|
|
18
|
+
TopoAwareWardenRule,
|
|
19
|
+
WardenDiagnostic,
|
|
20
|
+
WardenExportedSymbolDefinition,
|
|
21
|
+
WardenFix,
|
|
22
|
+
WardenFixCapability,
|
|
23
|
+
WardenFixClass,
|
|
24
|
+
WardenFixEdit,
|
|
25
|
+
WardenFixScanTargets,
|
|
26
|
+
WardenFixSafety,
|
|
27
|
+
WardenGuidance,
|
|
28
|
+
WardenGuidanceLink,
|
|
29
|
+
WardenRule,
|
|
30
|
+
WardenRuleConcern,
|
|
31
|
+
WardenRuleLifecycle,
|
|
32
|
+
WardenRuleLifecycleState,
|
|
33
|
+
WardenRuleMetadata,
|
|
34
|
+
WardenRuleScope,
|
|
35
|
+
WardenRuleTier,
|
|
36
|
+
WardenSeverity,
|
|
37
|
+
} from './rules/index.js';
|
|
38
|
+
|
|
39
|
+
// Rule registry
|
|
40
|
+
export {
|
|
41
|
+
builtinWardenRuleMetadata,
|
|
42
|
+
formatGovernedVocabularyTransitionGuide,
|
|
43
|
+
getGovernedVocabularyTransition,
|
|
44
|
+
governedVocabularyLiteralRenameSchema,
|
|
45
|
+
governedVocabularyFileRenameSchema,
|
|
46
|
+
governedVocabularyHistoryProvenanceSchema,
|
|
47
|
+
governedVocabularyPreserveRuleSchema,
|
|
48
|
+
governedVocabularyProvenancePolicySchema,
|
|
49
|
+
governedVocabularyRegistrySchema,
|
|
50
|
+
governedVocabularyScopeSchema,
|
|
51
|
+
governedVocabularySymbolRenameSchema,
|
|
52
|
+
governedVocabularyTargetSchema,
|
|
53
|
+
governedVocabularyTransitionSchema,
|
|
54
|
+
governedVocabularyTransitionStatuses,
|
|
55
|
+
governedVocabularyTransitions,
|
|
56
|
+
getWardenRuleMetadata,
|
|
57
|
+
listWardenRuleMetadata,
|
|
58
|
+
listGovernedVocabularyTransitions,
|
|
59
|
+
requireGovernedVocabularyTransition,
|
|
60
|
+
wardenFixClasses,
|
|
61
|
+
wardenFixSafeties,
|
|
62
|
+
wardenRuleConcerns,
|
|
63
|
+
wardenRuleLifecycleStates,
|
|
64
|
+
wardenRuleScopes,
|
|
65
|
+
wardenRuleTiers,
|
|
66
|
+
wardenRules,
|
|
67
|
+
wardenTopoRules,
|
|
68
|
+
} from './rules/index.js';
|
|
69
|
+
export type {
|
|
70
|
+
GovernedVocabularyLiteralRename,
|
|
71
|
+
GovernedVocabularyHistoryProvenance,
|
|
72
|
+
GovernedVocabularyPreserveRule,
|
|
73
|
+
GovernedVocabularyProvenancePolicy,
|
|
74
|
+
GovernedVocabularyScope,
|
|
75
|
+
GovernedVocabularySymbolRename,
|
|
76
|
+
GovernedVocabularyTarget,
|
|
77
|
+
GovernedVocabularyTransition,
|
|
78
|
+
GovernedVocabularyTransitionInput,
|
|
79
|
+
} from './rules/index.js';
|
|
80
|
+
|
|
81
|
+
// Rule-scoped cache controls for long-lived consumers (watch mode, LSPs).
|
|
82
|
+
export { clearImplementationReturnsResultCache } from './rules/implementation-returns-result.js';
|
|
83
|
+
export {
|
|
84
|
+
isWardenDevPermitTestScanTarget,
|
|
85
|
+
isWardenInfrastructureScanTarget,
|
|
86
|
+
isWardenSourceScanTarget,
|
|
87
|
+
isWardenTestScanTarget,
|
|
88
|
+
} from './rules/scan.js';
|
|
89
|
+
|
|
90
|
+
// CLI runner
|
|
91
|
+
export type {
|
|
92
|
+
WardenOptions,
|
|
93
|
+
WardenReport,
|
|
94
|
+
WardenRunOptions,
|
|
95
|
+
WardenTopoDriftResult,
|
|
96
|
+
WardenTopoTarget,
|
|
97
|
+
} from './cli.js';
|
|
98
|
+
export { formatWardenReport, runWarden } from './cli.js';
|
|
99
|
+
|
|
100
|
+
// Adapter authoring checks
|
|
101
|
+
export {
|
|
102
|
+
adapterCheckRuleName,
|
|
103
|
+
runWardenAdapterChecks,
|
|
104
|
+
} from './adapter-check.js';
|
|
105
|
+
|
|
106
|
+
// CLI command surface
|
|
107
|
+
export type {
|
|
108
|
+
ParsedWardenCommand,
|
|
109
|
+
RunWardenCommandOptions,
|
|
110
|
+
WardenExpectedAppBinding,
|
|
111
|
+
WardenCommandResult,
|
|
112
|
+
} from './command.js';
|
|
113
|
+
export {
|
|
114
|
+
formatWardenCommandOutput,
|
|
115
|
+
loadWardenConfig,
|
|
116
|
+
parseWardenCommandArgs,
|
|
117
|
+
resolveWardenTopoTargets,
|
|
118
|
+
runWardenCommand,
|
|
119
|
+
} from './command.js';
|
|
120
|
+
|
|
121
|
+
// Config schema
|
|
122
|
+
export {
|
|
123
|
+
wardenConfigSchema,
|
|
124
|
+
wardenDepthValues,
|
|
125
|
+
wardenDraftsValues,
|
|
126
|
+
wardenFailOnValues,
|
|
127
|
+
wardenFormatValues,
|
|
128
|
+
wardenLockValues,
|
|
129
|
+
} from './config.js';
|
|
130
|
+
export type {
|
|
131
|
+
WardenConfig,
|
|
132
|
+
WardenConfigInput,
|
|
133
|
+
WardenDepth,
|
|
134
|
+
WardenDraftsMode,
|
|
135
|
+
WardenFailOn,
|
|
136
|
+
WardenFormat,
|
|
137
|
+
WardenScope,
|
|
138
|
+
WardenLockMode,
|
|
139
|
+
EffectiveWardenConfig,
|
|
140
|
+
ResolveWardenConfigOptions,
|
|
141
|
+
WardenConfigLayer,
|
|
142
|
+
WardenConfigResolution,
|
|
143
|
+
} from './config.js';
|
|
144
|
+
export { resolveWardenConfig } from './config.js';
|
|
145
|
+
|
|
146
|
+
// CI formatters
|
|
147
|
+
export {
|
|
148
|
+
formatGitHubAnnotations,
|
|
149
|
+
formatJson,
|
|
150
|
+
formatSummary,
|
|
151
|
+
} from './formatters.js';
|
|
152
|
+
|
|
153
|
+
// Drift detection
|
|
154
|
+
export type { CheckDriftOptions, DriftResult } from './drift.js';
|
|
155
|
+
export { checkDrift } from './drift.js';
|
|
156
|
+
|
|
157
|
+
// Project-local rule loading
|
|
158
|
+
export type { ProjectWardenRules } from './project-rules.js';
|
|
159
|
+
export { loadProjectWardenRules } from './project-rules.js';
|
|
160
|
+
|
|
161
|
+
// Guide rendering
|
|
162
|
+
export type {
|
|
163
|
+
WardenGuideFormat,
|
|
164
|
+
WardenGuideManifest,
|
|
165
|
+
WardenRuleGuideEntry,
|
|
166
|
+
} from './guide.js';
|
|
167
|
+
export {
|
|
168
|
+
buildWardenAgentGuide,
|
|
169
|
+
buildWardenGuideManifest,
|
|
170
|
+
formatWardenGuide,
|
|
171
|
+
formatWardenGuideMarkdown,
|
|
172
|
+
wardenGuideFormatValues,
|
|
173
|
+
} from './guide.js';
|
|
174
|
+
|
|
175
|
+
// Resolver helpers
|
|
176
|
+
export {
|
|
177
|
+
collectImportResolutionsForFile,
|
|
178
|
+
collectImportSpecifiers,
|
|
179
|
+
createWardenResolver,
|
|
180
|
+
defaultWardenResolveOptions,
|
|
181
|
+
} from './resolve.js';
|
|
182
|
+
export type {
|
|
183
|
+
WardenImportResolution,
|
|
184
|
+
WardenImportResolutionErrorKind,
|
|
185
|
+
WardenImportSpecifier,
|
|
186
|
+
WardenProjectResolver,
|
|
187
|
+
WardenResolverOptions,
|
|
188
|
+
} from './resolve.js';
|
|
189
|
+
|
|
190
|
+
// Draft helpers
|
|
191
|
+
export {
|
|
192
|
+
DRAFT_FILE_PREFIX,
|
|
193
|
+
DRAFT_FILE_SEGMENT,
|
|
194
|
+
isDraftMarkedFile,
|
|
195
|
+
stripDraftFileMarkers,
|
|
196
|
+
} from './draft.js';
|
|
197
|
+
|
|
198
|
+
// Trail layer
|
|
199
|
+
export { wardenTopo } from './trails/topo.js';
|
|
200
|
+
export {
|
|
201
|
+
runProjectWardenRules,
|
|
202
|
+
runTopoAwareWardenTrails,
|
|
203
|
+
runWardenTrails,
|
|
204
|
+
} from './trails/run.js';
|
|
205
|
+
export {
|
|
206
|
+
activationOrphanTrail,
|
|
207
|
+
capturedKernelTrail,
|
|
208
|
+
cliCommandRouteCoherenceTrail,
|
|
209
|
+
circularRefsTrail,
|
|
210
|
+
entityExistsTrail,
|
|
211
|
+
contextNoSurfaceTypesTrail,
|
|
212
|
+
composesDeclarationsTrail,
|
|
213
|
+
deadInternalTrailTrail,
|
|
214
|
+
deadPublicTrailTrail,
|
|
215
|
+
deprecationWithoutGuidanceTrail,
|
|
216
|
+
diagnosticSchema,
|
|
217
|
+
duplicateExportedSymbolTrail,
|
|
218
|
+
duplicatePublicContractTrail,
|
|
219
|
+
draftFileMarkingTrail,
|
|
220
|
+
draftVisibleDebtTrail,
|
|
221
|
+
errorMappingCompletenessTrail,
|
|
222
|
+
exampleValidTrail,
|
|
223
|
+
firesDeclarationsTrail,
|
|
224
|
+
forkWithoutPreservedImplementationTrail,
|
|
225
|
+
governedSymbolResidueTrail,
|
|
226
|
+
governedVocabularyPermutationWatchTrail,
|
|
227
|
+
implementationReturnsResultTrail,
|
|
228
|
+
incompleteAccessorForStandardOpTrail,
|
|
229
|
+
incompleteCrudTrail,
|
|
230
|
+
intentPropagationTrail,
|
|
231
|
+
layerFieldNameDriftTrail,
|
|
232
|
+
libraryRenderCoherenceTrail,
|
|
233
|
+
markerSchemaUnsupportedTrail,
|
|
234
|
+
missingVisibilityTrail,
|
|
235
|
+
missingReconcileTrail,
|
|
236
|
+
noDevPermitInSourceTrail,
|
|
237
|
+
noDestructuredComposeTrail,
|
|
238
|
+
noDirectImplementationCallTrail,
|
|
239
|
+
noLegacyCliAliasExportTrail,
|
|
240
|
+
noLegacyLayerImportsTrail,
|
|
241
|
+
noNativeErrorResultTrail,
|
|
242
|
+
noRedundantResultErrorWrapTrail,
|
|
243
|
+
noRetiredCrossVocabularyTrail,
|
|
244
|
+
noSyncResultAssumptionTrail,
|
|
245
|
+
noThrowInDetourRecoverTrail,
|
|
246
|
+
noThrowInImplementationTrail,
|
|
247
|
+
noTopLevelSurfaceTrail,
|
|
248
|
+
onReferencesExistTrail,
|
|
249
|
+
orphanedSignalTrail,
|
|
250
|
+
ownerRenderParityTrail,
|
|
251
|
+
pendingForceTrail,
|
|
252
|
+
permitGovernanceTrail,
|
|
253
|
+
preferSchemaInferenceTrail,
|
|
254
|
+
projectAwareRuleInput,
|
|
255
|
+
publicExportExampleCoverageTrail,
|
|
256
|
+
publicInternalDeepImportsTrail,
|
|
257
|
+
publicOutputSchemaTrail,
|
|
258
|
+
publicUnionOutputDiscriminantsTrail,
|
|
259
|
+
readIntentFiresTrail,
|
|
260
|
+
referenceExistsTrail,
|
|
261
|
+
resolvedImportBoundaryTrail,
|
|
262
|
+
ruleInput,
|
|
263
|
+
ruleOutput,
|
|
264
|
+
resourceDeclarationsTrail,
|
|
265
|
+
resourceIdGrammarTrail,
|
|
266
|
+
resourceExistsTrail,
|
|
267
|
+
resourceMockCoverageTrail,
|
|
268
|
+
scheduledDestroyIntentTrail,
|
|
269
|
+
signalGraphCoachingTrail,
|
|
270
|
+
staticResourceAccessorPreferenceTrail,
|
|
271
|
+
surfaceOverlayCoherenceTrail,
|
|
272
|
+
surfaceTrailheadCoherenceTrail,
|
|
273
|
+
trailForkCoachingTrail,
|
|
274
|
+
trailheadOverrideDivergenceTrail,
|
|
275
|
+
unmaterializedActivationSourceTrail,
|
|
276
|
+
topoAwareRuleInput,
|
|
277
|
+
unreachableDetourShadowingTrail,
|
|
278
|
+
validDetourContractTrail,
|
|
279
|
+
validDescribeRefsTrail,
|
|
280
|
+
versionGapTrail,
|
|
281
|
+
versionPinnedComposeTrail,
|
|
282
|
+
versionWithoutExamplesTrail,
|
|
283
|
+
wardenExportSymmetryTrail,
|
|
284
|
+
wardenRulesUseAstTrail,
|
|
285
|
+
webhookRouteCollisionTrail,
|
|
286
|
+
workspaceLockOwnershipTrail,
|
|
287
|
+
wrapRule,
|
|
288
|
+
wrapTopoRule,
|
|
289
|
+
} from './trails/index.js';
|
|
290
|
+
export type {
|
|
291
|
+
ProjectAwareRuleInput,
|
|
292
|
+
RuleInput,
|
|
293
|
+
RuleOutput,
|
|
294
|
+
TopoAwareRuleInput,
|
|
295
|
+
} from './trails/index.js';
|