@oss-autopilot/core 0.49.0 → 0.51.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.
- package/dist/cli-registry.js +44 -98
- package/dist/cli.bundle.cjs +43 -45
- package/dist/cli.bundle.cjs.map +4 -4
- package/dist/commands/daily.d.ts +1 -1
- package/dist/commands/daily.js +5 -42
- package/dist/commands/dashboard-server.d.ts +1 -1
- package/dist/commands/dashboard-server.js +19 -29
- package/dist/commands/dismiss.d.ts +1 -1
- package/dist/commands/dismiss.js +4 -4
- package/dist/commands/index.d.ts +3 -5
- package/dist/commands/index.js +2 -4
- package/dist/commands/move.d.ts +16 -0
- package/dist/commands/move.js +56 -0
- package/dist/commands/setup.d.ts +3 -0
- package/dist/commands/setup.js +62 -0
- package/dist/commands/shelve.d.ts +4 -0
- package/dist/commands/shelve.js +8 -2
- package/dist/core/category-mapping.d.ts +19 -0
- package/dist/core/category-mapping.js +58 -0
- package/dist/core/daily-logic.d.ts +1 -1
- package/dist/core/daily-logic.js +8 -5
- package/dist/core/issue-discovery.js +55 -3
- package/dist/core/issue-scoring.d.ts +3 -0
- package/dist/core/issue-scoring.js +5 -0
- package/dist/core/issue-vetting.js +12 -0
- package/dist/core/pr-monitor.d.ts +2 -27
- package/dist/core/pr-monitor.js +4 -110
- package/dist/core/state.d.ts +8 -40
- package/dist/core/state.js +36 -93
- package/dist/core/status-determination.d.ts +35 -0
- package/dist/core/status-determination.js +112 -0
- package/dist/core/types.d.ts +18 -12
- package/dist/core/types.js +11 -1
- package/package.json +1 -1
- package/dist/commands/override.d.ts +0 -21
- package/dist/commands/override.js +0 -35
- package/dist/commands/snooze.d.ts +0 -24
- package/dist/commands/snooze.js +0 -40
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status determination logic for PRs — extracted from PRMonitor (#263).
|
|
3
|
+
*
|
|
4
|
+
* Computes the top-level status (needs_addressing vs waiting_on_maintainer),
|
|
5
|
+
* granular action/wait reasons, and staleness tier for a single PR based on
|
|
6
|
+
* its CI, review, merge-conflict, and timeline signals.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* CI-fix bots that push commits as a direct result of the contributor's push (#568).
|
|
10
|
+
* Their commits represent contributor work and should count as addressing feedback.
|
|
11
|
+
* This is intentionally an allowlist — not all `[bot]` accounts are CI-fix bots
|
|
12
|
+
* (e.g. dependabot[bot] and renovate[bot] open their own PRs).
|
|
13
|
+
* Values must be lowercase — lookup uses .toLowerCase() for case-insensitive matching.
|
|
14
|
+
*/
|
|
15
|
+
export const CI_FIX_BOTS = new Set(['autofix-ci[bot]', 'prettier-ci[bot]', 'pre-commit-ci[bot]']);
|
|
16
|
+
/** Minimum gap (ms) between maintainer comment and contributor commit for
|
|
17
|
+
* the commit to count as "addressing" the feedback (#547). Prevents false
|
|
18
|
+
* positives from race conditions, clock skew, and in-flight pushes. */
|
|
19
|
+
export const MIN_RESPONSE_GAP_MS = 2 * 60 * 1000; // 2 minutes
|
|
20
|
+
/**
|
|
21
|
+
* Check whether the HEAD commit was authored by the contributor (#547).
|
|
22
|
+
* Returns true when the author matches, when the author is a known CI-fix
|
|
23
|
+
* bot (#568), or when author info is unavailable (graceful degradation).
|
|
24
|
+
*/
|
|
25
|
+
export function isContributorCommit(commitAuthor, contributorUsername) {
|
|
26
|
+
if (!commitAuthor || !contributorUsername)
|
|
27
|
+
return true; // degrade gracefully
|
|
28
|
+
const author = commitAuthor.toLowerCase();
|
|
29
|
+
if (CI_FIX_BOTS.has(author))
|
|
30
|
+
return true; // CI-fix bots act on behalf of the contributor (#568)
|
|
31
|
+
return author === contributorUsername.toLowerCase();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Check whether the contributor's commit is meaningfully after the maintainer's
|
|
35
|
+
* comment — i.e. the commit timestamp is at least MIN_RESPONSE_GAP_MS later (#547).
|
|
36
|
+
*/
|
|
37
|
+
export function isCommitAfterComment(commitDate, commentDate) {
|
|
38
|
+
const commitMs = new Date(commitDate).getTime();
|
|
39
|
+
const commentMs = new Date(commentDate).getTime();
|
|
40
|
+
if (Number.isNaN(commitMs) || Number.isNaN(commentMs)) {
|
|
41
|
+
// Fall back to simple string comparison (pre-#547 behavior)
|
|
42
|
+
return commitDate > commentDate;
|
|
43
|
+
}
|
|
44
|
+
return commitMs - commentMs >= MIN_RESPONSE_GAP_MS;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Determine the overall status of a PR based on its signals.
|
|
48
|
+
*/
|
|
49
|
+
export function determineStatus(input) {
|
|
50
|
+
const { ciStatus, hasMergeConflict, hasUnrespondedComment, hasIncompleteChecklist, reviewDecision, daysSinceActivity, dormantThreshold, approachingThreshold, latestCommitDate: rawCommitDate, latestCommitAuthor, contributorUsername, lastMaintainerCommentDate, latestChangesRequestedDate, hasActionableCIFailure = true, } = input;
|
|
51
|
+
// Compute staleness tier (independent of status)
|
|
52
|
+
let stalenessTier = 'active';
|
|
53
|
+
if (daysSinceActivity >= dormantThreshold)
|
|
54
|
+
stalenessTier = 'dormant';
|
|
55
|
+
else if (daysSinceActivity >= approachingThreshold)
|
|
56
|
+
stalenessTier = 'approaching_dormant';
|
|
57
|
+
// Only count the latest commit if it was authored by the contributor or a
|
|
58
|
+
// CI bot (#547, #568). Non-contributor commits (maintainer merge commits,
|
|
59
|
+
// GitHub suggestion commits) should not mask unaddressed feedback.
|
|
60
|
+
const latestCommitDate = rawCommitDate && isContributorCommit(latestCommitAuthor, contributorUsername) ? rawCommitDate : undefined;
|
|
61
|
+
// Priority order: needs_addressing (response/changes/ci/conflict/checklist) > waiting_on_maintainer (review/merge/addressed/ci_blocked)
|
|
62
|
+
if (hasUnrespondedComment) {
|
|
63
|
+
// If the contributor pushed a commit after the maintainer's comment,
|
|
64
|
+
// the changes have been addressed — waiting for maintainer re-review.
|
|
65
|
+
// Require a minimum 2-minute gap to avoid false positives from race
|
|
66
|
+
// conditions (pushing while review is being submitted) (#547).
|
|
67
|
+
if (latestCommitDate &&
|
|
68
|
+
lastMaintainerCommentDate &&
|
|
69
|
+
isCommitAfterComment(latestCommitDate, lastMaintainerCommentDate)) {
|
|
70
|
+
// Safety net (#431): if a CHANGES_REQUESTED review was submitted after
|
|
71
|
+
// the commit, the maintainer still expects changes — don't mask it
|
|
72
|
+
if (latestChangesRequestedDate && latestCommitDate < latestChangesRequestedDate) {
|
|
73
|
+
return { status: 'needs_addressing', actionReason: 'needs_response', stalenessTier };
|
|
74
|
+
}
|
|
75
|
+
if (ciStatus === 'failing' && hasActionableCIFailure)
|
|
76
|
+
return { status: 'needs_addressing', actionReason: 'failing_ci', stalenessTier };
|
|
77
|
+
// Non-actionable CI failures (infrastructure, fork, auth) don't block changes_addressed —
|
|
78
|
+
// the contributor can't fix them, so the relevant status is "waiting for re-review" (#502)
|
|
79
|
+
return { status: 'waiting_on_maintainer', waitReason: 'changes_addressed', stalenessTier };
|
|
80
|
+
}
|
|
81
|
+
return { status: 'needs_addressing', actionReason: 'needs_response', stalenessTier };
|
|
82
|
+
}
|
|
83
|
+
// Review requested changes but no unresponded comment.
|
|
84
|
+
// If the latest commit is before the review, the contributor hasn't addressed it yet.
|
|
85
|
+
if (reviewDecision === 'changes_requested' && latestChangesRequestedDate) {
|
|
86
|
+
if (!latestCommitDate || latestCommitDate < latestChangesRequestedDate) {
|
|
87
|
+
return { status: 'needs_addressing', actionReason: 'needs_changes', stalenessTier };
|
|
88
|
+
}
|
|
89
|
+
// Commit is after review — changes have been addressed
|
|
90
|
+
if (ciStatus === 'failing' && hasActionableCIFailure)
|
|
91
|
+
return { status: 'needs_addressing', actionReason: 'failing_ci', stalenessTier };
|
|
92
|
+
// Non-actionable CI failures don't block changes_addressed (#502)
|
|
93
|
+
return { status: 'waiting_on_maintainer', waitReason: 'changes_addressed', stalenessTier };
|
|
94
|
+
}
|
|
95
|
+
if (ciStatus === 'failing') {
|
|
96
|
+
return hasActionableCIFailure
|
|
97
|
+
? { status: 'needs_addressing', actionReason: 'failing_ci', stalenessTier }
|
|
98
|
+
: { status: 'waiting_on_maintainer', waitReason: 'ci_blocked', stalenessTier };
|
|
99
|
+
}
|
|
100
|
+
if (hasMergeConflict) {
|
|
101
|
+
return { status: 'needs_addressing', actionReason: 'merge_conflict', stalenessTier };
|
|
102
|
+
}
|
|
103
|
+
if (hasIncompleteChecklist) {
|
|
104
|
+
return { status: 'needs_addressing', actionReason: 'incomplete_checklist', stalenessTier };
|
|
105
|
+
}
|
|
106
|
+
// Approved and CI passing/unknown = waiting on maintainer to merge
|
|
107
|
+
if (reviewDecision === 'approved' && (ciStatus === 'passing' || ciStatus === 'unknown')) {
|
|
108
|
+
return { status: 'waiting_on_maintainer', waitReason: 'pending_merge', stalenessTier };
|
|
109
|
+
}
|
|
110
|
+
// Default: no actionable issues found. Covers pending CI, no reviews yet, etc.
|
|
111
|
+
return { status: 'waiting_on_maintainer', waitReason: 'pending_review', stalenessTier };
|
|
112
|
+
}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export interface RepoGroup {
|
|
|
41
41
|
}
|
|
42
42
|
/** GitHub's pull request review decision (from the reviewDecision GraphQL field). */
|
|
43
43
|
export type ReviewDecision = 'approved' | 'changes_requested' | 'review_required' | 'unknown';
|
|
44
|
-
/** Input options for `
|
|
44
|
+
/** Input options for `determineStatus()` (see status-determination.ts). */
|
|
45
45
|
export interface DetermineStatusInput {
|
|
46
46
|
ciStatus: CIStatus;
|
|
47
47
|
hasMergeConflict: boolean;
|
|
@@ -61,6 +61,13 @@ export interface DetermineStatusInput {
|
|
|
61
61
|
/** True if at least one failing CI check is classified as 'actionable'. */
|
|
62
62
|
hasActionableCIFailure?: boolean;
|
|
63
63
|
}
|
|
64
|
+
/** Result of `determineStatus()` — the PR's computed status classification. */
|
|
65
|
+
export interface DetermineStatusResult {
|
|
66
|
+
status: FetchedPRStatus;
|
|
67
|
+
actionReason?: ActionReason;
|
|
68
|
+
waitReason?: WaitReason;
|
|
69
|
+
stalenessTier: StalenessTier;
|
|
70
|
+
}
|
|
64
71
|
/**
|
|
65
72
|
* Granular reason why a PR needs addressing (contributor's turn).
|
|
66
73
|
* Active values (produced by determineStatus): needs_response, needs_changes,
|
|
@@ -97,7 +104,7 @@ export interface FetchedPR {
|
|
|
97
104
|
repo: string;
|
|
98
105
|
number: number;
|
|
99
106
|
title: string;
|
|
100
|
-
/** Computed by `
|
|
107
|
+
/** Computed by `determineStatus()` based on the fields below. */
|
|
101
108
|
status: FetchedPRStatus;
|
|
102
109
|
/** Granular reason for needs_addressing status. Undefined when waiting_on_maintainer. */
|
|
103
110
|
actionReason?: ActionReason;
|
|
@@ -414,12 +421,6 @@ export interface LocalRepoCache {
|
|
|
414
421
|
/** ISO 8601 timestamp of when the scan was performed */
|
|
415
422
|
cachedAt: string;
|
|
416
423
|
}
|
|
417
|
-
/** Metadata for a snoozed PR's CI failure. */
|
|
418
|
-
export interface SnoozeInfo {
|
|
419
|
-
reason: string;
|
|
420
|
-
snoozedAt: string;
|
|
421
|
-
expiresAt: string;
|
|
422
|
-
}
|
|
423
424
|
/** Filter for excluding repos below a minimum star count from PR count queries. */
|
|
424
425
|
export interface StarFilter {
|
|
425
426
|
minStars: number;
|
|
@@ -479,12 +480,14 @@ export interface AgentConfig {
|
|
|
479
480
|
aiPolicyBlocklist?: string[];
|
|
480
481
|
/** PR URLs manually shelved by the user. Shelved PRs are excluded from capacity and actionable issues. Auto-unshelved when maintainers engage. */
|
|
481
482
|
shelvedPRUrls?: string[];
|
|
482
|
-
/** Issue
|
|
483
|
+
/** Issue URLs dismissed by the user, mapped to ISO timestamp of when dismissed. Issues with new responses after the dismiss timestamp resurface automatically. */
|
|
483
484
|
dismissedIssues?: Record<string, string>;
|
|
484
|
-
/** PR URLs with snoozed CI failures, mapped to snooze metadata. Snoozed PRs are excluded from actionable CI failure list until expiry. */
|
|
485
|
-
snoozedPRs?: Record<string, SnoozeInfo>;
|
|
486
485
|
/** Manual status overrides for PRs. Maps PR URL to override metadata. Auto-clears when the PR has new activity. */
|
|
487
486
|
statusOverrides?: Record<string, StatusOverride>;
|
|
487
|
+
/** Project categories the user is interested in (e.g., devtools, nonprofit). Used to prioritize search results. */
|
|
488
|
+
projectCategories?: ProjectCategory[];
|
|
489
|
+
/** GitHub organizations the user wants to prioritize in issue search. Org names only (not owner/repo). */
|
|
490
|
+
preferredOrgs?: string[];
|
|
488
491
|
}
|
|
489
492
|
/** Status of a user's comment thread on a GitHub issue. */
|
|
490
493
|
export type IssueConversationStatus = 'new_response' | 'waiting' | 'acknowledged';
|
|
@@ -523,7 +526,10 @@ export type CommentedIssue = CommentedIssueWithResponse | CommentedIssueWithoutR
|
|
|
523
526
|
export declare const DEFAULT_CONFIG: AgentConfig;
|
|
524
527
|
/** Initial state written to `~/.oss-autopilot/state.json` on first run. Uses v2 architecture. */
|
|
525
528
|
export declare const INITIAL_STATE: AgentState;
|
|
526
|
-
export
|
|
529
|
+
export declare const PROJECT_CATEGORIES: readonly ["nonprofit", "devtools", "infrastructure", "web-frameworks", "data-ml", "education"];
|
|
530
|
+
export type ProjectCategory = (typeof PROJECT_CATEGORIES)[number];
|
|
531
|
+
/** Priority tier for issue search results. Ordered: merged_pr > preferred_org > starred > normal. */
|
|
532
|
+
export type SearchPriority = 'merged_pr' | 'preferred_org' | 'starred' | 'normal';
|
|
527
533
|
export interface IssueCandidate {
|
|
528
534
|
issue: TrackedIssue;
|
|
529
535
|
vettingResult: IssueVettingResult;
|
package/dist/core/types.js
CHANGED
|
@@ -28,7 +28,8 @@ export const DEFAULT_CONFIG = {
|
|
|
28
28
|
aiPolicyBlocklist: ['matplotlib/matplotlib'],
|
|
29
29
|
shelvedPRUrls: [],
|
|
30
30
|
dismissedIssues: {},
|
|
31
|
-
|
|
31
|
+
projectCategories: [],
|
|
32
|
+
preferredOrgs: [],
|
|
32
33
|
};
|
|
33
34
|
/** Initial state written to `~/.oss-autopilot/state.json` on first run. Uses v2 architecture. */
|
|
34
35
|
export const INITIAL_STATE = {
|
|
@@ -39,3 +40,12 @@ export const INITIAL_STATE = {
|
|
|
39
40
|
events: [],
|
|
40
41
|
lastRunAt: new Date().toISOString(),
|
|
41
42
|
};
|
|
43
|
+
// -- Project category types --
|
|
44
|
+
export const PROJECT_CATEGORIES = [
|
|
45
|
+
'nonprofit',
|
|
46
|
+
'devtools',
|
|
47
|
+
'infrastructure',
|
|
48
|
+
'web-frameworks',
|
|
49
|
+
'data-ml',
|
|
50
|
+
'education',
|
|
51
|
+
];
|
package/package.json
CHANGED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Override command
|
|
3
|
-
* Manually override a PR's status (needs_addressing ↔ waiting_on_maintainer).
|
|
4
|
-
* Overrides auto-clear when the PR has new activity.
|
|
5
|
-
*/
|
|
6
|
-
import type { FetchedPRStatus } from '../core/types.js';
|
|
7
|
-
export interface OverrideOutput {
|
|
8
|
-
url: string;
|
|
9
|
-
status: FetchedPRStatus;
|
|
10
|
-
}
|
|
11
|
-
export interface ClearOverrideOutput {
|
|
12
|
-
url: string;
|
|
13
|
-
cleared: boolean;
|
|
14
|
-
}
|
|
15
|
-
export declare function runOverride(options: {
|
|
16
|
-
prUrl: string;
|
|
17
|
-
status: string;
|
|
18
|
-
}): Promise<OverrideOutput>;
|
|
19
|
-
export declare function runClearOverride(options: {
|
|
20
|
-
prUrl: string;
|
|
21
|
-
}): Promise<ClearOverrideOutput>;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Override command
|
|
3
|
-
* Manually override a PR's status (needs_addressing ↔ waiting_on_maintainer).
|
|
4
|
-
* Overrides auto-clear when the PR has new activity.
|
|
5
|
-
*/
|
|
6
|
-
import { getStateManager } from '../core/index.js';
|
|
7
|
-
import { PR_URL_PATTERN, validateGitHubUrl, validateUrl } from './validation.js';
|
|
8
|
-
const VALID_STATUSES = ['needs_addressing', 'waiting_on_maintainer'];
|
|
9
|
-
export async function runOverride(options) {
|
|
10
|
-
validateUrl(options.prUrl);
|
|
11
|
-
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, 'PR');
|
|
12
|
-
if (!VALID_STATUSES.includes(options.status)) {
|
|
13
|
-
throw new Error(`Invalid status "${options.status}". Must be one of: ${VALID_STATUSES.join(', ')}`);
|
|
14
|
-
}
|
|
15
|
-
const status = options.status;
|
|
16
|
-
const stateManager = getStateManager();
|
|
17
|
-
// Use current time as lastActivityAt — the CLI doesn't have cached PR data.
|
|
18
|
-
// This means the override will auto-clear on the next daily run if the PR's
|
|
19
|
-
// updatedAt is after this timestamp (which is the desired behavior: the override
|
|
20
|
-
// will persist until new activity occurs on the PR).
|
|
21
|
-
const lastActivityAt = new Date().toISOString();
|
|
22
|
-
stateManager.setStatusOverride(options.prUrl, status, lastActivityAt);
|
|
23
|
-
stateManager.save();
|
|
24
|
-
return { url: options.prUrl, status };
|
|
25
|
-
}
|
|
26
|
-
export async function runClearOverride(options) {
|
|
27
|
-
validateUrl(options.prUrl);
|
|
28
|
-
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, 'PR');
|
|
29
|
-
const stateManager = getStateManager();
|
|
30
|
-
const cleared = stateManager.clearStatusOverride(options.prUrl);
|
|
31
|
-
if (cleared) {
|
|
32
|
-
stateManager.save();
|
|
33
|
-
}
|
|
34
|
-
return { url: options.prUrl, cleared };
|
|
35
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Snooze/Unsnooze commands
|
|
3
|
-
* Manages snoozing CI failure notifications for PRs with known upstream/infrastructure issues.
|
|
4
|
-
* Snoozed PRs are excluded from the actionable CI failure list until the snooze expires.
|
|
5
|
-
*/
|
|
6
|
-
export interface SnoozeOutput {
|
|
7
|
-
snoozed: boolean;
|
|
8
|
-
url: string;
|
|
9
|
-
days: number;
|
|
10
|
-
reason: string;
|
|
11
|
-
expiresAt: string | undefined;
|
|
12
|
-
}
|
|
13
|
-
export interface UnsnoozeOutput {
|
|
14
|
-
unsnoozed: boolean;
|
|
15
|
-
url: string;
|
|
16
|
-
}
|
|
17
|
-
export declare function runSnooze(options: {
|
|
18
|
-
prUrl: string;
|
|
19
|
-
reason: string;
|
|
20
|
-
days?: number;
|
|
21
|
-
}): Promise<SnoozeOutput>;
|
|
22
|
-
export declare function runUnsnooze(options: {
|
|
23
|
-
prUrl: string;
|
|
24
|
-
}): Promise<UnsnoozeOutput>;
|
package/dist/commands/snooze.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Snooze/Unsnooze commands
|
|
3
|
-
* Manages snoozing CI failure notifications for PRs with known upstream/infrastructure issues.
|
|
4
|
-
* Snoozed PRs are excluded from the actionable CI failure list until the snooze expires.
|
|
5
|
-
*/
|
|
6
|
-
import { getStateManager } from '../core/index.js';
|
|
7
|
-
import { PR_URL_PATTERN, validateGitHubUrl, validateUrl, validateMessage } from './validation.js';
|
|
8
|
-
const DEFAULT_SNOOZE_DAYS = 7;
|
|
9
|
-
export async function runSnooze(options) {
|
|
10
|
-
validateUrl(options.prUrl);
|
|
11
|
-
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, 'PR');
|
|
12
|
-
validateMessage(options.reason);
|
|
13
|
-
const days = options.days ?? DEFAULT_SNOOZE_DAYS;
|
|
14
|
-
if (!Number.isFinite(days) || days <= 0) {
|
|
15
|
-
throw new Error('Snooze duration must be a positive number of days.');
|
|
16
|
-
}
|
|
17
|
-
const stateManager = getStateManager();
|
|
18
|
-
const added = stateManager.snoozePR(options.prUrl, options.reason, days);
|
|
19
|
-
if (added) {
|
|
20
|
-
stateManager.save();
|
|
21
|
-
}
|
|
22
|
-
const snoozeInfo = stateManager.getSnoozeInfo(options.prUrl);
|
|
23
|
-
return {
|
|
24
|
-
snoozed: added,
|
|
25
|
-
url: options.prUrl,
|
|
26
|
-
days,
|
|
27
|
-
reason: options.reason,
|
|
28
|
-
expiresAt: snoozeInfo?.expiresAt,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
export async function runUnsnooze(options) {
|
|
32
|
-
validateUrl(options.prUrl);
|
|
33
|
-
validateGitHubUrl(options.prUrl, PR_URL_PATTERN, 'PR');
|
|
34
|
-
const stateManager = getStateManager();
|
|
35
|
-
const removed = stateManager.unsnoozePR(options.prUrl);
|
|
36
|
-
if (removed) {
|
|
37
|
-
stateManager.save();
|
|
38
|
-
}
|
|
39
|
-
return { unsnoozed: removed, url: options.prUrl };
|
|
40
|
-
}
|