@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 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({ template: options.marketplaceId, skipIntro: options.skipIntro, yes: options.yes });
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 # only needed for deliver: issue
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@v4
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@v4
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@v4
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sweny-ai/core",
3
- "version": "0.1.167",
3
+ "version": "0.1.168",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "sweny": "./dist/cli/main.js"