@sweny-ai/core 0.1.167 → 0.1.168
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/main.js +2 -1
- package/dist/cli/new.d.ts +15 -0
- package/dist/cli/new.js +43 -2
- package/dist/cli/packs.js +78 -4
- package/package.json +1 -1
package/dist/cli/main.js
CHANGED
|
@@ -93,6 +93,7 @@ program
|
|
|
93
93
|
.description("Create a new workflow. With no id, opens the interactive picker. With an id, uses that built-in template.")
|
|
94
94
|
.option("--template <id>", "Use a built-in template without the picker")
|
|
95
95
|
.option("-y, --yes", "Skip every prompt (never overwrites existing files)")
|
|
96
|
+
.option("--no-ci", "Do not write a pack's GitHub Actions trigger to .github/workflows/")
|
|
96
97
|
.action(async (id, options) => {
|
|
97
98
|
// Prompts need a terminal. Without one (CI, pipes), a prompt never
|
|
98
99
|
// settles, so print usage and exit 2 instead of hanging.
|
|
@@ -100,7 +101,7 @@ program
|
|
|
100
101
|
console.error(nonInteractiveUsage());
|
|
101
102
|
process.exit(2);
|
|
102
103
|
}
|
|
103
|
-
await runNew({ marketplaceId: id, template: options.template, yes: options.yes });
|
|
104
|
+
await runNew({ marketplaceId: id, template: options.template, yes: options.yes, ci: options.ci });
|
|
104
105
|
});
|
|
105
106
|
// ── sweny try ─────────────────────────────────────────────────────────
|
|
106
107
|
registerTryCommand(program);
|
package/dist/cli/new.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Pure functions for file generation + thin @clack/prompts interactive layer.
|
|
8
8
|
* Tests cover the pure functions; the interactive wizard is thin glue.
|
|
9
9
|
*/
|
|
10
|
+
import { type WorkflowTemplate } from "./templates.js";
|
|
10
11
|
import { type Skill } from "../types.js";
|
|
11
12
|
export interface Credential {
|
|
12
13
|
key: string;
|
|
@@ -113,6 +114,18 @@ export declare function writeSwenyYmlIfMissing(cwd: string, sourceControl: strin
|
|
|
113
114
|
/** Append credential keys to .env that aren't already present. Returns count appended. */
|
|
114
115
|
export declare function appendMissingEnvKeys(cwd: string, credentials: Credential[]): number;
|
|
115
116
|
/** Write workflow YAML into .sweny/workflows/<id>.yml. Returns info about what happened. */
|
|
117
|
+
/** Where a pack's GitHub Actions trigger is written: `.github/workflows/sweny-<pack>.yml`. */
|
|
118
|
+
export declare function packTriggerPath(id: string): string;
|
|
119
|
+
/**
|
|
120
|
+
* Write a pack's Actions trigger (#474). Never overwrites: an existing file is
|
|
121
|
+
* left exactly as it is. Returns `skipped` for a template that is not a pack.
|
|
122
|
+
*/
|
|
123
|
+
export declare function writePackTrigger(cwd: string, template: WorkflowTemplate): {
|
|
124
|
+
written: boolean;
|
|
125
|
+
exists?: boolean;
|
|
126
|
+
skipped?: boolean;
|
|
127
|
+
path: string;
|
|
128
|
+
};
|
|
116
129
|
export declare function writeWorkflowFile(cwd: string, id: string, yaml: string, options?: {
|
|
117
130
|
overwrite?: boolean;
|
|
118
131
|
}): {
|
|
@@ -153,6 +166,8 @@ export declare function runNew(options?: {
|
|
|
153
166
|
template?: string;
|
|
154
167
|
/** Skip every confirmation prompt (never overwrites existing files). */
|
|
155
168
|
yes?: boolean;
|
|
169
|
+
/** `false` (`--no-ci`) skips writing a pack's GitHub Actions trigger file. */
|
|
170
|
+
ci?: boolean;
|
|
156
171
|
/** Internal: the id came from the marketplace picker, skip the built-in shortcut. */
|
|
157
172
|
forceMarketplace?: boolean;
|
|
158
173
|
}): Promise<void>;
|
package/dist/cli/new.js
CHANGED
|
@@ -431,6 +431,24 @@ export function appendMissingEnvKeys(cwd, credentials) {
|
|
|
431
431
|
return newKeys.length;
|
|
432
432
|
}
|
|
433
433
|
/** Write workflow YAML into .sweny/workflows/<id>.yml. Returns info about what happened. */
|
|
434
|
+
/** Where a pack's GitHub Actions trigger is written: `.github/workflows/sweny-<pack>.yml`. */
|
|
435
|
+
export function packTriggerPath(id) {
|
|
436
|
+
return path.join(".github", "workflows", `sweny-${id}.yml`);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Write a pack's Actions trigger (#474). Never overwrites: an existing file is
|
|
440
|
+
* left exactly as it is. Returns `skipped` for a template that is not a pack.
|
|
441
|
+
*/
|
|
442
|
+
export function writePackTrigger(cwd, template) {
|
|
443
|
+
const target = path.join(cwd, packTriggerPath(template.id));
|
|
444
|
+
if (!template.pack)
|
|
445
|
+
return { written: false, skipped: true, path: target };
|
|
446
|
+
if (fs.existsSync(target))
|
|
447
|
+
return { written: false, exists: true, path: target };
|
|
448
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
449
|
+
fs.writeFileSync(target, template.pack.trigger, "utf-8");
|
|
450
|
+
return { written: true, path: target };
|
|
451
|
+
}
|
|
434
452
|
export function writeWorkflowFile(cwd, id, yaml, options) {
|
|
435
453
|
const dir = path.join(cwd, ".sweny", "workflows");
|
|
436
454
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -522,7 +540,12 @@ export async function runNew(options) {
|
|
|
522
540
|
// so it works with no network. Only other ids go to the marketplace.
|
|
523
541
|
if (options?.marketplaceId && options.marketplaceId !== "e2e" && !options.forceMarketplace) {
|
|
524
542
|
if (WORKFLOW_TEMPLATES.some((t) => t.id === options.marketplaceId)) {
|
|
525
|
-
return runNew({
|
|
543
|
+
return runNew({
|
|
544
|
+
template: options.marketplaceId,
|
|
545
|
+
skipIntro: options.skipIntro,
|
|
546
|
+
yes: options.yes,
|
|
547
|
+
ci: options.ci,
|
|
548
|
+
});
|
|
526
549
|
}
|
|
527
550
|
}
|
|
528
551
|
if (options?.template && !WORKFLOW_TEMPLATES.some((t) => t.id === options.template)) {
|
|
@@ -700,6 +723,9 @@ export async function runNew(options) {
|
|
|
700
723
|
files.push(".sweny.yml", ".env");
|
|
701
724
|
if (template)
|
|
702
725
|
files.push(`.sweny/workflows/${template.id}.yml`);
|
|
726
|
+
const writeTrigger = !!template?.pack && options?.ci !== false;
|
|
727
|
+
if (template && writeTrigger)
|
|
728
|
+
files.push(packTriggerPath(template.id));
|
|
703
729
|
const inferred = [
|
|
704
730
|
` Source control: ${sourceControl}${gitInfo ? " (detected)" : ""}`,
|
|
705
731
|
` Issue tracker: ${issueTracker}`,
|
|
@@ -759,11 +785,22 @@ export async function runNew(options) {
|
|
|
759
785
|
else {
|
|
760
786
|
p.log.info(".env already contains all required keys — skipped");
|
|
761
787
|
}
|
|
788
|
+
// The pack's Actions trigger (#474): never overwritten, skipped with --no-ci.
|
|
789
|
+
const installTrigger = () => {
|
|
790
|
+
if (!template || !writeTrigger)
|
|
791
|
+
return;
|
|
792
|
+
const r = writePackTrigger(cwd, template);
|
|
793
|
+
if (r.written)
|
|
794
|
+
p.log.success(`Created ${packTriggerPath(template.id)}`);
|
|
795
|
+
else if (r.exists)
|
|
796
|
+
p.log.info(`${packTriggerPath(template.id)} already exists, not overwritten`);
|
|
797
|
+
};
|
|
762
798
|
// 3. Workflow template
|
|
763
799
|
if (template) {
|
|
764
800
|
const firstAttempt = writeWorkflowFile(cwd, template.id, template.yaml, { overwrite: false });
|
|
765
801
|
if (firstAttempt.exists && options?.yes) {
|
|
766
802
|
p.log.info(`.sweny/workflows/${template.id}.yml already exists, not overwritten`);
|
|
803
|
+
installTrigger();
|
|
767
804
|
p.outro("Done.");
|
|
768
805
|
return;
|
|
769
806
|
}
|
|
@@ -782,6 +819,7 @@ export async function runNew(options) {
|
|
|
782
819
|
writeWorkflowFile(cwd, template.id, template.yaml, { overwrite: true });
|
|
783
820
|
}
|
|
784
821
|
p.log.success(`Created .sweny/workflows/${template.id}.yml`);
|
|
822
|
+
installTrigger();
|
|
785
823
|
}
|
|
786
824
|
// 4. Keep .env out of version control
|
|
787
825
|
const ignoreResult = ensureGitignoreEnv(cwd);
|
|
@@ -801,7 +839,10 @@ export async function runNew(options) {
|
|
|
801
839
|
}
|
|
802
840
|
if (template) {
|
|
803
841
|
steps.push(`${stepNum++}. Run your workflow:`, ` ${cli} workflow run .sweny/workflows/${template.id}.yml`);
|
|
804
|
-
if (template.pack) {
|
|
842
|
+
if (template.pack && writeTrigger) {
|
|
843
|
+
steps.push("", `${stepNum++}. Run it automatically: add the CLAUDE_CODE_OAUTH_TOKEN repo secret, then commit`, ` .sweny/workflows/${template.id}.yml and ${packTriggerPath(template.id)}`, ` https://docs.sweny.ai/workflows/packs/#${template.id}`);
|
|
844
|
+
}
|
|
845
|
+
else if (template.pack) {
|
|
805
846
|
steps.push("", `${stepNum++}. Run it automatically (GitHub Actions trigger and permissions):`, ` https://docs.sweny.ai/workflows/packs/#${template.id}`);
|
|
806
847
|
}
|
|
807
848
|
}
|
package/dist/cli/packs.js
CHANGED
|
@@ -336,11 +336,45 @@ nodes:
|
|
|
336
336
|
- github_add_comment
|
|
337
337
|
- github_create_issue
|
|
338
338
|
|
|
339
|
+
|
|
340
|
+
quiet:
|
|
341
|
+
name: Nothing to Report
|
|
342
|
+
instruction: |
|
|
343
|
+
The week had no commits, merged PRs, or issues opened or closed, so there is nothing worth a digest. Do not call any tool and deliver nothing. Return
|
|
344
|
+
delivered false and one short sentence saying why.
|
|
345
|
+
permissions: read
|
|
346
|
+
max_turns: 2
|
|
347
|
+
output:
|
|
348
|
+
type: object
|
|
349
|
+
properties:
|
|
350
|
+
delivered:
|
|
351
|
+
type: boolean
|
|
352
|
+
reason:
|
|
353
|
+
type: string
|
|
354
|
+
required: [delivered, reason]
|
|
355
|
+
eval:
|
|
356
|
+
- name: quiet_delivers_nothing
|
|
357
|
+
kind: function
|
|
358
|
+
rule:
|
|
359
|
+
no_tool_called:
|
|
360
|
+
- github_create_issue
|
|
361
|
+
- github_add_comment
|
|
362
|
+
- github_create_pr
|
|
363
|
+
- slack_send_message
|
|
364
|
+
- notify_webhook
|
|
365
|
+
|
|
366
|
+
# Quiet weeks (#474): sweny decides on the counts, with no model call. A week
|
|
367
|
+
# with any activity is delivered; an empty one routes to quiet and nothing is
|
|
368
|
+
# sent, so the digest is never noise.
|
|
339
369
|
edges:
|
|
340
370
|
- from: collect
|
|
341
371
|
to: analyze
|
|
342
372
|
- from: analyze
|
|
343
373
|
to: publish
|
|
374
|
+
when:
|
|
375
|
+
expr: "analyze.stats.commits > 0 || analyze.stats.prs_merged > 0 || analyze.stats.issues_opened > 0 || analyze.stats.issues_closed > 0"
|
|
376
|
+
- from: analyze
|
|
377
|
+
to: quiet
|
|
344
378
|
`;
|
|
345
379
|
const DIGEST_TRIGGER = `name: SWEny weekly digest
|
|
346
380
|
on:
|
|
@@ -350,19 +384,21 @@ on:
|
|
|
350
384
|
|
|
351
385
|
permissions:
|
|
352
386
|
contents: read
|
|
353
|
-
issues: write #
|
|
387
|
+
issues: write # deliver: issue, and the failure alert
|
|
354
388
|
|
|
355
389
|
jobs:
|
|
356
390
|
digest:
|
|
357
391
|
runs-on: ubuntu-latest
|
|
358
392
|
timeout-minutes: 15
|
|
359
393
|
steps:
|
|
360
|
-
- uses: actions/checkout@
|
|
394
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
361
395
|
with:
|
|
362
396
|
fetch-depth: 0
|
|
397
|
+
persist-credentials: false
|
|
363
398
|
- uses: swenyai/sweny@v5
|
|
364
399
|
with:
|
|
365
400
|
workflow: .sweny/workflows/weekly-digest.yml
|
|
401
|
+
notify-on-failure: issue # one sticky issue, only when a run fails
|
|
366
402
|
claude-oauth-token: \${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
367
403
|
input: '{"repo": "\${{ github.repository }}"}'
|
|
368
404
|
# input: '{"repo": "\${{ github.repository }}", "days": 7, "deliver": "slack"}'
|
|
@@ -680,6 +716,35 @@ nodes:
|
|
|
680
716
|
- path: result
|
|
681
717
|
in: [created, updated, unchanged, none]
|
|
682
718
|
|
|
719
|
+
|
|
720
|
+
quiet:
|
|
721
|
+
name: Nothing to Report
|
|
722
|
+
instruction: |
|
|
723
|
+
Nothing actionable: no advisory at or above the severity floor and no lockfile drift. Do not call any tool and deliver nothing. Return
|
|
724
|
+
delivered false and one short sentence saying why.
|
|
725
|
+
permissions: read
|
|
726
|
+
max_turns: 2
|
|
727
|
+
output:
|
|
728
|
+
type: object
|
|
729
|
+
properties:
|
|
730
|
+
delivered:
|
|
731
|
+
type: boolean
|
|
732
|
+
reason:
|
|
733
|
+
type: string
|
|
734
|
+
required: [delivered, reason]
|
|
735
|
+
eval:
|
|
736
|
+
- name: quiet_delivers_nothing
|
|
737
|
+
kind: function
|
|
738
|
+
rule:
|
|
739
|
+
no_tool_called:
|
|
740
|
+
- github_create_issue
|
|
741
|
+
- github_add_comment
|
|
742
|
+
- github_create_pr
|
|
743
|
+
- slack_send_message
|
|
744
|
+
- notify_webhook
|
|
745
|
+
|
|
746
|
+
# Quiet weeks (#474): sweny routes on the action, with no model call. Nothing
|
|
747
|
+
# actionable skips the issue step, so a clean week files and posts nothing.
|
|
683
748
|
edges:
|
|
684
749
|
- from: inventory
|
|
685
750
|
to: advisories
|
|
@@ -687,6 +752,10 @@ edges:
|
|
|
687
752
|
to: assess
|
|
688
753
|
- from: assess
|
|
689
754
|
to: file-issue
|
|
755
|
+
when:
|
|
756
|
+
expr: "assess.action == 'file'"
|
|
757
|
+
- from: assess
|
|
758
|
+
to: quiet
|
|
690
759
|
`;
|
|
691
760
|
const DRIFT_TRIGGER = `name: SWEny dependency drift
|
|
692
761
|
on:
|
|
@@ -703,10 +772,13 @@ jobs:
|
|
|
703
772
|
runs-on: ubuntu-latest
|
|
704
773
|
timeout-minutes: 15
|
|
705
774
|
steps:
|
|
706
|
-
- uses: actions/checkout@
|
|
775
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
776
|
+
with:
|
|
777
|
+
persist-credentials: false
|
|
707
778
|
- uses: swenyai/sweny@v5
|
|
708
779
|
with:
|
|
709
780
|
workflow: .sweny/workflows/dependency-drift.yml
|
|
781
|
+
notify-on-failure: issue # one sticky issue, only when a run fails
|
|
710
782
|
claude-oauth-token: \${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
711
783
|
input: '{"repo": "\${{ github.repository }}"}'
|
|
712
784
|
env:
|
|
@@ -994,7 +1066,9 @@ jobs:
|
|
|
994
1066
|
runs-on: ubuntu-latest
|
|
995
1067
|
timeout-minutes: 10
|
|
996
1068
|
steps:
|
|
997
|
-
- uses: actions/checkout@
|
|
1069
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
1070
|
+
with:
|
|
1071
|
+
persist-credentials: false
|
|
998
1072
|
- uses: swenyai/sweny@v5
|
|
999
1073
|
with:
|
|
1000
1074
|
workflow: .sweny/workflows/pr-risk-review.yml
|