@oss-autopilot/core 3.5.0 → 3.7.0

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 (40) hide show
  1. package/dist/cli-registry.js +143 -1
  2. package/dist/cli.bundle.cjs +120 -108
  3. package/dist/commands/daily.d.ts +8 -0
  4. package/dist/commands/daily.js +21 -0
  5. package/dist/commands/dashboard-lifecycle.d.ts +7 -0
  6. package/dist/commands/dashboard-lifecycle.js +12 -2
  7. package/dist/commands/dashboard-process.d.ts +8 -0
  8. package/dist/commands/dashboard-process.js +20 -0
  9. package/dist/commands/features.d.ts +50 -0
  10. package/dist/commands/features.js +131 -0
  11. package/dist/commands/index.d.ts +5 -1
  12. package/dist/commands/index.js +4 -0
  13. package/dist/commands/scout-bridge.d.ts +12 -0
  14. package/dist/commands/scout-bridge.js +42 -2
  15. package/dist/commands/search.js +3 -1
  16. package/dist/commands/startup.js +75 -7
  17. package/dist/commands/vet-list.js +21 -5
  18. package/dist/commands/vet.js +3 -1
  19. package/dist/core/anti-llm-policy.d.ts +42 -13
  20. package/dist/core/anti-llm-policy.js +102 -13
  21. package/dist/core/ci-analysis.d.ts +32 -1
  22. package/dist/core/ci-analysis.js +92 -0
  23. package/dist/core/errors.d.ts +19 -0
  24. package/dist/core/errors.js +54 -0
  25. package/dist/core/index.d.ts +1 -1
  26. package/dist/core/index.js +1 -1
  27. package/dist/core/linked-pr-classification.d.ts +28 -0
  28. package/dist/core/linked-pr-classification.js +32 -0
  29. package/dist/core/pr-monitor.d.ts +1 -1
  30. package/dist/core/pr-monitor.js +31 -11
  31. package/dist/core/state-schema.d.ts +1 -0
  32. package/dist/core/state-schema.js +9 -0
  33. package/dist/core/state.d.ts +7 -0
  34. package/dist/core/state.js +10 -0
  35. package/dist/core/strategy.d.ts +21 -1
  36. package/dist/core/strategy.js +44 -0
  37. package/dist/core/types.d.ts +49 -0
  38. package/dist/formatters/json.d.ts +329 -35
  39. package/dist/formatters/json.js +102 -0
  40. package/package.json +2 -2
@@ -97,6 +97,13 @@ export interface DailyOutput {
97
97
  * on clean runs. See #1042.
98
98
  */
99
99
  warnings: DailyWarning[];
100
+ /**
101
+ * Periodic contribution-strategy snapshot (#1270). Populated when the
102
+ * cadence trigger fires AND the user has crossed the merge floor. The
103
+ * `/oss` action menu renders this inline ahead of the action options;
104
+ * absent or null on runs where the gate stays silent.
105
+ */
106
+ strategySummary?: import('../core/strategy.js').StrategyResult | null;
100
107
  }
101
108
  /**
102
109
  * Compact version of DailyOutput for reduced JSON payload size (#763).
@@ -122,6 +129,8 @@ export interface CompactDailyOutput {
122
129
  * the `--compact` payload. See #1042.
123
130
  */
124
131
  warnings: DailyWarning[];
132
+ /** Periodic strategy snapshot, threaded through compact mode for parity. See {@link DailyOutput.strategySummary}. */
133
+ strategySummary?: import('../core/strategy.js').StrategyResult | null;
125
134
  }
