skillscript-runtime 0.3.2 → 0.4.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/ARCHITECTURE.md +74 -43
- package/README.md +26 -0
- package/dist/bootstrap.d.ts +15 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +26 -0
- package/dist/bootstrap.js.map +1 -1
- package/dist/compile.d.ts +13 -0
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +12 -1
- package/dist/compile.js.map +1 -1
- package/dist/connectors/config.d.ts +72 -0
- package/dist/connectors/config.d.ts.map +1 -0
- package/dist/connectors/config.js +147 -0
- package/dist/connectors/config.js.map +1 -0
- package/dist/filters.d.ts +6 -1
- package/dist/filters.d.ts.map +1 -1
- package/dist/filters.js +7 -17
- package/dist/filters.js.map +1 -1
- package/dist/help-content.d.ts.map +1 -1
- package/dist/help-content.js +18 -1
- package/dist/help-content.js.map +1 -1
- package/dist/lint.d.ts +18 -0
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +137 -4
- package/dist/lint.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +13 -1
- package/dist/mcp-server.js.map +1 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +69 -15
- package/dist/parser.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +71 -25
- package/dist/runtime.js.map +1 -1
- package/examples/hello.skill.provenance.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// v0.4.0 — `connectors.json` loader. Reads + validates the per-host
|
|
2
|
+
// connector configuration file at runtime startup and wires the
|
|
3
|
+
// declared instances into the Registry.
|
|
4
|
+
//
|
|
5
|
+
// Spec: ERD §3 + Perry's v0.4.0 kickoff (b3f6c5ed) + amendment (58a9d3d3).
|
|
6
|
+
//
|
|
7
|
+
// Surface shape (matches Claude Desktop's `mcp.json` convention so authors
|
|
8
|
+
// don't carry two mental models):
|
|
9
|
+
//
|
|
10
|
+
// {
|
|
11
|
+
// "youtrack": {
|
|
12
|
+
// "class": "RemoteMcpConnector",
|
|
13
|
+
// "config": {
|
|
14
|
+
// "command": "npx",
|
|
15
|
+
// "args": ["mcp-remote", "https://...", "--header", "Authorization:${AUTH_HEADER}"],
|
|
16
|
+
// "env": { "AUTH_HEADER": "Bearer ${YOUTRACK_TOKEN}" }
|
|
17
|
+
// }
|
|
18
|
+
// }
|
|
19
|
+
// }
|
|
20
|
+
//
|
|
21
|
+
// **Credential discipline (v0.4.0 hard requirement, Perry's amendment):**
|
|
22
|
+
// `connectors.json` is secret-bearing. Default `.gitignore` excludes it;
|
|
23
|
+
// `connectors.json.example` ships at repo root as the template. See README.
|
|
24
|
+
import { readFileSync } from "node:fs";
|
|
25
|
+
import { CallbackMcpConnector } from "./mcp.js";
|
|
26
|
+
export const KNOWN_CONNECTOR_CLASSES = new Map([
|
|
27
|
+
["CallbackMcpConnector", { ctor: CallbackMcpConnector }],
|
|
28
|
+
]);
|
|
29
|
+
/** Listable for error messages + runtime_capabilities discovery. */
|
|
30
|
+
export function listKnownConnectorClasses() {
|
|
31
|
+
return [...KNOWN_CONNECTOR_CLASSES.keys()];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolve `${NAME}` patterns in a string against the provided env.
|
|
35
|
+
* Missing var → throws (clear error rather than silent empty string).
|
|
36
|
+
* Used by the loader on every string value in the `config` block.
|
|
37
|
+
*/
|
|
38
|
+
export function resolveEnvSubstitution(value, env) {
|
|
39
|
+
return value.replace(/\$\{([A-Z_][A-Z0-9_]*)\}/g, (_match, name) => {
|
|
40
|
+
const v = env[name];
|
|
41
|
+
if (v === undefined) {
|
|
42
|
+
throw new Error(`Environment variable '\${${name}}' referenced in connectors.json is not set.`);
|
|
43
|
+
}
|
|
44
|
+
return v;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** Walk a config tree and resolve `${NAME}` substitutions on every string leaf. */
|
|
48
|
+
function resolveConfigEnv(value, env) {
|
|
49
|
+
if (typeof value === "string")
|
|
50
|
+
return resolveEnvSubstitution(value, env);
|
|
51
|
+
if (Array.isArray(value))
|
|
52
|
+
return value.map((v) => resolveConfigEnv(v, env));
|
|
53
|
+
if (value !== null && typeof value === "object") {
|
|
54
|
+
const out = {};
|
|
55
|
+
for (const [k, v] of Object.entries(value))
|
|
56
|
+
out[k] = resolveConfigEnv(v, env);
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Read + parse `connectors.json` at the given path. Validate the top-
|
|
63
|
+
* level shape. Resolve `${VAR}` substitutions. Instantiate each entry
|
|
64
|
+
* via the closed-set class registry's `fromConfig` factory (when
|
|
65
|
+
* present; entries pointing at classes without one get a clear error
|
|
66
|
+
* and a `null` instance, surfaced via `errors`).
|
|
67
|
+
*
|
|
68
|
+
* Missing file → returns `{connectors: [], errors: []}` (graceful: not
|
|
69
|
+
* every deployment uses external connectors). Malformed JSON or
|
|
70
|
+
* structural errors → returned in `errors[]` for the bootstrap caller
|
|
71
|
+
* to log and refuse to start, or for the lint surface to consume.
|
|
72
|
+
*/
|
|
73
|
+
export function loadConnectorsConfig(opts) {
|
|
74
|
+
const env = opts.env ?? process.env;
|
|
75
|
+
let raw;
|
|
76
|
+
try {
|
|
77
|
+
raw = readFileSync(opts.path, "utf8");
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
const code = err.code;
|
|
81
|
+
if (code === "ENOENT")
|
|
82
|
+
return { connectors: [], errors: [] };
|
|
83
|
+
return { connectors: [], errors: [`connectors.json: failed to read '${opts.path}': ${err.message}`] };
|
|
84
|
+
}
|
|
85
|
+
let parsed;
|
|
86
|
+
try {
|
|
87
|
+
parsed = JSON.parse(raw);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
return { connectors: [], errors: [`connectors.json: malformed JSON in '${opts.path}': ${err.message}`] };
|
|
91
|
+
}
|
|
92
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
93
|
+
return { connectors: [], errors: [`connectors.json: top-level must be an object mapping connector names to {class, config}. Got: ${Array.isArray(parsed) ? "array" : typeof parsed}.`] };
|
|
94
|
+
}
|
|
95
|
+
const connectors = [];
|
|
96
|
+
const errors = [];
|
|
97
|
+
for (const [name, entry] of Object.entries(parsed)) {
|
|
98
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
|
|
99
|
+
errors.push(`connectors.json: entry '${name}' must be an object with {class, config}. Got: ${Array.isArray(entry) ? "array" : entry === null ? "null" : typeof entry}.`);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const obj = entry;
|
|
103
|
+
const className = obj["class"];
|
|
104
|
+
if (typeof className !== "string" || className === "") {
|
|
105
|
+
errors.push(`connectors.json: entry '${name}' is missing required string field 'class'.`);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const rawConfig = obj["config"] ?? {};
|
|
109
|
+
if (rawConfig === null || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
|
|
110
|
+
errors.push(`connectors.json: entry '${name}' field 'config' must be an object. Got: ${Array.isArray(rawConfig) ? "array" : typeof rawConfig}.`);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
let resolvedConfig;
|
|
114
|
+
try {
|
|
115
|
+
resolvedConfig = resolveConfigEnv(rawConfig, env);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
errors.push(`connectors.json: entry '${name}': ${err.message}`);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const classEntry = KNOWN_CONNECTOR_CLASSES.get(className);
|
|
122
|
+
if (classEntry === undefined) {
|
|
123
|
+
errors.push(`connectors.json: entry '${name}' references unknown connector class '${className}'. Known classes: ${listKnownConnectorClasses().join(", ")}.`);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
let instance;
|
|
127
|
+
if (classEntry.fromConfig !== undefined) {
|
|
128
|
+
try {
|
|
129
|
+
instance = classEntry.fromConfig(resolvedConfig);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
errors.push(`connectors.json: entry '${name}' failed to instantiate '${className}': ${err.message}`);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// No fromConfig means the class can't be JSON-instantiated. v0.4.0
|
|
138
|
+
// ships with CallbackMcpConnector in this state — wire it via
|
|
139
|
+
// embedder code. v0.4.1 adds RemoteMcpConnector with a fromConfig.
|
|
140
|
+
errors.push(`connectors.json: entry '${name}' uses class '${className}' which doesn't support configuration via connectors.json. Wire this connector via embedder code instead, or use a JSON-instantiable class.`);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
connectors.push({ name, className, config: resolvedConfig, instance });
|
|
144
|
+
}
|
|
145
|
+
return { connectors, errors };
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/connectors/config.ts"],"names":[],"mappings":"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"}
|
package/dist/filters.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The names of every registered filter. Lint's `unknown-filter` rule consults this. */
|
|
2
|
-
export declare const KNOWN_FILTERS: readonly ["url", "shell", "json", "
|
|
2
|
+
export declare const KNOWN_FILTERS: readonly ["url", "shell", "json", "trim", "length"];
|
|
3
3
|
export type KnownFilter = (typeof KNOWN_FILTERS)[number];
|
|
4
4
|
/**
|
|
5
5
|
* Apply a named pipe filter to a string value. Filters supported in v1:
|
|
@@ -15,6 +15,11 @@ export type KnownFilter = (typeof KNOWN_FILTERS)[number];
|
|
|
15
15
|
*
|
|
16
16
|
* Unknown filter names throw — typos are caught at compile time when the value
|
|
17
17
|
* is already resolved, or at runtime for ambient refs.
|
|
18
|
+
*
|
|
19
|
+
* v0.3.3 — `|json_parse` filter removed. Use `$ json_parse $(VAR) -> P` op
|
|
20
|
+
* instead, which binds the parsed structure so `$(P.field)` works via
|
|
21
|
+
* resolveRef's dotted descent. Filter was string-in/string-out which couldn't
|
|
22
|
+
* propagate parsed shape through `.field` access.
|
|
18
23
|
*/
|
|
19
24
|
export declare function applyFilter(value: string, filter: string): string;
|
|
20
25
|
//# sourceMappingURL=filters.d.ts.map
|
package/dist/filters.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filters.d.ts","sourceRoot":"","sources":["../src/filters.ts"],"names":[],"mappings":"AAEA,wFAAwF;AACxF,eAAO,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"filters.d.ts","sourceRoot":"","sources":["../src/filters.ts"],"names":[],"mappings":"AAEA,wFAAwF;AACxF,eAAO,MAAM,aAAa,qDAAsD,CAAC;AACjF,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAYzD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAwBjE"}
|
package/dist/filters.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Pipe-filter implementations. `$(NAME|filter)` syntax dispatches here.
|
|
2
2
|
/** The names of every registered filter. Lint's `unknown-filter` rule consults this. */
|
|
3
|
-
export const KNOWN_FILTERS = ["url", "shell", "json", "
|
|
3
|
+
export const KNOWN_FILTERS = ["url", "shell", "json", "trim", "length"];
|
|
4
4
|
//
|
|
5
5
|
// Adding a new filter:
|
|
6
6
|
// 1. Add a case in `applyFilter` below.
|
|
@@ -25,6 +25,11 @@ export const KNOWN_FILTERS = ["url", "shell", "json", "json_parse", "trim", "len
|
|
|
25
25
|
*
|
|
26
26
|
* Unknown filter names throw — typos are caught at compile time when the value
|
|
27
27
|
* is already resolved, or at runtime for ambient refs.
|
|
28
|
+
*
|
|
29
|
+
* v0.3.3 — `|json_parse` filter removed. Use `$ json_parse $(VAR) -> P` op
|
|
30
|
+
* instead, which binds the parsed structure so `$(P.field)` works via
|
|
31
|
+
* resolveRef's dotted descent. Filter was string-in/string-out which couldn't
|
|
32
|
+
* propagate parsed shape through `.field` access.
|
|
28
33
|
*/
|
|
29
34
|
export function applyFilter(value, filter) {
|
|
30
35
|
switch (filter) {
|
|
@@ -34,21 +39,6 @@ export function applyFilter(value, filter) {
|
|
|
34
39
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
35
40
|
case "json":
|
|
36
41
|
return JSON.stringify(value);
|
|
37
|
-
case "json_parse": {
|
|
38
|
-
// v0.3.2: sibling to `|json` (stringify). Parses the input as JSON and
|
|
39
|
-
// re-stringifies — round-trip for valid JSON, throws for malformed.
|
|
40
|
-
// Useful chain with `|length` for array counting + as a validation gate
|
|
41
|
-
// before downstream string ops. Field-access on parsed structures is a
|
|
42
|
-
// separate concern (parsed value isn't propagated through string-in/
|
|
43
|
-
// string-out filter signature). See $ json_parse intercept (deferred).
|
|
44
|
-
try {
|
|
45
|
-
const parsed = JSON.parse(value);
|
|
46
|
-
return JSON.stringify(parsed);
|
|
47
|
-
}
|
|
48
|
-
catch (err) {
|
|
49
|
-
throw new Error(`\`|json_parse\` filter: input is not valid JSON. Got: '${value.slice(0, 40)}${value.length > 40 ? "..." : ""}' — ${err.message}`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
42
|
case "trim":
|
|
53
43
|
return value.trim();
|
|
54
44
|
case "length": {
|
|
@@ -65,7 +55,7 @@ export function applyFilter(value, filter) {
|
|
|
65
55
|
return String(value.length);
|
|
66
56
|
}
|
|
67
57
|
default:
|
|
68
|
-
throw new Error(`Unknown filter '${filter}' — supported: url, shell, json,
|
|
58
|
+
throw new Error(`Unknown filter '${filter}' — supported: url, shell, json, trim, length`);
|
|
69
59
|
}
|
|
70
60
|
}
|
|
71
61
|
//# sourceMappingURL=filters.js.map
|
package/dist/filters.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filters.js","sourceRoot":"","sources":["../src/filters.ts"],"names":[],"mappings":"AAAA,wEAAwE;AAExE,wFAAwF;AACxF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"filters.js","sourceRoot":"","sources":["../src/filters.ts"],"names":[],"mappings":"AAAA,wEAAwE;AAExE,wFAAwF;AACxF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAU,CAAC;AAEjF,EAAE;AACF,uBAAuB;AACvB,0CAA0C;AAC1C,kFAAkF;AAClF,2EAA2E;AAC3E,gFAAgF;AAChF,iDAAiD;AACjD,EAAE;AACF,8EAA8E;AAC9E,yEAAyE;AAEzE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,MAAc;IACvD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,KAAK;YACR,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACnC,KAAK,OAAO;YACV,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;QAC7C,KAAK,MAAM;YACT,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC/B,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;QACtB,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,8DAA8D;YAC9D,qEAAqE;YACrE,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAY,CAAC;gBAC5C,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;oBAAE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC1D,CAAC;YAAC,MAAM,CAAC;gBACP,wDAAwD;YAC1D,CAAC;YACD,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,+CAA+C,CAAC,CAAC;IAC9F,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help-content.d.ts","sourceRoot":"","sources":["../src/help-content.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"help-content.d.ts","sourceRoot":"","sources":["../src/help-content.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAumBzD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,cAAc,EAAE,MAAM,EACtB,QAAQ,CAAC,EAAE,QAAQ,GAClB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqBzB"}
|
package/dist/help-content.js
CHANGED
|
@@ -131,6 +131,23 @@ Calls an MCP tool. Without an explicit connector prefix, routes to the
|
|
|
131
131
|
another stored skill end-to-end without requiring an MCP connector — pass
|
|
132
132
|
input vars as additional kwargs.
|
|
133
133
|
|
|
134
|
+
**Built-in (v0.3.3):** \`$ json_parse $(VAR) -> P\` parses the input as JSON
|
|
135
|
+
and binds the structured value to \`P\`. Dotted descent via \`$(P.field)\`
|
|
136
|
+
works in conditions and emit — no filter+field grammar gymnastics.
|
|
137
|
+
|
|
138
|
+
\`\`\`
|
|
139
|
+
# Vars: PAYLOAD={"status":"ok","count":3}
|
|
140
|
+
|
|
141
|
+
read:
|
|
142
|
+
$ json_parse $(PAYLOAD) -> P
|
|
143
|
+
if $(P.status) == "ok" and $(P.count) > "0":
|
|
144
|
+
! processing $(P.count) items
|
|
145
|
+
\`\`\`
|
|
146
|
+
|
|
147
|
+
Throws on malformed JSON (caught by \`else:\` / \`# OnError:\`). Replaces
|
|
148
|
+
the v0.3.2 \`|json_parse\` filter, which couldn't propagate parsed
|
|
149
|
+
structure through \`.field\` access.
|
|
150
|
+
|
|
134
151
|
### \`~\` — LocalModel call
|
|
135
152
|
|
|
136
153
|
\`\`\`
|
|
@@ -222,7 +239,6 @@ Apply on \`$(VAR|filter)\` references; chain left-to-right.
|
|
|
222
239
|
| \`url\` | encodeURIComponent |
|
|
223
240
|
| \`shell\` | POSIX single-quote escape |
|
|
224
241
|
| \`json\` | JSON.stringify |
|
|
225
|
-
| \`json_parse\` | Parse JSON string; round-trips for valid JSON, throws for malformed (v0.3.2). Pairs with \`|length\` for array counts on JSON arrays. |
|
|
226
242
|
| \`trim\` | Whitespace trim |
|
|
227
243
|
| \`length\` | Array element count or string char count (v0.2.5) |
|
|
228
244
|
|
|
@@ -597,6 +613,7 @@ Three tiers per ERD §3:
|
|
|
597
613
|
- \`duplicate-skill-name\` — name collides with an existing stored skill
|
|
598
614
|
- \`plugin-collision\` — placeholder for v1.x plugin-loader name conflicts
|
|
599
615
|
- \`deferred-skill-reference\` — composition ref (\`&\` / \`$ execute_skill\` / \`# Templates:\`) targets a skill not currently in the SkillStore; resolution deferred to execute time (v0.3.1+). Confirms the forward-reference path is engaged; clears once the target is stored.
|
|
616
|
+
- \`unparsed-json-field-access\` — op text contains \`$(VAR|json_parse).field\`; the \`|json_parse\` filter was removed in v0.3.3. Replace with \`$ json_parse $(VAR) -> P\` then \`$(P.field)\`.
|
|
600
617
|
|
|
601
618
|
\`compile_skill({source})\` runs the full lint preflight and reports
|
|
602
619
|
findings in the \`errors\` + \`warnings\` arrays. \`lint_skill({source})\`
|
package/dist/help-content.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help-content.js","sourceRoot":"","sources":["../src/help-content.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,kEAAkE;AAClE,qEAAqE;AACrE,2BAA2B;AAC3B,EAAE;AACF,kBAAkB;AAClB,0EAA0E;AAC1E,+DAA+D;AAC/D,uCAAuC;AACvC,kFAAkF;AAClF,kFAAkF;AAClF,uDAAuD;AACvD,EAAE;AACF,uEAAuE;AACvE,yDAAyD;AAIzD,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmGlB,CAAC;AAEF,MAAM,GAAG,GAAG
|
|
1
|
+
{"version":3,"file":"help-content.js","sourceRoot":"","sources":["../src/help-content.ts"],"names":[],"mappings":"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"}
|
package/dist/lint.d.ts
CHANGED
|
@@ -78,6 +78,22 @@ export interface LintOptions {
|
|
|
78
78
|
* warning fires.
|
|
79
79
|
*/
|
|
80
80
|
enableUnsafeShell?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Names of registered MCP connector instances (v0.4.0). When provided,
|
|
83
|
+
* `unknown-connector` lint rule fires tier-1 on `$ name.tool` refs to
|
|
84
|
+
* names not in the list. When undefined, the rule is silent (caller
|
|
85
|
+
* doesn't know what's wired). Derived from `registry` if only the
|
|
86
|
+
* registry is provided.
|
|
87
|
+
*/
|
|
88
|
+
mcpConnectorNames?: string[];
|
|
89
|
+
/**
|
|
90
|
+
* Errors from `connectors.json` load pass (v0.4.0). When provided,
|
|
91
|
+
* `unknown-connector-class` lint rule re-surfaces the subset of these
|
|
92
|
+
* about unknown class names so cold-author tooling sees them through
|
|
93
|
+
* the lint API. Other config errors flow through `parse-error`-style
|
|
94
|
+
* surfacing in the bootstrap result.
|
|
95
|
+
*/
|
|
96
|
+
connectorConfigErrors?: string[];
|
|
81
97
|
}
|
|
82
98
|
interface LintContext {
|
|
83
99
|
parsed: ParsedSkill;
|
|
@@ -88,6 +104,8 @@ interface LintContext {
|
|
|
88
104
|
hasSkillStore: boolean;
|
|
89
105
|
callSite: "cli" | "api" | "compile-preflight";
|
|
90
106
|
enableUnsafeShell: boolean | undefined;
|
|
107
|
+
mcpConnectorNames: string[] | undefined;
|
|
108
|
+
connectorConfigErrors: string[];
|
|
91
109
|
}
|
|
92
110
|
export interface LintRule {
|
|
93
111
|
id: string;
|
package/dist/lint.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,WAAW,EAAgB,MAAM,aAAa,CAAC;AAEpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,kBAAkB,IAAI,kBAAkB,CAAA;KAAE,CAAC,CAAC;IAC9D,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,mBAAmB,CAAC;IAC/C;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,WAAW,EAAgB,MAAM,aAAa,CAAC;AAEpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,kBAAkB,IAAI,kBAAkB,CAAA;KAAE,CAAC,CAAC;IAC9D,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,mBAAmB,CAAC;IAC/C;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;CAClC;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,WAAW,CAAC;IACpB,iBAAiB,EAAE,KAAK,CAAC;QAAE,kBAAkB,IAAI,kBAAkB,CAAA;KAAE,CAAC,GAAG,IAAI,CAAC;IAC9E,UAAU,EAAE,UAAU,GAAG,SAAS,CAAC;IACnC,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,KAAK,GAAG,KAAK,GAAG,mBAAmB,CAAC;IAC9C,iBAAiB,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,iBAAiB,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACxC,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,YAAY,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,GAAG,EAAE,WAAW,GAAG,WAAW,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAChE,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,wBAAsB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAmCrF;AAED,kFAAkF;AAClF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,UAAU,CAkC1E;AAED,6HAA6H;AAC7H,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAW3D;AA2wCD,mFAAmF;AACnF,wBAAgB,SAAS,IAAI,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAElE"}
|
package/dist/lint.js
CHANGED
|
@@ -10,6 +10,8 @@ export async function lint(source, options) {
|
|
|
10
10
|
hasSkillStore: options?.skillStore !== undefined,
|
|
11
11
|
callSite: options?.callSite ?? "api",
|
|
12
12
|
enableUnsafeShell: options?.enableUnsafeShell,
|
|
13
|
+
mcpConnectorNames: options?.mcpConnectorNames ?? collectMcpConnectorNamesFromRegistry(options?.registry),
|
|
14
|
+
connectorConfigErrors: options?.connectorConfigErrors ?? [],
|
|
13
15
|
};
|
|
14
16
|
const findings = [];
|
|
15
17
|
for (const rule of RULES) {
|
|
@@ -43,6 +45,8 @@ export function lintSync(source, options) {
|
|
|
43
45
|
hasSkillStore: options?.skillStore !== undefined,
|
|
44
46
|
callSite: options?.callSite ?? "api",
|
|
45
47
|
enableUnsafeShell: options?.enableUnsafeShell,
|
|
48
|
+
mcpConnectorNames: options?.mcpConnectorNames ?? collectMcpConnectorNamesFromRegistry(options?.registry),
|
|
49
|
+
connectorConfigErrors: options?.connectorConfigErrors ?? [],
|
|
46
50
|
};
|
|
47
51
|
const findings = [];
|
|
48
52
|
for (const rule of RULES) {
|
|
@@ -84,9 +88,23 @@ export function formatLintResult(result) {
|
|
|
84
88
|
const PARSE_ERROR = {
|
|
85
89
|
id: "parse-error",
|
|
86
90
|
severity: "error",
|
|
87
|
-
description: "Any syntax error collected by the parser.",
|
|
91
|
+
description: "Any syntax error collected by the parser (catch-all for shapes not owned by a more specific tier-1 rule).",
|
|
88
92
|
remediation: "Fix the grammar error per the message. Check op syntax, header form, indent levels.",
|
|
89
|
-
check: (ctx) => ctx.parsed.parseErrors
|
|
93
|
+
check: (ctx) => ctx.parsed.parseErrors
|
|
94
|
+
// v0.3.4: skip messages a more specific tier-1 rule already owns —
|
|
95
|
+
// pre-fix both this rule and the specific one fired identical bodies,
|
|
96
|
+
// doubling noise. Each pattern below mirrors the corresponding tier-1
|
|
97
|
+
// rule's filter regex; PARSE_ERROR stays catch-all for unowned shapes
|
|
98
|
+
// (header issues, foreach/needs malformed, etc.).
|
|
99
|
+
//
|
|
100
|
+
// Owning rules:
|
|
101
|
+
// invalid-conditional-syntax → Unsupported condition
|
|
102
|
+
// single-equals → `=` is not valid in a condition
|
|
103
|
+
// malformed-op-grammar → Malformed `<op>`
|
|
104
|
+
// reserved-keyword → is a reserved keyword
|
|
105
|
+
// indentation → Tab characters / Mid-block indent change
|
|
106
|
+
.filter((msg) => !/Unsupported condition|`=` is not valid in a condition|Malformed `[~>&$@!?]|is a reserved keyword|Tab characters in indentation|Mid-block indent change/.test(msg))
|
|
107
|
+
.map((msg) => ({
|
|
90
108
|
rule: "parse-error",
|
|
91
109
|
severity: "error",
|
|
92
110
|
message: msg,
|
|
@@ -306,8 +324,8 @@ const MALFORMED_OP_GRAMMAR = {
|
|
|
306
324
|
const INVALID_CONDITIONAL_SYNTAX = {
|
|
307
325
|
id: "invalid-conditional-syntax",
|
|
308
326
|
severity: "error",
|
|
309
|
-
description: "An `if:` / `elif:` condition uses syntax outside the
|
|
310
|
-
remediation: "
|
|
327
|
+
description: "An `if:` / `elif:` condition uses syntax outside the supported grammar.",
|
|
328
|
+
remediation: "Use a supported shape: truthy `$(REF)`; `$(REF) ==/!=/</>/<=/>= \"literal\"` or `$(REF) ==/!=/</>/<=/>= $(REF)`; `$(REF) (not) in $(REF)`; composable with `and` / `or` / `not` and parens. Filters + dotted-field allowed inside `$(REF)`. For field access on parsed JSON, use `$ json_parse $(VAR) -> P` then `$(P.field)`.",
|
|
311
329
|
check: (ctx) => ctx.parsed.parseErrors
|
|
312
330
|
.filter((msg) => /Unsupported condition/.test(msg))
|
|
313
331
|
.map((msg) => ({
|
|
@@ -426,6 +444,62 @@ const UNKNOWN_RETRIEVAL_ARG = {
|
|
|
426
444
|
return findings;
|
|
427
445
|
},
|
|
428
446
|
};
|
|
447
|
+
// v0.4.0 — `$ name.tool` references a connector name not registered.
|
|
448
|
+
// `connectorNames` is the authoritative list from the Registry (passed
|
|
449
|
+
// through LintOptions); when undefined (caller doesn't know what's
|
|
450
|
+
// wired) the rule is silent rather than risk false positives.
|
|
451
|
+
const UNKNOWN_CONNECTOR = {
|
|
452
|
+
id: "unknown-connector",
|
|
453
|
+
severity: "error",
|
|
454
|
+
description: "A `$ name.tool` op references a connector name that's not registered. Either the name is misspelled or `connectors.json` is missing an entry.",
|
|
455
|
+
remediation: "Check the connector name against `connectors.json` (or whatever wired the registry). Either fix the spelling or add the entry. `runtime_capabilities()` lists the names currently wired.",
|
|
456
|
+
check: (ctx) => {
|
|
457
|
+
if (ctx.mcpConnectorNames === undefined)
|
|
458
|
+
return [];
|
|
459
|
+
const known = new Set(ctx.mcpConnectorNames);
|
|
460
|
+
const findings = [];
|
|
461
|
+
const reported = new Set();
|
|
462
|
+
for (const [targetName, target] of ctx.parsed.targets) {
|
|
463
|
+
walkOps(target.ops, (op) => {
|
|
464
|
+
if (op.kind !== "$" || op.mcpConnector === undefined)
|
|
465
|
+
return;
|
|
466
|
+
const ref = op.mcpConnector;
|
|
467
|
+
if (known.has(ref))
|
|
468
|
+
return;
|
|
469
|
+
const key = `${targetName}:${ref}`;
|
|
470
|
+
if (reported.has(key))
|
|
471
|
+
return;
|
|
472
|
+
reported.add(key);
|
|
473
|
+
findings.push({
|
|
474
|
+
rule: "unknown-connector",
|
|
475
|
+
severity: "error",
|
|
476
|
+
message: `\`$ ${ref}.<tool>\` in target '${targetName}' references unknown connector '${ref}'. Wired connectors: ${known.size === 0 ? "(none)" : [...known].join(", ")}.`,
|
|
477
|
+
block: targetName,
|
|
478
|
+
extras: { referenced_connector: ref },
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
return findings;
|
|
483
|
+
},
|
|
484
|
+
};
|
|
485
|
+
// v0.4.0 — `connectors.json` declares `class: "Foo"` where `Foo` is not
|
|
486
|
+
// in the closed-set class registry. The loader catches this at startup
|
|
487
|
+
// and surfaces via `connectorConfigErrors`; this rule re-surfaces the
|
|
488
|
+
// subset that's class-related into the lint diagnostic stream so cold-
|
|
489
|
+
// author tooling (compile_skill / lint_skill MCP) sees them.
|
|
490
|
+
const UNKNOWN_CONNECTOR_CLASS = {
|
|
491
|
+
id: "unknown-connector-class",
|
|
492
|
+
severity: "error",
|
|
493
|
+
description: "`connectors.json` references a connector class that's not in the closed-set class registry (v0.4.0).",
|
|
494
|
+
remediation: "Use one of the known classes (see `runtime_capabilities()` for the list shipped in this runtime). Plugin-style runtime-arbitrary class loading is deliberately out of scope; future classes ship via CHANGELOG-tracked additions to the registry.",
|
|
495
|
+
check: (ctx) => ctx.connectorConfigErrors
|
|
496
|
+
.filter((msg) => /unknown connector class/.test(msg))
|
|
497
|
+
.map((msg) => ({
|
|
498
|
+
rule: "unknown-connector-class",
|
|
499
|
+
severity: "error",
|
|
500
|
+
message: msg,
|
|
501
|
+
})),
|
|
502
|
+
};
|
|
429
503
|
// v0.3.1: tier-3 advisory that fires alongside the demoted tier-2
|
|
430
504
|
// unknown-skill-reference / unknown-template-reference. Confirms the
|
|
431
505
|
// deferred-resolution path is engaged so cold authors can distinguish
|
|
@@ -476,6 +550,57 @@ const DEFERRED_SKILL_REFERENCE = {
|
|
|
476
550
|
return findings;
|
|
477
551
|
},
|
|
478
552
|
};
|
|
553
|
+
// v0.3.3 — `|json_parse` filter removed; the shape `$(VAR|json_parse).field`
|
|
554
|
+
// is statically detectable. Fire a tier-3 advisory pointing at the new
|
|
555
|
+
// `$ json_parse $(VAR) -> P` op so authors who carried the v0.3.2 pattern
|
|
556
|
+
// forward get a direct remediation instead of the generic "unknown filter"
|
|
557
|
+
// error from applyFilter.
|
|
558
|
+
const UNPARSED_JSON_FIELD_ACCESS = {
|
|
559
|
+
id: "unparsed-json-field-access",
|
|
560
|
+
severity: "info",
|
|
561
|
+
description: "Op text contains `$(VAR|json_parse).field` — the `|json_parse` filter was removed in v0.3.3.",
|
|
562
|
+
remediation: "Replace with `$ json_parse $(VAR) -> P` then access `$(P.field)`. The op binds the parsed structure so dotted descent works in conditions + emit. See help({topic: \"ops\"}).",
|
|
563
|
+
check: (ctx) => {
|
|
564
|
+
const findings = [];
|
|
565
|
+
const BAD = /\$\([^)]*\|\s*json_parse\s*\)\.([A-Za-z_]\w*)/;
|
|
566
|
+
const reportIfMatches = (text, targetName) => {
|
|
567
|
+
const m = BAD.exec(text);
|
|
568
|
+
if (m === null)
|
|
569
|
+
return;
|
|
570
|
+
findings.push({
|
|
571
|
+
rule: "unparsed-json-field-access",
|
|
572
|
+
severity: "info",
|
|
573
|
+
message: `In target '${targetName}': \`$(...|json_parse).${m[1]}\` — the \`|json_parse\` filter was removed in v0.3.3. Replace with \`$ json_parse $(VAR) -> P\` then \`$(P.${m[1]})\`.`,
|
|
574
|
+
block: targetName,
|
|
575
|
+
});
|
|
576
|
+
};
|
|
577
|
+
for (const [targetName, target] of ctx.parsed.targets) {
|
|
578
|
+
walkOps(target.ops, (op) => {
|
|
579
|
+
if (op.body !== undefined)
|
|
580
|
+
reportIfMatches(op.body, targetName);
|
|
581
|
+
if (op.foreachList !== undefined)
|
|
582
|
+
reportIfMatches(op.foreachList, targetName);
|
|
583
|
+
if (op.ifBranches !== undefined) {
|
|
584
|
+
for (const b of op.ifBranches)
|
|
585
|
+
reportIfMatches(b.cond, targetName);
|
|
586
|
+
}
|
|
587
|
+
if (op.retrievalParams !== undefined) {
|
|
588
|
+
reportIfMatches(op.retrievalParams.query, targetName);
|
|
589
|
+
for (const v of Object.values(op.retrievalParams.extra))
|
|
590
|
+
reportIfMatches(String(v), targetName);
|
|
591
|
+
}
|
|
592
|
+
if (op.localModelParams !== undefined) {
|
|
593
|
+
reportIfMatches(op.localModelParams.prompt, targetName);
|
|
594
|
+
}
|
|
595
|
+
if (op.ampParams !== undefined) {
|
|
596
|
+
for (const v of Object.values(op.ampParams.args))
|
|
597
|
+
reportIfMatches(v, targetName);
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
return findings;
|
|
602
|
+
},
|
|
603
|
+
};
|
|
479
604
|
// v0.2.12 Bug 17. `# Templates:` refs were not lint-validated despite
|
|
480
605
|
// `# OnError:` having compile-time validation (since v0.2.10).
|
|
481
606
|
// v0.3.1: demoted tier-1 → tier-2 alongside unknown-skill-reference.
|
|
@@ -1244,6 +1369,8 @@ const RULES = [
|
|
|
1244
1369
|
UNKNOWN_TEMPLATE_REFERENCE,
|
|
1245
1370
|
DEFERRED_SKILL_REFERENCE,
|
|
1246
1371
|
UNKNOWN_RETRIEVAL_ARG,
|
|
1372
|
+
UNKNOWN_CONNECTOR,
|
|
1373
|
+
UNKNOWN_CONNECTOR_CLASS,
|
|
1247
1374
|
UNINITIALIZED_APPEND,
|
|
1248
1375
|
FOREACH_LOCAL_ACCUMULATOR_TARGET,
|
|
1249
1376
|
APPEND_TO_NON_LIST,
|
|
@@ -1267,6 +1394,7 @@ const RULES = [
|
|
|
1267
1394
|
NO_DEFAULT_TARGET,
|
|
1268
1395
|
DUPLICATE_SKILL_NAME,
|
|
1269
1396
|
PLUGIN_COLLISION,
|
|
1397
|
+
UNPARSED_JSON_FIELD_ACCESS,
|
|
1270
1398
|
];
|
|
1271
1399
|
/** Read-only view of the rule registry — for tooling that introspects v1 rules. */
|
|
1272
1400
|
export function listRules() {
|
|
@@ -1397,6 +1525,11 @@ function collectClassesFromRegistry(registry) {
|
|
|
1397
1525
|
...registry.listMcpConnectorClasses(),
|
|
1398
1526
|
];
|
|
1399
1527
|
}
|
|
1528
|
+
function collectMcpConnectorNamesFromRegistry(registry) {
|
|
1529
|
+
if (registry === undefined)
|
|
1530
|
+
return undefined;
|
|
1531
|
+
return registry.listMcpConnectors().map((e) => e.name);
|
|
1532
|
+
}
|
|
1400
1533
|
function buildFeatureSet(classes) {
|
|
1401
1534
|
const provided = new Set();
|
|
1402
1535
|
for (const Ctor of classes) {
|