@yansirplus/cli 0.5.17

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.
Files changed (47) hide show
  1. package/PUBLIC_API.md +22 -0
  2. package/README.md +34 -0
  3. package/dist/build/agent-authoring/config.d.ts +177 -0
  4. package/dist/build/agent-authoring/config.js +607 -0
  5. package/dist/build/agent-authoring/manifest-compiler.d.ts +159 -0
  6. package/dist/build/agent-authoring/manifest-compiler.js +737 -0
  7. package/dist/build/agent-authoring/shared.d.ts +10 -0
  8. package/dist/build/agent-authoring/shared.js +57 -0
  9. package/dist/build/agent-authoring/static-target.d.ts +59 -0
  10. package/dist/build/agent-authoring/static-target.js +1857 -0
  11. package/dist/build/agent-authoring.d.ts +9 -0
  12. package/dist/build/agent-authoring.js +5 -0
  13. package/dist/build/build-cli.d.ts +2 -0
  14. package/dist/build/build-cli.js +264 -0
  15. package/dist/check/algorithmic/architecture-checks.mjs +971 -0
  16. package/dist/check/algorithmic/client-boundary-checks.mjs +337 -0
  17. package/dist/check/algorithmic/convergence-smoke-checks.mjs +608 -0
  18. package/dist/check/algorithmic/distribution-checks.mjs +919 -0
  19. package/dist/check/algorithmic/owner-checks.mjs +647 -0
  20. package/dist/check/algorithmic/package-boundary-checks.mjs +985 -0
  21. package/dist/check/algorithmic/projection-boundary-checks.mjs +302 -0
  22. package/dist/check/algorithmic/repo-surface-checks.mjs +267 -0
  23. package/dist/check/algorithmic/runtime-structural-checks.mjs +264 -0
  24. package/dist/check/algorithmic/source-alias-checks.mjs +106 -0
  25. package/dist/check/algorithmic/static-target-checks.mjs +447 -0
  26. package/dist/check/algorithmic-checks.mjs +482 -0
  27. package/dist/check/check-coverage.mjs +231 -0
  28. package/dist/check/command-runner.mjs +22 -0
  29. package/dist/check/default-gate.mjs +51 -0
  30. package/dist/check/gate-selector.mjs +305 -0
  31. package/dist/check/manifest-rules.mjs +223 -0
  32. package/dist/check/package-graph.mjs +464 -0
  33. package/dist/generate/generate-agent-docs.mjs +435 -0
  34. package/dist/generate/generate-carrier-reference.mjs +514 -0
  35. package/dist/generate/generate-docs.mjs +345 -0
  36. package/dist/generate/generate-effect-skill-manifests.mjs +193 -0
  37. package/dist/generate/project-docs-site.mjs +190 -0
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.js +25 -0
  40. package/dist/lib/agent-docs-model.mjs +888 -0
  41. package/dist/lib/boundary-rules.mjs +63 -0
  42. package/dist/lib/capability-routes.mjs +354 -0
  43. package/dist/lib/projection-sink.mjs +113 -0
  44. package/dist/lib/public-api-model.mjs +306 -0
  45. package/dist/main.mjs +233 -0
  46. package/dist/runner.mjs +127 -0
  47. package/package.json +32 -0
