@sweny-ai/core 0.1.77 → 0.1.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/dist/browser.d.ts +11 -5
  2. package/dist/browser.js +11 -11
  3. package/dist/claude.d.ts +16 -0
  4. package/dist/claude.js +98 -9
  5. package/dist/cli/config.d.ts +22 -0
  6. package/dist/cli/config.js +56 -9
  7. package/dist/cli/main.d.ts +1 -1
  8. package/dist/cli/main.js +56 -57
  9. package/dist/cli/marketplace.js +35 -9
  10. package/dist/cli/publish.d.ts +10 -0
  11. package/dist/cli/publish.js +22 -14
  12. package/dist/loader.d.ts +40 -0
  13. package/dist/loader.js +73 -0
  14. package/dist/mcp-catalog.d.ts +47 -0
  15. package/dist/mcp-catalog.js +304 -0
  16. package/dist/mcp.d.ts +6 -9
  17. package/dist/mcp.js +24 -314
  18. package/dist/schema.d.ts +183 -151
  19. package/dist/schema.js +49 -7
  20. package/dist/skills/custom-loader.d.ts +40 -0
  21. package/dist/skills/custom-loader.js +107 -32
  22. package/dist/skills/index.d.ts +13 -1
  23. package/dist/skills/index.js +12 -3
  24. package/dist/studio.js +4 -1
  25. package/dist/templates.d.ts +29 -8
  26. package/dist/templates.js +30 -20
  27. package/dist/types.d.ts +12 -0
  28. package/dist/verify.js +42 -4
  29. package/package.json +8 -4
  30. package/dist/__tests__/cascade.test.d.ts +0 -11
  31. package/dist/__tests__/cascade.test.js +0 -452
  32. package/dist/__tests__/claude.test.d.ts +0 -1
  33. package/dist/__tests__/claude.test.js +0 -427
  34. package/dist/__tests__/custom-loader.test.d.ts +0 -1
  35. package/dist/__tests__/custom-loader.test.js +0 -116
  36. package/dist/__tests__/executor.test.d.ts +0 -1
  37. package/dist/__tests__/executor.test.js +0 -1635
  38. package/dist/__tests__/integration/datadog.integration.test.d.ts +0 -1
  39. package/dist/__tests__/integration/datadog.integration.test.js +0 -23
  40. package/dist/__tests__/integration/e2e-workflow.integration.test.d.ts +0 -1
  41. package/dist/__tests__/integration/e2e-workflow.integration.test.js +0 -75
  42. package/dist/__tests__/integration/github.integration.test.d.ts +0 -1
  43. package/dist/__tests__/integration/github.integration.test.js +0 -37
  44. package/dist/__tests__/integration/harness.d.ts +0 -24
  45. package/dist/__tests__/integration/harness.js +0 -34
  46. package/dist/__tests__/integration/linear.integration.test.d.ts +0 -1
  47. package/dist/__tests__/integration/linear.integration.test.js +0 -15
  48. package/dist/__tests__/integration/sentry.integration.test.d.ts +0 -1
  49. package/dist/__tests__/integration/sentry.integration.test.js +0 -20
  50. package/dist/__tests__/integration/slack.integration.test.d.ts +0 -1
  51. package/dist/__tests__/integration/slack.integration.test.js +0 -22
  52. package/dist/__tests__/integration/sources.integration.test.d.ts +0 -1
  53. package/dist/__tests__/integration/sources.integration.test.js +0 -123
  54. package/dist/__tests__/publish.test.d.ts +0 -1
  55. package/dist/__tests__/publish.test.js +0 -180
  56. package/dist/__tests__/requires.test.d.ts +0 -1
  57. package/dist/__tests__/requires.test.js +0 -82
  58. package/dist/__tests__/retry.test.d.ts +0 -1
  59. package/dist/__tests__/retry.test.js +0 -213
  60. package/dist/__tests__/schema.test.d.ts +0 -1
  61. package/dist/__tests__/schema.test.js +0 -600
  62. package/dist/__tests__/skill-config-integration.test.d.ts +0 -1
  63. package/dist/__tests__/skill-config-integration.test.js +0 -204
  64. package/dist/__tests__/skills-index.test.d.ts +0 -1
  65. package/dist/__tests__/skills-index.test.js +0 -119
  66. package/dist/__tests__/skills.test.d.ts +0 -1
  67. package/dist/__tests__/skills.test.js +0 -565
  68. package/dist/__tests__/spec-conformance.test.d.ts +0 -18
  69. package/dist/__tests__/spec-conformance.test.js +0 -1084
  70. package/dist/__tests__/studio.test.d.ts +0 -1
  71. package/dist/__tests__/studio.test.js +0 -403
  72. package/dist/__tests__/templates.test.d.ts +0 -1
  73. package/dist/__tests__/templates.test.js +0 -49
  74. package/dist/__tests__/testing.test.d.ts +0 -1
  75. package/dist/__tests__/testing.test.js +0 -242
  76. package/dist/__tests__/verify.test.d.ts +0 -1
  77. package/dist/__tests__/verify.test.js +0 -517
  78. package/dist/__tests__/version-consistency.test.d.ts +0 -1
  79. package/dist/__tests__/version-consistency.test.js +0 -312
  80. package/dist/__tests__/workflow-builder.test.d.ts +0 -1
  81. package/dist/__tests__/workflow-builder.test.js +0 -110
  82. package/dist/__tests__/workflow-yaml.test.d.ts +0 -1
  83. package/dist/__tests__/workflow-yaml.test.js +0 -201
  84. package/dist/cli/__tests__/diagram.test.d.ts +0 -1
  85. package/dist/cli/__tests__/diagram.test.js +0 -225
  86. package/dist/cli/__tests__/report-cloud.test.d.ts +0 -1
  87. package/dist/cli/__tests__/report-cloud.test.js +0 -165
  88. package/dist/cli/__tests__/upgrade.test.d.ts +0 -1
  89. package/dist/cli/__tests__/upgrade.test.js +0 -317
  90. package/dist/cli/__tests__/version-check.test.d.ts +0 -1
  91. package/dist/cli/__tests__/version-check.test.js +0 -209
  92. package/dist/cli/config.test.d.ts +0 -1
  93. package/dist/cli/config.test.js +0 -200
  94. package/dist/cli/credentials.test.d.ts +0 -1
  95. package/dist/cli/credentials.test.js +0 -172
  96. package/dist/cli/e2e.test.d.ts +0 -1
  97. package/dist/cli/e2e.test.js +0 -690
  98. package/dist/cli/marketplace.test.d.ts +0 -1
  99. package/dist/cli/marketplace.test.js +0 -355
  100. package/dist/cli/new.test.d.ts +0 -1
  101. package/dist/cli/new.test.js +0 -1043
  102. package/dist/cli/output.test.d.ts +0 -1
  103. package/dist/cli/output.test.js +0 -180
  104. package/dist/cli/publish.test.d.ts +0 -1
  105. package/dist/cli/publish.test.js +0 -610
  106. package/dist/cli/renderer.test.d.ts +0 -1
  107. package/dist/cli/renderer.test.js +0 -302
  108. package/dist/executor.test.d.ts +0 -1
  109. package/dist/executor.test.js +0 -618
  110. package/dist/mcp.test.d.ts +0 -1
  111. package/dist/mcp.test.js +0 -735
  112. package/dist/mermaid.test.d.ts +0 -1
  113. package/dist/mermaid.test.js +0 -256
  114. package/dist/sources.test.d.ts +0 -1
  115. package/dist/sources.test.js +0 -265
  116. package/dist/sources.validation.test.d.ts +0 -12
  117. package/dist/sources.validation.test.js +0 -380
  118. package/dist/workflow-builder.test.d.ts +0 -1
  119. package/dist/workflow-builder.test.js +0 -123
