@sweny-ai/core 0.1.89 → 0.1.91
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/dist/cli/cloud-lifecycle.d.ts +161 -0
- package/dist/cli/cloud-lifecycle.js +328 -0
- package/dist/cli/main.js +66 -12
- package/dist/cli/publish.js +3 -3
- package/dist/cli/skill.d.ts +2 -9
- package/dist/cli/skill.js +8 -12
- package/dist/schema.d.ts +186 -6
- package/dist/schema.js +170 -9
- package/dist/skills/custom-loader.js +9 -6
- package/dist/types.d.ts +57 -2
- package/dist/types.js +42 -0
- package/dist/workflows/browser.js +3 -3
- package/dist/workflows/implement.yml +1 -0
- package/dist/workflows/seed-content.yml +1 -0
- package/dist/workflows/triage.yml +1 -0
- package/package.json +2 -2
package/dist/cli/skill.d.ts
CHANGED
|
@@ -14,14 +14,7 @@
|
|
|
14
14
|
* and `../skills/index.js`; this CLI is a thin authoring shell over them.
|
|
15
15
|
*/
|
|
16
16
|
import { Command } from "commander";
|
|
17
|
-
import { type SkillCategory } from "../types.js";
|
|
18
|
-
declare const HARNESS_DIRS: {
|
|
19
|
-
readonly claude: ".claude/skills";
|
|
20
|
-
readonly sweny: ".sweny/skills";
|
|
21
|
-
readonly agents: ".agents/skills";
|
|
22
|
-
readonly gemini: ".gemini/skills";
|
|
23
|
-
};
|
|
24
|
-
type HarnessKey = keyof typeof HARNESS_DIRS;
|
|
17
|
+
import { type SkillCategory, type SkillHarnessKey } from "../types.js";
|
|
25
18
|
/** Build the SKILL.md body for a fresh scaffold. Exported for tests. */
|
|
26
19
|
export declare function renderSkillTemplate(opts: {
|
|
27
20
|
id: string;
|
|
@@ -31,7 +24,7 @@ export declare function renderSkillTemplate(opts: {
|
|
|
31
24
|
interface NewOptions {
|
|
32
25
|
description?: string;
|
|
33
26
|
category?: string;
|
|
34
|
-
harness?:
|
|
27
|
+
harness?: SkillHarnessKey;
|
|
35
28
|
force?: boolean;
|
|
36
29
|
}
|
|
37
30
|
/**
|
package/dist/cli/skill.js
CHANGED
|
@@ -18,15 +18,11 @@ import * as path from "node:path";
|
|
|
18
18
|
import chalk from "chalk";
|
|
19
19
|
import { builtinSkills } from "../skills/index.js";
|
|
20
20
|
import { configuredSkillsWithDiagnostics, discoverSkillsWithDiagnostics } from "../skills/custom-loader.js";
|
|
21
|
-
import { SKILL_CATEGORIES } from "../types.js";
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
const HARNESS_DIRS =
|
|
25
|
-
|
|
26
|
-
sweny: ".sweny/skills",
|
|
27
|
-
agents: ".agents/skills",
|
|
28
|
-
gemini: ".gemini/skills",
|
|
29
|
-
};
|
|
21
|
+
import { SKILL_CATEGORIES, SKILL_HARNESSES, isValidSkillId, } from "../types.js";
|
|
22
|
+
// Lookup table: harness key -> directory. Built once from SKILL_HARNESSES so
|
|
23
|
+
// the CLI cannot drift from the loader's scan list.
|
|
24
|
+
const HARNESS_DIRS = Object.fromEntries(SKILL_HARNESSES.map((h) => [h.key, h.path]));
|
|
25
|
+
const HARNESS_KEYS = SKILL_HARNESSES.map((h) => h.key);
|
|
30
26
|
/** Build the SKILL.md body for a fresh scaffold. Exported for tests. */
|
|
31
27
|
export function renderSkillTemplate(opts) {
|
|
32
28
|
const { id, description, category } = opts;
|
|
@@ -87,7 +83,7 @@ export function renderSkillTemplate(opts) {
|
|
|
87
83
|
*/
|
|
88
84
|
export function runSkillNew(idArg, options, cwd = process.cwd()) {
|
|
89
85
|
const id = idArg.toLowerCase();
|
|
90
|
-
if (!
|
|
86
|
+
if (!isValidSkillId(id)) {
|
|
91
87
|
console.error(chalk.red(` Invalid skill id "${idArg}".\n Skill ids must be lowercase alphanumeric + hyphens (no consecutive hyphens), max 64 chars.`));
|
|
92
88
|
process.exit(2);
|
|
93
89
|
return;
|
|
@@ -102,7 +98,7 @@ export function runSkillNew(idArg, options, cwd = process.cwd()) {
|
|
|
102
98
|
const harness = options.harness ?? "claude";
|
|
103
99
|
const baseDir = HARNESS_DIRS[harness];
|
|
104
100
|
if (!baseDir) {
|
|
105
|
-
console.error(chalk.red(` Unknown harness "${harness}". Allowed: ${
|
|
101
|
+
console.error(chalk.red(` Unknown harness "${harness}". Allowed: ${HARNESS_KEYS.join(", ")}`));
|
|
106
102
|
process.exit(2);
|
|
107
103
|
return;
|
|
108
104
|
}
|
|
@@ -197,7 +193,7 @@ export function registerSkillCommand(program) {
|
|
|
197
193
|
.description("Scaffold a new SKILL.md in .claude/skills/<id>/")
|
|
198
194
|
.option("-d, --description <text>", "One-line description (required field in frontmatter)")
|
|
199
195
|
.option("-c, --category <category>", `Skill category (${SKILL_CATEGORIES.join("|")})`, "general")
|
|
200
|
-
.option("--harness <harness>", `Where to place the skill (${
|
|
196
|
+
.option("--harness <harness>", `Where to place the skill (${HARNESS_KEYS.join("|")})`, "claude")
|
|
201
197
|
.option("--force", "Overwrite an existing SKILL.md")
|
|
202
198
|
.action((id, options) => runSkillNew(id, options));
|
|
203
199
|
skillCmd
|
package/dist/schema.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export declare const toolZ: z.ZodObject<{
|
|
|
42
42
|
description: string;
|
|
43
43
|
input_schema: Record<string, unknown>;
|
|
44
44
|
}>;
|
|
45
|
-
export declare const skillCategoryZ: z.ZodEnum<["
|
|
45
|
+
export declare const skillCategoryZ: z.ZodEnum<["general", "git", "tasks", "notification", "observability", "data"]>;
|
|
46
46
|
export declare const mcpServerConfigZ: z.ZodEffects<z.ZodObject<{
|
|
47
47
|
type: z.ZodOptional<z.ZodEnum<["stdio", "http"]>>;
|
|
48
48
|
command: z.ZodOptional<z.ZodString>;
|
|
@@ -172,7 +172,7 @@ export declare const skillZ: z.ZodEffects<z.ZodObject<{
|
|
|
172
172
|
id: z.ZodString;
|
|
173
173
|
name: z.ZodString;
|
|
174
174
|
description: z.ZodString;
|
|
175
|
-
category: z.ZodEnum<["
|
|
175
|
+
category: z.ZodEnum<["general", "git", "tasks", "notification", "observability", "data"]>;
|
|
176
176
|
config: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
177
177
|
description: z.ZodString;
|
|
178
178
|
required: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -241,7 +241,7 @@ export declare const skillZ: z.ZodEffects<z.ZodObject<{
|
|
|
241
241
|
id: string;
|
|
242
242
|
name: string;
|
|
243
243
|
description: string;
|
|
244
|
-
category: "general" | "git" | "tasks" | "notification" | "observability";
|
|
244
|
+
category: "general" | "git" | "tasks" | "notification" | "observability" | "data";
|
|
245
245
|
config: Record<string, {
|
|
246
246
|
description: string;
|
|
247
247
|
required?: boolean | undefined;
|
|
@@ -266,7 +266,7 @@ export declare const skillZ: z.ZodEffects<z.ZodObject<{
|
|
|
266
266
|
id: string;
|
|
267
267
|
name: string;
|
|
268
268
|
description: string;
|
|
269
|
-
category: "general" | "git" | "tasks" | "notification" | "observability";
|
|
269
|
+
category: "general" | "git" | "tasks" | "notification" | "observability" | "data";
|
|
270
270
|
instruction?: string | undefined;
|
|
271
271
|
mcp?: {
|
|
272
272
|
url?: string | undefined;
|
|
@@ -291,7 +291,7 @@ export declare const skillZ: z.ZodEffects<z.ZodObject<{
|
|
|
291
291
|
id: string;
|
|
292
292
|
name: string;
|
|
293
293
|
description: string;
|
|
294
|
-
category: "general" | "git" | "tasks" | "notification" | "observability";
|
|
294
|
+
category: "general" | "git" | "tasks" | "notification" | "observability" | "data";
|
|
295
295
|
config: Record<string, {
|
|
296
296
|
description: string;
|
|
297
297
|
required?: boolean | undefined;
|
|
@@ -316,7 +316,7 @@ export declare const skillZ: z.ZodEffects<z.ZodObject<{
|
|
|
316
316
|
id: string;
|
|
317
317
|
name: string;
|
|
318
318
|
description: string;
|
|
319
|
-
category: "general" | "git" | "tasks" | "notification" | "observability";
|
|
319
|
+
category: "general" | "git" | "tasks" | "notification" | "observability" | "data";
|
|
320
320
|
instruction?: string | undefined;
|
|
321
321
|
mcp?: {
|
|
322
322
|
url?: string | undefined;
|
|
@@ -1375,10 +1375,25 @@ export declare const edgeZ: z.ZodObject<{
|
|
|
1375
1375
|
when?: string | undefined;
|
|
1376
1376
|
max_iterations?: number | undefined;
|
|
1377
1377
|
}>;
|
|
1378
|
+
/**
|
|
1379
|
+
* Workflow type discriminator. Cloud uses this to route runs to a
|
|
1380
|
+
* type-specific renderer (Caught/Calibration/Missed for `pr_review`, flake
|
|
1381
|
+
* heatmap for `e2e_test`, source coverage delta for `monitor`, etc.).
|
|
1382
|
+
*
|
|
1383
|
+
* The field is optional on the workflow YAML; absence defaults to `generic`,
|
|
1384
|
+
* which gets a baseline renderer. Marketplace templates published in v1+
|
|
1385
|
+
* are required to declare it.
|
|
1386
|
+
*
|
|
1387
|
+
* Adding a new value here is a deliberate spec change: it requires a new
|
|
1388
|
+
* cloud renderer + metric schema. Don't extend casually.
|
|
1389
|
+
*/
|
|
1390
|
+
export declare const workflowTypeZ: z.ZodEnum<["pr_review", "e2e_test", "content_generation", "monitor", "data_sync", "generic"]>;
|
|
1391
|
+
export type WorkflowType = z.infer<typeof workflowTypeZ>;
|
|
1378
1392
|
export declare const workflowZ: z.ZodObject<{
|
|
1379
1393
|
id: z.ZodString;
|
|
1380
1394
|
name: z.ZodString;
|
|
1381
1395
|
description: z.ZodDefault<z.ZodString>;
|
|
1396
|
+
workflow_type: z.ZodOptional<z.ZodEnum<["pr_review", "e2e_test", "content_generation", "monitor", "data_sync", "generic"]>>;
|
|
1382
1397
|
nodes: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
1383
1398
|
name: z.ZodString;
|
|
1384
1399
|
instruction: z.ZodUnion<[z.ZodString, z.ZodObject<{
|
|
@@ -2250,6 +2265,7 @@ export declare const workflowZ: z.ZodObject<{
|
|
|
2250
2265
|
type?: string | undefined;
|
|
2251
2266
|
})[] | undefined;
|
|
2252
2267
|
judge_model?: string | undefined;
|
|
2268
|
+
workflow_type?: "pr_review" | "e2e_test" | "content_generation" | "monitor" | "data_sync" | "generic" | undefined;
|
|
2253
2269
|
judge_budget?: number | undefined;
|
|
2254
2270
|
}, {
|
|
2255
2271
|
id: string;
|
|
@@ -2381,6 +2397,7 @@ export declare const workflowZ: z.ZodObject<{
|
|
|
2381
2397
|
type?: string | undefined;
|
|
2382
2398
|
})[] | undefined;
|
|
2383
2399
|
judge_model?: string | undefined;
|
|
2400
|
+
workflow_type?: "pr_review" | "e2e_test" | "content_generation" | "monitor" | "data_sync" | "generic" | undefined;
|
|
2384
2401
|
judge_budget?: number | undefined;
|
|
2385
2402
|
}>;
|
|
2386
2403
|
/** Parse + validate a raw object as a Workflow. Throws on invalid input. */
|
|
@@ -2514,6 +2531,7 @@ export declare function parseWorkflow(raw: unknown): {
|
|
|
2514
2531
|
type?: string | undefined;
|
|
2515
2532
|
})[] | undefined;
|
|
2516
2533
|
judge_model?: string | undefined;
|
|
2534
|
+
workflow_type?: "pr_review" | "e2e_test" | "content_generation" | "monitor" | "data_sync" | "generic" | undefined;
|
|
2517
2535
|
judge_budget?: number | undefined;
|
|
2518
2536
|
};
|
|
2519
2537
|
export interface WorkflowError {
|
|
@@ -2767,6 +2785,11 @@ export declare const workflowJsonSchema: {
|
|
|
2767
2785
|
};
|
|
2768
2786
|
readonly description: "Background knowledge prepended to every node's instruction.";
|
|
2769
2787
|
};
|
|
2788
|
+
readonly workflow_type: {
|
|
2789
|
+
readonly type: "string";
|
|
2790
|
+
readonly enum: readonly ["pr_review", "e2e_test", "content_generation", "monitor", "data_sync", "generic"];
|
|
2791
|
+
readonly description: "Workflow type discriminator. Cloud uses this to route runs to a type-specific renderer. Optional; absence defaults to 'generic'.";
|
|
2792
|
+
};
|
|
2770
2793
|
readonly judge_model: {
|
|
2771
2794
|
readonly type: "string";
|
|
2772
2795
|
readonly minLength: 1;
|
|
@@ -3002,3 +3025,160 @@ export declare const workflowJsonSchema: {
|
|
|
3002
3025
|
};
|
|
3003
3026
|
};
|
|
3004
3027
|
};
|
|
3028
|
+
/**
|
|
3029
|
+
* Skill JSON Schema, generated from runtime constants.
|
|
3030
|
+
*
|
|
3031
|
+
* Companion to {@link workflowJsonSchema}: published at
|
|
3032
|
+
* https://spec.sweny.ai/schemas/skill.json by the
|
|
3033
|
+
* `write-public-schema.mjs` build step. Validates the structural shape
|
|
3034
|
+
* of a Skill (id, name, description, category, config, plus optional
|
|
3035
|
+
* tools/instruction/mcp).
|
|
3036
|
+
*
|
|
3037
|
+
* Every enum and the `id` pattern + maxLength are imported from
|
|
3038
|
+
* `types.ts` so adding a category, harness, transport, or changing the
|
|
3039
|
+
* id rule updates the published schema with no manual sync.
|
|
3040
|
+
*/
|
|
3041
|
+
export declare const skillJsonSchema: {
|
|
3042
|
+
readonly $schema: "https://json-schema.org/draft/2020-12/schema";
|
|
3043
|
+
readonly $id: "https://spec.sweny.ai/schemas/skill.json";
|
|
3044
|
+
readonly title: "SWEny Skill";
|
|
3045
|
+
readonly description: "A composable tool bundle that provides capabilities to workflow nodes.";
|
|
3046
|
+
readonly type: "object";
|
|
3047
|
+
readonly required: readonly ["id", "name", "description", "category", "config"];
|
|
3048
|
+
readonly anyOf: readonly [{
|
|
3049
|
+
readonly required: readonly ["tools"];
|
|
3050
|
+
}, {
|
|
3051
|
+
readonly required: readonly ["instruction"];
|
|
3052
|
+
}, {
|
|
3053
|
+
readonly required: readonly ["mcp"];
|
|
3054
|
+
}];
|
|
3055
|
+
readonly additionalProperties: false;
|
|
3056
|
+
readonly properties: {
|
|
3057
|
+
readonly id: {
|
|
3058
|
+
readonly type: "string";
|
|
3059
|
+
readonly minLength: 1;
|
|
3060
|
+
readonly maxLength: 64;
|
|
3061
|
+
readonly pattern: string;
|
|
3062
|
+
readonly description: "Unique skill identifier. Referenced by nodes' skills arrays. Lowercase kebab-case recommended.";
|
|
3063
|
+
};
|
|
3064
|
+
readonly name: {
|
|
3065
|
+
readonly type: "string";
|
|
3066
|
+
readonly minLength: 1;
|
|
3067
|
+
readonly description: "Human-readable skill name.";
|
|
3068
|
+
};
|
|
3069
|
+
readonly description: {
|
|
3070
|
+
readonly type: "string";
|
|
3071
|
+
readonly description: "What this skill provides.";
|
|
3072
|
+
};
|
|
3073
|
+
readonly category: {
|
|
3074
|
+
readonly type: "string";
|
|
3075
|
+
readonly enum: readonly ["general", "git", "tasks", "notification", "observability", "data"];
|
|
3076
|
+
readonly description: "Functional category.";
|
|
3077
|
+
};
|
|
3078
|
+
readonly config: {
|
|
3079
|
+
readonly type: "object";
|
|
3080
|
+
readonly description: "Configuration fields required by this skill.";
|
|
3081
|
+
readonly additionalProperties: {
|
|
3082
|
+
readonly $ref: "#/$defs/ConfigField";
|
|
3083
|
+
};
|
|
3084
|
+
};
|
|
3085
|
+
readonly tools: {
|
|
3086
|
+
readonly type: "array";
|
|
3087
|
+
readonly description: "Tools this skill provides to nodes.";
|
|
3088
|
+
readonly items: {
|
|
3089
|
+
readonly $ref: "#/$defs/Tool";
|
|
3090
|
+
};
|
|
3091
|
+
};
|
|
3092
|
+
readonly instruction: {
|
|
3093
|
+
readonly type: "string";
|
|
3094
|
+
readonly description: "Natural language expertise injected into the node prompt when this skill is referenced.";
|
|
3095
|
+
};
|
|
3096
|
+
readonly mcp: {
|
|
3097
|
+
readonly $ref: "#/$defs/McpServerConfig";
|
|
3098
|
+
readonly description: "External MCP server definition wired for nodes referencing this skill.";
|
|
3099
|
+
};
|
|
3100
|
+
};
|
|
3101
|
+
readonly $defs: {
|
|
3102
|
+
readonly ConfigField: {
|
|
3103
|
+
readonly type: "object";
|
|
3104
|
+
readonly required: readonly ["description"];
|
|
3105
|
+
readonly additionalProperties: false;
|
|
3106
|
+
readonly properties: {
|
|
3107
|
+
readonly description: {
|
|
3108
|
+
readonly type: "string";
|
|
3109
|
+
readonly description: "Human-readable description of this config field.";
|
|
3110
|
+
};
|
|
3111
|
+
readonly required: {
|
|
3112
|
+
readonly type: "boolean";
|
|
3113
|
+
readonly default: false;
|
|
3114
|
+
readonly description: "Whether this field must be provided for the skill to function.";
|
|
3115
|
+
};
|
|
3116
|
+
readonly env: {
|
|
3117
|
+
readonly type: "string";
|
|
3118
|
+
readonly description: "Default environment variable to read this value from.";
|
|
3119
|
+
};
|
|
3120
|
+
};
|
|
3121
|
+
};
|
|
3122
|
+
readonly Tool: {
|
|
3123
|
+
readonly type: "object";
|
|
3124
|
+
readonly required: readonly ["name", "description", "input_schema"];
|
|
3125
|
+
readonly additionalProperties: false;
|
|
3126
|
+
readonly properties: {
|
|
3127
|
+
readonly name: {
|
|
3128
|
+
readonly type: "string";
|
|
3129
|
+
readonly description: "Tool name. Must be unique within the skill.";
|
|
3130
|
+
};
|
|
3131
|
+
readonly description: {
|
|
3132
|
+
readonly type: "string";
|
|
3133
|
+
readonly description: "What this tool does. Provided to the AI model for tool selection.";
|
|
3134
|
+
};
|
|
3135
|
+
readonly input_schema: {
|
|
3136
|
+
readonly type: "object";
|
|
3137
|
+
readonly description: "JSON Schema defining the tool's input parameters.";
|
|
3138
|
+
};
|
|
3139
|
+
};
|
|
3140
|
+
};
|
|
3141
|
+
readonly McpServerConfig: {
|
|
3142
|
+
readonly type: "object";
|
|
3143
|
+
readonly description: "External MCP server definition.";
|
|
3144
|
+
readonly additionalProperties: false;
|
|
3145
|
+
readonly properties: {
|
|
3146
|
+
readonly type: {
|
|
3147
|
+
readonly type: "string";
|
|
3148
|
+
readonly enum: readonly ["stdio", "http"];
|
|
3149
|
+
readonly description: "Transport type. Inferred from presence of command (stdio) or url (http) when omitted.";
|
|
3150
|
+
};
|
|
3151
|
+
readonly command: {
|
|
3152
|
+
readonly type: "string";
|
|
3153
|
+
readonly description: "Spawn command (stdio transport).";
|
|
3154
|
+
};
|
|
3155
|
+
readonly args: {
|
|
3156
|
+
readonly type: "array";
|
|
3157
|
+
readonly items: {
|
|
3158
|
+
readonly type: "string";
|
|
3159
|
+
};
|
|
3160
|
+
readonly description: "Arguments for the command.";
|
|
3161
|
+
};
|
|
3162
|
+
readonly url: {
|
|
3163
|
+
readonly type: "string";
|
|
3164
|
+
readonly format: "uri";
|
|
3165
|
+
readonly description: "HTTP endpoint (HTTP transport).";
|
|
3166
|
+
};
|
|
3167
|
+
readonly headers: {
|
|
3168
|
+
readonly type: "object";
|
|
3169
|
+
readonly additionalProperties: {
|
|
3170
|
+
readonly type: "string";
|
|
3171
|
+
};
|
|
3172
|
+
readonly description: "HTTP headers (HTTP transport only).";
|
|
3173
|
+
};
|
|
3174
|
+
readonly env: {
|
|
3175
|
+
readonly type: "object";
|
|
3176
|
+
readonly additionalProperties: {
|
|
3177
|
+
readonly type: "string";
|
|
3178
|
+
};
|
|
3179
|
+
readonly description: "Environment variable names the server needs. Values are descriptions, not secrets.";
|
|
3180
|
+
};
|
|
3181
|
+
};
|
|
3182
|
+
};
|
|
3183
|
+
};
|
|
3184
|
+
};
|
package/dist/schema.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* parsing untrusted input (JSON/YAML import, Studio editor).
|
|
8
8
|
*/
|
|
9
9
|
import { z } from "zod";
|
|
10
|
+
import { EVALUATOR_KINDS, EVAL_POLICIES, MCP_TRANSPORTS, REQUIRES_ON_FAIL, SKILL_CATEGORIES, SKILL_ID_MAX_LENGTH, SKILL_ID_PATTERN, } from "./types.js";
|
|
10
11
|
import { sourceZ } from "./sources.js";
|
|
11
12
|
export { sourceZ };
|
|
12
13
|
// ─── Zod Schemas ─────────────────────────────────────────────────
|
|
@@ -26,10 +27,10 @@ export const toolZ = z.object({
|
|
|
26
27
|
description: z.string(),
|
|
27
28
|
input_schema: jsonSchemaZ,
|
|
28
29
|
});
|
|
29
|
-
export const skillCategoryZ = z.enum(
|
|
30
|
+
export const skillCategoryZ = z.enum(SKILL_CATEGORIES);
|
|
30
31
|
export const mcpServerConfigZ = z
|
|
31
32
|
.object({
|
|
32
|
-
type: z.enum(
|
|
33
|
+
type: z.enum(MCP_TRANSPORTS).optional(),
|
|
33
34
|
command: z.string().optional(),
|
|
34
35
|
args: z.array(z.string()).optional(),
|
|
35
36
|
url: z.string().optional(),
|
|
@@ -90,8 +91,8 @@ export const outputMatchZ = z
|
|
|
90
91
|
const operators = [m.equals !== undefined, m.in !== undefined, m.matches !== undefined];
|
|
91
92
|
return operators.filter(Boolean).length === 1;
|
|
92
93
|
}, { message: "output_matches entry must declare exactly one of: equals, in, matches" });
|
|
93
|
-
export const evaluatorKindZ = z.enum(
|
|
94
|
-
export const evalPolicyZ = z.enum(
|
|
94
|
+
export const evaluatorKindZ = z.enum(EVALUATOR_KINDS);
|
|
95
|
+
export const evalPolicyZ = z.enum(EVAL_POLICIES);
|
|
95
96
|
export const evaluatorRuleZ = z
|
|
96
97
|
.object({
|
|
97
98
|
any_tool_called: z.array(z.string().min(1)).min(1).optional(),
|
|
@@ -170,7 +171,7 @@ export const nodeRequiresZ = z
|
|
|
170
171
|
.object({
|
|
171
172
|
output_required: z.array(z.string().min(1)).min(1).optional(),
|
|
172
173
|
output_matches: z.array(outputMatchZ).min(1).optional(),
|
|
173
|
-
on_fail: z.enum(
|
|
174
|
+
on_fail: z.enum(REQUIRES_ON_FAIL).optional(),
|
|
174
175
|
})
|
|
175
176
|
.strict()
|
|
176
177
|
.refine((r) => r.output_required !== undefined || r.output_matches !== undefined, {
|
|
@@ -208,6 +209,19 @@ export const edgeZ = z
|
|
|
208
209
|
max_iterations: z.number().int().min(1).optional(),
|
|
209
210
|
})
|
|
210
211
|
.strict();
|
|
212
|
+
/**
|
|
213
|
+
* Workflow type discriminator. Cloud uses this to route runs to a
|
|
214
|
+
* type-specific renderer (Caught/Calibration/Missed for `pr_review`, flake
|
|
215
|
+
* heatmap for `e2e_test`, source coverage delta for `monitor`, etc.).
|
|
216
|
+
*
|
|
217
|
+
* The field is optional on the workflow YAML; absence defaults to `generic`,
|
|
218
|
+
* which gets a baseline renderer. Marketplace templates published in v1+
|
|
219
|
+
* are required to declare it.
|
|
220
|
+
*
|
|
221
|
+
* Adding a new value here is a deliberate spec change: it requires a new
|
|
222
|
+
* cloud renderer + metric schema. Don't extend casually.
|
|
223
|
+
*/
|
|
224
|
+
export const workflowTypeZ = z.enum(["pr_review", "e2e_test", "content_generation", "monitor", "data_sync", "generic"]);
|
|
211
225
|
// Fix #4 gap — workflowZ is intentionally NOT .strict(). The published
|
|
212
226
|
// JSON Schema's `additionalProperties: false` only scopes the properties
|
|
213
227
|
// block, but marketplace workflows routinely carry top-level metadata
|
|
@@ -218,6 +232,7 @@ export const workflowZ = z.object({
|
|
|
218
232
|
id: z.string().min(1),
|
|
219
233
|
name: z.string().min(1),
|
|
220
234
|
description: z.string().default(""),
|
|
235
|
+
workflow_type: workflowTypeZ.optional(),
|
|
221
236
|
nodes: z.record(nodeZ),
|
|
222
237
|
edges: z.array(edgeZ),
|
|
223
238
|
entry: z.string().min(1),
|
|
@@ -482,7 +497,7 @@ export const workflowJsonSchema = {
|
|
|
482
497
|
minLength: 1,
|
|
483
498
|
description: "Stable identifier for this evaluator. Used in EvalResult and retry preambles.",
|
|
484
499
|
},
|
|
485
|
-
kind: { type: "string", enum: [
|
|
500
|
+
kind: { type: "string", enum: [...EVALUATOR_KINDS] },
|
|
486
501
|
rule: {
|
|
487
502
|
type: "object",
|
|
488
503
|
description: "Required for value and function kinds. Shape depends on kind.",
|
|
@@ -540,6 +555,11 @@ export const workflowJsonSchema = {
|
|
|
540
555
|
items: { $ref: "#/$defs/Source" },
|
|
541
556
|
description: "Background knowledge prepended to every node's instruction.",
|
|
542
557
|
},
|
|
558
|
+
workflow_type: {
|
|
559
|
+
type: "string",
|
|
560
|
+
enum: ["pr_review", "e2e_test", "content_generation", "monitor", "data_sync", "generic"],
|
|
561
|
+
description: "Workflow type discriminator. Cloud uses this to route runs to a type-specific renderer. Optional; absence defaults to 'generic'.",
|
|
562
|
+
},
|
|
543
563
|
judge_model: {
|
|
544
564
|
type: "string",
|
|
545
565
|
minLength: 1,
|
|
@@ -591,7 +611,7 @@ export const workflowJsonSchema = {
|
|
|
591
611
|
},
|
|
592
612
|
eval_policy: {
|
|
593
613
|
type: "string",
|
|
594
|
-
enum: [
|
|
614
|
+
enum: [...EVAL_POLICIES],
|
|
595
615
|
default: "all_pass",
|
|
596
616
|
description: "How evaluator results aggregate. v1 implements all_pass; the others are reserved.",
|
|
597
617
|
},
|
|
@@ -618,7 +638,7 @@ export const workflowJsonSchema = {
|
|
|
618
638
|
items: { $ref: "#/$defs/OutputMatch" },
|
|
619
639
|
minItems: 1,
|
|
620
640
|
},
|
|
621
|
-
on_fail: { type: "string", enum: [
|
|
641
|
+
on_fail: { type: "string", enum: [...REQUIRES_ON_FAIL] },
|
|
622
642
|
},
|
|
623
643
|
},
|
|
624
644
|
retry: {
|
|
@@ -689,7 +709,7 @@ export const workflowJsonSchema = {
|
|
|
689
709
|
// Round 2: reject unknown keys to match Zod mcpServerConfigZ.strict().
|
|
690
710
|
additionalProperties: false,
|
|
691
711
|
properties: {
|
|
692
|
-
type: { type: "string", enum: [
|
|
712
|
+
type: { type: "string", enum: [...MCP_TRANSPORTS] },
|
|
693
713
|
command: { type: "string" },
|
|
694
714
|
args: { type: "array", items: { type: "string" } },
|
|
695
715
|
url: { type: "string" },
|
|
@@ -702,3 +722,144 @@ export const workflowJsonSchema = {
|
|
|
702
722
|
},
|
|
703
723
|
},
|
|
704
724
|
};
|
|
725
|
+
/**
|
|
726
|
+
* Skill JSON Schema, generated from runtime constants.
|
|
727
|
+
*
|
|
728
|
+
* Companion to {@link workflowJsonSchema}: published at
|
|
729
|
+
* https://spec.sweny.ai/schemas/skill.json by the
|
|
730
|
+
* `write-public-schema.mjs` build step. Validates the structural shape
|
|
731
|
+
* of a Skill (id, name, description, category, config, plus optional
|
|
732
|
+
* tools/instruction/mcp).
|
|
733
|
+
*
|
|
734
|
+
* Every enum and the `id` pattern + maxLength are imported from
|
|
735
|
+
* `types.ts` so adding a category, harness, transport, or changing the
|
|
736
|
+
* id rule updates the published schema with no manual sync.
|
|
737
|
+
*/
|
|
738
|
+
export const skillJsonSchema = {
|
|
739
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
740
|
+
$id: "https://spec.sweny.ai/schemas/skill.json",
|
|
741
|
+
title: "SWEny Skill",
|
|
742
|
+
description: "A composable tool bundle that provides capabilities to workflow nodes.",
|
|
743
|
+
type: "object",
|
|
744
|
+
required: ["id", "name", "description", "category", "config"],
|
|
745
|
+
anyOf: [{ required: ["tools"] }, { required: ["instruction"] }, { required: ["mcp"] }],
|
|
746
|
+
additionalProperties: false,
|
|
747
|
+
properties: {
|
|
748
|
+
id: {
|
|
749
|
+
type: "string",
|
|
750
|
+
minLength: 1,
|
|
751
|
+
maxLength: SKILL_ID_MAX_LENGTH,
|
|
752
|
+
pattern: SKILL_ID_PATTERN.source,
|
|
753
|
+
description: "Unique skill identifier. Referenced by nodes' skills arrays. Lowercase kebab-case recommended.",
|
|
754
|
+
},
|
|
755
|
+
name: {
|
|
756
|
+
type: "string",
|
|
757
|
+
minLength: 1,
|
|
758
|
+
description: "Human-readable skill name.",
|
|
759
|
+
},
|
|
760
|
+
description: {
|
|
761
|
+
type: "string",
|
|
762
|
+
description: "What this skill provides.",
|
|
763
|
+
},
|
|
764
|
+
category: {
|
|
765
|
+
type: "string",
|
|
766
|
+
enum: [...SKILL_CATEGORIES],
|
|
767
|
+
description: "Functional category.",
|
|
768
|
+
},
|
|
769
|
+
config: {
|
|
770
|
+
type: "object",
|
|
771
|
+
description: "Configuration fields required by this skill.",
|
|
772
|
+
additionalProperties: { $ref: "#/$defs/ConfigField" },
|
|
773
|
+
},
|
|
774
|
+
tools: {
|
|
775
|
+
type: "array",
|
|
776
|
+
description: "Tools this skill provides to nodes.",
|
|
777
|
+
items: { $ref: "#/$defs/Tool" },
|
|
778
|
+
},
|
|
779
|
+
instruction: {
|
|
780
|
+
type: "string",
|
|
781
|
+
description: "Natural language expertise injected into the node prompt when this skill is referenced.",
|
|
782
|
+
},
|
|
783
|
+
mcp: {
|
|
784
|
+
$ref: "#/$defs/McpServerConfig",
|
|
785
|
+
description: "External MCP server definition wired for nodes referencing this skill.",
|
|
786
|
+
},
|
|
787
|
+
},
|
|
788
|
+
$defs: {
|
|
789
|
+
ConfigField: {
|
|
790
|
+
type: "object",
|
|
791
|
+
required: ["description"],
|
|
792
|
+
additionalProperties: false,
|
|
793
|
+
properties: {
|
|
794
|
+
description: {
|
|
795
|
+
type: "string",
|
|
796
|
+
description: "Human-readable description of this config field.",
|
|
797
|
+
},
|
|
798
|
+
required: {
|
|
799
|
+
type: "boolean",
|
|
800
|
+
default: false,
|
|
801
|
+
description: "Whether this field must be provided for the skill to function.",
|
|
802
|
+
},
|
|
803
|
+
env: {
|
|
804
|
+
type: "string",
|
|
805
|
+
description: "Default environment variable to read this value from.",
|
|
806
|
+
},
|
|
807
|
+
},
|
|
808
|
+
},
|
|
809
|
+
Tool: {
|
|
810
|
+
type: "object",
|
|
811
|
+
required: ["name", "description", "input_schema"],
|
|
812
|
+
additionalProperties: false,
|
|
813
|
+
properties: {
|
|
814
|
+
name: {
|
|
815
|
+
type: "string",
|
|
816
|
+
description: "Tool name. Must be unique within the skill.",
|
|
817
|
+
},
|
|
818
|
+
description: {
|
|
819
|
+
type: "string",
|
|
820
|
+
description: "What this tool does. Provided to the AI model for tool selection.",
|
|
821
|
+
},
|
|
822
|
+
input_schema: {
|
|
823
|
+
type: "object",
|
|
824
|
+
description: "JSON Schema defining the tool's input parameters.",
|
|
825
|
+
},
|
|
826
|
+
},
|
|
827
|
+
},
|
|
828
|
+
McpServerConfig: {
|
|
829
|
+
type: "object",
|
|
830
|
+
description: "External MCP server definition.",
|
|
831
|
+
additionalProperties: false,
|
|
832
|
+
properties: {
|
|
833
|
+
type: {
|
|
834
|
+
type: "string",
|
|
835
|
+
enum: [...MCP_TRANSPORTS],
|
|
836
|
+
description: "Transport type. Inferred from presence of command (stdio) or url (http) when omitted.",
|
|
837
|
+
},
|
|
838
|
+
command: {
|
|
839
|
+
type: "string",
|
|
840
|
+
description: "Spawn command (stdio transport).",
|
|
841
|
+
},
|
|
842
|
+
args: {
|
|
843
|
+
type: "array",
|
|
844
|
+
items: { type: "string" },
|
|
845
|
+
description: "Arguments for the command.",
|
|
846
|
+
},
|
|
847
|
+
url: {
|
|
848
|
+
type: "string",
|
|
849
|
+
format: "uri",
|
|
850
|
+
description: "HTTP endpoint (HTTP transport).",
|
|
851
|
+
},
|
|
852
|
+
headers: {
|
|
853
|
+
type: "object",
|
|
854
|
+
additionalProperties: { type: "string" },
|
|
855
|
+
description: "HTTP headers (HTTP transport only).",
|
|
856
|
+
},
|
|
857
|
+
env: {
|
|
858
|
+
type: "object",
|
|
859
|
+
additionalProperties: { type: "string" },
|
|
860
|
+
description: "Environment variable names the server needs. Values are descriptions, not secrets.",
|
|
861
|
+
},
|
|
862
|
+
},
|
|
863
|
+
},
|
|
864
|
+
},
|
|
865
|
+
};
|
|
@@ -11,12 +11,15 @@
|
|
|
11
11
|
import { readdirSync, readFileSync, existsSync } from "node:fs";
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
import YAML from "yaml";
|
|
14
|
-
import { SKILL_CATEGORIES } from "../types.js";
|
|
14
|
+
import { SKILL_CATEGORIES, SKILL_HARNESSES, isValidSkillId } from "../types.js";
|
|
15
15
|
import { builtinSkills, isSkillConfigured } from "./index.js";
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Directories to scan, in ascending priority order. Last match wins.
|
|
18
|
+
*
|
|
19
|
+
* Derived from {@link SKILL_HARNESSES} so the loader and the CLI authoring
|
|
20
|
+
* side cannot disagree on what counts as a skill harness directory.
|
|
21
|
+
*/
|
|
22
|
+
const SKILL_DIRS = SKILL_HARNESSES.map((h) => h.path);
|
|
20
23
|
/**
|
|
21
24
|
* Discover custom skills and surface diagnostics for broken ones.
|
|
22
25
|
*
|
|
@@ -104,7 +107,7 @@ function parseSkillMd(content, path) {
|
|
|
104
107
|
? fm.category
|
|
105
108
|
: "general";
|
|
106
109
|
const id = String(fm.name);
|
|
107
|
-
if (!
|
|
110
|
+
if (!isValidSkillId(id)) {
|
|
108
111
|
return {
|
|
109
112
|
kind: "err",
|
|
110
113
|
diagnostic: {
|