@yansirplus/cli 0.5.17 → 0.5.19
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/README.md +12 -6
- package/agent-catalog/agentOS/SKILL.md +22 -0
- package/agent-catalog/agentOS/references/agent/decision-graph.json +530 -0
- package/agent-catalog/agentOS/references/agent/errors.json +497 -0
- package/agent-catalog/agentOS/references/agent/invariant-matrix.json +337 -0
- package/agent-catalog/agentOS/references/agent/primitives.json +989 -0
- package/agent-catalog/agentOS/references/agent/recipes.json +109 -0
- package/agent-catalog/agentOS/references/agent/start-here.md +25 -0
- package/agent-catalog/agentOS/references/package-map.md +73 -0
- package/agent-catalog/agentOS/references/provenance.json +251 -0
- package/agent-catalog/agentOS/references/public-api/cli.md +20 -0
- package/agent-catalog/agentOS/references/public-api/client.md +90 -0
- package/agent-catalog/agentOS/references/public-api/core.md +1907 -0
- package/agent-catalog/agentOS/references/public-api/runtime.md +843 -0
- package/dist/build/agent-authoring/config.d.ts +20 -5
- package/dist/build/agent-authoring/config.js +132 -32
- package/dist/build/agent-authoring/manifest-compiler.d.ts +131 -2
- package/dist/build/agent-authoring/manifest-compiler.js +630 -8
- package/dist/build/agent-authoring/shared.d.ts +2 -0
- package/dist/build/agent-authoring/shared.js +2 -0
- package/dist/build/agent-authoring/static-target.d.ts +6 -3
- package/dist/build/agent-authoring/static-target.js +1900 -281
- package/dist/build/agent-authoring.d.ts +3 -3
- package/dist/build/agent-authoring.js +1 -1
- package/dist/build/build-cli.d.ts +1 -1
- package/dist/build/build-cli.js +1629 -26
- package/dist/check/algorithmic/client-boundary-checks.mjs +3 -34
- package/dist/check/algorithmic/convergence-smoke-checks.mjs +652 -6
- package/dist/check/algorithmic/distribution-checks.mjs +8 -7
- package/dist/check/algorithmic/package-boundary-checks.mjs +3 -2
- package/dist/check/algorithmic/repo-surface-checks.mjs +55 -1
- package/dist/check/algorithmic/static-target-checks.mjs +83 -5
- package/dist/check/algorithmic-checks.mjs +10 -17
- package/dist/check/default-gate.mjs +3 -3
- package/dist/check/effect-scan-gate.mjs +121 -0
- package/dist/check/package-graph.mjs +2 -32
- package/dist/consumer-overlay.mjs +1281 -0
- package/dist/lib/public-api-model.mjs +19 -0
- package/dist/lib/repo-source-files.mjs +26 -0
- package/dist/lib/ts-module-loader.mjs +44 -0
- package/dist/lib/workspace-manifest.mjs +77 -0
- package/dist/main.mjs +171 -21
- package/dist/release-status.mjs +515 -0
- package/package.json +8 -4
- package/dist/check/check-coverage.mjs +0 -231
- package/dist/generate/generate-agent-docs.mjs +0 -435
- package/dist/generate/generate-carrier-reference.mjs +0 -514
- package/dist/generate/generate-docs.mjs +0 -345
- package/dist/generate/generate-effect-skill-manifests.mjs +0 -193
- package/dist/generate/project-docs-site.mjs +0 -190
- package/dist/lib/boundary-rules.mjs +0 -63
- package/dist/lib/capability-routes.mjs +0 -354
- package/dist/lib/projection-sink.mjs +0 -113
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { listAlgorithmicCheckers } from "./algorithmic-checks.mjs";
|
|
6
|
-
|
|
7
|
-
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../..");
|
|
8
|
-
|
|
9
|
-
const readJson = (relativePath) =>
|
|
10
|
-
JSON.parse(fs.readFileSync(path.join(repoRoot, relativePath), "utf8"));
|
|
11
|
-
|
|
12
|
-
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
13
|
-
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
14
|
-
|
|
15
|
-
const failures = [];
|
|
16
|
-
const fail = (message) => failures.push(message);
|
|
17
|
-
|
|
18
|
-
const coverage = readJson("packages/cli/src/check/check-coverage.source.json");
|
|
19
|
-
const rulesSource = readJson("docs/agent/boundary-rules.source.json");
|
|
20
|
-
const cliPackage = readJson("packages/cli/package.json");
|
|
21
|
-
|
|
22
|
-
if (coverage.schemaVersion !== 1) fail("check coverage schemaVersion must be 1");
|
|
23
|
-
if (!Array.isArray(coverage.entries) || coverage.entries.length === 0) {
|
|
24
|
-
fail("check coverage entries must be non-empty");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const rules = new Map((rulesSource.rules ?? []).map((rule) => [rule.id, rule]));
|
|
28
|
-
const algorithmicCheckers = new Set(listAlgorithmicCheckers());
|
|
29
|
-
const coveredRuleIds = new Set();
|
|
30
|
-
const genericPackageNegativeWitness =
|
|
31
|
-
"package-owned test fails when the owned contract is violated";
|
|
32
|
-
|
|
33
|
-
const assertProofWitnesses = (label, assertionIndex, assertion) => {
|
|
34
|
-
const witnesses = assertion.packageWitnesses;
|
|
35
|
-
if (witnesses === undefined) return;
|
|
36
|
-
if (!Array.isArray(witnesses) || witnesses.length === 0)
|
|
37
|
-
fail(`${label}: assertions[${assertionIndex}] packageWitnesses must be non-empty when present`);
|
|
38
|
-
|
|
39
|
-
for (const [witnessIndex, witness] of witnesses.entries()) {
|
|
40
|
-
const witnessLabel = `${label}: assertions[${assertionIndex}].packageWitnesses[${witnessIndex}]`;
|
|
41
|
-
if (!isRecord(witness)) {
|
|
42
|
-
fail(`${witnessLabel} must be an object`);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
if (typeof witness.file !== "string" || !witness.file.startsWith("packages/")) {
|
|
46
|
-
fail(`${witnessLabel}.file must be a package test path`);
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (typeof witness.name !== "string" || witness.name.length === 0) {
|
|
50
|
-
fail(`${witnessLabel}.name must be non-empty`);
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const absolutePath = path.join(repoRoot, witness.file);
|
|
55
|
-
if (!fs.existsSync(absolutePath)) {
|
|
56
|
-
fail(`${witnessLabel}.file does not exist`);
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
const source = fs.readFileSync(absolutePath, "utf8");
|
|
60
|
-
const testNamePattern = new RegExp(
|
|
61
|
-
`\\b(?:describe|it|test)(?:\\.effect)?\\(\\s*(["'\`])${escapeRegExp(witness.name)}\\1`,
|
|
62
|
-
"u",
|
|
63
|
-
);
|
|
64
|
-
if (!testNamePattern.test(source)) {
|
|
65
|
-
fail(`${witnessLabel}.name was not found as a test or describe name`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
for (const [index, entry] of (coverage.entries ?? []).entries()) {
|
|
71
|
-
const label =
|
|
72
|
-
isRecord(entry) && typeof entry.ruleId === "string" ? entry.ruleId : `entry[${index}]`;
|
|
73
|
-
if (!isRecord(entry)) {
|
|
74
|
-
fail(`${label}: coverage entry must be an object`);
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
if (!isRecord(entry.source)) fail(`${label}: missing source`);
|
|
78
|
-
if (!isRecord(entry.target)) fail(`${label}: missing target`);
|
|
79
|
-
if (!Array.isArray(entry.assertions) || entry.assertions.length === 0) {
|
|
80
|
-
fail(`${label}: assertions must be non-empty`);
|
|
81
|
-
} else {
|
|
82
|
-
for (const [assertionIndex, assertion] of entry.assertions.entries()) {
|
|
83
|
-
if (!isRecord(assertion)) {
|
|
84
|
-
fail(`${label}: assertions[${assertionIndex}] must be an object`);
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
if (
|
|
88
|
-
typeof assertion.failureCondition !== "string" ||
|
|
89
|
-
assertion.failureCondition.length === 0
|
|
90
|
-
) {
|
|
91
|
-
fail(`${label}: assertions[${assertionIndex}] missing failureCondition`);
|
|
92
|
-
}
|
|
93
|
-
if (typeof assertion.negativeWitness !== "string" || assertion.negativeWitness.length === 0) {
|
|
94
|
-
fail(`${label}: assertions[${assertionIndex}] missing negativeWitness`);
|
|
95
|
-
}
|
|
96
|
-
if (assertion.negativeWitness === genericPackageNegativeWitness) {
|
|
97
|
-
fail(`${label}: assertions[${assertionIndex}] has generic package negativeWitness`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (typeof entry.ruleId === "string" && entry.ruleId.includes(",")) {
|
|
103
|
-
for (const ruleId of entry.ruleId.split(",")) coveredRuleIds.add(ruleId);
|
|
104
|
-
} else if (typeof entry.ruleId === "string") {
|
|
105
|
-
coveredRuleIds.add(entry.ruleId);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (entry.target?.kind === "algorithmic") {
|
|
109
|
-
const rule = rules.get(entry.target.ruleId);
|
|
110
|
-
if (!isRecord(rule)) {
|
|
111
|
-
fail(`${label}: algorithmic target references unknown rule ${entry.target.ruleId}`);
|
|
112
|
-
} else if (rule.acceptance?.engine !== "algorithmic") {
|
|
113
|
-
fail(`${label}: algorithmic target rule must use algorithmic acceptance`);
|
|
114
|
-
} else if (rule.acceptance.checker !== entry.target.checker) {
|
|
115
|
-
fail(`${label}: coverage checker ${entry.target.checker} does not match rule acceptance`);
|
|
116
|
-
}
|
|
117
|
-
if (!algorithmicCheckers.has(entry.target.checker)) {
|
|
118
|
-
fail(`${label}: missing algorithmic checker ${entry.target.checker}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (entry.target?.kind === "proofClass") {
|
|
123
|
-
const rule = rules.get(entry.target.ruleId);
|
|
124
|
-
if (!isRecord(rule)) {
|
|
125
|
-
fail(`${label}: proofClass target references unknown rule ${entry.target.ruleId}`);
|
|
126
|
-
} else if (rule.acceptance?.engine !== "proofClass") {
|
|
127
|
-
fail(`${label}: proofClass target rule must use proofClass acceptance`);
|
|
128
|
-
} else {
|
|
129
|
-
for (const [assertionIndex, assertion] of (entry.assertions ?? []).entries()) {
|
|
130
|
-
if (isRecord(assertion)) {
|
|
131
|
-
assertProofWitnesses(label, assertionIndex, assertion);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (entry.target?.kind === "manifestRule") {
|
|
138
|
-
const rule = rules.get(entry.target.ruleId);
|
|
139
|
-
if (!isRecord(rule)) {
|
|
140
|
-
fail(`${label}: manifestRule target references unknown rule ${entry.target.ruleId}`);
|
|
141
|
-
} else if (rule.acceptance?.engine !== entry.target.engine) {
|
|
142
|
-
fail(`${label}: manifestRule target engine must match rule acceptance`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (typeof entry.ruleId === "string" && !entry.ruleId.includes(",")) {
|
|
147
|
-
const rule = rules.get(entry.ruleId);
|
|
148
|
-
if (isRecord(rule) && entry.owner !== rule.owner) {
|
|
149
|
-
fail(
|
|
150
|
-
`${label}: coverage owner ${entry.owner} does not match boundary rule owner ${rule.owner}`,
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (entry.target?.kind === "algorithmic-helper") {
|
|
156
|
-
for (const checker of entry.target.checkers ?? []) {
|
|
157
|
-
if (!algorithmicCheckers.has(checker))
|
|
158
|
-
fail(`${label}: helper references unknown checker ${checker}`);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
for (const rule of rulesSource.rules ?? []) {
|
|
164
|
-
if (!coveredRuleIds.has(rule.id)) fail(`${rule.id}: rule lacks check coverage entry`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const sourceText = fs.readFileSync(
|
|
168
|
-
path.join(repoRoot, "docs/agent/boundary-rules.source.json"),
|
|
169
|
-
"utf8",
|
|
170
|
-
);
|
|
171
|
-
if (sourceText.includes("positiveAcceptance")) {
|
|
172
|
-
fail("docs/agent/boundary-rules.source.json: positiveAcceptance is no longer allowed");
|
|
173
|
-
}
|
|
174
|
-
if (/\s--fix(?:\s|")/u.test(sourceText)) {
|
|
175
|
-
fail("docs/agent/boundary-rules.source.json: check commands must not include --fix");
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const walk = (relativePath) => {
|
|
179
|
-
const absolutePath = path.join(repoRoot, relativePath);
|
|
180
|
-
const files = [];
|
|
181
|
-
for (const entry of fs.readdirSync(absolutePath, { withFileTypes: true })) {
|
|
182
|
-
const child = path.join(relativePath, entry.name);
|
|
183
|
-
if (entry.isDirectory()) files.push(...walk(child));
|
|
184
|
-
if (entry.isFile()) files.push(child.split(path.sep).join("/"));
|
|
185
|
-
}
|
|
186
|
-
return files;
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
const cliMjsFiles = walk("packages/cli/src").filter((file) => file.endsWith(".mjs"));
|
|
190
|
-
const checkMjsFiles = cliMjsFiles.filter((file) => file.startsWith("packages/cli/src/check/"));
|
|
191
|
-
if (cliMjsFiles.length > 19) {
|
|
192
|
-
fail(`packages/cli/src: expected at most 19 .mjs files; observed ${cliMjsFiles.length}`);
|
|
193
|
-
}
|
|
194
|
-
if (checkMjsFiles.length > 12) {
|
|
195
|
-
fail(`packages/cli/src/check: expected at most 12 .mjs files; observed ${checkMjsFiles.length}`);
|
|
196
|
-
}
|
|
197
|
-
for (const file of checkMjsFiles) {
|
|
198
|
-
const content = fs.readFileSync(path.join(repoRoot, file), "utf8");
|
|
199
|
-
const selfTestFlag = "--" + "self-test";
|
|
200
|
-
if (content.includes(selfTestFlag)) fail(`${file}: per-checker self-test flag is not allowed`);
|
|
201
|
-
const harnessPattern = new RegExp(
|
|
202
|
-
["mkd" + "te" + "mp", "tm" + "pdir", "Tem" + "por" + "ary", "te" + "mp"].join("|"),
|
|
203
|
-
"i",
|
|
204
|
-
);
|
|
205
|
-
if (file !== "packages/cli/src/check/algorithmic-checks.mjs" && harnessPattern.test(content)) {
|
|
206
|
-
fail(`${file}: per-checker ad hoc harness is not allowed`);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const cliDependencies = Object.keys(cliPackage.dependencies ?? {});
|
|
211
|
-
for (const dependency of cliDependencies) {
|
|
212
|
-
if (
|
|
213
|
-
dependency === "@effect/cli" ||
|
|
214
|
-
dependency === "@effect/platform-node" ||
|
|
215
|
-
dependency === "@effect/printer" ||
|
|
216
|
-
dependency === "@effect/printer-ansi" ||
|
|
217
|
-
dependency === "@effect/typeclass"
|
|
218
|
-
) {
|
|
219
|
-
fail(`packages/cli/package.json: removed Effect CLI dependency returned: ${dependency}`);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
if (cliPackage.dependencies?.effect === "3.21.2") {
|
|
223
|
-
fail("packages/cli/package.json: Effect v3 CLI dependency returned");
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (failures.length > 0) {
|
|
227
|
-
console.error(failures.join("\n"));
|
|
228
|
-
process.exit(1);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
console.log("guard coverage passed");
|
|
@@ -1,435 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import os from "node:os";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { spawnSync } from "node:child_process";
|
|
6
|
-
import {
|
|
7
|
-
checkProjectionSink,
|
|
8
|
-
defineProjectionSpec,
|
|
9
|
-
runProjectionSink,
|
|
10
|
-
} from "../lib/projection-sink.mjs";
|
|
11
|
-
import { collectAgentDocsModel } from "../lib/agent-docs-model.mjs";
|
|
12
|
-
import { buildCapabilityRouteProjection } from "../lib/capability-routes.mjs";
|
|
13
|
-
|
|
14
|
-
const root = process.cwd();
|
|
15
|
-
const check = process.argv.includes("--check");
|
|
16
|
-
const failures = [];
|
|
17
|
-
|
|
18
|
-
const abs = (file) => path.join(root, file);
|
|
19
|
-
const exists = (file) => fs.existsSync(abs(file));
|
|
20
|
-
const read = (file) => fs.readFileSync(abs(file), "utf8");
|
|
21
|
-
|
|
22
|
-
const generatedNotice = (source) =>
|
|
23
|
-
`<!-- generated by packages/cli/src/generate/generate-agent-docs.mjs; edit ${source} -->`;
|
|
24
|
-
|
|
25
|
-
const agentDocsSourceFacts = {
|
|
26
|
-
kind: "source-set",
|
|
27
|
-
ref: "docs.generate-agent-docs.source-facts",
|
|
28
|
-
sources: [
|
|
29
|
-
{ kind: "directory", ref: "docs/agent/*.source.json" },
|
|
30
|
-
{ kind: "file", ref: "docs/surface.json" },
|
|
31
|
-
{ kind: "directory", ref: "packages/*/package.json" },
|
|
32
|
-
{ kind: "tsdoc", ref: "exported @agentosPrimitive tags" },
|
|
33
|
-
],
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const fileProjection = (file) =>
|
|
37
|
-
defineProjectionSpec({
|
|
38
|
-
id: `docs.generate-agent-docs:${file}`,
|
|
39
|
-
version: 1,
|
|
40
|
-
source: agentDocsSourceFacts,
|
|
41
|
-
project: (expected, ctx) => ctx.ok(expected),
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const fileSink = (file) => {
|
|
45
|
-
const target = abs(file);
|
|
46
|
-
return {
|
|
47
|
-
id: file,
|
|
48
|
-
read: () =>
|
|
49
|
-
fs.existsSync(target)
|
|
50
|
-
? { _tag: "found", output: fs.readFileSync(target, "utf8") }
|
|
51
|
-
: { _tag: "missing" },
|
|
52
|
-
write: (output) => {
|
|
53
|
-
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
54
|
-
fs.writeFileSync(target, output);
|
|
55
|
-
},
|
|
56
|
-
equals: (actual, expected) => actual === expected,
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const formatGeneratedText = (file, text) => {
|
|
61
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "agent-docs-"));
|
|
62
|
-
const tmpFile = path.join(tmpDir, path.basename(file));
|
|
63
|
-
try {
|
|
64
|
-
fs.writeFileSync(tmpFile, `${text.replace(/\s+$/u, "")}\n`);
|
|
65
|
-
const result = spawnSync("vp", ["fmt", tmpFile, "--write"], {
|
|
66
|
-
cwd: root,
|
|
67
|
-
encoding: "utf8",
|
|
68
|
-
});
|
|
69
|
-
if (result.status !== 0) {
|
|
70
|
-
failures.push(
|
|
71
|
-
`${file} failed generated formatting: ${result.stderr || result.stdout || result.status}`,
|
|
72
|
-
);
|
|
73
|
-
return `${text.replace(/\s+$/u, "")}\n`;
|
|
74
|
-
}
|
|
75
|
-
return fs.readFileSync(tmpFile, "utf8");
|
|
76
|
-
} finally {
|
|
77
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const write = async (file, text) => {
|
|
82
|
-
const expected = formatGeneratedText(file, text);
|
|
83
|
-
const spec = fileProjection(file);
|
|
84
|
-
const sink = fileSink(file);
|
|
85
|
-
if (check) {
|
|
86
|
-
const result = await checkProjectionSink(spec, expected, sink);
|
|
87
|
-
if (result._tag === "projection_failed") {
|
|
88
|
-
failures.push(`${file} projection failed: ${result.result.reason}`);
|
|
89
|
-
} else if (result._tag === "stale") {
|
|
90
|
-
failures.push(`${file} is stale`);
|
|
91
|
-
}
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
const result = await runProjectionSink(spec, expected, sink);
|
|
95
|
-
if (result._tag === "projection_failed") {
|
|
96
|
-
failures.push(`${file} projection failed: ${result.result.reason}`);
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const writeJson = async (file, value) => write(file, JSON.stringify(value, null, 2));
|
|
101
|
-
|
|
102
|
-
const replaceGeneratedBlock = async ({ file, startMarker, endMarker, block, insertBefore }) => {
|
|
103
|
-
const current = exists(file) ? read(file) : "";
|
|
104
|
-
const startIndex = current.indexOf(startMarker);
|
|
105
|
-
const endIndex = current.indexOf(endMarker);
|
|
106
|
-
const normalizedBlock = block.replace(/\s+$/u, "");
|
|
107
|
-
let next;
|
|
108
|
-
|
|
109
|
-
if ((startIndex === -1) !== (endIndex === -1)) {
|
|
110
|
-
failures.push(`${file} has incomplete generated block markers`);
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (startIndex >= 0 && endIndex > startIndex) {
|
|
115
|
-
const endLineIndex = current.indexOf("\n", endIndex);
|
|
116
|
-
let afterIndex = endLineIndex === -1 ? current.length : endLineIndex + 1;
|
|
117
|
-
while (current[afterIndex] === "\n") afterIndex += 1;
|
|
118
|
-
next = `${current.slice(0, startIndex)}${normalizedBlock}\n\n${current.slice(afterIndex)}`;
|
|
119
|
-
} else {
|
|
120
|
-
const insertIndex = current.indexOf(insertBefore);
|
|
121
|
-
if (insertIndex < 0) {
|
|
122
|
-
failures.push(`${file} missing insertion anchor ${insertBefore}`);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
next = `${current.slice(0, insertIndex)}${normalizedBlock}\n\n${current.slice(insertIndex)}`;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
await write(file, next);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const relativeLink = (fromFile, targetFile) => {
|
|
132
|
-
const fromDir = path.posix.dirname(fromFile);
|
|
133
|
-
const target = path.posix.relative(fromDir, targetFile);
|
|
134
|
-
return target.length === 0 ? path.posix.basename(targetFile) : target;
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const mdLink = (fromFile, label, targetFile) => `[${label}](${relativeLink(fromFile, targetFile)})`;
|
|
138
|
-
|
|
139
|
-
const table = (headers, rows) => {
|
|
140
|
-
const widths = headers.map((header, index) =>
|
|
141
|
-
Math.max(header.length, ...rows.map((row) => row[index].length)),
|
|
142
|
-
);
|
|
143
|
-
const line = (row) =>
|
|
144
|
-
`| ${row.map((cell, index) => `${cell}${" ".repeat(Math.max(0, widths[index] - cell.length))}`).join(" | ")} |`;
|
|
145
|
-
return [
|
|
146
|
-
line(headers),
|
|
147
|
-
`| ${widths.map((width) => "-".repeat(width)).join(" | ")} |`,
|
|
148
|
-
...rows.map(line),
|
|
149
|
-
].join("\n");
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const model = collectAgentDocsModel(root);
|
|
153
|
-
failures.push(...model.failures);
|
|
154
|
-
|
|
155
|
-
const {
|
|
156
|
-
recipesSource,
|
|
157
|
-
capabilityRulesSource,
|
|
158
|
-
invariantsSource,
|
|
159
|
-
primitives,
|
|
160
|
-
errors,
|
|
161
|
-
invariantMatrix,
|
|
162
|
-
rootScripts,
|
|
163
|
-
namespaceModel,
|
|
164
|
-
} = model;
|
|
165
|
-
const boundaryRuleSource = JSON.parse(read("docs/agent/boundary-rules.source.json"));
|
|
166
|
-
const boundaryRuleCommands = Object.fromEntries(
|
|
167
|
-
(Array.isArray(boundaryRuleSource.rules) ? boundaryRuleSource.rules : []).map((rule) => [
|
|
168
|
-
rule.id,
|
|
169
|
-
`agentos check guard ${rule.id}`,
|
|
170
|
-
]),
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
const capabilityProjection = buildCapabilityRouteProjection({
|
|
174
|
-
source: capabilityRulesSource,
|
|
175
|
-
recipes: recipesSource.recipes,
|
|
176
|
-
primitives,
|
|
177
|
-
invariants: invariantsSource.invariants,
|
|
178
|
-
rootScripts: { ...rootScripts, ...boundaryRuleCommands },
|
|
179
|
-
namespaceOwners: namespaceModel.owners,
|
|
180
|
-
});
|
|
181
|
-
failures.push(...capabilityProjection.failures);
|
|
182
|
-
|
|
183
|
-
const primitivesJson = {
|
|
184
|
-
generatedBy: "packages/cli/src/generate/generate-agent-docs.mjs",
|
|
185
|
-
source: [
|
|
186
|
-
"exported TSDoc @agentosPrimitive tags",
|
|
187
|
-
"docs/agent/invariants.source.json",
|
|
188
|
-
"docs/agent/primitive-evidence.source.json",
|
|
189
|
-
],
|
|
190
|
-
primitives,
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
const recipesJson = {
|
|
194
|
-
generatedBy: "packages/cli/src/generate/generate-agent-docs.mjs",
|
|
195
|
-
source: "docs/agent/recipes.source.json",
|
|
196
|
-
recipes: recipesSource.recipes,
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
const errorsJson = {
|
|
200
|
-
generatedBy: "packages/cli/src/generate/generate-agent-docs.mjs",
|
|
201
|
-
source: ["packages/**/*.ts agent_os.* tags", "docs/agent/error-metadata.source.json"],
|
|
202
|
-
errors,
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
const invariantMatrixJson = {
|
|
206
|
-
generatedBy: "packages/cli/src/generate/generate-agent-docs.mjs",
|
|
207
|
-
source: [
|
|
208
|
-
"docs/agent/invariants.source.json",
|
|
209
|
-
"exported TSDoc @agentosInvariant tags",
|
|
210
|
-
"docs/agent/error-metadata.source.json",
|
|
211
|
-
],
|
|
212
|
-
invariants: invariantMatrix,
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
const decisionGraphJson = {
|
|
216
|
-
generatedBy: "packages/cli/src/generate/generate-agent-docs.mjs",
|
|
217
|
-
source: [
|
|
218
|
-
"docs/agent/capability-rules.source.json",
|
|
219
|
-
"docs/agent/recipes.source.json",
|
|
220
|
-
"exported TSDoc @agentosPrimitive tags",
|
|
221
|
-
"BoundaryContract/event namespace declarations",
|
|
222
|
-
"docs/agent/boundary-rules.source.json",
|
|
223
|
-
],
|
|
224
|
-
routes: capabilityProjection.routes,
|
|
225
|
-
coverage: capabilityProjection.coverage,
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
const startHere = [
|
|
229
|
-
generatedNotice("docs/agent/*.source.json and exported TSDoc @agentosPrimitive tags"),
|
|
230
|
-
"",
|
|
231
|
-
"# Start Here",
|
|
232
|
-
"",
|
|
233
|
-
"Agent-facing docs are generated projections. Source facts live in `docs/agent/*.source.json`, `docs/surface.json`, package manifests, and exported TSDoc annotation tags.",
|
|
234
|
-
"",
|
|
235
|
-
"## Navigation",
|
|
236
|
-
"",
|
|
237
|
-
table(
|
|
238
|
-
["Surface", "Generated File"],
|
|
239
|
-
[
|
|
240
|
-
[
|
|
241
|
-
"Recipes",
|
|
242
|
-
mdLink("docs/start-here.md", "docs/agent/recipes.json", "docs/agent/recipes.json"),
|
|
243
|
-
],
|
|
244
|
-
[
|
|
245
|
-
"Primitives",
|
|
246
|
-
mdLink("docs/start-here.md", "docs/agent/primitives.md", "docs/agent/primitives.md"),
|
|
247
|
-
],
|
|
248
|
-
[
|
|
249
|
-
"Decision Graph",
|
|
250
|
-
mdLink(
|
|
251
|
-
"docs/start-here.md",
|
|
252
|
-
"docs/agent/decision-graph.md",
|
|
253
|
-
"docs/agent/decision-graph.md",
|
|
254
|
-
),
|
|
255
|
-
],
|
|
256
|
-
["Errors", mdLink("docs/start-here.md", "docs/agent/errors.md", "docs/agent/errors.md")],
|
|
257
|
-
[
|
|
258
|
-
"Invariant Matrix",
|
|
259
|
-
mdLink(
|
|
260
|
-
"docs/start-here.md",
|
|
261
|
-
"docs/agent/invariant-matrix.md",
|
|
262
|
-
"docs/agent/invariant-matrix.md",
|
|
263
|
-
),
|
|
264
|
-
],
|
|
265
|
-
],
|
|
266
|
-
),
|
|
267
|
-
"",
|
|
268
|
-
"## Recipes",
|
|
269
|
-
"",
|
|
270
|
-
table(
|
|
271
|
-
["Recipe", "Tutorial", "Primary Primitives"],
|
|
272
|
-
recipesSource.recipes.map((recipe) => [
|
|
273
|
-
`\`${recipe.id}\``,
|
|
274
|
-
mdLink("docs/start-here.md", recipe.title, recipe.tutorial),
|
|
275
|
-
recipe.primitives.map((primitive) => `\`${primitive}\``).join(", "),
|
|
276
|
-
]),
|
|
277
|
-
),
|
|
278
|
-
"",
|
|
279
|
-
].join("\n");
|
|
280
|
-
|
|
281
|
-
const agentNavigationStart = "<!-- agent-docs-navigation:start -->";
|
|
282
|
-
const agentNavigationEnd = "<!-- agent-docs-navigation:end -->";
|
|
283
|
-
const agentNavigation = [
|
|
284
|
-
agentNavigationStart,
|
|
285
|
-
generatedNotice("docs/agent/*.source.json and exported TSDoc @agentosPrimitive tags"),
|
|
286
|
-
"## Agent Navigation Index",
|
|
287
|
-
"",
|
|
288
|
-
"Start at [docs/start-here.md](docs/start-here.md). It is the generated route from intent to primitive, invariant, error, docs, and tests.",
|
|
289
|
-
"",
|
|
290
|
-
table(
|
|
291
|
-
["Intent", "Route"],
|
|
292
|
-
[
|
|
293
|
-
["Start route", "[docs/start-here.md](docs/start-here.md)"],
|
|
294
|
-
["Primitive catalog", "[docs/agent/primitives.md](docs/agent/primitives.md)"],
|
|
295
|
-
["Decision graph", "[docs/agent/decision-graph.md](docs/agent/decision-graph.md)"],
|
|
296
|
-
["Error catalog", "[docs/agent/errors.md](docs/agent/errors.md)"],
|
|
297
|
-
["Invariant matrix", "[docs/agent/invariant-matrix.md](docs/agent/invariant-matrix.md)"],
|
|
298
|
-
...recipesSource.recipes.map((recipe) => [
|
|
299
|
-
recipe.title,
|
|
300
|
-
`[${recipe.tutorial}](${recipe.tutorial})`,
|
|
301
|
-
]),
|
|
302
|
-
],
|
|
303
|
-
),
|
|
304
|
-
agentNavigationEnd,
|
|
305
|
-
].join("\n");
|
|
306
|
-
|
|
307
|
-
const primitivesMd = [
|
|
308
|
-
generatedNotice(
|
|
309
|
-
"exported TSDoc @agentosPrimitive tags and docs/agent/primitive-evidence.source.json",
|
|
310
|
-
),
|
|
311
|
-
"",
|
|
312
|
-
"# Agent Primitives",
|
|
313
|
-
"",
|
|
314
|
-
table(
|
|
315
|
-
["Primitive", "Kind", "Export", "Invariant", "Docs", "Test Evidence"],
|
|
316
|
-
primitives.map((primitive) => [
|
|
317
|
-
`\`${primitive.id}\``,
|
|
318
|
-
`\`${primitive.capabilityKind}\``,
|
|
319
|
-
`\`${primitive.package}:${primitive.symbol}\``,
|
|
320
|
-
primitive.invariants.map((invariant) => `\`${invariant}\``).join(", "),
|
|
321
|
-
mdLink("docs/agent/primitives.md", path.posix.basename(primitive.docs), primitive.docs),
|
|
322
|
-
primitive.testEvidence.tests === undefined
|
|
323
|
-
? `no test: ${primitive.testEvidence.noTestReason}`
|
|
324
|
-
: primitive.testEvidence.tests
|
|
325
|
-
.map((test) => mdLink("docs/agent/primitives.md", path.posix.basename(test), test))
|
|
326
|
-
.join(", "),
|
|
327
|
-
]),
|
|
328
|
-
),
|
|
329
|
-
"",
|
|
330
|
-
].join("\n");
|
|
331
|
-
|
|
332
|
-
const errorsMd = [
|
|
333
|
-
generatedNotice("packages/**/*.ts agent_os.* tags and docs/agent/error-metadata.source.json"),
|
|
334
|
-
"",
|
|
335
|
-
"# Agent Error Catalog",
|
|
336
|
-
"",
|
|
337
|
-
table(
|
|
338
|
-
["Error", "Invariant", "Docs", "Fix"],
|
|
339
|
-
errors.map((error) => [
|
|
340
|
-
`\`${error.tag}\``,
|
|
341
|
-
error.invariants.map((invariant) => `\`${invariant}\``).join(", "),
|
|
342
|
-
mdLink("docs/agent/errors.md", path.posix.basename(error.docs), error.docs),
|
|
343
|
-
error.fix,
|
|
344
|
-
]),
|
|
345
|
-
),
|
|
346
|
-
"",
|
|
347
|
-
].join("\n");
|
|
348
|
-
|
|
349
|
-
const decisionGraphMd = [
|
|
350
|
-
generatedNotice(
|
|
351
|
-
"docs/agent/capability-rules.source.json, docs/agent/recipes.source.json, exported TSDoc @agentosPrimitive tags, BoundaryContract/event namespace declarations, and docs/agent/boundary-rules.source.json",
|
|
352
|
-
),
|
|
353
|
-
"",
|
|
354
|
-
"# Agent Decision Graph",
|
|
355
|
-
"",
|
|
356
|
-
"This is the generated owner compiler from natural-language intent to allowed substrate motion. Source rules name intent and constraints only; owner and package fields are generated.",
|
|
357
|
-
"",
|
|
358
|
-
table(
|
|
359
|
-
["Primitive", "Kind", "Intents", "Source Fact Owners", "Allowed Packages", "Gates"],
|
|
360
|
-
capabilityProjection.routes.map((route) => [
|
|
361
|
-
`\`${route.primitive}\``,
|
|
362
|
-
`\`${route.coordinationCapabilityKind}\``,
|
|
363
|
-
route.intents.join("; "),
|
|
364
|
-
route.sourceFactOwners
|
|
365
|
-
.map((entry) => `\`${String(entry.prefix)}\` -> \`${String(entry.owner)}\``)
|
|
366
|
-
.join(", "),
|
|
367
|
-
route.allowedPrimitivePackages.map((pkg) => `\`${String(pkg)}\``).join(", "),
|
|
368
|
-
route.gates.map((gate) => `\`${gate}\``).join(", "),
|
|
369
|
-
]),
|
|
370
|
-
),
|
|
371
|
-
"",
|
|
372
|
-
"## Coverage",
|
|
373
|
-
"",
|
|
374
|
-
table(
|
|
375
|
-
["Recipe", "Status"],
|
|
376
|
-
capabilityProjection.coverage.recipes.map((recipe) => [
|
|
377
|
-
`\`${recipe.id}\``,
|
|
378
|
-
recipe.routePrimitives.length > 0
|
|
379
|
-
? recipe.routePrimitives.map((primitive) => `\`${primitive}\``).join(", ")
|
|
380
|
-
: `no route: ${recipe.noRouteReason}`,
|
|
381
|
-
]),
|
|
382
|
-
),
|
|
383
|
-
"",
|
|
384
|
-
].join("\n");
|
|
385
|
-
|
|
386
|
-
const invariantMatrixMd = [
|
|
387
|
-
generatedNotice(
|
|
388
|
-
"docs/agent/invariants.source.json, exported TSDoc @agentosInvariant tags, and docs/agent/error-metadata.source.json",
|
|
389
|
-
),
|
|
390
|
-
"",
|
|
391
|
-
"# Agent Invariant Matrix",
|
|
392
|
-
"",
|
|
393
|
-
table(
|
|
394
|
-
["Invariant", "Primitives", "Errors", "Docs", "Tests"],
|
|
395
|
-
invariantMatrix.map((row) => [
|
|
396
|
-
`\`${row.invariant}\``,
|
|
397
|
-
row.primitives.map((primitive) => `\`${primitive}\``).join(", "),
|
|
398
|
-
row.errors.map((error) => `\`${error}\``).join(", "),
|
|
399
|
-
row.docs
|
|
400
|
-
.map((doc) => mdLink("docs/agent/invariant-matrix.md", path.posix.basename(doc), doc))
|
|
401
|
-
.join(", "),
|
|
402
|
-
row.tests.map((test) => `\`${test}\``).join(", "),
|
|
403
|
-
]),
|
|
404
|
-
),
|
|
405
|
-
"",
|
|
406
|
-
].join("\n");
|
|
407
|
-
|
|
408
|
-
if (failures.length === 0) {
|
|
409
|
-
await writeJson("docs/agent/recipes.json", recipesJson);
|
|
410
|
-
await writeJson("docs/agent/primitives.json", primitivesJson);
|
|
411
|
-
await write("docs/agent/primitives.md", primitivesMd);
|
|
412
|
-
await writeJson("docs/agent/decision-graph.json", decisionGraphJson);
|
|
413
|
-
await write("docs/agent/decision-graph.md", decisionGraphMd);
|
|
414
|
-
await writeJson("docs/agent/errors.json", errorsJson);
|
|
415
|
-
await write("docs/agent/errors.md", errorsMd);
|
|
416
|
-
await writeJson("docs/agent/invariant-matrix.json", invariantMatrixJson);
|
|
417
|
-
await write("docs/agent/invariant-matrix.md", invariantMatrixMd);
|
|
418
|
-
await write("docs/start-here.md", startHere);
|
|
419
|
-
await replaceGeneratedBlock({
|
|
420
|
-
file: "AGENTS.md",
|
|
421
|
-
startMarker: agentNavigationStart,
|
|
422
|
-
endMarker: agentNavigationEnd,
|
|
423
|
-
block: agentNavigation,
|
|
424
|
-
insertBefore: "## BoundaryContract Checklist",
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
if (failures.length > 0) {
|
|
429
|
-
console.error(failures.join("\n"));
|
|
430
|
-
process.exit(1);
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
console.log(
|
|
434
|
-
`generated agent docs: ${recipesSource.recipes.length} recipes, ${primitives.length} primitives, ${errors.length} errors`,
|
|
435
|
-
);
|