@sweny-ai/core 0.1.53 → 0.1.55
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__/executor.test.js +2 -1
- package/dist/__tests__/integration/sources.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/sources.integration.test.js +123 -0
- package/dist/__tests__/workflow-yaml.test.js +12 -5
- package/dist/cli/__tests__/report-cloud.test.d.ts +1 -0
- package/dist/cli/__tests__/report-cloud.test.js +165 -0
- package/dist/cli/cloud-report.d.ts +12 -0
- package/dist/cli/cloud-report.js +79 -0
- package/dist/cli/config-file.d.ts +3 -3
- package/dist/cli/config-file.js +18 -0
- package/dist/cli/config.d.ts +3 -0
- package/dist/cli/config.js +18 -2
- package/dist/cli/config.test.js +65 -1
- package/dist/cli/e2e.js +4 -2
- package/dist/cli/e2e.test.js +9 -2
- package/dist/cli/main.js +46 -19
- package/dist/cli/new.d.ts +0 -5
- package/dist/cli/new.js +0 -5
- package/dist/cli/new.test.js +1 -9
- package/dist/cli/output.test.js +1 -0
- package/dist/cli/publish.js +23 -12
- package/dist/cli/publish.test.d.ts +1 -0
- package/dist/cli/publish.test.js +610 -0
- package/dist/executor.d.ts +8 -0
- package/dist/executor.js +21 -3
- package/dist/executor.test.js +56 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/mermaid.test.js +2 -0
- package/dist/schema.d.ts +174 -46
- package/dist/schema.js +31 -2
- package/dist/sources.d.ts +70 -0
- package/dist/sources.js +194 -0
- package/dist/sources.test.d.ts +1 -0
- package/dist/sources.test.js +264 -0
- package/dist/sources.validation.test.d.ts +12 -0
- package/dist/sources.validation.test.js +360 -0
- package/dist/templates.d.ts +6 -2
- package/dist/templates.js +36 -46
- package/dist/testing.js +4 -1
- package/dist/types.d.ts +8 -1
- package/dist/types.js +0 -5
- package/dist/workflows/browser.js +1 -1
- package/dist/workflows/triage.yml +1 -23
- package/package.json +1 -5
package/dist/cli/config.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { validateInputs } from "./config.js";
|
|
2
|
+
import { validateInputs, parseCliInputs } from "./config.js";
|
|
3
3
|
/**
|
|
4
4
|
* Focused tests for validateInputs() — specifically the notification-provider
|
|
5
5
|
* switch. Regressions here break every `swenyai/triage` action run because
|
|
@@ -65,6 +65,9 @@ function baseConfig(overrides = {}) {
|
|
|
65
65
|
workspaceTools: [],
|
|
66
66
|
rules: [],
|
|
67
67
|
context: [],
|
|
68
|
+
offline: false,
|
|
69
|
+
fetchAuth: {},
|
|
70
|
+
cloudToken: "",
|
|
68
71
|
...overrides,
|
|
69
72
|
};
|
|
70
73
|
}
|
|
@@ -109,3 +112,64 @@ describe("validateInputs — notification provider", () => {
|
|
|
109
112
|
expect(err).toContain("console");
|
|
110
113
|
});
|
|
111
114
|
});
|
|
115
|
+
describe("fetch.auth + offline parsing", () => {
|
|
116
|
+
it("parses offline flag", () => {
|
|
117
|
+
const config = parseCliInputs({ offline: true }, {});
|
|
118
|
+
expect(config.offline).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
it("defaults fetchAuth to {} and offline to false", () => {
|
|
121
|
+
const config = parseCliInputs({}, {});
|
|
122
|
+
expect(config.offline).toBe(false);
|
|
123
|
+
expect(config.fetchAuth).toEqual({});
|
|
124
|
+
});
|
|
125
|
+
it("parses fetch.auth from file config", () => {
|
|
126
|
+
const config = parseCliInputs({}, { "fetch.auth": { "api.example.com": "MY_TOKEN" } });
|
|
127
|
+
expect(config.fetchAuth).toEqual({ "api.example.com": "MY_TOKEN" });
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
describe("parseCliInputs — cloud token", () => {
|
|
131
|
+
it("reads SWENY_CLOUD_TOKEN from env", () => {
|
|
132
|
+
const original = process.env.SWENY_CLOUD_TOKEN;
|
|
133
|
+
process.env.SWENY_CLOUD_TOKEN = "sweny_pk_test123";
|
|
134
|
+
try {
|
|
135
|
+
const config = parseCliInputs({});
|
|
136
|
+
expect(config.cloudToken).toBe("sweny_pk_test123");
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
if (original === undefined)
|
|
140
|
+
delete process.env.SWENY_CLOUD_TOKEN;
|
|
141
|
+
else
|
|
142
|
+
process.env.SWENY_CLOUD_TOKEN = original;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
it("reads cloud-token from config file", () => {
|
|
146
|
+
const config = parseCliInputs({}, { "cloud-token": "sweny_pk_fromfile" });
|
|
147
|
+
expect(config.cloudToken).toBe("sweny_pk_fromfile");
|
|
148
|
+
});
|
|
149
|
+
it("env var overrides config file", () => {
|
|
150
|
+
const original = process.env.SWENY_CLOUD_TOKEN;
|
|
151
|
+
process.env.SWENY_CLOUD_TOKEN = "sweny_pk_env";
|
|
152
|
+
try {
|
|
153
|
+
const config = parseCliInputs({}, { "cloud-token": "sweny_pk_file" });
|
|
154
|
+
expect(config.cloudToken).toBe("sweny_pk_env");
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
if (original === undefined)
|
|
158
|
+
delete process.env.SWENY_CLOUD_TOKEN;
|
|
159
|
+
else
|
|
160
|
+
process.env.SWENY_CLOUD_TOKEN = original;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
it("defaults to empty string", () => {
|
|
164
|
+
const original = process.env.SWENY_CLOUD_TOKEN;
|
|
165
|
+
delete process.env.SWENY_CLOUD_TOKEN;
|
|
166
|
+
try {
|
|
167
|
+
const config = parseCliInputs({});
|
|
168
|
+
expect(config.cloudToken).toBe("");
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
if (original !== undefined)
|
|
172
|
+
process.env.SWENY_CLOUD_TOKEN = original;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
});
|
package/dist/cli/e2e.js
CHANGED
|
@@ -830,9 +830,11 @@ export async function runE2eRun(options) {
|
|
|
830
830
|
runResults.push({ file: fileName, passed: false });
|
|
831
831
|
continue;
|
|
832
832
|
}
|
|
833
|
-
// Replace template vars in node instructions
|
|
833
|
+
// Replace template vars in node instructions (e2e workflows always use string instructions)
|
|
834
834
|
for (const node of Object.values(workflow.nodes)) {
|
|
835
|
-
|
|
835
|
+
if (typeof node.instruction === "string") {
|
|
836
|
+
node.instruction = resolveTemplateVars(node.instruction, vars);
|
|
837
|
+
}
|
|
836
838
|
}
|
|
837
839
|
// Build skills + Claude client
|
|
838
840
|
const skills = createSkillMap(configuredSkills(process.env, cwd));
|
package/dist/cli/e2e.test.js
CHANGED
|
@@ -247,7 +247,13 @@ describe("buildFlowNodes", () => {
|
|
|
247
247
|
const { nodes } = buildFlowNodes(flow);
|
|
248
248
|
for (const [id, node] of Object.entries(nodes)) {
|
|
249
249
|
expect(node.name, `${id}.name`).toBeTruthy();
|
|
250
|
-
|
|
250
|
+
const instr = node.instruction;
|
|
251
|
+
if (typeof instr === "string") {
|
|
252
|
+
expect(instr.length, `${id}.instruction`).toBeGreaterThan(0);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
expect(instr, `${id}.instruction`).toBeTruthy();
|
|
256
|
+
}
|
|
251
257
|
expect(Array.isArray(node.skills), `${id}.skills`).toBe(true);
|
|
252
258
|
}
|
|
253
259
|
});
|
|
@@ -607,7 +613,8 @@ describe("template variable completeness — all {vars} in instructions are reso
|
|
|
607
613
|
it(`${flow.type}: all template vars in instructions resolve to values`, () => {
|
|
608
614
|
const wf = buildFlowWorkflow(flow, "http://localhost:3000");
|
|
609
615
|
for (const [nodeId, node] of Object.entries(wf.nodes)) {
|
|
610
|
-
const
|
|
616
|
+
const instrText = typeof node.instruction === "string" ? node.instruction : "";
|
|
617
|
+
const resolved = resolveTemplateVars(instrText, vars);
|
|
611
618
|
// After resolution, no {word} placeholders should remain
|
|
612
619
|
// (except for agent-browser refs like @<ref> which are not {vars})
|
|
613
620
|
const unresolvedVars = resolved.match(/\{(\w+)\}/g) || [];
|
package/dist/cli/main.js
CHANGED
|
@@ -23,12 +23,13 @@ import * as readline from "node:readline";
|
|
|
23
23
|
import { loadDotenv, loadConfigFile } from "./config-file.js";
|
|
24
24
|
import { buildCredentialMap } from "./credentials.js";
|
|
25
25
|
import { runNew } from "./new.js";
|
|
26
|
-
import {
|
|
26
|
+
import { 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
31
|
import { registerPublishCommand } from "./publish.js";
|
|
32
|
+
import { reportToCloud } from "./cloud-report.js";
|
|
32
33
|
// ── Stream observer (NDJSON) ────────────────────────────────────────
|
|
33
34
|
/**
|
|
34
35
|
* Create an observer that writes NDJSON ExecutionEvents to stdout.
|
|
@@ -64,23 +65,8 @@ program
|
|
|
64
65
|
.action(async () => {
|
|
65
66
|
await runNew();
|
|
66
67
|
});
|
|
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();
|
|
74
|
-
});
|
|
75
68
|
// ── sweny e2e ────────────────────────────────────────────────────────
|
|
76
69
|
const e2eCmd = program.command("e2e").description("End-to-end browser testing");
|
|
77
|
-
e2eCmd
|
|
78
|
-
.command("init")
|
|
79
|
-
.description("[DEPRECATED] Use `sweny new` and pick 'End-to-end browser testing'")
|
|
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");
|
|
82
|
-
await runE2eInit();
|
|
83
|
-
});
|
|
84
70
|
e2eCmd
|
|
85
71
|
.command("run [file]")
|
|
86
72
|
.description("Run e2e workflow files from .sweny/e2e/")
|
|
@@ -339,10 +325,9 @@ triageCmd.action(async (options) => {
|
|
|
339
325
|
...(config.observabilityCredentials.tableName && {
|
|
340
326
|
betterstackTableName: config.observabilityCredentials.tableName,
|
|
341
327
|
}),
|
|
342
|
-
// Structured rules/context for executor
|
|
328
|
+
// Structured rules/context for executor (URLs resolved eagerly by loadAdditionalContext)
|
|
343
329
|
rules,
|
|
344
330
|
context: fullContext,
|
|
345
|
-
// URLs for the prepare node to fetch at runtime
|
|
346
331
|
rulesUrls,
|
|
347
332
|
contextUrls,
|
|
348
333
|
};
|
|
@@ -352,6 +337,10 @@ triageCmd.action(async (options) => {
|
|
|
352
337
|
claude,
|
|
353
338
|
observer,
|
|
354
339
|
logger: consoleLogger,
|
|
340
|
+
cwd: process.cwd(),
|
|
341
|
+
env: process.env,
|
|
342
|
+
fetchAuth: config.fetchAuth,
|
|
343
|
+
offline: config.offline,
|
|
355
344
|
});
|
|
356
345
|
const durationMs = Date.now() - runStart;
|
|
357
346
|
// Output
|
|
@@ -375,6 +364,13 @@ triageCmd.action(async (options) => {
|
|
|
375
364
|
console.error(c.subtle(` ⚠ could not write GITHUB_STEP_SUMMARY: ${err instanceof Error ? err.message : err}`));
|
|
376
365
|
}
|
|
377
366
|
}
|
|
367
|
+
// Report to SWEny Cloud (best-effort, never block the run)
|
|
368
|
+
try {
|
|
369
|
+
await reportToCloud(results, durationMs, config, "triage");
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
// silent — cloud reporting is optional
|
|
373
|
+
}
|
|
378
374
|
// Terminal bell
|
|
379
375
|
if (config.bell)
|
|
380
376
|
process.stderr.write("\x07");
|
|
@@ -435,6 +431,7 @@ implementCmd.action(async (issueId, options) => {
|
|
|
435
431
|
mcpServers,
|
|
436
432
|
});
|
|
437
433
|
console.log(chalk.cyan(`\n sweny implement ${issueId}\n`));
|
|
434
|
+
const implRunStart = Date.now();
|
|
438
435
|
const isTTY = process.stderr.isTTY ?? false;
|
|
439
436
|
const implProgressObserver = (event) => {
|
|
440
437
|
switch (event.type) {
|
|
@@ -492,6 +489,10 @@ implementCmd.action(async (issueId, options) => {
|
|
|
492
489
|
claude,
|
|
493
490
|
observer,
|
|
494
491
|
logger: consoleLogger,
|
|
492
|
+
cwd: process.cwd(),
|
|
493
|
+
env: process.env,
|
|
494
|
+
fetchAuth: config.fetchAuth,
|
|
495
|
+
offline: config.offline,
|
|
495
496
|
});
|
|
496
497
|
const hasFailed = [...results.values()].some((r) => r.status === "failed");
|
|
497
498
|
if (hasFailed) {
|
|
@@ -506,6 +507,13 @@ implementCmd.action(async (issueId, options) => {
|
|
|
506
507
|
else {
|
|
507
508
|
console.log(chalk.green(`\n Implement workflow completed\n`));
|
|
508
509
|
}
|
|
510
|
+
// Report to SWEny Cloud (best-effort)
|
|
511
|
+
try {
|
|
512
|
+
await reportToCloud(results, Date.now() - implRunStart, config, "implement");
|
|
513
|
+
}
|
|
514
|
+
catch {
|
|
515
|
+
// silent
|
|
516
|
+
}
|
|
509
517
|
process.exit(0);
|
|
510
518
|
}
|
|
511
519
|
catch (err) {
|
|
@@ -555,6 +563,7 @@ export async function workflowRunAction(file, options) {
|
|
|
555
563
|
}
|
|
556
564
|
process.exit(0);
|
|
557
565
|
}
|
|
566
|
+
const runStart = Date.now();
|
|
558
567
|
const fileConfig = loadConfigFile();
|
|
559
568
|
const config = parseCliInputs(options, fileConfig);
|
|
560
569
|
const isJson = Boolean(options.json);
|
|
@@ -674,6 +683,10 @@ export async function workflowRunAction(file, options) {
|
|
|
674
683
|
claude,
|
|
675
684
|
observer,
|
|
676
685
|
logger: consoleLogger,
|
|
686
|
+
cwd: process.cwd(),
|
|
687
|
+
env: process.env,
|
|
688
|
+
fetchAuth: config.fetchAuth,
|
|
689
|
+
offline: config.offline,
|
|
677
690
|
});
|
|
678
691
|
if (isJson) {
|
|
679
692
|
process.stdout.write(JSON.stringify(Object.fromEntries(results), null, 2) + "\n");
|
|
@@ -689,7 +702,21 @@ export async function workflowRunAction(file, options) {
|
|
|
689
702
|
}
|
|
690
703
|
process.stdout.write(toMermaidBlock(workflow, { state, trace, title: workflow.name }) + "\n");
|
|
691
704
|
}
|
|
705
|
+
// Report to SWEny Cloud (best-effort)
|
|
706
|
+
try {
|
|
707
|
+
await reportToCloud(results, Date.now() - runStart, config, workflow.id);
|
|
708
|
+
}
|
|
709
|
+
catch {
|
|
710
|
+
// silent
|
|
711
|
+
}
|
|
692
712
|
const hasFailed = [...results.values()].some((r) => r.status === "failed");
|
|
713
|
+
// Report to SWEny Cloud (best-effort)
|
|
714
|
+
try {
|
|
715
|
+
await reportToCloud(results, 0, config, workflow.id);
|
|
716
|
+
}
|
|
717
|
+
catch {
|
|
718
|
+
// silent
|
|
719
|
+
}
|
|
693
720
|
if (hasFailed) {
|
|
694
721
|
console.error(chalk.red(` Workflow failed\n`));
|
|
695
722
|
process.exit(1);
|
|
@@ -823,7 +850,7 @@ workflowCmd
|
|
|
823
850
|
.option("--json", "Output workflow JSON to stdout (no interactive prompt)")
|
|
824
851
|
.action(async (description, options) => {
|
|
825
852
|
if (!options.json) {
|
|
826
|
-
console.warn("\x1B[33m ⚠ `sweny workflow create` is deprecated
|
|
853
|
+
console.warn("\x1B[33m ⚠ `sweny workflow create` is deprecated. Use `sweny new` instead.\x1B[0m\n");
|
|
827
854
|
}
|
|
828
855
|
const skills = configuredSkills();
|
|
829
856
|
const claude = new ClaudeClient({
|
package/dist/cli/new.d.ts
CHANGED
|
@@ -94,8 +94,3 @@ export declare function buildActionWorkflow(credentials: Credential[], cronExpre
|
|
|
94
94
|
* Safe to re-run in an existing project to add additional workflows.
|
|
95
95
|
*/
|
|
96
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;
|
package/dist/cli/new.js
CHANGED
|
@@ -578,11 +578,6 @@ export async function runNew() {
|
|
|
578
578
|
p.note(steps.join("\n"), "Next steps");
|
|
579
579
|
p.outro(hasExistingConfig ? "Workflow added!" : "You're all set!");
|
|
580
580
|
}
|
|
581
|
-
/**
|
|
582
|
-
* @deprecated Use `runNew` instead. Kept as a named re-export so external
|
|
583
|
-
* callers (e.g. create-sweny, plugin skills) don't break during the migration.
|
|
584
|
-
*/
|
|
585
|
-
export const runInit = runNew;
|
|
586
581
|
// ── Custom workflow branch ────────────────────────────────────────────
|
|
587
582
|
/**
|
|
588
583
|
* Prompt the user for a description, generate a workflow via the LLM,
|
package/dist/cli/new.test.js
CHANGED
|
@@ -3,7 +3,7 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import * as os from "node:os";
|
|
5
5
|
import { parse as parseYaml } from "yaml";
|
|
6
|
-
import { collectCredentials, collectCredentialsForSkills, extractSkillsFromYaml, detectGitRemote, buildSwenyYml, buildEnvTemplate, buildActionWorkflow,
|
|
6
|
+
import { collectCredentials, collectCredentialsForSkills, extractSkillsFromYaml, detectGitRemote, buildSwenyYml, buildEnvTemplate, buildActionWorkflow, PROVIDER_CREDENTIALS, SKILL_CREDENTIALS, } from "./new.js";
|
|
7
7
|
// ── detectGitRemote ────────────────────────────────────────────────────
|
|
8
8
|
describe("detectGitRemote", () => {
|
|
9
9
|
const tmpDirs = [];
|
|
@@ -763,14 +763,6 @@ describe("extractSkillsFromYaml — block-style arrays", () => {
|
|
|
763
763
|
expect(extractSkillsFromYaml(yaml)).toEqual([]);
|
|
764
764
|
});
|
|
765
765
|
});
|
|
766
|
-
// ── Deprecated alias: runInit === runNew ──────────────────────────────
|
|
767
|
-
describe("runInit deprecation alias", () => {
|
|
768
|
-
it("re-exports runNew as runInit (identity pin)", () => {
|
|
769
|
-
// Callers using `import { runInit } from "@sweny-ai/core/new"` must
|
|
770
|
-
// continue to hit the same function during the deprecation window.
|
|
771
|
-
expect(runInit).toBe(runNew);
|
|
772
|
-
});
|
|
773
|
-
});
|
|
774
766
|
describe("SKILL_CREDENTIALS", () => {
|
|
775
767
|
it("has entries for all built-in skills", () => {
|
|
776
768
|
const expected = [
|
package/dist/cli/output.test.js
CHANGED
package/dist/cli/publish.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import * as fs from "node:fs";
|
|
10
10
|
import * as path from "node:path";
|
|
11
|
-
import {
|
|
11
|
+
import { execFileSync } from "node:child_process";
|
|
12
12
|
import * as p from "@clack/prompts";
|
|
13
13
|
import chalk from "chalk";
|
|
14
14
|
import { parse as parseYaml } from "yaml";
|
|
@@ -108,7 +108,7 @@ export function validateSkillDir(dirPath) {
|
|
|
108
108
|
// ── GitHub PR helpers ──────────────────────────────────────────────────
|
|
109
109
|
function ghAvailable() {
|
|
110
110
|
try {
|
|
111
|
-
|
|
111
|
+
execFileSync("gh", ["--version"], { stdio: "pipe" });
|
|
112
112
|
return true;
|
|
113
113
|
}
|
|
114
114
|
catch {
|
|
@@ -117,7 +117,7 @@ function ghAvailable() {
|
|
|
117
117
|
}
|
|
118
118
|
function ghAuthenticated() {
|
|
119
119
|
try {
|
|
120
|
-
|
|
120
|
+
execFileSync("gh", ["auth", "status"], { stdio: "pipe" });
|
|
121
121
|
return true;
|
|
122
122
|
}
|
|
123
123
|
catch {
|
|
@@ -287,21 +287,21 @@ async function openGitHubPR(type, id, sourcePath, meta) {
|
|
|
287
287
|
s.start("Forking marketplace repository...");
|
|
288
288
|
try {
|
|
289
289
|
// Fork the marketplace repo (idempotent — if already forked, just continues)
|
|
290
|
-
|
|
290
|
+
execFileSync("gh", ["repo", "fork", "swenyai/marketplace", "--clone=false"], { stdio: "pipe" });
|
|
291
291
|
// Get the fork name
|
|
292
|
-
const forkInfo =
|
|
292
|
+
const forkInfo = execFileSync("gh", ["api", "user"], { stdio: "pipe", encoding: "utf-8" });
|
|
293
293
|
const username = JSON.parse(forkInfo).login;
|
|
294
294
|
const forkRepo = `${username}/marketplace`;
|
|
295
295
|
s.message("Creating branch...");
|
|
296
296
|
const branchName = `publish/${type}/${id}`;
|
|
297
297
|
// Clone to temp, create branch, add file, push, create PR
|
|
298
298
|
const tmpDir = fs.mkdtempSync(path.join(process.env.TMPDIR ?? "/tmp", "sweny-publish-"));
|
|
299
|
-
|
|
300
|
-
|
|
299
|
+
execFileSync("gh", ["repo", "clone", forkRepo, tmpDir, "--", "--depth", "1"], { stdio: "pipe" });
|
|
300
|
+
execFileSync("git", ["-C", tmpDir, "checkout", "-b", branchName], { stdio: "pipe" });
|
|
301
301
|
if (type === "workflow") {
|
|
302
302
|
const dest = path.join(tmpDir, "workflows", "community", `${id}.yml`);
|
|
303
303
|
fs.copyFileSync(sourcePath, dest);
|
|
304
|
-
|
|
304
|
+
execFileSync("git", ["-C", tmpDir, "add", `workflows/community/${id}.yml`], { stdio: "pipe" });
|
|
305
305
|
}
|
|
306
306
|
else {
|
|
307
307
|
const destDir = path.join(tmpDir, "skills", "community", id);
|
|
@@ -311,17 +311,28 @@ async function openGitHubPR(type, id, sourcePath, meta) {
|
|
|
311
311
|
for (const file of files) {
|
|
312
312
|
fs.copyFileSync(path.join(sourcePath, file), path.join(destDir, file));
|
|
313
313
|
}
|
|
314
|
-
|
|
314
|
+
execFileSync("git", ["-C", tmpDir, "add", `skills/community/${id}`], { stdio: "pipe" });
|
|
315
315
|
}
|
|
316
316
|
s.message("Pushing...");
|
|
317
317
|
const commitMsg = type === "workflow" ? `feat: add ${id} workflow` : `feat: add ${id} skill`;
|
|
318
|
-
|
|
319
|
-
|
|
318
|
+
execFileSync("git", ["-C", tmpDir, "commit", "-m", commitMsg], { stdio: "pipe" });
|
|
319
|
+
execFileSync("git", ["-C", tmpDir, "push", "origin", branchName], { stdio: "pipe" });
|
|
320
320
|
s.message("Creating pull request...");
|
|
321
321
|
const prBody = type === "workflow"
|
|
322
322
|
? `## New Workflow: ${id}\n\n**Author:** ${meta.author}\n**Category:** ${meta.category}\n**Tags:** ${meta.tags?.join(", ")}\n\nSubmitted via \`sweny publish\`.`
|
|
323
323
|
: `## New Skill: ${id}\n\nSubmitted via \`sweny publish\`.`;
|
|
324
|
-
const prResult =
|
|
324
|
+
const prResult = execFileSync("gh", [
|
|
325
|
+
"pr",
|
|
326
|
+
"create",
|
|
327
|
+
"--repo",
|
|
328
|
+
"swenyai/marketplace",
|
|
329
|
+
"--head",
|
|
330
|
+
`${username}:${branchName}`,
|
|
331
|
+
"--title",
|
|
332
|
+
`feat: add ${type} ${id}`,
|
|
333
|
+
"--body",
|
|
334
|
+
prBody,
|
|
335
|
+
], { stdio: "pipe", encoding: "utf-8" });
|
|
325
336
|
const prUrl = prResult.trim();
|
|
326
337
|
// Cleanup temp dir
|
|
327
338
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|