126
135
  /**
127
136
  * Strip a full DailyOutput down to the compact subset (#763).
@@ -275,6 +284,50 @@ export declare const DailyOutputSchema: z.ZodObject<{
275
284
  timestamp: z.ZodOptional<z.ZodString>;
276
285
  details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
277
286
  }, z.core.$strip>>;
287
+ strategySummary: z.ZodOptional<z.ZodNullable<z.ZodObject<{
288
+ profile: z.ZodObject<{
289
+ style: z.ZodEnum<{
290
+ maintainer: "maintainer";
291
+ explorer: "explorer";
292
+ specialist: "specialist";
293
+ generalist: "generalist";
294
+ }>;
295
+ totalPRs: z.ZodNumber;
296
+ mergedCount: z.ZodNumber;
297
+ mergeRate: z.ZodNumber;
298
+ primaryLanguages: z.ZodArray<z.ZodString>;
299
+ favoriteRepos: z.ZodArray<z.ZodString>;
300
+ }, z.core.$loose>;
301
+ capacity: z.ZodObject<{
302
+ openPRCount: z.ZodNumber;
303
+ dormantPRCount: z.ZodNumber;
304
+ dormantRepoCount: z.ZodNumber;
305
+ overExtended: z.ZodBoolean;
306
+ suggestedAction: z.ZodUnion<readonly [z.ZodLiteral<"open_more">, z.ZodLiteral<"follow_up_dormant">, z.ZodLiteral<"wait_on_maintainers">, z.ZodNull]>;
307
+ }, z.core.$loose>;
308
+ patterns: z.ZodObject<{
309
+ prTypeDistribution: z.ZodObject<{
310
+ docs: z.ZodNumber;
311
+ fixes: z.ZodNumber;
312
+ features: z.ZodNumber;
313
+ refactors: z.ZodNumber;
314
+ tests: z.ZodNumber;
315
+ other: z.ZodNumber;
316
+ }, z.core.$loose>;
317
+ trajectoryDirection: z.ZodEnum<{
318
+ growing: "growing";
319
+ steady: "steady";
320
+ declining: "declining";
321
+ }>;
322
+ averagePRSize: z.ZodNumber;
323
+ }, z.core.$loose>;
324
+ recommendations: z.ZodObject<{
325
+ languages: z.ZodArray<z.ZodString>;
326
+ repos: z.ZodArray<z.ZodString>;
327
+ issueTypes: z.ZodArray<z.ZodString>;
328
+ avoidPatterns: z.ZodArray<z.ZodString>;
329
+ }, z.core.$loose>;
330
+ }, z.core.$loose>>>;
278
331
  }, z.core.$strip>;
279
332
  export declare const CompactDailyOutputSchema: z.ZodObject<{
280
333
  digest: z.ZodObject<{
@@ -371,6 +424,50 @@ export declare const CompactDailyOutputSchema: z.ZodObject<{
371
424
  timestamp: z.ZodOptional<z.ZodString>;
372
425
  details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
373
426
  }, z.core.$strip>>;
427
+ strategySummary: z.ZodOptional<z.ZodNullable<z.ZodObject<{
428
+ profile: z.ZodObject<{
429
+ style: z.ZodEnum<{
430
+ maintainer: "maintainer";
431
+ explorer: "explorer";
432
+ specialist: "specialist";
433
+ generalist: "generalist";
434
+ }>;
435
+ totalPRs: z.ZodNumber;
436
+ mergedCount: z.ZodNumber;
437
+ mergeRate: z.ZodNumber;
438
+ primaryLanguages: z.ZodArray<z.ZodString>;
439
+ favoriteRepos: z.ZodArray<z.ZodString>;
440
+ }, z.core.$loose>;
441
+ capacity: z.ZodObject<{
442
+ openPRCount: z.ZodNumber;
443
+ dormantPRCount: z.ZodNumber;
444
+ dormantRepoCount: z.ZodNumber;
445
+ overExtended: z.ZodBoolean;
446
+ suggestedAction: z.ZodUnion<readonly [z.ZodLiteral<"open_more">, z.ZodLiteral<"follow_up_dormant">, z.ZodLiteral<"wait_on_maintainers">, z.ZodNull]>;
447
+ }, z.core.$loose>;
448
+ patterns: z.ZodObject<{
449
+ prTypeDistribution: z.ZodObject<{
450
+ docs: z.ZodNumber;
451
+ fixes: z.ZodNumber;
452
+ features: z.ZodNumber;
453
+ refactors: z.ZodNumber;
454
+ tests: z.ZodNumber;
455
+ other: z.ZodNumber;
456
+ }, z.core.$loose>;
457
+ trajectoryDirection: z.ZodEnum<{
458
+ growing: "growing";
459
+ steady: "steady";
460
+ declining: "declining";
461
+ }>;
462
+ averagePRSize: z.ZodNumber;
463
+ }, z.core.$loose>;
464
+ recommendations: z.ZodObject<{
465
+ languages: z.ZodArray<z.ZodString>;
466
+ repos: z.ZodArray<z.ZodString>;
467
+ issueTypes: z.ZodArray<z.ZodString>;
468
+ avoidPatterns: z.ZodArray<z.ZodString>;
469
+ }, z.core.$loose>;
470
+ }, z.core.$loose>>>;
374
471
  }, z.core.$strip>;
375
472
  export declare const SearchOutputSchema: z.ZodObject<{
376
473
  candidates: z.ZodArray<z.ZodObject<{
@@ -412,11 +509,137 @@ export declare const SearchOutputSchema: z.ZodObject<{
412
509
  isResponsive: z.ZodBoolean;
413
510
  lastMergedAt: z.ZodOptional<z.ZodString>;
414
511
  }, z.core.$strip>>;
512
+ linkedPR: z.ZodOptional<z.ZodObject<{
513
+ number: z.ZodNumber;
514
+ state: z.ZodEnum<{
515
+ closed: "closed";
516
+ open: "open";
517
+ merged: "merged";
518
+ }>;
519
+ url: z.ZodString;
520
+ updatedAt: z.ZodOptional<z.ZodString>;
521
+ isStalled: z.ZodBoolean;
522
+ }, z.core.$strip>>;
415
523
  }, z.core.$strip>>;
416
524
  excludedRepos: z.ZodArray<z.ZodString>;
417
525
  aiPolicyBlocklist: z.ZodArray<z.ZodString>;
418
526
  rateLimitWarning: z.ZodOptional<z.ZodString>;
419
527
  }, z.core.$strip>;
528
+ export declare const FeaturesOutputSchema: z.ZodObject<{
529
+ quickWins: z.ZodArray<z.ZodObject<{
530
+ issue: z.ZodObject<{
531
+ repo: z.ZodString;
532
+ repoUrl: z.ZodString;
533
+ number: z.ZodNumber;
534
+ title: z.ZodString;
535
+ url: z.ZodString;
536
+ labels: z.ZodArray<z.ZodString>;
537
+ }, z.core.$strip>;
538
+ recommendation: z.ZodEnum<{
539
+ approve: "approve";
540
+ skip: "skip";
541
+ needs_review: "needs_review";
542
+ }>;
543
+ reasonsToApprove: z.ZodArray<z.ZodString>;
544
+ reasonsToSkip: z.ZodArray<z.ZodString>;
545
+ searchPriority: z.ZodEnum<{
546
+ normal: "normal";
547
+ merged_pr: "merged_pr";
548
+ preferred_org: "preferred_org";
549
+ starred: "starred";
550
+ }>;
551
+ viabilityScore: z.ZodNumber;
552
+ grade: z.ZodObject<{
553
+ letter: z.ZodEnum<{
554
+ A: "A";
555
+ B: "B";
556
+ C: "C";
557
+ F: "F";
558
+ }>;
559
+ reason: z.ZodString;
560
+ }, z.core.$strip>;
561
+ repoScore: z.ZodOptional<z.ZodObject<{
562
+ score: z.ZodNumber;
563
+ mergedPRCount: z.ZodNumber;
564
+ closedWithoutMergeCount: z.ZodNumber;
565
+ isResponsive: z.ZodBoolean;
566
+ lastMergedAt: z.ZodOptional<z.ZodString>;
567
+ }, z.core.$strip>>;
568
+ linkedPR: z.ZodOptional<z.ZodObject<{
569
+ number: z.ZodNumber;
570
+ state: z.ZodEnum<{
571
+ closed: "closed";
572
+ open: "open";
573
+ merged: "merged";
574
+ }>;
575
+ url: z.ZodString;
576
+ updatedAt: z.ZodOptional<z.ZodString>;
577
+ isStalled: z.ZodBoolean;
578
+ }, z.core.$strip>>;
579
+ horizon: z.ZodEnum<{
580
+ "quick-win": "quick-win";
581
+ "bigger-bet": "bigger-bet";
582
+ }>;
583
+ }, z.core.$strip>>;
584
+ biggerBets: z.ZodArray<z.ZodObject<{
585
+ issue: z.ZodObject<{
586
+ repo: z.ZodString;
587
+ repoUrl: z.ZodString;
588
+ number: z.ZodNumber;
589
+ title: z.ZodString;
590
+ url: z.ZodString;
591
+ labels: z.ZodArray<z.ZodString>;
592
+ }, z.core.$strip>;
593
+ recommendation: z.ZodEnum<{
594
+ approve: "approve";
595
+ skip: "skip";
596
+ needs_review: "needs_review";
597
+ }>;
598
+ reasonsToApprove: z.ZodArray<z.ZodString>;
599
+ reasonsToSkip: z.ZodArray<z.ZodString>;
600
+ searchPriority: z.ZodEnum<{
601
+ normal: "normal";
602
+ merged_pr: "merged_pr";
603
+ preferred_org: "preferred_org";
604
+ starred: "starred";
605
+ }>;
606
+ viabilityScore: z.ZodNumber;
607
+ grade: z.ZodObject<{
608
+ letter: z.ZodEnum<{
609
+ A: "A";
610
+ B: "B";
611
+ C: "C";
612
+ F: "F";
613
+ }>;
614
+ reason: z.ZodString;
615
+ }, z.core.$strip>;
616
+ repoScore: z.ZodOptional<z.ZodObject<{
617
+ score: z.ZodNumber;
618
+ mergedPRCount: z.ZodNumber;
619
+ closedWithoutMergeCount: z.ZodNumber;
620
+ isResponsive: z.ZodBoolean;
621
+ lastMergedAt: z.ZodOptional<z.ZodString>;
622
+ }, z.core.$strip>>;
623
+ linkedPR: z.ZodOptional<z.ZodObject<{
624
+ number: z.ZodNumber;
625
+ state: z.ZodEnum<{
626
+ closed: "closed";
627
+ open: "open";
628
+ merged: "merged";
629
+ }>;
630
+ url: z.ZodString;
631
+ updatedAt: z.ZodOptional<z.ZodString>;
632
+ isStalled: z.ZodBoolean;
633
+ }, z.core.$strip>>;
634
+ horizon: z.ZodEnum<{
635
+ "quick-win": "quick-win";
636
+ "bigger-bet": "bigger-bet";
637
+ }>;
638
+ }, z.core.$strip>>;
639
+ anchorRepos: z.ZodArray<z.ZodString>;
640
+ message: z.ZodNullable<z.ZodString>;
641
+ rateLimitWarning: z.ZodOptional<z.ZodString>;
642
+ }, z.core.$strip>;
420
643
  export declare const DoctorOutputSchema: z.ZodObject<{
421
644
  checks: z.ZodArray<z.ZodObject<{
422
645
  name: z.ZodString;
@@ -454,6 +677,14 @@ export declare const ListMoveTierOutputSchema: z.ZodObject<{
454
677
  count: z.ZodNumber;
455
678
  reason: z.ZodOptional<z.ZodString>;
456
679
  }, z.core.$strip>;
680
+ export declare const ListMarkDoneOutputSchema: z.ZodObject<{
681
+ marked: z.ZodBoolean;
682
+ filePath: z.ZodString;
683
+ url: z.ZodString;
684
+ repoHeadingStruck: z.ZodBoolean;
685
+ remainingUnderRepo: z.ZodNumber;
686
+ reason: z.ZodOptional<z.ZodString>;
687
+ }, z.core.$strip>;
457
688
  export declare const PostOutputSchema: z.ZodObject<{
458
689
  commentUrl: z.ZodString;
459
690
  url: z.ZodString;
@@ -741,46 +972,95 @@ export declare const LocalReposOutputSchema: z.ZodObject<{
741
972
  cachedAt: z.ZodString;
742
973
  fromCache: z.ZodBoolean;
743
974
  }, z.core.$strip>;
975
+ /**
976
+ * Compact summary of an issue's first linked PR, surfaced on candidate
977
+ * outputs (#97 / scout 0.9.0). `isStalled` is `true` when the PR is open
978
+ * and has not been updated for `STALLED_PR_THRESHOLD_DAYS` (default 30) —
979
+ * a revive-opportunity signal callers can render or filter on.
980
+ *
981
+ * `state` mirrors autopilot's existing tri-state classifier (`'merged'` is
982
+ * folded in from scout's `merged: true` boolean), not scout's raw enum.
983
+ */
984
+ export interface CandidateLinkedPR {
985
+ number: number;
986
+ state: 'open' | 'closed' | 'merged';
987
+ url: string;
988
+ /** ISO timestamp of the PR's last update (when scout surfaces it). */
989
+ updatedAt?: string;
990
+ /** True when the PR is open AND `updatedAt` is more than 30 days old. */
991
+ isStalled: boolean;
992
+ }
993
+ /**
994
+ * One candidate row in `SearchOutput`/`FeaturesOutput`. Extracted so the
995
+ * features command can reuse the exact contract `runSearch` already
996
+ * publishes — keeping the two outputs structurally identical for everything
997
+ * except the bucket-specific `horizon` annotation.
998
+ */
999
+ export interface SearchCandidate {
1000
+ issue: {
1001
+ repo: string;
1002
+ repoUrl: string;
1003
+ number: number;
1004
+ title: string;
1005
+ url: string;
1006
+ labels: string[];
1007
+ };
1008
+ recommendation: 'approve' | 'skip' | 'needs_review';
1009
+ reasonsToApprove: string[];
1010
+ reasonsToSkip: string[];
1011
+ searchPriority: SearchPriority;
1012
+ /** 0-100 scale composite viability score. Sanitized on the boundary (#1043): out-of-contract values are coerced to 0 and logged. */
1013
+ viabilityScore: number;
1014
+ /**
1015
+ * Letter grade (A/B/C/F) computed from the autopilot-tracked repoScore.
1016
+ * Scout's `search` does not emit per-candidate projectHealth, so scout-side
1017
+ * signals are treated as unknown; unscored repos grade 'F'. See #1043.
1018
+ */
1019
+ grade: {
1020
+ letter: 'A' | 'B' | 'C' | 'F';
1021
+ reason: string;
1022
+ };
1023
+ repoScore?: {
1024
+ /** 1-10 scale repository quality score */
1025
+ score: number;
1026
+ mergedPRCount: number;
1027
+ closedWithoutMergeCount: number;
1028
+ isResponsive: boolean;
1029
+ lastMergedAt?: string;
1030
+ };
1031
+ /**
1032
+ * First linked PR on the issue, when scout surfaced one. Optional —
1033
+ * absent when no linked PR exists. `isStalled` flags revive
1034
+ * opportunities (open PR + no updates for 30+ days, scout 0.9.0 #97).
1035
+ */
1036
+ linkedPR?: CandidateLinkedPR;
1037
+ }
744
1038
  export interface SearchOutput {
745
- candidates: Array<{
746
- issue: {
747
- repo: string;
748
- repoUrl: string;
749
- number: number;
750
- title: string;
751
- url: string;
752
- labels: string[];
753
- };
754
- recommendation: 'approve' | 'skip' | 'needs_review';
755
- reasonsToApprove: string[];
756
- reasonsToSkip: string[];
757
- searchPriority: SearchPriority;
758
- /** 0-100 scale composite viability score. Sanitized on the boundary (#1043): out-of-contract values are coerced to 0 and logged. */
759
- viabilityScore: number;
760
- /**
761
- * Letter grade (A/B/C/F) computed from the autopilot-tracked repoScore.
762
- * Scout's `search` does not emit per-candidate projectHealth, so scout-side
763
- * signals are treated as unknown; unscored repos grade 'F'. See #1043.
764
- */
765
- grade: {
766
- letter: 'A' | 'B' | 'C' | 'F';
767
- reason: string;
768
- };
769
- repoScore?: {
770
- /** 1-10 scale repository quality score */
771
- score: number;
772
- mergedPRCount: number;
773
- closedWithoutMergeCount: number;
774
- isResponsive: boolean;
775
- lastMergedAt?: string;
776
- };
777
- }>;
1039
+ candidates: SearchCandidate[];
778
1040
  excludedRepos: string[];
779
1041
  /** Repos with known anti-AI contribution policies, filtered from search results (#108). */
780
1042
  aiPolicyBlocklist: string[];
781
1043
  /** Present when rate limits affected the search — either low pre-flight quota or mid-search rate limit hits (#100). */
782
1044
  rateLimitWarning?: string;
783
1045
  }
