@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,264 @@
1
+ export const createRuntimeStructuralChecks = ({
2
+ fs,
3
+ path,
4
+ ts,
5
+ execFileSync,
6
+ repoRoot,
7
+ read,
8
+ readJson,
9
+ walk,
10
+ isRecord,
11
+ failIfAny,
12
+ packageSourceFiles,
13
+ nodeLabel,
14
+ unwrap,
15
+ callName,
16
+ graphWorkspacePackageRecords,
17
+ graphPackageSourceImportEdges,
18
+ packageManifestDependencyEdges,
19
+ tsconfigReferenceEdges,
20
+ }) => {
21
+ const checkTransactionSync = () => {
22
+ const failures = [];
23
+ const inspectBody = (sourceFile, body, label) => {
24
+ const visit = (node) => {
25
+ if (ts.isAwaitExpression(node))
26
+ failures.push(`${nodeLabel(sourceFile, node)} ${label} must not await`);
27
+ if (
28
+ ts.isNewExpression(node) &&
29
+ ts.isIdentifier(node.expression) &&
30
+ node.expression.text === "Promise"
31
+ ) {
32
+ failures.push(`${nodeLabel(sourceFile, node)} ${label} must not create Promise`);
33
+ }
34
+ if (
35
+ ts.isCallExpression(node) &&
36
+ ts.isPropertyAccessExpression(node.expression) &&
37
+ ts.isIdentifier(node.expression.expression) &&
38
+ node.expression.expression.text === "Promise"
39
+ ) {
40
+ failures.push(
41
+ `${nodeLabel(sourceFile, node)} ${label} must not call Promise static helpers`,
42
+ );
43
+ }
44
+ if (
45
+ ts.isCallExpression(node) &&
46
+ ts.isPropertyAccessExpression(node.expression) &&
47
+ node.expression.name.text === "then"
48
+ ) {
49
+ failures.push(`${nodeLabel(sourceFile, node)} ${label} must not call .then`);
50
+ }
51
+ if (
52
+ ts.isCallExpression(node) &&
53
+ ["setTimeout", "setInterval", "setImmediate", "queueMicrotask"].includes(
54
+ callName(node.expression),
55
+ )
56
+ ) {
57
+ failures.push(`${nodeLabel(sourceFile, node)} ${label} must not schedule async work`);
58
+ }
59
+ ts.forEachChild(node, visit);
60
+ };
61
+ visit(body);
62
+ };
63
+ for (const file of packageSourceFiles()) {
64
+ const sourceFile = ts.createSourceFile(
65
+ path.join(repoRoot, file),
66
+ read(file),
67
+ ts.ScriptTarget.Latest,
68
+ true,
69
+ );
70
+ const visit = (node) => {
71
+ if (ts.isCallExpression(node) && callName(node.expression) === "transactionSync") {
72
+ const builder = unwrap(node.arguments[0]);
73
+ if (!builder || (!ts.isArrowFunction(builder) && !ts.isFunctionExpression(builder))) {
74
+ failures.push(
75
+ `${nodeLabel(sourceFile, node)} transactionSync must use an inline sync builder`,
76
+ );
77
+ } else {
78
+ if (
79
+ builder.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword)
80
+ ) {
81
+ failures.push(
82
+ `${nodeLabel(sourceFile, builder)} transactionSync builder must not be async`,
83
+ );
84
+ }
85
+ inspectBody(sourceFile, builder.body, "transactionSync builder");
86
+ }
87
+ }
88
+ ts.forEachChild(node, visit);
89
+ };
90
+ visit(sourceFile);
91
+ }
92
+ failIfAny("transactionSync sync-only projection", failures);
93
+ };
94
+
95
+ const checkBackendNeutrality = () => {
96
+ const failures = [];
97
+ const rootPackage = readJson("package.json");
98
+ const status = rootPackage.agentos?.backendNeutralityStatus;
99
+ if (!["boundary-prepared", "backend-neutral"].includes(status)) {
100
+ failures.push("package.json must declare agentos.backendNeutralityStatus");
101
+ }
102
+ const profiles = rootPackage.agentos?.backendNeutrality?.productionRuntimeProfiles;
103
+ if (!Array.isArray(profiles)) {
104
+ failures.push(
105
+ "package.json must declare agentos.backendNeutrality.productionRuntimeProfiles",
106
+ );
107
+ }
108
+ if (status === "backend-neutral" && Array.isArray(profiles) && profiles.length < 2) {
109
+ failures.push("backend-neutral status requires at least 2 production runtime profiles");
110
+ }
111
+ for (const [index, profile] of (profiles ?? []).entries()) {
112
+ const label = `package.json:agentos.backendNeutrality.productionRuntimeProfiles[${index}]`;
113
+ if (!isRecord(profile)) {
114
+ failures.push(`${label}: profile must be an object`);
115
+ continue;
116
+ }
117
+ if (typeof profile.id !== "string" || profile.id.length === 0) {
118
+ failures.push(`${label}: id must be a non-empty string`);
119
+ }
120
+ if (profile.sourcePackageName !== "@agent-os/runtime") {
121
+ failures.push(`${label}: sourcePackageName must be @agent-os/runtime`);
122
+ }
123
+ if (
124
+ typeof profile.subpath !== "string" ||
125
+ !profile.subpath.startsWith("@agent-os/runtime/")
126
+ ) {
127
+ failures.push(`${label}: subpath must be an @agent-os/runtime/* subpath`);
128
+ }
129
+ if (typeof profile.contractTest !== "string" || profile.contractTest.length === 0) {
130
+ failures.push(`${label}: contractTest must be a non-empty path`);
131
+ continue;
132
+ }
133
+ if (!fs.existsSync(path.join(repoRoot, profile.contractTest))) {
134
+ failures.push(`${profile.contractTest}: missing backend protocol contract test`);
135
+ }
136
+ }
137
+ failIfAny("backend neutrality", failures);
138
+ };
139
+
140
+ const checkGateTierGovernance = () => {
141
+ const failures = [];
142
+ const gates = readJson("docs/agent/gates.source.json");
143
+ const records = graphWorkspacePackageRecords(repoRoot).filter((record) =>
144
+ record.name?.startsWith("@agent-os/"),
145
+ );
146
+ const proofClassIds = new Set(Object.keys(gates.proofClasses ?? {}));
147
+ for (const required of ["structural", "typecheck", "test", "runtime", "distribution"]) {
148
+ if (!proofClassIds.has(required))
149
+ failures.push(`gates.source.json: missing ${required} proof class`);
150
+ }
151
+ for (const proofClass of gates.expensiveProofClasses ?? []) {
152
+ if (!proofClassIds.has(proofClass))
153
+ failures.push(`gates.source.json: unknown expensive proof ${proofClass}`);
154
+ }
155
+ for (const proofClass of gates.fullAffectedProofClasses ?? []) {
156
+ if (!proofClassIds.has(proofClass))
157
+ failures.push(`gates.source.json: unknown full proof ${proofClass}`);
158
+ }
159
+
160
+ const overrideByPath = new Map(
161
+ (gates.packageOverrides ?? []).map((entry) => [entry.path, entry]),
162
+ );
163
+ for (const override of gates.packageOverrides ?? []) {
164
+ if (!records.some((record) => record.path === override.path)) {
165
+ failures.push(
166
+ `gates.source.json: package override references unknown package ${override.path}`,
167
+ );
168
+ }
169
+ for (const proofClass of [
170
+ ...(override.proofClasses ?? []),
171
+ ...(override.affectedProofClasses ?? []),
172
+ ]) {
173
+ if (!proofClassIds.has(proofClass)) {
174
+ failures.push(`${override.path}: unknown proof class ${proofClass}`);
175
+ }
176
+ }
177
+ }
178
+
179
+ for (const record of records) {
180
+ const manifest = readJson(`${record.path}/package.json`);
181
+ const override = overrideByPath.get(record.path);
182
+ const fastProof = override?.fastProof ?? gates.defaultPackageProof?.fastProof;
183
+ if (fastProof === undefined) failures.push(`${record.path}: missing fast proof ownership`);
184
+ if (manifest.scripts?.test?.includes("--passWithNoTests") && fastProof !== "none") {
185
+ failures.push(
186
+ `${record.path}: --passWithNoTests requires fastProof none in gates.source.json`,
187
+ );
188
+ }
189
+ if (fastProof === "none") {
190
+ if (!manifest.scripts?.test?.includes("--passWithNoTests")) {
191
+ failures.push(
192
+ `${record.path}: fastProof none requires explicit --passWithNoTests script`,
193
+ );
194
+ }
195
+ if (!(override?.affectedProofClasses ?? []).includes("runtime")) {
196
+ failures.push(`${record.path}: fastProof none must route affected changes to runtime`);
197
+ }
198
+ }
199
+ if (
200
+ typeof manifest.scripts?.test === "string" &&
201
+ manifest.scripts.test.startsWith("vp test run") &&
202
+ !manifest.scripts.test.includes("*.runtime.test.ts")
203
+ ) {
204
+ failures.push(`${record.path}: fast test script must exclude *.runtime.test.ts`);
205
+ }
206
+ }
207
+
208
+ for (const file of walk("packages").filter((entry) => entry.endsWith(".worker.test.ts"))) {
209
+ failures.push(`${file}: runtime tests must use *.runtime.test.ts`);
210
+ }
211
+ const runtimeTestFiles = walk("packages").filter((entry) => entry.endsWith(".runtime.test.ts"));
212
+ for (const file of runtimeTestFiles) {
213
+ const owner = records
214
+ .filter((record) => file === record.path || file.startsWith(`${record.path}/`))
215
+ .sort((left, right) => right.path.length - left.path.length)[0];
216
+ const override = owner === undefined ? undefined : overrideByPath.get(owner.path);
217
+ if (owner === undefined || !(override?.proofClasses ?? []).includes("runtime")) {
218
+ failures.push(`${file}: runtime test has no runtime proof owner`);
219
+ }
220
+ }
221
+ for (const file of walk(".").filter((entry) => entry.endsWith(".tsbuildinfo"))) {
222
+ if (!file.startsWith(".cache/"))
223
+ failures.push(`${file}: tsbuildinfo must live under .cache/`);
224
+ }
225
+
226
+ const sourceEdges = graphPackageSourceImportEdges(repoRoot, records);
227
+ const graphEdges = [
228
+ ...sourceEdges,
229
+ ...packageManifestDependencyEdges(repoRoot, records),
230
+ ...tsconfigReferenceEdges(repoRoot, records),
231
+ ];
232
+ const graphKeys = new Set(graphEdges.map((edge) => `${edge.from.name}->${edge.to.name}`));
233
+ for (const edge of sourceEdges) {
234
+ if (!graphKeys.has(`${edge.from.name}->${edge.to.name}`)) {
235
+ failures.push(
236
+ `${edge.file}: affected graph is missing source import edge ${edge.specifier}`,
237
+ );
238
+ }
239
+ }
240
+
241
+ failIfAny("gate tier governance", failures);
242
+ };
243
+
244
+ const checkSpikeHygiene = () => {
245
+ const tracked = execFileSync("git", ["ls-files", "spikes"], { cwd: repoRoot, encoding: "utf8" })
246
+ .split("\n")
247
+ .map((line) => line.trim())
248
+ .filter((file) => file.length > 0 && fs.existsSync(path.join(repoRoot, file)));
249
+ const allowed = new Set();
250
+ failIfAny(
251
+ "spike hygiene",
252
+ tracked
253
+ .filter((file) => !allowed.has(file))
254
+ .map((file) => `tracked spike file is not allowed: ${file}`),
255
+ );
256
+ };
257
+
258
+ return {
259
+ checkTransactionSync,
260
+ checkBackendNeutrality,
261
+ checkGateTierGovernance,
262
+ checkSpikeHygiene,
263
+ };
264
+ };
@@ -0,0 +1,106 @@
1
+ export const createSourceAliasChecks = ({
2
+ fs,
3
+ path,
4
+ pathToFileURL,
5
+ repoRoot,
6
+ readJson,
7
+ toRepoPath,
8
+ compare,
9
+ isRecord,
10
+ failIfAny,
11
+ }) => {
12
+ const packageJsonFiles = () => {
13
+ const files = [];
14
+ const visit = (dir) => {
15
+ const packageJson = path.join(dir, "package.json");
16
+ if (fs.existsSync(packageJson)) {
17
+ files.push(packageJson);
18
+ return;
19
+ }
20
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
21
+ if (entry.isDirectory() && entry.name !== "node_modules") visit(path.join(dir, entry.name));
22
+ }
23
+ };
24
+ for (const root of ["packages", "tooling"]) visit(path.join(repoRoot, root));
25
+ return files.sort(compare);
26
+ };
27
+
28
+ const checkSourceAliases = async () => {
29
+ const failures = [];
30
+ const { agentOsSourceAliasSpecs } = await import(
31
+ pathToFileURL(path.join(repoRoot, "tooling/vitest-config/source-aliases.ts")).href
32
+ );
33
+ const actual = new Map(
34
+ [...agentOsSourceAliasSpecs].sort(([left], [right]) => left.localeCompare(right)),
35
+ );
36
+ const expected = new Map();
37
+ for (const packageJsonPath of packageJsonFiles()) {
38
+ const packageDir = path.dirname(packageJsonPath);
39
+ const manifest = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
40
+ if (!manifest.name?.startsWith("@agent-os/")) continue;
41
+ const exportsValue =
42
+ manifest.exports ??
43
+ (fs.existsSync(path.join(packageDir, "src/index.ts")) ? { ".": "./src/index.ts" } : {});
44
+ for (const [exportPath, exportTarget] of Object.entries(
45
+ isRecord(exportsValue) ? exportsValue : { ".": exportsValue },
46
+ )) {
47
+ const target =
48
+ typeof exportTarget === "string"
49
+ ? exportTarget
50
+ : (exportTarget?.default ?? exportTarget?.import ?? exportTarget?.types);
51
+ if (typeof target !== "string" || !target.startsWith("./")) continue;
52
+ const specifier =
53
+ exportPath === "."
54
+ ? manifest.name
55
+ : `${manifest.name}/${exportPath.replace(/^\.\//u, "")}`;
56
+ expected.set(specifier, toRepoPath(path.join(packageDir, target)));
57
+ }
58
+ }
59
+ for (const [specifier, sourcePath] of expected) {
60
+ if (actual.get(specifier) !== sourcePath) {
61
+ failures.push(
62
+ `source alias ${String(specifier)}: expected ${String(sourcePath)}; actual ${String(actual.get(specifier))}`,
63
+ );
64
+ }
65
+ }
66
+ for (const specifier of actual.keys()) {
67
+ if (!expected.has(specifier)) failures.push(`extra source alias ${String(specifier)}`);
68
+ }
69
+ const tsconfig = readJson("tsconfig.source-paths.json");
70
+ if (tsconfig.compilerOptions?.baseUrl !== undefined) {
71
+ failures.push("tsconfig.source-paths.json must not set compilerOptions.baseUrl");
72
+ }
73
+ const actualPaths = tsconfig.compilerOptions?.paths ?? {};
74
+ for (const [specifier, sourcePath] of actual) {
75
+ const expectedPaths = [`./${String(sourcePath)}`];
76
+ if (JSON.stringify(actualPaths[specifier]) !== JSON.stringify(expectedPaths)) {
77
+ failures.push(
78
+ `tsconfig.source-paths.json paths.${String(specifier)}: expected ${JSON.stringify(expectedPaths)}`,
79
+ );
80
+ }
81
+ }
82
+ for (const file of packageJsonFiles()
83
+ .map((packageJsonPath) => path.join(path.dirname(packageJsonPath), "tsconfig.json"))
84
+ .filter((file) => fs.existsSync(file))) {
85
+ const tsconfigSource = readJson(toRepoPath(file));
86
+ const expectedExtends = path
87
+ .relative(path.dirname(file), path.join(repoRoot, "tsconfig.source-paths.json"))
88
+ .split(path.sep)
89
+ .join("/");
90
+ if (tsconfigSource.extends !== expectedExtends) {
91
+ failures.push(`${toRepoPath(file)}: expected extends ${JSON.stringify(expectedExtends)}`);
92
+ }
93
+ const localAgentOsPaths = Object.keys(tsconfigSource.compilerOptions?.paths ?? {}).filter(
94
+ (specifier) => specifier.startsWith("@agent-os/"),
95
+ );
96
+ if (localAgentOsPaths.length > 0) {
97
+ failures.push(
98
+ `${toRepoPath(file)} has package-local @agent-os paths: ${localAgentOsPaths.join(", ")}`,
99
+ );
100
+ }
101
+ }
102
+ failIfAny("source aliases", failures);
103
+ };
104
+
105
+ return { checkSourceAliases };
106
+ };