@@ -0,0 +1,302 @@
1
+ export const createProjectionBoundaryChecks = ({
2
+ fs,
3
+ path,
4
+ ts,
5
+ repoRoot,
6
+ read,
7
+ readJson,
8
+ walk,
9
+ isRecord,
10
+ failIfAny,
11
+ graphWorkspacePackageRecords,
12
+ sourceModuleGraph,
13
+ workspacePackageRecords,
14
+ }) => {
15
+ const projectionFoldBoundaryPath = "architecture/projection-fold-boundary.json";
16
+
17
+ const hasExportModifier = (node) =>
18
+ ts.canHaveModifiers(node) &&
19
+ (ts.getModifiers(node) ?? []).some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
20
+
21
+ const addBindingNames = (name, names) => {
22
+ if (ts.isIdentifier(name)) {
23
+ names.add(name.text);
24
+ return;
25
+ }
26
+ if (ts.isObjectBindingPattern(name) || ts.isArrayBindingPattern(name)) {
27
+ for (const element of name.elements) {
28
+ if (ts.isBindingElement(element)) addBindingNames(element.name, names);
29
+ }
30
+ }
31
+ };
32
+
33
+ const exportedSymbolNamesForFile = (file) => {
34
+ const names = new Set();
35
+ const sourceFile = ts.createSourceFile(
36
+ file,
37
+ read(file),
38
+ ts.ScriptTarget.Latest,
39
+ true,
40
+ file.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
41
+ );
42
+ const visit = (node) => {
43
+ if (hasExportModifier(node)) {
44
+ if (
45
+ (ts.isFunctionDeclaration(node) ||
46
+ ts.isClassDeclaration(node) ||
47
+ ts.isInterfaceDeclaration(node) ||
48
+ ts.isTypeAliasDeclaration(node) ||
49
+ ts.isEnumDeclaration(node)) &&
50
+ node.name !== undefined
51
+ ) {
52
+ names.add(node.name.text);
53
+ }
54
+ if (ts.isVariableStatement(node)) {
55
+ for (const declaration of node.declarationList.declarations) {
56
+ addBindingNames(declaration.name, names);
57
+ }
58
+ }
59
+ }
60
+ if (
61
+ ts.isExportDeclaration(node) &&
62
+ node.exportClause !== undefined &&
63
+ ts.isNamedExports(node.exportClause)
64
+ ) {
65
+ for (const element of node.exportClause.elements) names.add(element.name.text);
66
+ }
67
+ ts.forEachChild(node, visit);
68
+ };
69
+ visit(sourceFile);
70
+ return names;
71
+ };
72
+
73
+ const exportedSymbolNamesUnder = (relativePath) => {
74
+ const names = new Set();
75
+ for (const file of walk(relativePath).filter((entry) => /\.(?:ts|tsx|mts|cts)$/u.test(entry))) {
76
+ for (const name of exportedSymbolNamesForFile(file)) names.add(name);
77
+ }
78
+ return names;
79
+ };
80
+
81
+ const projectionFoldBoundaryFailures = () => {
82
+ const failures = [];
83
+ if (!fs.existsSync(path.join(repoRoot, projectionFoldBoundaryPath))) {
84
+ failures.push(`${projectionFoldBoundaryPath}: missing projection fold audit decision`);
85
+ return failures;
86
+ }
87
+
88
+ const registry = readJson(projectionFoldBoundaryPath);
89
+ if (registry.schemaVersion !== 1) {
90
+ failures.push(`${projectionFoldBoundaryPath}: schemaVersion must be 1`);
91
+ }
92
+ if (registry.decision !== "keep-consumer-boundary") {
93
+ failures.push(
94
+ `${projectionFoldBoundaryPath}: decision must stay keep-consumer-boundary until a shared core fold source exists`,
95
+ );
96
+ }
97
+ if (registry.sharedFold !== false) {
98
+ failures.push(`${projectionFoldBoundaryPath}: sharedFold must be false for this decision`);
99
+ }
100
+ for (const key of ["blockingContractReason", "corePromotionCondition"]) {
101
+ if (typeof registry[key] !== "string" || registry[key].trim().length === 0) {
102
+ failures.push(`${projectionFoldBoundaryPath}: ${key} must record the audit contract`);
103
+ }
104
+ }
105
+
106
+ const packagePathByName = new Map(
107
+ workspacePackageRecords().map((record) => [record.name, record.path]),
108
+ );
109
+ const records = graphWorkspacePackageRecords(repoRoot).filter(
110
+ (record) => typeof record.name === "string" && record.name.startsWith("@agent-os/"),
111
+ );
112
+ const graph = sourceModuleGraph(repoRoot, records);
113
+ const ownedFolds = Array.isArray(registry.ownedFolds) ? registry.ownedFolds : [];
114
+ if (ownedFolds.length === 0) {
115
+ failures.push(`${projectionFoldBoundaryPath}: ownedFolds must be non-empty`);
116
+ }
117
+ for (const [index, fold] of ownedFolds.entries()) {
118
+ const label =
119
+ isRecord(fold) && typeof fold.id === "string" ? fold.id : `ownedFolds[${index}]`;
120
+ if (!isRecord(fold)) {
121
+ failures.push(`${projectionFoldBoundaryPath}:${label}: fold entry must be an object`);
122
+ continue;
123
+ }
124
+ const packageName = fold.packageName;
125
+ const file = fold.file;
126
+ const exports = Array.isArray(fold.exports) ? fold.exports : [];
127
+ if (typeof packageName !== "string" || !packageName.startsWith("@agent-os/")) {
128
+ failures.push(`${projectionFoldBoundaryPath}:${label}: packageName must be @agent-os/*`);
129
+ continue;
130
+ }
131
+ const packagePath = packagePathByName.get(packageName);
132
+ if (typeof packagePath !== "string") {
133
+ failures.push(`${projectionFoldBoundaryPath}:${label}: package ${packageName} is missing`);
134
+ continue;
135
+ }
136
+ if (typeof file !== "string" || !file.startsWith(`${packagePath}/`)) {
137
+ failures.push(
138
+ `${projectionFoldBoundaryPath}:${label}: file must live under package ${packageName}`,
139
+ );
140
+ continue;
141
+ }
142
+ if (!fs.existsSync(path.join(repoRoot, file))) {
143
+ failures.push(`${projectionFoldBoundaryPath}:${label}: file is missing: ${file}`);
144
+ continue;
145
+ }
146
+ if (exports.length === 0 || exports.some((name) => typeof name !== "string")) {
147
+ failures.push(`${projectionFoldBoundaryPath}:${label}: exports must be non-empty strings`);
148
+ continue;
149
+ }
150
+ const actualExports = exportedSymbolNamesForFile(file);
151
+ for (const name of exports) {
152
+ if (!actualExports.has(name)) {
153
+ failures.push(`${file}: projection-fold-boundary:${label}: missing export ${name}`);
154
+ }
155
+ }
156
+ if (
157
+ !Array.isArray(fold.consumerRoots) ||
158
+ fold.consumerRoots.length === 0 ||
159
+ fold.consumerRoots.some((root) => typeof root !== "string")
160
+ ) {
161
+ failures.push(
162
+ `${projectionFoldBoundaryPath}:${label}: consumerRoots must name the true consumer set`,
163
+ );
164
+ }
165
+ if (
166
+ !Array.isArray(fold.allowedConsumers) ||
167
+ fold.allowedConsumers.some((consumer) => typeof consumer !== "string")
168
+ ) {
169
+ failures.push(`${projectionFoldBoundaryPath}:${label}: allowedConsumers must be strings`);
170
+ continue;
171
+ }
172
+ const allowedConsumers = new Set(fold.allowedConsumers);
173
+ for (const consumer of allowedConsumers) {
174
+ if (!fs.existsSync(path.join(repoRoot, consumer))) {
175
+ failures.push(
176
+ `${projectionFoldBoundaryPath}:${label}: consumer file is missing ${String(consumer)}`,
177
+ );
178
+ }
179
+ }
180
+ const incoming = graph.edges.filter((edge) => edge.toFile === file && edge.fromFile !== file);
181
+ const actualConsumers = new Set(incoming.map((edge) => edge.fromFile));
182
+ for (const edge of incoming) {
183
+ if (!allowedConsumers.has(edge.fromFile)) {
184
+ failures.push(
185
+ `${edge.fromFile}:${edge.line}:${edge.column}: projection-fold-boundary:${label}: unexpected consumer of ${file} via ${edge.specifier}`,
186
+ );
187
+ }
188
+ }
189
+ for (const consumer of allowedConsumers) {
190
+ if (!actualConsumers.has(consumer)) {
191
+ failures.push(
192
+ `${projectionFoldBoundaryPath}:${label}: declared consumer ${String(consumer)} does not import ${file}`,
193
+ );
194
+ }
195
+ }
196
+ }
197
+
198
+ const forbiddenCoreExports = Array.isArray(registry.coreForbiddenExports)
199
+ ? registry.coreForbiddenExports
200
+ : [];
201
+ if (
202
+ forbiddenCoreExports.length === 0 ||
203
+ forbiddenCoreExports.some((name) => typeof name !== "string")
204
+ ) {
205
+ failures.push(
206
+ `${projectionFoldBoundaryPath}: coreForbiddenExports must be non-empty strings`,
207
+ );
208
+ } else {
209
+ const coreExports = exportedSymbolNamesUnder("packages/core/src");
210
+ for (const name of forbiddenCoreExports) {
211
+ if (coreExports.has(name)) {
212
+ failures.push(
213
+ `packages/core/src: projection-fold-boundary: core must not export disputed event fold symbol ${name}`,
214
+ );
215
+ }
216
+ }
217
+ }
218
+
219
+ return failures;
220
+ };
221
+
222
+ const checkProjectionFoldBoundary = () => {
223
+ const failures = projectionFoldBoundaryFailures();
224
+ failIfAny("projection fold boundary", failures);
225
+ };
226
+
227
+ const checkLimitRegistry = () => {
228
+ const failures = [];
229
+ if (!fs.existsSync(path.join(repoRoot, "docs/limits.json"))) {
230
+ failures.push("docs/limits.json is missing");
231
+ failIfAny("limit registry", failures);
232
+ return;
233
+ }
234
+ const registry = readJson("docs/limits.json");
235
+ const allowedClasses = new Set(["contract", "policy", "closed"]);
236
+ if (!Number.isInteger(registry.version) || registry.version <= 0) {
237
+ failures.push("docs/limits.json version must be a positive integer");
238
+ }
239
+ if (!Array.isArray(registry.limits) || registry.limits.length === 0) {
240
+ failures.push("docs/limits.json limits must be a non-empty array");
241
+ failIfAny("limit registry", failures);
242
+ return;
243
+ }
244
+ const seen = new Set();
245
+ for (const [index, limit] of registry.limits.entries()) {
246
+ const label = isRecord(limit) && typeof limit.id === "string" ? limit.id : `limits[${index}]`;
247
+ if (!isRecord(limit)) {
248
+ failures.push(`${label}: expected object`);
249
+ continue;
250
+ }
251
+ if (!/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/u.test(limit.id ?? "")) {
252
+ failures.push(`${label}.id: expected stable lowercase dotted identifier`);
253
+ }
254
+ if (seen.has(limit.id)) failures.push(`${label}.id: duplicate ${limit.id}`);
255
+ seen.add(limit.id);
256
+ if (!allowedClasses.has(limit.class))
257
+ failures.push(`${label}.class: expected contract, policy, or closed`);
258
+ for (const field of ["owner", "value", "reason"]) {
259
+ if (typeof limit[field] !== "string" || limit[field].trim().length === 0) {
260
+ failures.push(`${label}.${field}: required`);
261
+ }
262
+ }
263
+ if (!Array.isArray(limit.sourcePaths) || limit.sourcePaths.length === 0) {
264
+ failures.push(`${label}.sourcePaths: non-empty array required`);
265
+ } else {
266
+ for (const sourcePath of limit.sourcePaths) {
267
+ const file = String(sourcePath).split("#", 1)[0];
268
+ if (
269
+ path.isAbsolute(file) ||
270
+ file.includes("..") ||
271
+ !fs.existsSync(path.join(repoRoot, file))
272
+ ) {
273
+ failures.push(`${label}.sourcePaths: ${sourcePath} does not point to a repo file`);
274
+ }
275
+ }
276
+ }
277
+ if (
278
+ limit.class === "policy" &&
279
+ (typeof limit.overrideSurface !== "string" || limit.overrideSurface.trim().length === 0)
280
+ ) {
281
+ failures.push(
282
+ `${label}.overrideSurface: policy limits require an ordinary override surface`,
283
+ );
284
+ }
285
+ if (
286
+ (limit.class === "contract" || limit.class === "closed") &&
287
+ limit.overrideSurface !== undefined
288
+ ) {
289
+ failures.push(
290
+ `${label}.overrideSurface: ${limit.class} limits must not expose ordinary overrides`,
291
+ );
292
+ }
293
+ }
294
+ failIfAny("limit registry", failures);
295
+ };
296
+
297
+ return {
298
+ projectionFoldBoundaryFailures,
299
+ checkProjectionFoldBoundary,
300
+ checkLimitRegistry,
301
+ };
302
+ };
@@ -0,0 +1,267 @@
1
+ export const createRepoSurfaceChecks = ({
2
+ fs,
3
+ path,
4
+ ts,
5
+ repoRoot,
6
+ read,
7
+ readJson,
8
+ walk,
9
+ compare,
10
+ isRecord,
11
+ failIfAny,
12
+ collectAgentDocsModel,
13
+ apiSourceMode,
14
+ exportedNamesForPackage,
15
+ sourceTsdocApiMarkdown,
16
+ sourceTsdocModes,
17
+ sourceTsdocRecordsForPackage,
18
+ validateSourceTsdocRecords,
19
+ }) => {
20
+ const manifestEntries = (file, section) => {
21
+ const source = fs.readFileSync(file, "utf8");
22
+ const start = source.indexOf(`## ${section}`);
23
+ if (start === -1) return [];
24
+ const rest = source.slice(start + section.length + 3);
25
+ const next = rest.search(/^## /mu);
26
+ const body = next === -1 ? rest : rest.slice(0, next);
27
+ return [...body.matchAll(/`([^`:]+):([^`]+)`/gu)].map((match) => ({
28
+ name: match[0].slice(1, -1),
29
+ section,
30
+ line: source.slice(0, start + section.length + 3 + match.index).split("\n").length,
31
+ }));
32
+ };
33
+
34
+ const manifestNames = (file, section) =>
35
+ new Set(manifestEntries(file, section).map((entry) => entry.name));
36
+
37
+ const duplicateManifestEntries = (file, sections) => {
38
+ const entries = sections.flatMap((section) => manifestEntries(file, section));
39
+ const byName = new Map();
40
+ for (const entry of entries) {
41
+ byName.set(entry.name, [...(byName.get(entry.name) ?? []), entry]);
42
+ }
43
+ return [...byName.entries()]
44
+ .filter(([, occurrences]) => occurrences.length > 1)
45
+ .map(([name, occurrences]) => ({ name, occurrences }));
46
+ };
47
+
48
+ const targetPackages = () => {
49
+ const surface = readJson("docs/surface.json");
50
+ return surface.packages.filter((pkg) => {
51
+ const packageJson = path.join(repoRoot, pkg.path, "package.json");
52
+ if (!fs.existsSync(packageJson)) return false;
53
+ const manifest = JSON.parse(fs.readFileSync(packageJson, "utf8"));
54
+ return (
55
+ pkg.apiSource !== undefined || (pkg.published === true && manifest.exports !== undefined)
56
+ );
57
+ });
58
+ };
59
+
60
+ const checkPublicApi = () => {
61
+ const failures = [];
62
+ for (const target of targetPackages()) {
63
+ if (target.apiSource === undefined) {
64
+ failures.push(
65
+ `${target.name}: published package exports require apiSource in docs/surface.json`,
66
+ );
67
+ continue;
68
+ }
69
+ const manifest = path.join(repoRoot, target.apiSource);
70
+ if (!fs.existsSync(manifest)) {
71
+ failures.push(`missing public API intent source for ${target.name}: ${target.apiSource}`);
72
+ continue;
73
+ }
74
+
75
+ const mode = apiSourceMode(target);
76
+ if (sourceTsdocModes.has(mode)) {
77
+ const records = sourceTsdocRecordsForPackage(repoRoot, target);
78
+ failures.push(...validateSourceTsdocRecords(target, records));
79
+ const expected = `${sourceTsdocApiMarkdown(target, records).replace(/\s+$/u, "")}\n`;
80
+ if (fs.readFileSync(manifest, "utf8") !== expected) {
81
+ failures.push(`${target.apiSource} is stale; run bun run docs:generate`);
82
+ }
83
+ } else if (mode !== "manual") {
84
+ failures.push(`${target.name}: unsupported apiSourceMode ${mode}`);
85
+ }
86
+
87
+ const publicSections = ["Public exports", "Experimental exports", "Deprecated exports"];
88
+ for (const duplicate of duplicateManifestEntries(manifest, [
89
+ ...publicSections,
90
+ "Internal-only exports",
91
+ ])) {
92
+ const refs = duplicate.occurrences
93
+ .map((entry) => `${entry.section}:${entry.line}`)
94
+ .join(", ");
95
+ failures.push(
96
+ `${target.name}: ${target.apiSource} declares duplicate API entry ${duplicate.name} at ${refs}`,
97
+ );
98
+ }
99
+
100
+ const declaredPublic = new Set(
101
+ publicSections.flatMap((section) => [...manifestNames(manifest, section)]),
102
+ );
103
+ const internal = manifestNames(manifest, "Internal-only exports");
104
+ const actual = exportedNamesForPackage(repoRoot, target)
105
+ .map((record) => record.key)
106
+ .sort();
107
+
108
+ for (const name of actual) {
109
+ if (!declaredPublic.has(name)) {
110
+ failures.push(
111
+ `${target.name}: exported but not declared in ${target.apiSource}: ${name}`,
112
+ );
113
+ }
114
+ if (internal.has(name)) {
115
+ failures.push(`${target.name}: internal export is still exported: ${name}`);
116
+ }
117
+ }
118
+ for (const name of declaredPublic) {
119
+ if (!actual.includes(String(name))) {
120
+ failures.push(
121
+ `${target.name}: ${target.apiSource} lists missing export: ${String(name)}`,
122
+ );
123
+ }
124
+ }
125
+ }
126
+ failIfAny("public API projection", failures);
127
+ };
128
+
129
+ const checkEventNamespaces = () => {
130
+ const failures = collectAgentDocsModel(repoRoot).namespaceModel.failures;
131
+ failIfAny("event namespace projection", failures);
132
+ };
133
+
134
+ const importSpecifiers = (content) => {
135
+ const sourceFile = ts.createSourceFile(
136
+ "agentos-check.mjs",
137
+ content,
138
+ ts.ScriptTarget.Latest,
139
+ true,
140
+ ts.ScriptKind.JS,
141
+ );
142
+ const specifiers = [];
143
+ const visit = (node) => {
144
+ if (
145
+ (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) &&
146
+ node.moduleSpecifier !== undefined &&
147
+ ts.isStringLiteralLike(node.moduleSpecifier)
148
+ ) {
149
+ specifiers.push(node.moduleSpecifier.text);
150
+ }
151
+ if (
152
+ ts.isCallExpression(node) &&
153
+ node.expression.kind === ts.SyntaxKind.ImportKeyword &&
154
+ node.arguments.length === 1 &&
155
+ ts.isStringLiteralLike(node.arguments[0])
156
+ ) {
157
+ specifiers.push(node.arguments[0].text);
158
+ }
159
+ ts.forEachChild(node, visit);
160
+ };
161
+ visit(sourceFile);
162
+ return specifiers;
163
+ };
164
+
165
+ const ruleConstraints = (ruleId) => {
166
+ const source = readJson("docs/agent/boundary-rules.source.json");
167
+ const rule = source.rules?.find((entry) => isRecord(entry) && entry.id === ruleId);
168
+ if (!isRecord(rule) || !isRecord(rule.constraints)) {
169
+ throw new Error(`docs/agent/boundary-rules.source.json: ${ruleId} missing constraints`);
170
+ }
171
+ return rule.constraints;
172
+ };
173
+
174
+ const checkRepoToolingSurface = () => {
175
+ const constraints = ruleConstraints("repo-tooling-surface");
176
+ const failures = [];
177
+ const expected = [...constraints.rootScripts].sort(compare);
178
+ const actual = Object.keys(readJson("package.json").scripts ?? {}).sort(compare);
179
+ for (const scriptName of expected.filter((name) => !actual.includes(name))) {
180
+ failures.push(`package.json: missing root script ${scriptName}`);
181
+ }
182
+ for (const scriptName of actual.filter((name) => !expected.includes(name))) {
183
+ failures.push(`package.json: unexpected root script ${scriptName}`);
184
+ }
185
+ for (const scriptName of actual) {
186
+ if (
187
+ /^(check|test):/u.test(scriptName) &&
188
+ !constraints.allowedPrefixedRootScripts.includes(scriptName)
189
+ ) {
190
+ failures.push(`package.json: unexpected fine-grained root script ${scriptName}`);
191
+ }
192
+ }
193
+
194
+ for (const file of walk("scripts")) {
195
+ if (!constraints.scriptsDirectoryAllowPrefixes.some((prefix) => file.startsWith(prefix))) {
196
+ failures.push(`scripts/: non-parallel-dev script remains at ${file}`);
197
+ continue;
198
+ }
199
+ if (!constraints.scriptsDirectoryAllowedExtensions.includes(path.extname(file))) {
200
+ failures.push(`scripts/: ${file} must use an allowed script extension`);
201
+ }
202
+ }
203
+
204
+ const packagesRoot = path.join(repoRoot, "packages");
205
+ const cliRoot = path.join(packagesRoot, "cli");
206
+ const cliManifest = readJson("packages/cli/package.json");
207
+ const declaredCliPackageDependencies = new Set(
208
+ ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"].flatMap(
209
+ (section) => Object.keys(cliManifest[section] ?? {}),
210
+ ),
211
+ );
212
+ const packageSpecPrefixesRequiringDeclaredDependency =
213
+ constraints.packageSpecPrefixesRequireDeclaredDependency ??
214
+ constraints.forbiddenPackageSpecPrefixes ??
215
+ [];
216
+ const declaredPackageForSpecifier = (specifier) =>
217
+ [...declaredCliPackageDependencies].find(
218
+ (name) => specifier === name || specifier.startsWith(`${name}/`),
219
+ );
220
+ for (const file of walk("packages/cli/src").filter((entry) =>
221
+ /\.(?:mjs|js|ts|tsx)$/u.test(entry),
222
+ )) {
223
+ const content = read(file);
224
+ for (const specifier of importSpecifiers(content)) {
225
+ if (
226
+ packageSpecPrefixesRequiringDeclaredDependency.some((prefix) =>
227
+ specifier.startsWith(prefix),
228
+ ) &&
229
+ declaredPackageForSpecifier(specifier) === undefined
230
+ ) {
231
+ failures.push(`${file}: CLI package specifier ${specifier} is not a declared dependency`);
232
+ }
233
+ if (specifier.startsWith(".")) {
234
+ const resolved = path.resolve(path.dirname(path.join(repoRoot, file)), specifier);
235
+ const isPackagesSource =
236
+ resolved === packagesRoot || resolved.startsWith(`${packagesRoot}${path.sep}`);
237
+ const isCliSource = resolved === cliRoot || resolved.startsWith(`${cliRoot}${path.sep}`);
238
+ if (isPackagesSource && !isCliSource) {
239
+ failures.push(`${file}: CLI must not import packages source via ${specifier}`);
240
+ }
241
+ }
242
+ }
243
+ }
244
+
245
+ const legacyPattern = new RegExp(constraints.forbiddenLegacyScriptReferencePattern, "u");
246
+ for (const file of constraints.legacyReferenceScanRoots.flatMap((root) => walk(root))) {
247
+ if (!/\.(?:json|jsonc|md|mjs|ts|tsx)$/u.test(file)) continue;
248
+ for (const [index, line] of read(file).split("\n").entries()) {
249
+ if (legacyPattern.test(line)) {
250
+ failures.push(`${file}:${index + 1}: legacy scripts/ check/generate reference remains`);
251
+ }
252
+ }
253
+ }
254
+ failIfAny("repo tooling surface", failures);
255
+ };
256
+
257
+ return {
258
+ manifestEntries,
259
+ manifestNames,
260
+ duplicateManifestEntries,
261
+ importSpecifiers,
262
+ ruleConstraints,
263
+ checkPublicApi,
264
+ checkEventNamespaces,
265
+ checkRepoToolingSurface,
266
+ };
267
+ };