1046
+ /** Horizon classification stamped on each features-mode candidate. */
1047
+ export type FeaturesHorizon = 'quick-win' | 'bigger-bet';
1048
+ /** A `SearchCandidate` augmented with its features-mode horizon. */
1049
+ export type FeaturesCandidate = SearchCandidate & {
1050
+ horizon: FeaturesHorizon;
1051
+ };
1052
+ export interface FeaturesOutput {
1053
+ /** "Quick-win" bucket: feature-scoped issues without strong commitment markers (no milestone, no roadmap, no bigger-bet labels). */
1054
+ quickWins: FeaturesCandidate[];
1055
+ /** "Bigger-bet" bucket: feature-scoped issues that carry maintainer-commitment signals (milestone, roadmap, bigger-bet label). */
1056
+ biggerBets: FeaturesCandidate[];
1057
+ /** Repos that qualified as anchors for this run (3+ merged PRs, configurable). Empty when the user has no anchor repos yet. */
1058
+ anchorRepos: string[];
1059
+ /** Human-friendly explainer shown when neither bucket has results (no anchors, or anchors but no open feature opportunities). `null` on success. */
1060
+ message: string | null;
1061
+ /** Present when rate limits affected the search. Mirrors `SearchOutput.rateLimitWarning`. */
1062
+ rateLimitWarning?: string;
1063
+ }
784
1064
  export interface TrackOutput {
785
1065
  pr: {
786
1066
  repo: string;
@@ -886,8 +1166,14 @@ export interface CheckIntegrationOutput {
886
1166
  newFiles: NewFileInfo[];
887
1167
  unreferencedCount: number;
888
1168
  }
889
- /** Status of a re-vetted issue from the curated list (#764). */
890
- export type VetListItemStatus = 'still_available' | 'claimed' | 'closed' | 'has_pr' | 'error';
1169
+ /**
1170
+ * Status of a re-vetted issue from the curated list (#764).
1171
+ *
1172
+ * `has_stalled_pr` (scout 0.9.0 #97) distinguishes open-but-stalled linked
1173
+ * PRs from cleanly-claimed `has_pr` issues — the issue is still actionable
1174
+ * as a revive opportunity rather than something to drop.
1175
+ */
1176
+ export type VetListItemStatus = 'still_available' | 'claimed' | 'closed' | 'has_pr' | 'has_stalled_pr' | 'error';
891
1177
  /** Output of the vet-list command (#764). */
892
1178
  export interface VetListOutput {
893
1179
  results: Array<VetOutput & {
@@ -900,6 +1186,8 @@ export interface VetListOutput {
900
1186
  claimed: number;
901
1187
  closed: number;
902
1188
  hasPR: number;
1189
+ /** Open linked PRs that haven't been touched in 30+ days (scout 0.9.0 #97). Surfaced as revive opportunities, not auto-dropped. */
1190
+ hasStalledPR: number;
903
1191
  errors: number;
904
1192
  };
905
1193
  pruneResult?: {
@@ -937,6 +1225,12 @@ export interface VetOutput {
937
1225
  * already has work in flight vs. a competing contributor.
938
1226
  */
939
1227
  linkedPRClassification?: 'none' | 'user_open' | 'user_closed' | 'user_merged' | 'other_open' | 'other_closed' | 'other_merged';
1228
+ /**
1229
+ * Compact linked-PR summary (#97 / scout 0.9.0). Present when the issue
1230
+ * has a linked PR; absent otherwise. `isStalled` flags open PRs that
1231
+ * haven't been touched in 30+ days as revive opportunities.
1232
+ */
1233
+ linkedPR?: CandidateLinkedPR;
940
1234
  /**
941
1235
  * Optional SLM pre-triage classification (#1122). Populated when the
942
1236
  * user has set `slmTriageModel` and a local Ollama instance answered
@@ -30,6 +30,7 @@ export function toCompactDailyOutput(output) {
30
30
  commentedIssues: output.commentedIssues,
31
31
  failureCount: output.failures.length,
32
32
  warnings: output.warnings,
33
+ strategySummary: output.strategySummary,
33
34
  };
34
35
  }
35
36
  /**
@@ -198,6 +199,65 @@ const CompactRepoGroupSchema = z.object({
198
199
  });
199
200
  // DailyWarning schemas were hoisted above StatusOutputSchema (#1193) so the
200
201
  // status output can reference them without `z.lazy()`.
202
+ // Mirrors {@link StrategyResult} in core/strategy.ts. Kept passthrough on the
203
+ // inner objects so additive shape changes there don't break Zod validation
204
+ // before the schema catches up — drift on required keys still fails.
205
+ const StrategyResultSchema = z
206
+ .object({
207
+ profile: z
208
+ .object({
209
+ style: z.enum(['maintainer', 'explorer', 'specialist', 'generalist']),
210
+ totalPRs: z.number().int().nonnegative(),
211
+ mergedCount: z.number().int().nonnegative(),
212
+ mergeRate: z.number(),
213
+ primaryLanguages: z.array(z.string()),
214
+ favoriteRepos: z.array(z.string()),
215
+ })
216
+ .passthrough(),
217
+ capacity: z
218
+ .object({
219
+ openPRCount: z.number().int().nonnegative(),
220
+ dormantPRCount: z.number().int().nonnegative(),
221
+ dormantRepoCount: z.number().int().nonnegative(),
222
+ overExtended: z.boolean(),
223
+ suggestedAction: z.union([
224
+ z.literal('open_more'),
225
+ z.literal('follow_up_dormant'),
226
+ z.literal('wait_on_maintainers'),
227
+ z.null(),
228
+ ]),
229
+ })
230
+ .passthrough(),
231
+ patterns: z
232
+ .object({
233
+ // Closed set on the six required PR-type buckets — drift on any
234
+ // of these breaks the snapshot rendering. `.passthrough()` allows
235
+ // additive growth (e.g., a future 'security' bucket) without a
236
+ // schema bump, but a typo on `features` → `feauters` fails here.
237
+ prTypeDistribution: z
238
+ .object({
239
+ docs: z.number(),
240
+ fixes: z.number(),
241
+ features: z.number(),
242
+ refactors: z.number(),
243
+ tests: z.number(),
244
+ other: z.number(),
245
+ })
246
+ .passthrough(),
247
+ trajectoryDirection: z.enum(['growing', 'steady', 'declining']),
248
+ averagePRSize: z.number(),
249
+ })
250
+ .passthrough(),
251
+ recommendations: z
252
+ .object({
253
+ languages: z.array(z.string()),
254
+ repos: z.array(z.string()),
255
+ issueTypes: z.array(z.string()),
256
+ avoidPatterns: z.array(z.string()),
257
+ })
258
+ .passthrough(),
259
+ })
260
+ .passthrough();
201
261
  export const DailyOutputSchema = z.object({
202
262
  digest: DailyDigestCompactSchema,
203
263
  capacity: CapacityAssessmentSchema,
@@ -209,6 +269,7 @@ export const DailyOutputSchema = z.object({
209
269
  repoGroups: z.array(CompactRepoGroupSchema),
210
270
  failures: z.array(PRCheckFailurePassthroughSchema),
211
271
  warnings: z.array(DailyWarningSchema),
272
+ strategySummary: StrategyResultSchema.nullable().optional(),
212
273
  });
213
274
  export const CompactDailyOutputSchema = z.object({
214
275
  digest: DailyDigestCompactSchema,
@@ -219,9 +280,21 @@ export const CompactDailyOutputSchema = z.object({
219
280
  commentedIssues: z.array(CommentedIssuePassthroughSchema),
220
281
  failureCount: z.number().int().nonnegative(),
221
282
  warnings: z.array(DailyWarningSchema),
283
+ strategySummary: StrategyResultSchema.nullable().optional(),
222
284
  });
223
285
  // ── Search output schema (#1147) ─────────────────────────────────────
224
286
  const SearchPrioritySchema = z.enum(['merged_pr', 'preferred_org', 'starred', 'normal']);
287
+ /**
288
+ * Schema for the compact linked-PR annotation surfaced on candidate
289
+ * outputs (#97 / scout 0.9.0). Mirrors {@link CandidateLinkedPR}.
290
+ */
291
+ const CandidateLinkedPRSchema = z.object({
292
+ number: z.number().int().positive(),
293
+ state: z.enum(['open', 'closed', 'merged']),
294
+ url: z.string(),
295
+ updatedAt: z.string().optional(),
296
+ isStalled: z.boolean(),
297
+ });
225
298
  const SearchCandidateSchema = z.object({
226
299
  issue: z.object({
227
300
  repo: z.string(),
@@ -249,6 +322,7 @@ const SearchCandidateSchema = z.object({
249
322
  lastMergedAt: z.string().optional(),
250
323
  })
251
324
  .optional(),
325
+ linkedPR: CandidateLinkedPRSchema.optional(),
252
326
  });
253
327
  export const SearchOutputSchema = z.object({
254
328
  candidates: z.array(SearchCandidateSchema),
@@ -256,6 +330,22 @@ export const SearchOutputSchema = z.object({
256
330
  aiPolicyBlocklist: z.array(z.string()),
257
331
  rateLimitWarning: z.string().optional(),
258
332
  });
333
+ // ── Features output schema (scout 0.9.0 #97/#98/#99) ─────────────────
334
+ //
335
+ // `SearchCandidateSchema` augmented with the horizon literal that scout
336
+ // stamps in features mode. Reusing the search candidate keeps the two
337
+ // envelopes structurally identical apart from the bucket annotation.
338
+ const FeaturesHorizonSchema = z.enum(['quick-win', 'bigger-bet']);
339
+ const FeaturesCandidateSchema = SearchCandidateSchema.extend({
340
+ horizon: FeaturesHorizonSchema,
341
+ });
342
+ export const FeaturesOutputSchema = z.object({
343
+ quickWins: z.array(FeaturesCandidateSchema),
344
+ biggerBets: z.array(FeaturesCandidateSchema),
345
+ anchorRepos: z.array(z.string()),
346
+ message: z.string().nullable(),
347
+ rateLimitWarning: z.string().optional(),
348
+ });
259
349
  // ── Doctor / skip-add / list-move-tier output schemas (#1148) ────────
260
350
  const DoctorCheckSchema = z.object({
261
351
  name: z.string(),
@@ -287,6 +377,18 @@ export const ListMoveTierOutputSchema = z.object({
287
377
  count: z.number().int().nonnegative(),
288
378
  reason: z.string().optional(),
289
379
  });
380
+ // list-mark-done (#1299): mirrors {@link MarkDoneOutput} from the command
381
+ // module. Strict shape — additional keys must be added here AND in the
382
+ // command output, otherwise the validator's `parse()` rejects the response
383
+ // before it reaches consumers.
384
+ export const ListMarkDoneOutputSchema = z.object({
385
+ marked: z.boolean(),
386
+ filePath: z.string(),
387
+ url: z.string(),
388
+ repoHeadingStruck: z.boolean(),
389
+ remainingUnderRepo: z.number().int().nonnegative(),
390
+ reason: z.string().optional(),
391
+ });
290
392
  // ── #1155: Zod coverage for remaining CLI commands ───────────────────
291
393
  export const PostOutputSchema = z.object({
292
394
  commentUrl: z.string(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oss-autopilot/core",
3
- "version": "3.5.0",
3
+ "version": "3.7.0",
4
4
  "description": "CLI and core library for managing open source contributions",
5
5
  "type": "module",
6
6
  "bin": {
@@ -54,7 +54,7 @@
54
54
  "dependencies": {
55
55
  "@octokit/plugin-throttling": "^11.0.3",
56
56
  "@octokit/rest": "^22.0.1",
57
- "@oss-scout/core": "^0.8.0",
57
+ "@oss-scout/core": "^0.9.0",
58
58
  "commander": "^14.0.3",
59
59
  "zod": "^4.4.3"
60
60
  },