agentopology 0.7.0 → 0.9.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/README.md +5 -5
- package/dist/__tests__/e2e/compile.test.js +68 -0
- package/dist/__tests__/e2e/compile.test.js.map +1 -1
- package/dist/__tests__/e2e/import-project.test.d.ts +10 -0
- package/dist/__tests__/e2e/import-project.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/import-project.test.js +260 -0
- package/dist/__tests__/e2e/import-project.test.js.map +1 -0
- package/dist/__tests__/e2e/roundtrip.test.js +2 -4
- package/dist/__tests__/e2e/roundtrip.test.js.map +1 -1
- package/dist/__tests__/e2e/unknown-names.test.d.ts +12 -0
- package/dist/__tests__/e2e/unknown-names.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/unknown-names.test.js +268 -0
- package/dist/__tests__/e2e/unknown-names.test.js.map +1 -0
- package/dist/__tests__/live/claude-code.live.test.js +7 -0
- package/dist/__tests__/live/claude-code.live.test.js.map +1 -1
- package/dist/__tests__/open-findings.test.js +7 -3
- package/dist/__tests__/open-findings.test.js.map +1 -1
- package/dist/bindings/claude-code.d.ts.map +1 -1
- package/dist/bindings/claude-code.js +140 -11
- package/dist/bindings/claude-code.js.map +1 -1
- package/dist/cli/index.js +42 -27
- package/dist/cli/index.js.map +1 -1
- package/dist/docs/content.js +19 -19
- package/dist/import/claude-code.d.ts +2 -1
- package/dist/import/claude-code.d.ts.map +1 -1
- package/dist/import/claude-code.js +36 -12
- package/dist/import/claude-code.js.map +1 -1
- package/dist/import/flow.d.ts +64 -0
- package/dist/import/flow.d.ts.map +1 -0
- package/dist/import/flow.js +176 -0
- package/dist/import/flow.js.map +1 -0
- package/dist/import/index.d.ts +11 -0
- package/dist/import/index.d.ts.map +1 -1
- package/dist/import/index.js +15 -4
- package/dist/import/index.js.map +1 -1
- package/dist/import/serializer.d.ts.map +1 -1
- package/dist/import/serializer.js +44 -3
- package/dist/import/serializer.js.map +1 -1
- package/dist/import/walk.d.ts +56 -0
- package/dist/import/walk.d.ts.map +1 -0
- package/dist/import/walk.js +163 -0
- package/dist/import/walk.js.map +1 -0
- package/dist/parser/__tests__/mod-hooks.test.js +1 -1
- package/dist/parser/__tests__/mod-hooks.test.js.map +1 -1
- package/dist/parser/ast.d.ts +46 -3
- package/dist/parser/ast.d.ts.map +1 -1
- package/dist/parser/ast.js.map +1 -1
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +42 -0
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/known-fields.d.ts +82 -0
- package/dist/parser/known-fields.d.ts.map +1 -0
- package/dist/parser/known-fields.js +263 -0
- package/dist/parser/known-fields.js.map +1 -0
- package/dist/parser/validator.d.ts +1 -1
- package/dist/parser/validator.d.ts.map +1 -1
- package/dist/parser/validator.js +114 -3
- package/dist/parser/validator.js.map +1 -1
- package/examples/matchmat-ship.at +2 -8
- package/examples/memory-system.at +0 -1
- package/package.json +1 -1
- package/spec/grammar.md +24 -1
- package/spec/testing.md +19 -4
- package/spec/validation.md +6 -2
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V102: every name written in a checked block is one the parser reads.
|
|
3
|
+
*
|
|
4
|
+
* The parser pulls fields out of a block by name and ignores the rest, so a
|
|
5
|
+
* typo (`modle:`), a field from another node type (`produces:` on an action)
|
|
6
|
+
* or a block the language does not have (`contract c1 { }`) vanished from the
|
|
7
|
+
* AST while validate printed "All validation rules passed" (N23). This module
|
|
8
|
+
* lists the names each block takes, as the parser reads them, and reports any
|
|
9
|
+
* other name with the block it sits in.
|
|
10
|
+
*
|
|
11
|
+
* Blocks whose contents are free-form or not yet listed (prompt, extensions,
|
|
12
|
+
* settings, env, mcp-servers, memory, providers, ...) are not looked inside;
|
|
13
|
+
* their own name is still checked where they sit.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
/** What a block takes: its fields, and the sub-blocks it may hold. */
|
|
18
|
+
interface BlockSchema {
|
|
19
|
+
/** `key: value` names, as written in the source. */
|
|
20
|
+
fields: readonly string[];
|
|
21
|
+
/** Sub-block names; `null` means the contents are not checked. */
|
|
22
|
+
blocks?: Readonly<Record<string, BlockSchema | null>>;
|
|
23
|
+
}
|
|
24
|
+
declare const NODE_SCHEMAS: {
|
|
25
|
+
orchestrator: {
|
|
26
|
+
fields: string[];
|
|
27
|
+
blocks: {
|
|
28
|
+
outputs: null;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
agent: BlockSchema;
|
|
32
|
+
action: {
|
|
33
|
+
fields: string[];
|
|
34
|
+
};
|
|
35
|
+
gate: {
|
|
36
|
+
fields: string[];
|
|
37
|
+
blocks: {
|
|
38
|
+
extensions: null;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
human: {
|
|
42
|
+
fields: string[];
|
|
43
|
+
};
|
|
44
|
+
group: {
|
|
45
|
+
fields: string[];
|
|
46
|
+
};
|
|
47
|
+
hook: BlockSchema;
|
|
48
|
+
job: {
|
|
49
|
+
fields: string[];
|
|
50
|
+
};
|
|
51
|
+
artifact: {
|
|
52
|
+
fields: string[];
|
|
53
|
+
};
|
|
54
|
+
tool: {
|
|
55
|
+
fields: string[];
|
|
56
|
+
};
|
|
57
|
+
skill: {
|
|
58
|
+
fields: string[];
|
|
59
|
+
blocks: {
|
|
60
|
+
prompt: null;
|
|
61
|
+
extensions: null;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
meta: {
|
|
65
|
+
fields: string[];
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
export interface UnknownNameResult {
|
|
69
|
+
rule: "V102";
|
|
70
|
+
level: "error";
|
|
71
|
+
message: string;
|
|
72
|
+
node?: string;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Report every field and block name in a topology body that the parser does
|
|
76
|
+
* not read. `topBody` is the comment-stripped body of `topology { }`.
|
|
77
|
+
*/
|
|
78
|
+
export declare function findUnknownNames(topBody: string): UnknownNameResult[];
|
|
79
|
+
/** The source names each checked block takes (for docs and tests). */
|
|
80
|
+
export declare const KNOWN_FIELDS: Readonly<Record<keyof typeof NODE_SCHEMAS, readonly string[]>>;
|
|
81
|
+
export {};
|
|
82
|
+
//# sourceMappingURL=known-fields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"known-fields.d.ts","sourceRoot":"","sources":["../../src/parser/known-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,sEAAsE;AACtE,UAAU,WAAW;IACnB,oDAAoD;IACpD,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,kEAAkE;IAClE,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC;CACvD;AAuCD,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BqB,CAAC;AA2HxC,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAmDD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAIrE;AAED,sEAAsE;AACtE,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,OAAO,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC,CACE,CAAC"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V102: every name written in a checked block is one the parser reads.
|
|
3
|
+
*
|
|
4
|
+
* The parser pulls fields out of a block by name and ignores the rest, so a
|
|
5
|
+
* typo (`modle:`), a field from another node type (`produces:` on an action)
|
|
6
|
+
* or a block the language does not have (`contract c1 { }`) vanished from the
|
|
7
|
+
* AST while validate printed "All validation rules passed" (N23). This module
|
|
8
|
+
* lists the names each block takes, as the parser reads them, and reports any
|
|
9
|
+
* other name with the block it sits in.
|
|
10
|
+
*
|
|
11
|
+
* Blocks whose contents are free-form or not yet listed (prompt, extensions,
|
|
12
|
+
* settings, env, mcp-servers, memory, providers, ...) are not looked inside;
|
|
13
|
+
* their own name is still checked where they sit.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
const OPAQUE = null;
|
|
18
|
+
const HOOK = {
|
|
19
|
+
fields: ["on", "matcher", "run", "type", "flag", "timeout", "url", "server", "tool", "async", "model"],
|
|
20
|
+
blocks: { extensions: OPAQUE },
|
|
21
|
+
};
|
|
22
|
+
const AGENT = {
|
|
23
|
+
fields: [
|
|
24
|
+
"file", "role", "model", "permissions", "phase", "description",
|
|
25
|
+
"tools", "disallowed-tools", "skills", "reads", "writes", "mcp-servers",
|
|
26
|
+
"produces", "consumes", "outputs", "skip", "behavior", "invocation", "runs",
|
|
27
|
+
"independent", "color", "initial-prompt", "omit-claude-md", "retry", "timeout",
|
|
28
|
+
"on-fail", "temperature", "max-tokens", "top-p", "top-k", "stop", "seed",
|
|
29
|
+
"thinking", "thinking-budget", "output-format", "log-level", "join",
|
|
30
|
+
"compensates", "isolation", "background", "memory", "retrieval",
|
|
31
|
+
"custodian-of", "sandbox", "fallback-chain", "rate-limit", "max-turns",
|
|
32
|
+
// `prompt:` is a known misuse, reported by V89 with a better message.
|
|
33
|
+
"prompt",
|
|
34
|
+
],
|
|
35
|
+
blocks: {
|
|
36
|
+
prompt: OPAQUE,
|
|
37
|
+
outputs: OPAQUE,
|
|
38
|
+
extensions: OPAQUE,
|
|
39
|
+
"input-schema": OPAQUE,
|
|
40
|
+
"output-schema": OPAQUE,
|
|
41
|
+
retry: { fields: ["max", "backoff", "interval", "max-interval", "jitter", "non-retryable"] },
|
|
42
|
+
"circuit-breaker": { fields: ["threshold", "window", "cooldown"] },
|
|
43
|
+
scale: { fields: ["mode", "by", "min", "max", "batch-size"] },
|
|
44
|
+
hooks: { fields: [], blocks: { hook: HOOK } },
|
|
45
|
+
variants: {
|
|
46
|
+
fields: [],
|
|
47
|
+
blocks: { variant: { fields: ["weight", "model", "temperature"], blocks: { prompt: OPAQUE } } },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
const NODE_SCHEMAS = {
|
|
52
|
+
orchestrator: {
|
|
53
|
+
fields: ["model", "handles", "generates", "delegation", "outputs"],
|
|
54
|
+
blocks: { outputs: OPAQUE },
|
|
55
|
+
},
|
|
56
|
+
agent: AGENT,
|
|
57
|
+
action: { fields: ["kind", "source", "description", "commands", "timeout", "on-fail", "join"] },
|
|
58
|
+
gate: {
|
|
59
|
+
fields: ["after", "before", "run", "checks", "retry", "on-fail", "behavior", "timeout"],
|
|
60
|
+
blocks: { extensions: OPAQUE },
|
|
61
|
+
},
|
|
62
|
+
human: { fields: ["file", "description", "timeout", "on-timeout"] },
|
|
63
|
+
group: {
|
|
64
|
+
fields: ["members", "file", "speaker-selection", "max-rounds", "termination", "description", "timeout"],
|
|
65
|
+
},
|
|
66
|
+
hook: HOOK,
|
|
67
|
+
job: { fields: ["cron", "every", "agent", "action", "enabled"] },
|
|
68
|
+
artifact: { fields: ["type", "path", "retention", "depends-on"] },
|
|
69
|
+
tool: { fields: ["script", "description", "args", "lang"] },
|
|
70
|
+
skill: {
|
|
71
|
+
fields: [
|
|
72
|
+
"description", "scripts", "domains", "references", "prompt", "file", "when-to-use",
|
|
73
|
+
"argument-hint", "shell", "model", "thinking", "arguments", "paths", "disallowed-tools",
|
|
74
|
+
"disable-model-invocation", "user-invocable", "context", "agent", "allowed-tools",
|
|
75
|
+
],
|
|
76
|
+
blocks: { prompt: OPAQUE, extensions: OPAQUE },
|
|
77
|
+
},
|
|
78
|
+
meta: {
|
|
79
|
+
fields: ["version", "description", "domain", "foundations", "advanced", "timeout", "error-handler", "durable"],
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
/** The topology body: the sections it takes. Fields are not allowed there. */
|
|
83
|
+
const TOPOLOGY = {
|
|
84
|
+
fields: [],
|
|
85
|
+
blocks: {
|
|
86
|
+
meta: NODE_SCHEMAS.meta,
|
|
87
|
+
orchestrator: NODE_SCHEMAS.orchestrator,
|
|
88
|
+
agent: NODE_SCHEMAS.agent,
|
|
89
|
+
action: NODE_SCHEMAS.action,
|
|
90
|
+
human: NODE_SCHEMAS.human,
|
|
91
|
+
group: NODE_SCHEMAS.group,
|
|
92
|
+
skill: NODE_SCHEMAS.skill,
|
|
93
|
+
gates: { fields: [], blocks: { gate: NODE_SCHEMAS.gate } },
|
|
94
|
+
hooks: { fields: [], blocks: { hook: NODE_SCHEMAS.hook } },
|
|
95
|
+
schedule: { fields: [], blocks: { job: NODE_SCHEMAS.job } },
|
|
96
|
+
artifacts: { fields: [], blocks: { artifact: NODE_SCHEMAS.artifact } },
|
|
97
|
+
tools: { fields: [], blocks: { tool: NODE_SCHEMAS.tool } },
|
|
98
|
+
// Not in the grammar, but the parser reads the skills inside it.
|
|
99
|
+
skills: { fields: [], blocks: { skill: NODE_SCHEMAS.skill } },
|
|
100
|
+
// Sections whose contents are not checked yet.
|
|
101
|
+
roles: OPAQUE, flow: OPAQUE, depth: OPAQUE, memory: OPAQUE, batch: OPAQUE,
|
|
102
|
+
environments: OPAQUE, triggers: OPAQUE, settings: OPAQUE, "mcp-servers": OPAQUE,
|
|
103
|
+
metering: OPAQUE, context: OPAQUE, env: OPAQUE, providers: OPAQUE,
|
|
104
|
+
interfaces: OPAQUE, defaults: OPAQUE, schemas: OPAQUE, observability: OPAQUE,
|
|
105
|
+
checkpoint: OPAQUE, extensions: OPAQUE, params: OPAQUE, interface: OPAQUE,
|
|
106
|
+
// Children of a misplaced container keyword are reported by V98 instead.
|
|
107
|
+
gate: OPAQUE, hook: OPAQUE, tool: OPAQUE, job: OPAQUE, command: OPAQUE, level: OPAQUE,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
/** Which node kinds take a field, for "it is a field of X" in the message. */
|
|
111
|
+
function kindsWithField(field) {
|
|
112
|
+
return Object.entries(NODE_SCHEMAS)
|
|
113
|
+
.filter(([, s]) => s.fields.includes(field))
|
|
114
|
+
.map(([k]) => k);
|
|
115
|
+
}
|
|
116
|
+
function editDistance(a, b) {
|
|
117
|
+
const d = Array.from({ length: a.length + 1 }, (_, i) => [i, ...Array(b.length).fill(0)]);
|
|
118
|
+
for (let j = 1; j <= b.length; j++)
|
|
119
|
+
d[0][j] = j;
|
|
120
|
+
for (let i = 1; i <= a.length; i++) {
|
|
121
|
+
for (let j = 1; j <= b.length; j++) {
|
|
122
|
+
d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
123
|
+
// A swap of two neighbours (`modle`) is one edit, not two.
|
|
124
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
|
125
|
+
d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return d[a.length][b.length];
|
|
130
|
+
}
|
|
131
|
+
/** The closest known name, if it is close enough to be a typo. */
|
|
132
|
+
function closest(name, known) {
|
|
133
|
+
let best;
|
|
134
|
+
let bestD = Infinity;
|
|
135
|
+
for (const k of known) {
|
|
136
|
+
const dist = editDistance(name, k);
|
|
137
|
+
if (dist < bestD) {
|
|
138
|
+
best = k;
|
|
139
|
+
bestD = dist;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return bestD <= (name.length <= 4 ? 1 : 2) ? best : undefined;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The fields and sub-blocks written directly in `body` (not inside a nested
|
|
146
|
+
* block or a multi-line list). Quoted strings are skipped when counting
|
|
147
|
+
* braces and brackets.
|
|
148
|
+
*/
|
|
149
|
+
function topLevelItems(body) {
|
|
150
|
+
const items = [];
|
|
151
|
+
let depth = 0;
|
|
152
|
+
let bracket = 0;
|
|
153
|
+
let i = 0;
|
|
154
|
+
while (i < body.length) {
|
|
155
|
+
// At the start of a line, at depth 0, outside a list: read the head.
|
|
156
|
+
if (depth === 0 && bracket === 0) {
|
|
157
|
+
const rest = body.slice(i);
|
|
158
|
+
const field = /^[ \t]*([A-Za-z_][\w-]*)[ \t]*:/.exec(rest);
|
|
159
|
+
const block = field ? null : /^[ \t]*([A-Za-z_][\w-]*)(?:[ \t]+([A-Za-z_][\w-]*))?[^\n{:"]*\{/.exec(rest);
|
|
160
|
+
if (field) {
|
|
161
|
+
items.push({ kind: "field", name: field[1] });
|
|
162
|
+
}
|
|
163
|
+
else if (block) {
|
|
164
|
+
const open = i + block[0].length;
|
|
165
|
+
let d = 1;
|
|
166
|
+
let j = open;
|
|
167
|
+
while (j < body.length && d > 0) {
|
|
168
|
+
if (body[j] === "{")
|
|
169
|
+
d++;
|
|
170
|
+
else if (body[j] === "}")
|
|
171
|
+
d--;
|
|
172
|
+
j++;
|
|
173
|
+
}
|
|
174
|
+
items.push({ kind: "block", name: block[1], id: block[2], body: body.slice(open, j - 1) });
|
|
175
|
+
i = j;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Walk to the next line, tracking depth.
|
|
180
|
+
let inStr = false;
|
|
181
|
+
while (i < body.length && body[i] !== "\n") {
|
|
182
|
+
const ch = body[i];
|
|
183
|
+
if (ch === '"' && body[i - 1] !== "\\")
|
|
184
|
+
inStr = !inStr;
|
|
185
|
+
else if (!inStr) {
|
|
186
|
+
if (ch === "{")
|
|
187
|
+
depth++;
|
|
188
|
+
else if (ch === "}")
|
|
189
|
+
depth--;
|
|
190
|
+
else if (ch === "[")
|
|
191
|
+
bracket++;
|
|
192
|
+
else if (ch === "]")
|
|
193
|
+
bracket--;
|
|
194
|
+
}
|
|
195
|
+
i++;
|
|
196
|
+
}
|
|
197
|
+
i++;
|
|
198
|
+
}
|
|
199
|
+
return items;
|
|
200
|
+
}
|
|
201
|
+
/** "an agent", "a gate". */
|
|
202
|
+
function article(word) {
|
|
203
|
+
return /^[aeiou]/.test(word) ? `an ${word}` : `a ${word}`;
|
|
204
|
+
}
|
|
205
|
+
function describeBlock(kind, id) {
|
|
206
|
+
return id ? `${kind} "${id}"` : kind === "topology" ? "the topology" : `"${kind}"`;
|
|
207
|
+
}
|
|
208
|
+
function check(body, schema, kind, id, out) {
|
|
209
|
+
const where = describeBlock(kind, id);
|
|
210
|
+
const node = id ?? (kind === "topology" ? undefined : kind);
|
|
211
|
+
const blocks = schema.blocks ?? {};
|
|
212
|
+
for (const item of topLevelItems(body)) {
|
|
213
|
+
if (item.kind === "field") {
|
|
214
|
+
if (schema.fields.includes(item.name))
|
|
215
|
+
continue;
|
|
216
|
+
// `outputs: { ... }` and `custodian-of: [..] { }` are fields with a brace.
|
|
217
|
+
if (item.name in blocks)
|
|
218
|
+
continue;
|
|
219
|
+
const elsewhere = kindsWithField(item.name).filter((k) => k !== kind);
|
|
220
|
+
const guess = closest(item.name, schema.fields);
|
|
221
|
+
let message;
|
|
222
|
+
if (kind === "topology") {
|
|
223
|
+
message = `\`${item.name}:\` is not a topology section, so it is ignored. Fields go inside a block (meta, an agent, ...)`;
|
|
224
|
+
}
|
|
225
|
+
else if (elsewhere.length > 0) {
|
|
226
|
+
message = `\`${item.name}\` is not ${article(kind)} field (it is a field of ${elsewhere.join(", ")}), so on ${where} it is ignored`;
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
message = `${where} has \`${item.name}:\`, which is not ${article(kind)} field, so it is ignored` +
|
|
230
|
+
(guess ? `. Did you mean \`${guess}\`?` : "");
|
|
231
|
+
}
|
|
232
|
+
out.push({ rule: "V102", level: "error", message, ...(node ? { node } : {}) });
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (item.name in blocks) {
|
|
236
|
+
const sub = blocks[item.name];
|
|
237
|
+
if (sub)
|
|
238
|
+
check(item.body ?? "", sub, item.name, item.id, out);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const label = item.id ? `${item.name} ${item.id}` : item.name;
|
|
242
|
+
const guess = closest(item.name, Object.keys(blocks));
|
|
243
|
+
out.push({
|
|
244
|
+
rule: "V102",
|
|
245
|
+
level: "error",
|
|
246
|
+
message: `${where} has a \`${label} { }\` block, which is not ${kind === "topology" ? "a topology section" : article(`${kind} block`)}, so it is ignored` +
|
|
247
|
+
(guess ? `. Did you mean \`${guess}\`?` : ""),
|
|
248
|
+
...(node ? { node } : {}),
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Report every field and block name in a topology body that the parser does
|
|
254
|
+
* not read. `topBody` is the comment-stripped body of `topology { }`.
|
|
255
|
+
*/
|
|
256
|
+
export function findUnknownNames(topBody) {
|
|
257
|
+
const out = [];
|
|
258
|
+
check(topBody, TOPOLOGY, "topology", undefined, out);
|
|
259
|
+
return out;
|
|
260
|
+
}
|
|
261
|
+
/** The source names each checked block takes (for docs and tests). */
|
|
262
|
+
export const KNOWN_FIELDS = Object.fromEntries(Object.entries(NODE_SCHEMAS).map(([k, s]) => [k, s.fields]));
|
|
263
|
+
//# sourceMappingURL=known-fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"known-fields.js","sourceRoot":"","sources":["../../src/parser/known-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAUH,MAAM,MAAM,GAAG,IAAI,CAAC;AAEpB,MAAM,IAAI,GAAgB;IACxB,MAAM,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC;IACtG,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;CAC/B,CAAC;AAEF,MAAM,KAAK,GAAgB;IACzB,MAAM,EAAE;QACN,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa;QAC9D,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa;QACvE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM;QAC3E,aAAa,EAAE,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,OAAO,EAAE,SAAS;QAC9E,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM;QACxE,UAAU,EAAE,iBAAiB,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM;QACnE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW;QAC/D,cAAc,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW;QACtE,sEAAsE;QACtE,QAAQ;KACT;IACD,MAAM,EAAE;QACN,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,MAAM;QACf,UAAU,EAAE,MAAM;QAClB,cAAc,EAAE,MAAM;QACtB,eAAe,EAAE,MAAM;QACvB,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,eAAe,CAAC,EAAE;QAC5F,iBAAiB,EAAE,EAAE,MAAM,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE;QAClE,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,EAAE;QAC7D,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;QAC7C,QAAQ,EAAE;YACR,MAAM,EAAE,EAAE;YACV,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;SAChG;KACF;CACF,CAAC;AAEF,MAAM,YAAY,GAAG;IACnB,YAAY,EAAE;QACZ,MAAM,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,CAAC;QAClE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;KAC5B;IACD,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE;IAC/F,IAAI,EAAE;QACJ,MAAM,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC;QACvF,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;KAC/B;IACD,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,CAAC,EAAE;IACnE,KAAK,EAAE;QACL,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,CAAC;KACxG;IACD,IAAI,EAAE,IAAI;IACV,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE;IAChE,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE;IACjE,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE;IAC3D,KAAK,EAAE;QACL,MAAM,EAAE;YACN,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa;YAClF,eAAe,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,kBAAkB;YACvF,0BAA0B,EAAE,gBAAgB,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe;SAClF;QACD,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE;KAC/C;IACD,IAAI,EAAE;QACJ,MAAM,EAAE,CAAC,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,CAAC;KAC/G;CACoC,CAAC;AAExC,8EAA8E;AAC9E,MAAM,QAAQ,GAAgB;IAC5B,MAAM,EAAE,EAAE;IACV,MAAM,EAAE;QACN,IAAI,EAAE,YAAY,CAAC,IAAI;QACvB,YAAY,EAAE,YAAY,CAAC,YAAY;QACvC,KAAK,EAAE,YAAY,CAAC,KAAK;QACzB,MAAM,EAAE,YAAY,CAAC,MAAM;QAC3B,KAAK,EAAE,YAAY,CAAC,KAAK;QACzB,KAAK,EAAE,YAAY,CAAC,KAAK;QACzB,KAAK,EAAE,YAAY,CAAC,KAAK;QACzB,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,EAAE,EAAE;QAC1D,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,EAAE,EAAE;QAC1D,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,YAAY,CAAC,GAAG,EAAE,EAAE;QAC3D,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,YAAY,CAAC,QAAQ,EAAE,EAAE;QACtE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,EAAE,EAAE;QAC1D,iEAAiE;QACjE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,YAAY,CAAC,KAAK,EAAE,EAAE;QAC7D,+CAA+C;QAC/C,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;QACzE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;QAC/E,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;QACjE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;QAC5E,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;QACzE,yEAAyE;QACzE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;KACtF;CACF,CAAC;AAEF,8EAA8E;AAC9E,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC;SAChC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAE,CAAC,CAAC,MAA4B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SAClE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,YAAY,CAAC,CAAS,EAAE,CAAS;IACxC,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1F,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAChD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACnC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACxG,2DAA2D;YAC3D,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBACrE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,kEAAkE;AAClE,SAAS,OAAO,CAAC,IAAY,EAAE,KAAwB;IACrD,IAAI,IAAwB,CAAC;IAC7B,IAAI,KAAK,GAAG,QAAQ,CAAC;IACrB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACnC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;YAAC,IAAI,GAAG,CAAC,CAAC;YAAC,KAAK,GAAG,IAAI,CAAC;QAAC,CAAC;IAC/C,CAAC;IACD,OAAO,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AAChE,CAAC;AAWD;;;;GAIG;AACH,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,qEAAqE;QACrE,IAAI,KAAK,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,KAAK,GAAG,iCAAiC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3D,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,iEAAiE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1G,IAAI,KAAK,EAAE,CAAC;gBACV,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAChD,CAAC;iBAAM,IAAI,KAAK,EAAE,CAAC;gBACjB,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;gBACjC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACV,IAAI,CAAC,GAAG,IAAI,CAAC;gBACb,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBAChC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;wBAAE,CAAC,EAAE,CAAC;yBACpB,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;wBAAE,CAAC,EAAE,CAAC;oBAC9B,CAAC,EAAE,CAAC;gBACN,CAAC;gBACD,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC3F,CAAC,GAAG,CAAC,CAAC;gBACN,SAAS;YACX,CAAC;QACH,CAAC;QACD,yCAAyC;QACzC,IAAI,KAAK,GAAG,KAAK,CAAC;QAClB,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACnB,IAAI,EAAE,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI;gBAAE,KAAK,GAAG,CAAC,KAAK,CAAC;iBAClD,IAAI,CAAC,KAAK,EAAE,CAAC;gBAChB,IAAI,EAAE,KAAK,GAAG;oBAAE,KAAK,EAAE,CAAC;qBACnB,IAAI,EAAE,KAAK,GAAG;oBAAE,KAAK,EAAE,CAAC;qBACxB,IAAI,EAAE,KAAK,GAAG;oBAAE,OAAO,EAAE,CAAC;qBAC1B,IAAI,EAAE,KAAK,GAAG;oBAAE,OAAO,EAAE,CAAC;YACjC,CAAC;YACD,CAAC,EAAE,CAAC;QACN,CAAC;QACD,CAAC,EAAE,CAAC;IACN,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AASD,4BAA4B;AAC5B,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;AAC5D,CAAC;AAED,SAAS,aAAa,CAAC,IAAY,EAAE,EAAsB;IACzD,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC;AACrF,CAAC;AAED,SAAS,KAAK,CAAC,IAAY,EAAE,MAAmB,EAAE,IAAY,EAAE,EAAsB,EAAE,GAAwB;IAC9G,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,EAAE,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;IACnC,KAAK,MAAM,IAAI,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1B,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,SAAS;YAChD,2EAA2E;YAC3E,IAAI,IAAI,CAAC,IAAI,IAAI,MAAM;gBAAE,SAAS;YAClC,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;YACtE,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YAChD,IAAI,OAAe,CAAC;YACpB,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;gBACxB,OAAO,GAAG,KAAK,IAAI,CAAC,IAAI,iGAAiG,CAAC;YAC5H,CAAC;iBAAM,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAChC,OAAO,GAAG,KAAK,IAAI,CAAC,IAAI,aAAa,OAAO,CAAC,IAAI,CAAC,4BAA4B,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,KAAK,gBAAgB,CAAC;YACtI,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,GAAG,KAAK,UAAU,IAAI,CAAC,IAAI,qBAAqB,OAAO,CAAC,IAAI,CAAC,0BAA0B;oBAC/F,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAClD,CAAC;YACD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YAC/E,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,IAAI,MAAM,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,GAAG;gBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YAC9D,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QACtD,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,GAAG,KAAK,YAAY,KAAK,8BAA8B,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI,QAAQ,CAAC,oBAAoB;gBACvJ,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/C,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1B,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;IACrD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,sEAAsE;AACtE,MAAM,CAAC,MAAM,YAAY,GACvB,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAU,CAAC"}
|
|
@@ -55,6 +55,6 @@ export declare function findBackEdges(edges: EdgeDef[], nodeIds: Set<string>): E
|
|
|
55
55
|
export declare const MOD_EVENTS_PROVEN: readonly ["tool.call", "prompt.submit"];
|
|
56
56
|
export declare const MOD_EVENTS_SEEN: readonly ["prompt.context", "session.start", "session.end", "model.complete", "message.received"];
|
|
57
57
|
/** Hook types the grammar defines (`spec/grammar.md` §4.16). */
|
|
58
|
-
export declare const HOOK_TYPES: readonly ["command", "prompt", "mod"];
|
|
58
|
+
export declare const HOOK_TYPES: readonly ["command", "prompt", "agent", "http", "mcp_tool", "mod"];
|
|
59
59
|
export declare function validate(ast: TopologyAST): ValidationResult[];
|
|
60
60
|
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/parser/validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,KAAK,EAEV,WAAW,EAKX,OAAO,EAkBR,MAAM,UAAU,CAAC;AAMlB,mCAAmC;AACnC,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,KAAK,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAyMD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,EAAE,CAyD/E;
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/parser/validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,KAAK,EAEV,WAAW,EAKX,OAAO,EAkBR,MAAM,UAAU,CAAC;AAMlB,mCAAmC;AACnC,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,KAAK,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAyMD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,EAAE,CAyD/E;AA8iGD;;;;;GAKG;AACH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,yCAA0C,CAAC;AACzE,eAAO,MAAM,eAAe,mGAElB,CAAC;AAEX,gEAAgE;AAChE,eAAO,MAAM,UAAU,oEAAqE,CAAC;AAyH7F,wBAAgB,QAAQ,CAAC,GAAG,EAAE,WAAW,GAAG,gBAAgB,EAAE,CA0G7D"}
|
package/dist/parser/validator.js
CHANGED
|
@@ -1019,8 +1019,9 @@ function v96RunIsCommand(ast) {
|
|
|
1019
1019
|
...(nodeId ? { node: nodeId, line: lookupLine(ast, nodeId) } : {}),
|
|
1020
1020
|
});
|
|
1021
1021
|
};
|
|
1022
|
-
//
|
|
1023
|
-
|
|
1022
|
+
// Only a command hook's run: is a command: a prompt or agent hook's run: is
|
|
1023
|
+
// the prompt itself, and http, mcp_tool and mod hooks are checked by V100/V95.
|
|
1024
|
+
const isCommand = (hook) => (hook.type ?? "command") === "command";
|
|
1024
1025
|
for (const node of ast.nodes) {
|
|
1025
1026
|
if (isGate(node))
|
|
1026
1027
|
check(`Gate "${node.id}"`, node.run, node.id);
|
|
@@ -1064,6 +1065,112 @@ function v97FileIsProjectPath(ast) {
|
|
|
1064
1065
|
check(`Command "${trigger.name}"`, trigger.file);
|
|
1065
1066
|
return results;
|
|
1066
1067
|
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Claude Code events that take only command, http and mcp_tool hooks (no
|
|
1070
|
+
* prompt or agent judge), and the two that take only command and mcp_tool.
|
|
1071
|
+
* From the hooks reference, 2026-10-30.
|
|
1072
|
+
*/
|
|
1073
|
+
const NO_JUDGE_EVENTS = new Set([
|
|
1074
|
+
"ConfigChange", "CwdChanged", "DirectoryAdded", "Elicitation", "ElicitationResult", "FileChanged",
|
|
1075
|
+
"InstructionsLoaded", "MessageDisplay", "Notification", "PostCompact", "PostModelSwitch", "PreCompact",
|
|
1076
|
+
"PreModelSwitch", "SessionEnd", "StopFailure", "SubagentStart", "WorktreeCreate", "WorktreeRemove",
|
|
1077
|
+
"AgentStart",
|
|
1078
|
+
]);
|
|
1079
|
+
const COMMAND_OR_MCP_EVENTS = new Set(["SessionStart", "Setup"]);
|
|
1080
|
+
/**
|
|
1081
|
+
* V100: a hook has what its type needs, on an event that takes that type.
|
|
1082
|
+
*
|
|
1083
|
+
* A command needs run:, a prompt or agent hook needs run: (the prompt), an
|
|
1084
|
+
* http hook url:, an mcp_tool hook server: and tool:. A prompt or agent judge
|
|
1085
|
+
* on an event Claude Code runs only command hooks for never fires.
|
|
1086
|
+
*/
|
|
1087
|
+
function v100HookTypeFields(ast) {
|
|
1088
|
+
const results = [];
|
|
1089
|
+
for (const { hook, where } of allHooks(ast)) {
|
|
1090
|
+
const type = hook.type ?? "command";
|
|
1091
|
+
if (type === "mod")
|
|
1092
|
+
continue; // V95
|
|
1093
|
+
const err = (message) => results.push({ rule: "V100", level: "error", message: `Hook "${where}" ${message}`, node: where });
|
|
1094
|
+
if ((type === "command" || type === "prompt" || type === "agent") && !hook.run) {
|
|
1095
|
+
err(`is type ${type} but has no run: — ${type === "command" ? "name the command" : "write the prompt in run:"}.`);
|
|
1096
|
+
}
|
|
1097
|
+
if (type === "http" && !hook.url)
|
|
1098
|
+
err(`is type http but has no url: — name the endpoint the event is sent to.`);
|
|
1099
|
+
if (type === "mcp_tool" && (!hook.server || !hook.tool))
|
|
1100
|
+
err(`is type mcp_tool but needs server: and tool:.`);
|
|
1101
|
+
if (type !== "command" && hook.async !== undefined)
|
|
1102
|
+
err(`sets async: but is type ${type} — async: belongs to a command hook.`);
|
|
1103
|
+
const on = hook.on.trim();
|
|
1104
|
+
if ((type === "prompt" || type === "agent") && NO_JUDGE_EVENTS.has(on)) {
|
|
1105
|
+
err(`is a ${type} hook on ${on}, which Claude Code runs only command, http and mcp_tool hooks for — it would never fire.`);
|
|
1106
|
+
}
|
|
1107
|
+
else if ((type === "prompt" || type === "agent" || type === "http") && COMMAND_OR_MCP_EVENTS.has(on)) {
|
|
1108
|
+
err(`is a ${type} hook on ${on}, which takes only command and mcp_tool hooks — it would never fire.`);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
return results;
|
|
1112
|
+
}
|
|
1113
|
+
/**
|
|
1114
|
+
* V101: a node's run-mode is one Claude Code has, and an independent node
|
|
1115
|
+
* does not share its caller's context.
|
|
1116
|
+
*
|
|
1117
|
+
* runs: subagent (own agent), inline (a skill in the caller's context) or
|
|
1118
|
+
* fork (a skill in a fresh forked context). An independent critic running
|
|
1119
|
+
* inline would read the author's reasoning, which is what independent forbids.
|
|
1120
|
+
*/
|
|
1121
|
+
const RUN_MODES = ["subagent", "inline", "fork"];
|
|
1122
|
+
function v101RunMode(ast) {
|
|
1123
|
+
const results = [];
|
|
1124
|
+
for (const node of ast.nodes) {
|
|
1125
|
+
if (node.type !== "agent")
|
|
1126
|
+
continue;
|
|
1127
|
+
const agent = node;
|
|
1128
|
+
if (agent.runs !== undefined && !RUN_MODES.includes(agent.runs)) {
|
|
1129
|
+
results.push({
|
|
1130
|
+
rule: "V101", level: "error", node: agent.id, line: lookupLine(ast, agent.id),
|
|
1131
|
+
message: `Agent "${agent.id}" has runs: ${agent.runs} — a node runs as ${RUN_MODES.join(", ")}.`,
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
if (agent.independent && agent.runs === "inline") {
|
|
1135
|
+
results.push({
|
|
1136
|
+
rule: "V101", level: "error", node: agent.id, line: lookupLine(ast, agent.id),
|
|
1137
|
+
message: `Agent "${agent.id}" is independent but runs inline, in its caller's context, where it reads the author's reasoning. Use runs: fork or runs: subagent.`,
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
return results;
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* V102: every field and block name in a checked block is one the parser reads.
|
|
1145
|
+
*
|
|
1146
|
+
* Anything else (a typo, a field of another node type, a block the language
|
|
1147
|
+
* does not have) vanished from the AST while validate passed. Collected at
|
|
1148
|
+
* parse time by findUnknownNames (src/parser/known-fields.ts).
|
|
1149
|
+
*/
|
|
1150
|
+
function v102KnownNames(ast) {
|
|
1151
|
+
return (ast._unknownNameErrors ?? []).map((r) => r.node && lookupLine(ast, r.node) !== undefined ? { ...r, line: lookupLine(ast, r.node) } : r);
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* V103: an agent's or gate's `behavior` is `advisory` or `blocking`.
|
|
1155
|
+
*
|
|
1156
|
+
* Any other value was kept and emitted into the scaffold unchecked (N24).
|
|
1157
|
+
*/
|
|
1158
|
+
const BEHAVIORS = ["advisory", "blocking"];
|
|
1159
|
+
function v103BehaviorValue(ast) {
|
|
1160
|
+
const results = [];
|
|
1161
|
+
for (const node of ast.nodes) {
|
|
1162
|
+
if (node.type !== "agent" && node.type !== "gate")
|
|
1163
|
+
continue;
|
|
1164
|
+
const behavior = node.behavior;
|
|
1165
|
+
if (behavior === undefined || BEHAVIORS.includes(behavior))
|
|
1166
|
+
continue;
|
|
1167
|
+
results.push({
|
|
1168
|
+
rule: "V103", level: "error", node: node.id, line: lookupLine(ast, node.id),
|
|
1169
|
+
message: `${node.type === "agent" ? "Agent" : "Gate"} "${node.id}" has behavior: ${behavior} — it must be ${BEHAVIORS.join(" or ")}.`,
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
return results;
|
|
1173
|
+
}
|
|
1067
1174
|
/**
|
|
1068
1175
|
* V98: a block that belongs inside a container is not written beside it.
|
|
1069
1176
|
*
|
|
@@ -3078,7 +3185,7 @@ export const MOD_EVENTS_SEEN = [
|
|
|
3078
3185
|
"prompt.context", "session.start", "session.end", "model.complete", "message.received",
|
|
3079
3186
|
];
|
|
3080
3187
|
/** Hook types the grammar defines (`spec/grammar.md` §4.16). */
|
|
3081
|
-
export const HOOK_TYPES = ["command", "prompt", "mod"];
|
|
3188
|
+
export const HOOK_TYPES = ["command", "prompt", "agent", "http", "mcp_tool", "mod"];
|
|
3082
3189
|
/** Every hook in the topology — global first, then per-agent — with its owner. */
|
|
3083
3190
|
function allHooks(ast) {
|
|
3084
3191
|
const out = ast.hooks.map((hook) => ({ hook, where: hook.name }));
|
|
@@ -3236,6 +3343,10 @@ export function validate(ast) {
|
|
|
3236
3343
|
...v97FileIsProjectPath(ast),
|
|
3237
3344
|
...v98BlockInsideItsContainer(ast),
|
|
3238
3345
|
...v99ReferencesDeclared(ast),
|
|
3346
|
+
...v100HookTypeFields(ast),
|
|
3347
|
+
...v101RunMode(ast),
|
|
3348
|
+
...v102KnownNames(ast),
|
|
3349
|
+
...v103BehaviorValue(ast),
|
|
3239
3350
|
...v25BounceBackAdvisory(ast),
|
|
3240
3351
|
...v26ActionKindEnum(ast),
|
|
3241
3352
|
...v27AgentPermissionsEnum(ast),
|