@@ -1 +0,0 @@
1
- export {};
@@ -1,403 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { workflowToFlow, flowToWorkflow, applyExecutionEvent, exportAsTypescript, getSkillCatalog } from "../studio.js";
3
- const testWorkflow = {
4
- id: "test-studio",
5
- name: "Studio Test",
6
- description: "A→B, A→C",
7
- entry: "a",
8
- nodes: {
9
- a: { name: "Node A", instruction: "Do A", skills: ["github"] },
10
- b: { name: "Node B", instruction: "Do B", skills: [] },
11
- c: { name: "Node C", instruction: "Do C", skills: ["slack"] },
12
- },
13
- edges: [
14
- { from: "a", to: "b" },
15
- { from: "a", to: "c", when: "needs notification" },
16
- ],
17
- };
18
- describe("workflowToFlow", () => {
19
- it("converts nodes to FlowNodes", () => {
20
- const { nodes } = workflowToFlow(testWorkflow);
21
- expect(nodes).toHaveLength(3);
22
- const nodeA = nodes.find((n) => n.id === "a");
23
- expect(nodeA?.type).toBe("skillNode");
24
- expect(nodeA?.data.isEntry).toBe(true);
25
- expect(nodeA?.data.nodeId).toBe("a");
26
- expect(nodeA?.data.node.name).toBe("Node A");
27
- });
28
- it("marks terminal nodes", () => {
29
- const { nodes } = workflowToFlow(testWorkflow);
30
- const nodeB = nodes.find((n) => n.id === "b");
31
- const nodeC = nodes.find((n) => n.id === "c");
32
- expect(nodeB?.data.isTerminal).toBe(true);
33
- expect(nodeC?.data.isTerminal).toBe(true);
34
- // a is not terminal (it has outgoing edges)
35
- const nodeA = nodes.find((n) => n.id === "a");
36
- expect(nodeA?.data.isTerminal).toBe(false);
37
- });
38
- it("converts edges to FlowEdges", () => {
39
- const { edges } = workflowToFlow(testWorkflow);
40
- expect(edges).toHaveLength(2);
41
- const unconditional = edges.find((e) => e.source === "a" && e.target === "b");
42
- expect(unconditional?.data.isConditional).toBe(false);
43
- expect(unconditional?.data.when).toBeUndefined();
44
- const conditional = edges.find((e) => e.source === "a" && e.target === "c");
45
- expect(conditional?.data.isConditional).toBe(true);
46
- expect(conditional?.data.when).toBe("needs notification");
47
- });
48
- it("resolves skill metadata from catalog", () => {
49
- const { nodes } = workflowToFlow(testWorkflow);
50
- const nodeA = nodes.find((n) => n.id === "a");
51
- expect(nodeA?.data.skills).toHaveLength(1);
52
- expect(nodeA?.data.skills[0].id).toBe("github");
53
- expect(nodeA?.data.skills[0].name).toBe("GitHub");
54
- expect(nodeA?.data.skills[0].toolCount).toBeGreaterThan(0);
55
- });
56
- it("handles unknown skills gracefully", () => {
57
- const { nodes } = workflowToFlow(testWorkflow, []); // empty catalog
58
- const nodeA = nodes.find((n) => n.id === "a");
59
- expect(nodeA?.data.skills[0].name).toBe("github"); // falls back to ID
60
- expect(nodeA?.data.skills[0].toolCount).toBe(0);
61
- });
62
- it("initializes all nodes as pending", () => {
63
- const { nodes } = workflowToFlow(testWorkflow);
64
- for (const node of nodes) {
65
- expect(node.data.exec.status).toBe("pending");
66
- }
67
- });
68
- // Defensive behavior: streaming LLM YAML (and user-authored
69
- // workflows) routinely arrive with fields omitted. The viewer
70
- // must not crash on these — it should render an empty state for
71
- // the missing pieces.
72
- describe("defensive handling of partial workflows", () => {
73
- it("treats a node with undefined skills as having no skills", () => {
74
- const partial = {
75
- id: "partial",
76
- name: "Partial",
77
- description: "",
78
- entry: "a",
79
- nodes: {
80
- // Cast to unknown→Node: we're deliberately simulating
81
- // what a partial YAML parse produces at runtime.
82
- a: { name: "A", instruction: "Do A" },
83
- },
84
- edges: [],
85
- };
86
- expect(() => workflowToFlow(partial)).not.toThrow();
87
- const { nodes } = workflowToFlow(partial);
88
- expect(nodes[0].data.skills).toEqual([]);
89
- });
90
- it("treats a workflow with undefined edges as having no edges", () => {
91
- const partial = {
92
- id: "partial",
93
- name: "Partial",
94
- description: "",
95
- entry: "a",
96
- nodes: {
97
- a: { name: "A", instruction: "Do A", skills: [] },
98
- },
99
- };
100
- expect(() => workflowToFlow(partial)).not.toThrow();
101
- const { nodes, edges } = workflowToFlow(partial);
102
- expect(edges).toEqual([]);
103
- // The sole node is still terminal because there are no outgoing edges.
104
- expect(nodes[0].data.isTerminal).toBe(true);
105
- });
106
- it("treats a workflow with undefined nodes as having no nodes", () => {
107
- const partial = {
108
- id: "partial",
109
- name: "Partial",
110
- description: "",
111
- entry: "a",
112
- edges: [],
113
- };
114
- expect(() => workflowToFlow(partial)).not.toThrow();
115
- const { nodes, edges } = workflowToFlow(partial);
116
- expect(nodes).toEqual([]);
117
- expect(edges).toEqual([]);
118
- });
119
- it("treats a skill catalog entry with missing tools as toolCount=0", () => {
120
- const brokenSkill = {
121
- id: "broken",
122
- name: "Broken",
123
- description: "No tools array",
124
- category: "general",
125
- config: {},
126
- // tools omitted on purpose
127
- };
128
- const wf = {
129
- id: "t",
130
- name: "T",
131
- description: "",
132
- entry: "a",
133
- nodes: { a: { name: "A", instruction: "Do A", skills: ["broken"] } },
134
- edges: [],
135
- };
136
- expect(() => workflowToFlow(wf, [brokenSkill])).not.toThrow();
137
- const { nodes } = workflowToFlow(wf, [brokenSkill]);
138
- expect(nodes[0].data.skills[0].toolCount).toBe(0);
139
- });
140
- });
141
- });
142
- describe("edge IDs and edgeIndex", () => {
143
- it("generates unique edge IDs using array index", () => {
144
- const { edges } = workflowToFlow(testWorkflow);
145
- const ids = edges.map((e) => e.id);
146
- expect(ids[0]).toBe("edge-0");
147
- expect(ids[1]).toBe("edge-1");
148
- // All IDs are unique
149
- expect(new Set(ids).size).toBe(ids.length);
150
- });
151
- it("assigns correct edgeIndex matching array position", () => {
152
- const { edges } = workflowToFlow(testWorkflow);
153
- edges.forEach((edge, i) => {
154
- expect(edge.edgeIndex).toBe(i);
155
- });
156
- });
157
- it("produces unique IDs for multiple edges between the same node pair", () => {
158
- const wf = {
159
- id: "multi-edge",
160
- name: "Multi-Edge",
161
- description: "",
162
- entry: "a",
163
- nodes: {
164
- a: { name: "A", instruction: "A", skills: [] },
165
- b: { name: "B", instruction: "B", skills: [] },
166
- },
167
- edges: [
168
- { from: "a", to: "b" },
169
- { from: "a", to: "b", when: "error detected" },
170
- { from: "a", to: "b", when: "retry needed" },
171
- ],
172
- };
173
- const { edges } = workflowToFlow(wf);
174
- expect(edges).toHaveLength(3);
175
- const ids = edges.map((e) => e.id);
176
- expect(new Set(ids).size).toBe(3);
177
- expect(ids).toEqual(["edge-0", "edge-1", "edge-2"]);
178
- });
179
- it("produces stable IDs across multiple conversions", () => {
180
- const first = workflowToFlow(testWorkflow).edges.map((e) => e.id);
181
- const second = workflowToFlow(testWorkflow).edges.map((e) => e.id);
182
- expect(first).toEqual(second);
183
- });
184
- });
185
- describe("max_iterations in conversion", () => {
186
- const wfWithMaxIter = {
187
- id: "retry-loop",
188
- name: "Retry Loop",
189
- description: "",
190
- entry: "check",
191
- nodes: {
192
- check: { name: "Check", instruction: "Check it", skills: [] },
193
- fix: { name: "Fix", instruction: "Fix it", skills: [] },
194
- },
195
- edges: [
196
- { from: "check", to: "fix", when: "issue found" },
197
- { from: "fix", to: "check", when: "needs re-check", max_iterations: 3 },
198
- ],
199
- };
200
- it("includes max_iterations in flow edge data", () => {
201
- const { edges } = workflowToFlow(wfWithMaxIter);
202
- const retryEdge = edges.find((e) => e.source === "fix" && e.target === "check");
203
- expect(retryEdge?.data.max_iterations).toBe(3);
204
- });
205
- it("omits max_iterations when not set", () => {
206
- const { edges } = workflowToFlow(wfWithMaxIter);
207
- const firstEdge = edges.find((e) => e.source === "check" && e.target === "fix");
208
- expect(firstEdge?.data.max_iterations).toBeUndefined();
209
- });
210
- it("round-trips max_iterations through flowToWorkflow", () => {
211
- const { nodes, edges } = workflowToFlow(wfWithMaxIter);
212
- const rebuilt = flowToWorkflow({
213
- id: wfWithMaxIter.id,
214
- name: wfWithMaxIter.name,
215
- description: wfWithMaxIter.description,
216
- entry: wfWithMaxIter.entry,
217
- }, nodes, edges);
218
- const retryEdge = rebuilt.edges.find((e) => e.from === "fix" && e.to === "check");
219
- expect(retryEdge?.max_iterations).toBe(3);
220
- // Non-retry edge should not have max_iterations
221
- const normalEdge = rebuilt.edges.find((e) => e.from === "check" && e.to === "fix");
222
- expect(normalEdge).not.toHaveProperty("max_iterations");
223
- });
224
- });
225
- describe("Source type preservation", () => {
226
- it("preserves inline string instructions through round-trip", () => {
227
- const { nodes, edges } = workflowToFlow(testWorkflow);
228
- const rebuilt = flowToWorkflow({ id: "x", name: "X", description: "", entry: "a" }, nodes, edges);
229
- expect(rebuilt.nodes.a.instruction).toBe("Do A");
230
- });
231
- it("preserves {file:} Source through round-trip", () => {
232
- const wf = {
233
- id: "file-src",
234
- name: "File Source",
235
- description: "",
236
- entry: "a",
237
- nodes: {
238
- a: { name: "A", instruction: { file: "./instructions/step-a.md" }, skills: [] },
239
- },
240
- edges: [],
241
- };
242
- const { nodes, edges } = workflowToFlow(wf);
243
- const rebuilt = flowToWorkflow({ id: wf.id, name: wf.name, description: "", entry: "a" }, nodes, edges);
244
- expect(rebuilt.nodes.a.instruction).toEqual({ file: "./instructions/step-a.md" });
245
- });
246
- it("preserves {url:} Source through round-trip", () => {
247
- const wf = {
248
- id: "url-src",
249
- name: "URL Source",
250
- description: "",
251
- entry: "a",
252
- nodes: {
253
- a: { name: "A", instruction: { url: "https://example.com/instruction.md" }, skills: [] },
254
- },
255
- edges: [],
256
- };
257
- const { nodes, edges } = workflowToFlow(wf);
258
- const rebuilt = flowToWorkflow({ id: wf.id, name: wf.name, description: "", entry: "a" }, nodes, edges);
259
- expect(rebuilt.nodes.a.instruction).toEqual({ url: "https://example.com/instruction.md" });
260
- });
261
- it("preserves {inline:} Source through round-trip", () => {
262
- const wf = {
263
- id: "inline-src",
264
- name: "Inline Source",
265
- description: "",
266
- entry: "a",
267
- nodes: {
268
- a: { name: "A", instruction: { inline: "Explicit inline text" }, skills: [] },
269
- },
270
- edges: [],
271
- };
272
- const { nodes, edges } = workflowToFlow(wf);
273
- const rebuilt = flowToWorkflow({ id: wf.id, name: wf.name, description: "", entry: "a" }, nodes, edges);
274
- expect(rebuilt.nodes.a.instruction).toEqual({ inline: "Explicit inline text" });
275
- });
276
- it("preserves {url:, type:} Source through round-trip", () => {
277
- const wf = {
278
- id: "url-type-src",
279
- name: "URL Type Source",
280
- description: "",
281
- entry: "a",
282
- nodes: {
283
- a: { name: "A", instruction: { url: "https://example.com/data", type: "fetch" }, skills: [] },
284
- },
285
- edges: [],
286
- };
287
- const { nodes, edges } = workflowToFlow(wf);
288
- const rebuilt = flowToWorkflow({ id: wf.id, name: wf.name, description: "", entry: "a" }, nodes, edges);
289
- const src = rebuilt.nodes.a.instruction;
290
- expect(src).toEqual({ url: "https://example.com/data", type: "fetch" });
291
- });
292
- });
293
- describe("flowToWorkflow", () => {
294
- it("round-trips correctly", () => {
295
- const { nodes, edges } = workflowToFlow(testWorkflow);
296
- const rebuilt = flowToWorkflow({
297
- id: testWorkflow.id,
298
- name: testWorkflow.name,
299
- description: testWorkflow.description,
300
- entry: testWorkflow.entry,
301
- }, nodes, edges);
302
- expect(rebuilt.id).toBe(testWorkflow.id);
303
- expect(rebuilt.entry).toBe(testWorkflow.entry);
304
- expect(Object.keys(rebuilt.nodes)).toHaveLength(3);
305
- expect(rebuilt.edges).toHaveLength(2);
306
- expect(rebuilt.edges.find((e) => e.from === "a" && e.to === "c")?.when).toBe("needs notification");
307
- });
308
- it("omits when field for unconditional edges", () => {
309
- const { nodes, edges } = workflowToFlow(testWorkflow);
310
- const rebuilt = flowToWorkflow({ id: "x", name: "X", description: "", entry: "a" }, nodes, edges);
311
- const unconditional = rebuilt.edges.find((e) => e.from === "a" && e.to === "b");
312
- expect(unconditional).not.toHaveProperty("when");
313
- });
314
- it("omits max_iterations when not set", () => {
315
- const { nodes, edges } = workflowToFlow(testWorkflow);
316
- const rebuilt = flowToWorkflow({ id: "x", name: "X", description: "", entry: "a" }, nodes, edges);
317
- for (const edge of rebuilt.edges) {
318
- expect(edge).not.toHaveProperty("max_iterations");
319
- }
320
- });
321
- });
322
- describe("applyExecutionEvent", () => {
323
- function makeNodeDataMap() {
324
- const { nodes } = workflowToFlow(testWorkflow);
325
- return new Map(nodes.map((n) => [n.id, n.data]));
326
- }
327
- it("resets all nodes on workflow:start", () => {
328
- const map = makeNodeDataMap();
329
- map.get("a").exec = { status: "success" };
330
- applyExecutionEvent({ type: "workflow:start", workflow: "test" }, map);
331
- expect(map.get("a").exec.status).toBe("pending");
332
- });
333
- it("marks node as running on node:enter", () => {
334
- const map = makeNodeDataMap();
335
- applyExecutionEvent({ type: "node:enter", node: "a", instruction: "Do A" }, map);
336
- expect(map.get("a").exec.status).toBe("running");
337
- });
338
- it("tracks current tool on tool:call", () => {
339
- const map = makeNodeDataMap();
340
- map.get("a").exec = { status: "running" };
341
- applyExecutionEvent({ type: "tool:call", node: "a", tool: "github_search_code", input: {} }, map);
342
- expect(map.get("a").exec.currentTool).toBe("github_search_code");
343
- });
344
- it("clears current tool on tool:result", () => {
345
- const map = makeNodeDataMap();
346
- map.get("a").exec = { status: "running", currentTool: "github_search_code" };
347
- applyExecutionEvent({ type: "tool:result", node: "a", tool: "github_search_code", output: {} }, map);
348
- expect(map.get("a").exec.currentTool).toBeUndefined();
349
- });
350
- it("marks node as success on node:exit with success", () => {
351
- const map = makeNodeDataMap();
352
- const result = { status: "success", data: {}, toolCalls: [] };
353
- applyExecutionEvent({ type: "node:exit", node: "a", result }, map);
354
- expect(map.get("a").exec.status).toBe("success");
355
- expect(map.get("a").exec.result).toBe(result);
356
- });
357
- it("marks node as failed on node:exit with failure", () => {
358
- const map = makeNodeDataMap();
359
- const result = { status: "failed", data: { error: "oops" }, toolCalls: [] };
360
- applyExecutionEvent({ type: "node:exit", node: "a", result }, map);
361
- expect(map.get("a").exec.status).toBe("failed");
362
- });
363
- it("ignores events for unknown nodes", () => {
364
- const map = makeNodeDataMap();
365
- // Should not throw
366
- applyExecutionEvent({ type: "node:enter", node: "ghost", instruction: "" }, map);
367
- expect(map.has("ghost")).toBe(false);
368
- });
369
- });
370
- describe("exportAsTypescript", () => {
371
- it("generates valid TypeScript", () => {
372
- const ts = exportAsTypescript(testWorkflow);
373
- expect(ts).toContain("import type { Workflow }");
374
- expect(ts).toContain("test_studio");
375
- expect(ts).toContain('"Studio Test"');
376
- });
377
- it("sanitizes id for variable name", () => {
378
- const wf = { ...testWorkflow, id: "my-special_workflow.v2" };
379
- const ts = exportAsTypescript(wf);
380
- expect(ts).toContain("my_special_workflow_v2");
381
- });
382
- });
383
- describe("getSkillCatalog", () => {
384
- it("returns builtin skills", () => {
385
- const catalog = getSkillCatalog();
386
- expect(catalog.length).toBeGreaterThan(0);
387
- const github = catalog.find((s) => s.id === "github");
388
- expect(github).toBeDefined();
389
- expect(github.tools.length).toBeGreaterThan(0);
390
- });
391
- it("includes extra skills", () => {
392
- const extra = {
393
- id: "custom",
394
- name: "Custom",
395
- description: "A custom skill",
396
- category: "general",
397
- config: {},
398
- tools: [{ name: "custom_tool", description: "Does custom", input_schema: {}, handler: async () => { } }],
399
- };
400
- const catalog = getSkillCatalog([extra]);
401
- expect(catalog.find((s) => s.id === "custom")).toBeDefined();
402
- });
403
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,49 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { parse } from "yaml";
3
- import { WORKFLOW_TEMPLATES } from "../cli/templates.js";
4
- import { parseWorkflow, validateWorkflow } from "../schema.js";
5
- describe("WORKFLOW_TEMPLATES", () => {
6
- it("has at least 4 templates", () => {
7
- expect(WORKFLOW_TEMPLATES.length).toBeGreaterThanOrEqual(4);
8
- });
9
- it("each template has unique id, name, and description", () => {
10
- const ids = new Set();
11
- for (const t of WORKFLOW_TEMPLATES) {
12
- expect(t.id).toBeTruthy();
13
- expect(t.name).toBeTruthy();
14
- expect(t.description).toBeTruthy();
15
- expect(ids.has(t.id)).toBe(false);
16
- ids.add(t.id);
17
- }
18
- });
19
- for (const template of WORKFLOW_TEMPLATES) {
20
- describe(`template: ${template.id}`, () => {
21
- it("parses as valid YAML", () => {
22
- const parsed = parse(template.yaml);
23
- expect(parsed).toBeDefined();
24
- expect(parsed.id).toBe(template.id);
25
- });
26
- it("passes parseWorkflow validation", () => {
27
- const parsed = parse(template.yaml);
28
- const workflow = parseWorkflow(parsed);
29
- expect(workflow.id).toBe(template.id);
30
- expect(Object.keys(workflow.nodes).length).toBeGreaterThan(0);
31
- });
32
- it("passes validateWorkflow with no errors", () => {
33
- const parsed = parse(template.yaml);
34
- const workflow = parseWorkflow(parsed);
35
- const errors = validateWorkflow(workflow);
36
- expect(errors).toEqual([]);
37
- });
38
- it("uses only the github skill", () => {
39
- const parsed = parse(template.yaml);
40
- const workflow = parseWorkflow(parsed);
41
- for (const node of Object.values(workflow.nodes)) {
42
- for (const skill of node.skills) {
43
- expect(skill).toBe("github");
44
- }
45
- }
46
- });
47
- });
48
- }
49
- });
@@ -1 +0,0 @@
1
- export {};