@sweny-ai/core 0.1.51 → 0.1.53
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/__tests__/publish.test.js +146 -0
- package/dist/__tests__/schema.test.js +156 -1
- package/dist/__tests__/skills.test.js +135 -4
- package/dist/__tests__/templates.test.d.ts +1 -0
- package/dist/__tests__/templates.test.js +49 -0
- package/dist/__tests__/version-consistency.test.js +7 -7
- package/dist/cli/config-file.d.ts +1 -1
- package/dist/cli/config-file.js +1 -1
- package/dist/cli/credentials.d.ts +1 -1
- package/dist/cli/credentials.js +1 -1
- package/dist/cli/credentials.test.js +1 -1
- package/dist/cli/e2e.d.ts +5 -1
- package/dist/cli/e2e.js +7 -3
- package/dist/cli/e2e.test.js +46 -1
- package/dist/cli/main.js +21 -7
- package/dist/cli/new.d.ts +101 -0
- package/dist/cli/new.js +670 -0
- package/dist/cli/new.test.d.ts +1 -0
- package/dist/cli/{init.test.js → new.test.js} +216 -78
- package/dist/cli/publish.d.ts +34 -0
- package/dist/cli/publish.js +368 -0
- package/dist/cli/templates.d.ts +13 -0
- package/dist/cli/templates.js +181 -0
- package/dist/executor.js +63 -20
- package/dist/executor.test.js +233 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/mcp.d.ts +2 -0
- package/dist/mcp.js +8 -0
- package/dist/mcp.test.js +67 -0
- package/dist/schema.d.ts +428 -18
- package/dist/schema.js +71 -4
- package/dist/skills/custom-loader.d.ts +12 -12
- package/dist/skills/custom-loader.js +105 -50
- package/dist/types.d.ts +18 -1
- package/dist/workflows/browser.js +3 -3
- package/package.json +10 -2
- package/dist/cli/init.d.ts +0 -53
- package/dist/cli/init.js +0 -521
- /package/dist/{cli/init.test.d.ts → __tests__/publish.test.d.ts} +0 -0
package/dist/cli/e2e.js
CHANGED
|
@@ -614,15 +614,19 @@ async function askFlowQuestions(type) {
|
|
|
614
614
|
}
|
|
615
615
|
return config;
|
|
616
616
|
}
|
|
617
|
-
// ── Interactive wizard ─────────────────────────────────────────────────
|
|
618
617
|
/**
|
|
619
618
|
* Interactive E2E setup wizard — prompts through flow selection,
|
|
620
619
|
* then generates .sweny/e2e/*.yml and .env additions.
|
|
621
620
|
*/
|
|
622
|
-
export async function runE2eInit() {
|
|
621
|
+
export async function runE2eInit(options = {}) {
|
|
623
622
|
const cwd = process.cwd();
|
|
624
623
|
// ── Screen 1: Intro ──────────────────────────────────────────
|
|
625
|
-
|
|
624
|
+
if (!options.skipIntro) {
|
|
625
|
+
p.intro("Let's set up end-to-end testing for your app");
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
p.log.step("Setting up end-to-end browser testing");
|
|
629
|
+
}
|
|
626
630
|
// Check for existing files
|
|
627
631
|
const e2eDir = path.join(cwd, ".sweny", "e2e");
|
|
628
632
|
if (fs.existsSync(e2eDir)) {
|
package/dist/cli/e2e.test.js
CHANGED
|
@@ -1,7 +1,34 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
2
|
import { stringify as stringifyYaml, parse as parseYaml } from "yaml";
|
|
3
3
|
import { resolveTemplateVars, buildE2eVars, buildSetupNode, buildReportNode, buildCleanupNode, buildFlowNodes, buildFlowWorkflow, buildE2eEnvTemplate, } from "./e2e.js";
|
|
4
4
|
import { workflowZ, validateWorkflow } from "../schema.js";
|
|
5
|
+
// ── Clack mock for runE2eInit tests ────────────────────────────────────
|
|
6
|
+
// Hoisted spies so `vi.mock` (also hoisted) can reference them.
|
|
7
|
+
const { introSpy, logStepSpy } = vi.hoisted(() => ({
|
|
8
|
+
introSpy: vi.fn(),
|
|
9
|
+
logStepSpy: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
vi.mock("@clack/prompts", () => ({
|
|
12
|
+
intro: (...args) => introSpy(...args),
|
|
13
|
+
outro: vi.fn(),
|
|
14
|
+
cancel: vi.fn(),
|
|
15
|
+
log: {
|
|
16
|
+
step: (...args) => logStepSpy(...args),
|
|
17
|
+
info: vi.fn(),
|
|
18
|
+
success: vi.fn(),
|
|
19
|
+
warn: vi.fn(),
|
|
20
|
+
message: vi.fn(),
|
|
21
|
+
},
|
|
22
|
+
// Abort the wizard right after the intro/step decision so we can assert on spies.
|
|
23
|
+
multiselect: vi.fn(async () => {
|
|
24
|
+
throw new Error("__abort_test__");
|
|
25
|
+
}),
|
|
26
|
+
select: vi.fn(),
|
|
27
|
+
confirm: vi.fn(),
|
|
28
|
+
text: vi.fn(),
|
|
29
|
+
isCancel: vi.fn(() => false),
|
|
30
|
+
spinner: vi.fn(() => ({ start: vi.fn(), stop: vi.fn(), message: vi.fn() })),
|
|
31
|
+
}));
|
|
5
32
|
describe("resolveTemplateVars", () => {
|
|
6
33
|
it("replaces known variables", () => {
|
|
7
34
|
const result = resolveTemplateVars("Navigate to {base_url}/signup", {
|
|
@@ -636,3 +663,21 @@ describe("buildFlowWorkflow — node naming conventions", () => {
|
|
|
636
663
|
}
|
|
637
664
|
});
|
|
638
665
|
});
|
|
666
|
+
describe("runE2eInit — skipIntro option", () => {
|
|
667
|
+
beforeEach(() => {
|
|
668
|
+
introSpy.mockClear();
|
|
669
|
+
logStepSpy.mockClear();
|
|
670
|
+
});
|
|
671
|
+
it("calls p.intro when skipIntro is not set", async () => {
|
|
672
|
+
const { runE2eInit } = await import("./e2e.js");
|
|
673
|
+
await expect(runE2eInit()).rejects.toThrow("__abort_test__");
|
|
674
|
+
expect(introSpy).toHaveBeenCalledTimes(1);
|
|
675
|
+
expect(logStepSpy).not.toHaveBeenCalledWith("Setting up end-to-end browser testing");
|
|
676
|
+
});
|
|
677
|
+
it("skips p.intro and logs a step when skipIntro: true", async () => {
|
|
678
|
+
const { runE2eInit } = await import("./e2e.js");
|
|
679
|
+
await expect(runE2eInit({ skipIntro: true })).rejects.toThrow("__abort_test__");
|
|
680
|
+
expect(introSpy).not.toHaveBeenCalled();
|
|
681
|
+
expect(logStepSpy).toHaveBeenCalledWith("Setting up end-to-end browser testing");
|
|
682
|
+
});
|
|
683
|
+
});
|
package/dist/cli/main.js
CHANGED
|
@@ -22,12 +22,13 @@ import { DagRenderer } from "./renderer.js";
|
|
|
22
22
|
import * as readline from "node:readline";
|
|
23
23
|
import { loadDotenv, loadConfigFile } from "./config-file.js";
|
|
24
24
|
import { buildCredentialMap } from "./credentials.js";
|
|
25
|
-
import {
|
|
25
|
+
import { runNew } from "./new.js";
|
|
26
26
|
import { runE2eInit, runE2eRun } from "./e2e.js";
|
|
27
27
|
import { registerTriageCommand, registerImplementCommand, parseCliInputs, validateInputs, validateWarnings, } from "./config.js";
|
|
28
28
|
import { c, formatBanner, getStepDetails, formatStepLine, formatDagResultHuman, formatDagResultMarkdown, formatResultJson, formatValidationErrors, formatCrashError, formatCheckResults, } from "./output.js";
|
|
29
29
|
import { checkProviderConnectivity } from "./check.js";
|
|
30
30
|
import { registerSetupCommand } from "./setup.js";
|
|
31
|
+
import { registerPublishCommand } from "./publish.js";
|
|
31
32
|
// ── Stream observer (NDJSON) ────────────────────────────────────────
|
|
32
33
|
/**
|
|
33
34
|
* Create an observer that writes NDJSON ExecutionEvents to stdout.
|
|
@@ -56,19 +57,28 @@ const program = new Command()
|
|
|
56
57
|
.name("sweny")
|
|
57
58
|
.description("SWEny CLI \u2014 autonomous engineering workflows")
|
|
58
59
|
.version(version);
|
|
59
|
-
// ── sweny
|
|
60
|
+
// ── sweny new ─────────────────────────────────────────────────────────
|
|
60
61
|
program
|
|
61
|
-
.command("
|
|
62
|
-
.description("
|
|
62
|
+
.command("new")
|
|
63
|
+
.description("Create a new workflow — interactive picker or direct template")
|
|
63
64
|
.action(async () => {
|
|
64
|
-
await
|
|
65
|
+
await runNew();
|
|
66
|
+
});
|
|
67
|
+
// ── sweny init (deprecated alias) ─────────────────────────────────────
|
|
68
|
+
program
|
|
69
|
+
.command("init", { hidden: true })
|
|
70
|
+
.description("[DEPRECATED] Use `sweny new` instead")
|
|
71
|
+
.action(async () => {
|
|
72
|
+
console.warn("\x1B[33m ⚠ `sweny init` is deprecated and will be removed in v6. Use `sweny new` instead.\x1B[0m\n");
|
|
73
|
+
await runNew();
|
|
65
74
|
});
|
|
66
75
|
// ── sweny e2e ────────────────────────────────────────────────────────
|
|
67
76
|
const e2eCmd = program.command("e2e").description("End-to-end browser testing");
|
|
68
77
|
e2eCmd
|
|
69
78
|
.command("init")
|
|
70
|
-
.description("
|
|
79
|
+
.description("[DEPRECATED] Use `sweny new` and pick 'End-to-end browser testing'")
|
|
71
80
|
.action(async () => {
|
|
81
|
+
console.warn("\x1B[33m ⚠ `sweny e2e init` is deprecated and will be removed in v6. Use `sweny new` and pick 'End-to-end browser testing'.\x1B[0m\n");
|
|
72
82
|
await runE2eInit();
|
|
73
83
|
});
|
|
74
84
|
e2eCmd
|
|
@@ -100,6 +110,7 @@ program
|
|
|
100
110
|
process.exit(hasFailure ? 1 : 0);
|
|
101
111
|
});
|
|
102
112
|
registerSetupCommand(program);
|
|
113
|
+
registerPublishCommand(program);
|
|
103
114
|
// ── Credential map builder ──────────────────────────────────────────
|
|
104
115
|
// buildCredentialMap lives in ./credentials.ts so tests can import it
|
|
105
116
|
// without triggering main.ts's top-level program.parse() side effect.
|
|
@@ -808,9 +819,12 @@ workflowCmd
|
|
|
808
819
|
.action(workflowExportAction);
|
|
809
820
|
workflowCmd
|
|
810
821
|
.command("create <description>")
|
|
811
|
-
.description("
|
|
822
|
+
.description("[DEPRECATED] Use `sweny new` and pick 'Describe your own'")
|
|
812
823
|
.option("--json", "Output workflow JSON to stdout (no interactive prompt)")
|
|
813
824
|
.action(async (description, options) => {
|
|
825
|
+
if (!options.json) {
|
|
826
|
+
console.warn("\x1B[33m ⚠ `sweny workflow create` is deprecated and will be removed in v6. Use `sweny new` and pick 'Describe your own'.\x1B[0m\n");
|
|
827
|
+
}
|
|
814
828
|
const skills = configuredSkills();
|
|
815
829
|
const claude = new ClaudeClient({
|
|
816
830
|
maxTurns: 3,
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sweny new — Interactive workflow creation wizard
|
|
3
|
+
*
|
|
4
|
+
* Workflow-first: pick a template or describe what you want, then SWEny
|
|
5
|
+
* infers which skills are needed and only asks for those credentials.
|
|
6
|
+
*
|
|
7
|
+
* Pure functions for file generation + thin @clack/prompts interactive layer.
|
|
8
|
+
* Tests cover the pure functions; the interactive wizard is thin glue.
|
|
9
|
+
*/
|
|
10
|
+
export interface Credential {
|
|
11
|
+
key: string;
|
|
12
|
+
hint?: string;
|
|
13
|
+
url?: string;
|
|
14
|
+
default?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface InitSelections {
|
|
17
|
+
sourceControl: string;
|
|
18
|
+
observability: string | null;
|
|
19
|
+
issueTracker: string;
|
|
20
|
+
notification: string;
|
|
21
|
+
githubAction: boolean;
|
|
22
|
+
cronExpression: string | null;
|
|
23
|
+
}
|
|
24
|
+
export interface GitRemoteInfo {
|
|
25
|
+
provider: "github" | "gitlab";
|
|
26
|
+
remote: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Maps skill IDs (as used in workflow nodes) to the credentials they require.
|
|
30
|
+
* When a workflow uses a skill, we collect only those credentials.
|
|
31
|
+
*/
|
|
32
|
+
export declare const SKILL_CREDENTIALS: Record<string, Credential[]>;
|
|
33
|
+
export declare const PROVIDER_CREDENTIALS: Record<string, Credential[]>;
|
|
34
|
+
/**
|
|
35
|
+
* Extract all unique skill IDs used by nodes in a workflow YAML string.
|
|
36
|
+
*
|
|
37
|
+
* Handles both inline (`skills: [github, linear]`) and block-style arrays:
|
|
38
|
+
*
|
|
39
|
+
* ```yaml
|
|
40
|
+
* skills:
|
|
41
|
+
* - github
|
|
42
|
+
* - linear
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* Returns `[]` for malformed YAML or YAML without node skills.
|
|
46
|
+
*/
|
|
47
|
+
export declare function extractSkillsFromYaml(yaml: string): string[];
|
|
48
|
+
/**
|
|
49
|
+
* Gather credentials required for a set of skills.
|
|
50
|
+
* Always includes ANTHROPIC_API_KEY. Deduplicates by key name.
|
|
51
|
+
*/
|
|
52
|
+
export declare function collectCredentialsForSkills(skills: string[]): Credential[];
|
|
53
|
+
/**
|
|
54
|
+
* Gather all unique credentials required for the selected providers.
|
|
55
|
+
* Always includes ANTHROPIC_API_KEY. Deduplicates by key name.
|
|
56
|
+
* @deprecated Use collectCredentialsForSkills instead — kept for backward compat.
|
|
57
|
+
*/
|
|
58
|
+
export declare function collectCredentials(selections: InitSelections): Credential[];
|
|
59
|
+
/**
|
|
60
|
+
* Detect the git remote origin from `.git/config`.
|
|
61
|
+
* Returns provider + normalised remote (e.g. "github.com/owner/repo"), or null.
|
|
62
|
+
*/
|
|
63
|
+
export declare function detectGitRemote(cwd: string): GitRemoteInfo | null;
|
|
64
|
+
/**
|
|
65
|
+
* Generate .sweny.yml content from inferred providers.
|
|
66
|
+
*/
|
|
67
|
+
export declare function buildSwenyYml(sourceControl: string, observability: string | null, issueTracker: string): string;
|
|
68
|
+
/**
|
|
69
|
+
* Generate .env template with comments for each credential.
|
|
70
|
+
*/
|
|
71
|
+
export declare function buildEnvTemplate(credentials: Credential[]): string;
|
|
72
|
+
/**
|
|
73
|
+
* Generate a GitHub Actions workflow YAML for SWEny.
|
|
74
|
+
* Only includes credentials WITHOUT defaults as secrets.
|
|
75
|
+
*/
|
|
76
|
+
export declare function buildActionWorkflow(credentials: Credential[], cronExpression: string): string;
|
|
77
|
+
/**
|
|
78
|
+
* Interactive workflow creation wizard — workflow-first approach.
|
|
79
|
+
*
|
|
80
|
+
* Flow:
|
|
81
|
+
* 1. Detect git remote (used to infer source-control provider)
|
|
82
|
+
* 2. Pick one of four branches:
|
|
83
|
+
* - A built-in `WORKFLOW_TEMPLATES` entry → write that workflow file
|
|
84
|
+
* - `__custom` → LLM generates a workflow from a user description
|
|
85
|
+
* - `__e2e` → delegate to the e2e browser-testing wizard in `./e2e.ts`
|
|
86
|
+
* - `__blank` → only set up config / credentials (no workflow file)
|
|
87
|
+
* 3. Infer observability + issue-tracker providers from the workflow's skills
|
|
88
|
+
* 4. Collect only the credentials those skills need (+ ANTHROPIC_API_KEY)
|
|
89
|
+
* 5. Write files idempotently:
|
|
90
|
+
* - `.sweny.yml` is never overwritten — if it exists, we keep it
|
|
91
|
+
* - `.env` is append-only (dedupes by key, incl. commented placeholders)
|
|
92
|
+
* - Workflow files in `.sweny/workflows/` prompt before overwriting
|
|
93
|
+
*
|
|
94
|
+
* Safe to re-run in an existing project to add additional workflows.
|
|
95
|
+
*/
|
|
96
|
+
export declare function runNew(): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* @deprecated Use `runNew` instead. Kept as a named re-export so external
|
|
99
|
+
* callers (e.g. create-sweny, plugin skills) don't break during the migration.
|
|
100
|
+
*/
|
|
101
|
+
export declare const runInit: typeof runNew;
|