@hardlydifficult/pr-analyzer 1.0.73 → 1.0.75
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/README.md +107 -82
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @hardlydifficult/pr-analyzer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A TypeScript package for analyzing GitHub pull requests and classifying them into actionable buckets.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -11,60 +11,93 @@ npm install @hardlydifficult/pr-analyzer
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import { scanSinglePR } from "@hardlydifficult/pr-analyzer";
|
|
15
14
|
import { GitHubClient } from "@hardlydifficult/github";
|
|
15
|
+
import { scanSinglePR } from "@hardlydifficult/pr-analyzer";
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
// Create a GitHub client
|
|
18
|
+
const client = new GitHubClient({ token: "ghp_..." });
|
|
19
|
+
|
|
20
|
+
// Scan a PR
|
|
18
21
|
const pr = await scanSinglePR(
|
|
19
22
|
client,
|
|
20
|
-
"@cursor",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
23
|
+
"@cursor",
|
|
24
|
+
"owner",
|
|
25
|
+
"repo",
|
|
26
|
+
123
|
|
24
27
|
);
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
console.log(pr.
|
|
29
|
+
// Check status and available actions
|
|
30
|
+
console.log(pr.status); // "ready_to_merge"
|
|
31
|
+
console.log(pr.ciSummary); // "CI passed: 3 checks"
|
|
32
|
+
|
|
33
|
+
import { getAvailableActions } from "@hardlydifficult/pr-analyzer";
|
|
34
|
+
const actions = getAvailableActions(pr);
|
|
35
|
+
// actions = [{ type: "merge", label: "Merge", description: "Squash and merge this PR" }]
|
|
28
36
|
```
|
|
29
37
|
|
|
30
38
|
## Core Features
|
|
31
39
|
|
|
32
40
|
### Scanning and Analyzing PRs
|
|
33
41
|
|
|
34
|
-
|
|
42
|
+
#### `analyzePR`
|
|
43
|
+
|
|
44
|
+
Analyzes a single PR and returns its full status.
|
|
35
45
|
|
|
36
46
|
```typescript
|
|
37
|
-
import {
|
|
38
|
-
|
|
47
|
+
import { analyzePR, analyzeAll } from "@hardlydifficult/pr-analyzer";
|
|
48
|
+
|
|
49
|
+
const pr = await client.repo("owner", "repo").pr(123).get();
|
|
50
|
+
const scanned = await analyzePR(client, "owner", "repo", pr, "@bot");
|
|
51
|
+
|
|
52
|
+
scanned.status; // "ready_to_merge"
|
|
53
|
+
scanned.hasConflicts; // false
|
|
54
|
+
scanned.daysSinceUpdate; // 2
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### `analyzeAll`
|
|
39
58
|
|
|
40
|
-
|
|
41
|
-
|
|
59
|
+
Analyzes multiple PRs in parallel, logging failures.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import type { DiscoveredPR } from "@hardlydifficult/pr-analyzer";
|
|
42
63
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{ pr, repoOwner: "owner", repoName: "repo" },
|
|
64
|
+
const prs: readonly DiscoveredPR[] = [
|
|
65
|
+
{ pr: pr1, repoOwner: "owner", repoName: "repo" },
|
|
66
|
+
{ pr: pr2, repoOwner: "owner", repoName: "repo" },
|
|
46
67
|
];
|
|
47
|
-
|
|
68
|
+
|
|
69
|
+
const results = await analyzeAll(prs, client, "@bot");
|
|
70
|
+
// results = [scannedPR1, scannedPR2]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### `scanSinglePR`
|
|
74
|
+
|
|
75
|
+
Scans a single PR directly by repo and number (real-time event handling).
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { scanSinglePR } from "@hardlydifficult/pr-analyzer";
|
|
79
|
+
|
|
80
|
+
const pr = await scanSinglePR(client, "@cursor", "owner", "repo", 456);
|
|
48
81
|
```
|
|
49
82
|
|
|
50
83
|
### PR Status Determination
|
|
51
84
|
|
|
52
|
-
|
|
85
|
+
PRs are classified into core statuses based on priority:
|
|
53
86
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
87
|
+
1. `draft` — PR is a draft
|
|
88
|
+
2. `ci_running` — CI checks are in progress
|
|
89
|
+
3. `ci_failed` — At least one CI check failed
|
|
90
|
+
4. `has_conflicts` — PR has merge conflicts
|
|
91
|
+
5. `waiting_on_bot` — A bot was mentioned and hasn’t responded
|
|
92
|
+
6. `changes_requested` — A reviewer requested changes
|
|
93
|
+
7. `ready_to_merge` — CI passed and no conflicts
|
|
94
|
+
8. `approved` — PR approved (but not all CI passed)
|
|
95
|
+
9. `needs_review` — No reviews or approvals yet
|
|
63
96
|
|
|
64
|
-
|
|
97
|
+
Extensions via `AnalyzerHooks.resolveStatus` allow custom statuses.
|
|
65
98
|
|
|
66
99
|
```typescript
|
|
67
|
-
const hooks
|
|
100
|
+
const hooks = {
|
|
68
101
|
resolveStatus: (coreStatus, details) => {
|
|
69
102
|
if (coreStatus === "ci_failed" && details.checks.some(c => c.name === "CI")) {
|
|
70
103
|
return "ai_processing";
|
|
@@ -79,87 +112,79 @@ console.log(pr.status); // e.g. "ai_processing"
|
|
|
79
112
|
|
|
80
113
|
### Classification
|
|
81
114
|
|
|
82
|
-
|
|
115
|
+
Classifies PRs into action buckets: `readyForHuman`, `needsBotBump`, `inProgress`, `blocked`.
|
|
83
116
|
|
|
84
117
|
```typescript
|
|
85
118
|
import { classifyPRs } from "@hardlydifficult/pr-analyzer";
|
|
86
119
|
|
|
87
|
-
const result = classifyPRs(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
120
|
+
const result = classifyPRs([scannedPR1, scannedPR2]);
|
|
121
|
+
result.readyForHuman; // PRs needing human review/merge
|
|
122
|
+
result.needsBotBump; // PRs waiting on bot response
|
|
123
|
+
result.inProgress; // PRs with CI running
|
|
124
|
+
result.blocked; // PRs stalled (draft, failed CI, conflicts)
|
|
92
125
|
```
|
|
93
126
|
|
|
94
|
-
|
|
127
|
+
#### ClassificationConfig
|
|
128
|
+
|
|
129
|
+
Extend buckets with custom statuses:
|
|
95
130
|
|
|
96
131
|
```typescript
|
|
97
|
-
const config
|
|
132
|
+
const config = {
|
|
133
|
+
readyForHuman: ["custom_status"],
|
|
98
134
|
inProgress: ["ai_processing"],
|
|
99
|
-
blocked: ["
|
|
135
|
+
blocked: ["custom_blocked"],
|
|
136
|
+
needsBotBump: ["custom_waiting"],
|
|
100
137
|
};
|
|
101
|
-
|
|
138
|
+
|
|
139
|
+
const result = classifyPRs(prs, config);
|
|
102
140
|
```
|
|
103
141
|
|
|
104
142
|
### Available Actions
|
|
105
143
|
|
|
106
|
-
|
|
144
|
+
Determines which actions are available for a PR based on its status.
|
|
145
|
+
|
|
146
|
+
#### Core Actions
|
|
147
|
+
|
|
148
|
+
- `merge` — Available for `ready_to_merge`, `approved`
|
|
149
|
+
- `mark_ready` — Available for `draft` when CI passed and no conflicts
|
|
150
|
+
- `enable_auto_merge` — Available for `ci_running`, `needs_review` when PR is not draft, not merged, no conflicts
|
|
107
151
|
|
|
108
152
|
```typescript
|
|
109
153
|
import { getAvailableActions, PR_ACTIONS } from "@hardlydifficult/pr-analyzer";
|
|
110
154
|
|
|
111
155
|
const actions = getAvailableActions(pr);
|
|
112
|
-
|
|
156
|
+
// [{ type: "merge", label: "Merge", description: "Squash and merge this PR" }]
|
|
113
157
|
```
|
|
114
158
|
|
|
115
|
-
|
|
159
|
+
#### Custom Actions
|
|
116
160
|
|
|
117
|
-
|
|
118
|
-
|------------------|---------------|------------------------------------------|
|
|
119
|
-
| `"merge"` | `"Merge"` | Squash and merge this PR |
|
|
120
|
-
| `"mark_ready"` | `"Mark Ready"`| Mark this draft PR as ready for review |
|
|
121
|
-
| `"enable_auto_merge"` | `"Enable Auto-Merge"` | Enable GitHub auto-merge when checks pass |
|
|
122
|
-
|
|
123
|
-
Add custom actions with `ActionDefinition`.
|
|
161
|
+
Consumers can define extra actions with custom conditions.
|
|
124
162
|
|
|
125
163
|
```typescript
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
164
|
+
import { ActionDefinition } from "@hardlydifficult/pr-analyzer";
|
|
165
|
+
|
|
166
|
+
const fixCiAction: ActionDefinition = {
|
|
167
|
+
type: "fix_ci",
|
|
168
|
+
label: "Fix CI",
|
|
169
|
+
description: "Post @cursor fix CI comment",
|
|
170
|
+
when: (pr, ctx) => pr.status === "ci_failed" && ctx["isWorkPR"] === true,
|
|
171
|
+
};
|
|
134
172
|
|
|
135
|
-
const actions = getAvailableActions(pr,
|
|
173
|
+
const actions = getAvailableActions(pr, [fixCiAction], { isWorkPR: true });
|
|
136
174
|
```
|
|
137
175
|
|
|
138
176
|
## Types and Interfaces
|
|
139
177
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
} from "@hardlydifficult/pr-analyzer";
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
| Interface/Type | Purpose |
|
|
154
|
-
|---------------------|---------|
|
|
155
|
-
| `ScannedPR` | Full PR data after analysis |
|
|
156
|
-
| `CIStatus` | CI check summary (running, failed, passed) |
|
|
157
|
-
| `ScanResult` | PRs grouped into buckets |
|
|
158
|
-
| `AnalyzerHooks` | Extend status determination |
|
|
159
|
-
| `ClassificationConfig` | Extend classification buckets |
|
|
160
|
-
| `ActionDefinition` | Define custom PR actions |
|
|
161
|
-
| `CorePRStatus` | Built-in status types |
|
|
162
|
-
| `Logger` | Interface for logging info/errors |
|
|
178
|
+
| Type | Description |
|
|
179
|
+
|------|-------------|
|
|
180
|
+
| `ScannedPR` | A fully analyzed PR with status, CI summary, and metadata |
|
|
181
|
+
| `ScanResult` | Classified PR buckets: `all`, `readyForHuman`, `needsBotBump`, `inProgress`, `blocked` |
|
|
182
|
+
| `AnalyzerHooks` | Custom logic hook: `resolveStatus(coreStatus, details)` |
|
|
183
|
+
| `ClassificationConfig` | Extend status buckets with custom statuses |
|
|
184
|
+
| `ActionDefinition` | Define custom actions with conditional logic |
|
|
185
|
+
| `CorePRStatus` | Core PR status enum (`draft`, `ci_running`, etc.) |
|
|
186
|
+
| `CIStatus` | CI analysis: `isRunning`, `hasFailed`, `allPassed`, `summary` |
|
|
187
|
+
| `Logger` | Logging interface (`info`, `error`) |
|
|
163
188
|
|
|
164
189
|
## Appendix
|
|
165
190
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hardlydifficult/pr-analyzer",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.75",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"clean": "rm -rf dist"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@hardlydifficult/github": "1.0.
|
|
18
|
+
"@hardlydifficult/github": "1.0.34"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/node": "25.3.0",
|