gitpadi 2.0.2 ā 2.0.3
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 +57 -225
- package/dist/cli.js +1 -2
- package/dist/commands/contribute.js +25 -2
- package/dist/commands/issues.js +40 -21
- package/package.json +2 -2
- package/src/cli.ts +1 -2
- package/src/commands/contribute.ts +25 -2
- package/src/commands/issues.ts +47 -22
package/README.md
CHANGED
|
@@ -1,270 +1,102 @@
|
|
|
1
|
-
|
|
2
|
-
<h1 align="center">š¤ GitPadi</h1>
|
|
3
|
-
<p align="center"><strong>Your AI-powered GitHub management agent.</strong></p>
|
|
4
|
-
<p align="center">
|
|
5
|
-
Auto-score contributors, review PRs, manage issues & repos ā from your terminal or as a GitHub Action.
|
|
6
|
-
</p>
|
|
7
|
-
<p align="center">
|
|
8
|
-
<a href="#-use-the-cli">CLI</a> ā¢
|
|
9
|
-
<a href="#-use-as-github-action">GitHub Action</a> ā¢
|
|
10
|
-
<a href="#-scoring-algorithm">Scoring Algorithm</a>
|
|
11
|
-
</p>
|
|
12
|
-
</p>
|
|
1
|
+
# š¤ GitPadi
|
|
13
2
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
## ā” Two Ways to Use GitPadi
|
|
3
|
+
GitPadi is a GitHub management CLI and Action built to automate the repetitive parts of maintaining open-source projects.
|
|
17
4
|
|
|
18
|
-
|
|
19
|
-
|--------|----------|-------|
|
|
20
|
-
| **CLI** (`npx gitpadi`) | Hands-on management from your terminal | One command |
|
|
21
|
-
| **GitHub Action** | Automated scoring & reviews on every event | Copy one workflow file |
|
|
5
|
+
Whether you're a maintainer managing hundreds of issues or a contributor making your first PR, GitPadi turns multi-step GitHub workflows into single commands.
|
|
22
6
|
|
|
23
7
|
---
|
|
24
8
|
|
|
25
|
-
##
|
|
9
|
+
## š For Contributors
|
|
26
10
|
|
|
27
|
-
|
|
11
|
+
Stop worrying about forking, upstream remotes, and branch naming. GitPadi handles the plumbing so you can focus on the code.
|
|
28
12
|
|
|
13
|
+
### Start a new contribution
|
|
29
14
|
```bash
|
|
30
|
-
|
|
31
|
-
export GITHUB_OWNER=your-org
|
|
32
|
-
export GITHUB_REPO=your-repo
|
|
33
|
-
|
|
34
|
-
npx gitpadi
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
That launches the **interactive terminal**:
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
41
|
-
ā š¤ GitPadi v2.0 ā
|
|
42
|
-
ā Your GitHub Management Companion ā
|
|
43
|
-
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
44
|
-
|
|
45
|
-
? What would you like to do?
|
|
46
|
-
š Manage Issues
|
|
47
|
-
š Manage Pull Requests
|
|
48
|
-
š¦ Manage Repositories
|
|
49
|
-
š Score Contributors
|
|
50
|
-
š Create Release
|
|
51
|
-
āļø Switch Repo
|
|
52
|
-
š Exit
|
|
15
|
+
npx gitpadi start <issue-url>
|
|
53
16
|
```
|
|
17
|
+
This single command:
|
|
18
|
+
1. **Forks** the repository to your account.
|
|
19
|
+
2. **Clones** it to your machine.
|
|
20
|
+
3. Sets up the **upstream remote**.
|
|
21
|
+
4. Creates a **fresh feature branch** linked to the issue.
|
|
54
22
|
|
|
55
|
-
|
|
56
|
-
|
|
23
|
+
### Submit your work
|
|
57
24
|
```bash
|
|
58
|
-
npx gitpadi
|
|
59
|
-
npx gitpadi prs merge 5 --method squash
|
|
60
|
-
npx gitpadi contributors score octocat
|
|
61
|
-
npx gitpadi repo create my-app --org MyOrg
|
|
25
|
+
npx gitpadi submit
|
|
62
26
|
```
|
|
27
|
+
When you're done, this command:
|
|
28
|
+
1. **Stages** all your changes.
|
|
29
|
+
2. Creates a **commit** with a clean message.
|
|
30
|
+
3. **Pushes** to your fork.
|
|
31
|
+
4. Opens a **Pull Request** on the original repo, auto-linked to the issue.
|
|
32
|
+
|
|
33
|
+
---
|
|
63
34
|
|
|
64
|
-
|
|
35
|
+
## š ļø For Maintainers
|
|
65
36
|
|
|
66
|
-
|
|
67
|
-
<summary><b>š Issues</b></summary>
|
|
37
|
+
Manage your community and project health directly from your terminal.
|
|
68
38
|
|
|
39
|
+
### š Bulk Issue Creation
|
|
40
|
+
Create dozens of issues in seconds from a simple Markdown or JSON file.
|
|
69
41
|
```bash
|
|
70
|
-
npx gitpadi issues
|
|
71
|
-
npx gitpadi issues create -t "Bug fix" -l bug # Create issue
|
|
72
|
-
npx gitpadi issues bulk -f issues.json # Bulk create from JSON
|
|
73
|
-
npx gitpadi issues close 42 # Close issue
|
|
74
|
-
npx gitpadi issues reopen 42 # Reopen issue
|
|
75
|
-
npx gitpadi issues delete 42 # Close & lock
|
|
76
|
-
npx gitpadi issues assign 42 alice bob # Assign users
|
|
77
|
-
npx gitpadi issues assign-best 42 # š Auto-assign top scorer
|
|
78
|
-
npx gitpadi issues search "login bug" # Search issues
|
|
79
|
-
npx gitpadi issues label 42 bug priority-high # Add labels
|
|
42
|
+
npx gitpadi issues bulk -f roadmap.md
|
|
80
43
|
```
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<details>
|
|
84
|
-
<summary><b>š Pull Requests</b></summary>
|
|
44
|
+
*(Supports `## Heading` titles and `**Labels:**` tags).*
|
|
85
45
|
|
|
46
|
+
### š Smart Contributor Scoring
|
|
47
|
+
Auto-score applicants based on their GitHub history, account age, and relevance to the issue.
|
|
86
48
|
```bash
|
|
87
|
-
npx gitpadi
|
|
88
|
-
npx gitpadi prs merge 5 --method squash # Merge PR (squash/merge/rebase)
|
|
89
|
-
npx gitpadi prs close 5 # Close PR
|
|
90
|
-
npx gitpadi prs review 5 # Auto-review (size, tests, security)
|
|
91
|
-
npx gitpadi prs approve 5 # Approve PR
|
|
92
|
-
npx gitpadi prs diff 5 # Show changed files
|
|
49
|
+
npx gitpadi contributors score octocat
|
|
93
50
|
```
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
<details>
|
|
97
|
-
<summary><b>š¦ Repositories</b></summary>
|
|
98
|
-
|
|
51
|
+
Or let GitPadi find the best applicant for an issue:
|
|
99
52
|
```bash
|
|
100
|
-
npx gitpadi
|
|
101
|
-
npx gitpadi repo create my-app --org MyOrg # Create repo
|
|
102
|
-
npx gitpadi repo delete my-app --org MyOrg # Delete repo
|
|
103
|
-
npx gitpadi repo clone my-app # Clone repo
|
|
104
|
-
npx gitpadi repo info my-app # Show repo details
|
|
105
|
-
npx gitpadi repo topics my-app rust blockchain # Set topics
|
|
53
|
+
npx gitpadi issues assign-best 42
|
|
106
54
|
```
|
|
107
|
-
</details>
|
|
108
55
|
|
|
109
|
-
|
|
110
|
-
|
|
56
|
+
### š Advanced PR Management
|
|
57
|
+
- **Review PRs**: Get an automated review (size analysis, security checks, test coverage).
|
|
58
|
+
- **Fast Merge**: Squash and merge PRs directly from the CLI.
|
|
59
|
+
- **CI Status**: View GitHub Action logs for a PR without opening a browser.
|
|
111
60
|
|
|
112
|
-
|
|
113
|
-
npx gitpadi contributors score octocat # Score a user (0-100)
|
|
114
|
-
npx gitpadi contributors rank 42 # Rank all applicants for issue #42
|
|
115
|
-
npx gitpadi contributors list # List repo contributors
|
|
116
|
-
```
|
|
117
|
-
</details>
|
|
61
|
+
---
|
|
118
62
|
|
|
119
|
-
|
|
120
|
-
|
|
63
|
+
## ā” Quick Start
|
|
64
|
+
|
|
65
|
+
No installation required. Just set your token and go:
|
|
121
66
|
|
|
122
67
|
```bash
|
|
123
|
-
|
|
124
|
-
npx gitpadi
|
|
125
|
-
npx gitpadi release list # List releases
|
|
68
|
+
export GITHUB_TOKEN=ghp_your_token
|
|
69
|
+
npx gitpadi
|
|
126
70
|
```
|
|
127
|
-
</details>
|
|
128
|
-
|
|
129
|
-
### Or install globally:
|
|
130
71
|
|
|
72
|
+
### Direct Commands
|
|
131
73
|
```bash
|
|
132
|
-
|
|
133
|
-
gitpadi
|
|
74
|
+
npx gitpadi issues list --limit 100
|
|
75
|
+
npx gitpadi prs review 5
|
|
76
|
+
npx gitpadi release create v1.0.0
|
|
134
77
|
```
|
|
135
78
|
|
|
136
79
|
---
|
|
137
80
|
|
|
138
|
-
## š§ Use as GitHub Action
|
|
139
|
-
|
|
140
|
-
Just add a workflow file ā no setup, no tokens to manage.
|
|
81
|
+
## š§ Use as a GitHub Action
|
|
141
82
|
|
|
142
|
-
|
|
83
|
+
GitPadi can run on every event in your repo. Just add a workflow file.
|
|
143
84
|
|
|
85
|
+
**Auto-Score Applicants:**
|
|
144
86
|
```yaml
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
issue_comment:
|
|
149
|
-
types: [created]
|
|
150
|
-
permissions:
|
|
151
|
-
contents: read
|
|
152
|
-
issues: write
|
|
153
|
-
pull-requests: read
|
|
154
|
-
jobs:
|
|
155
|
-
score:
|
|
156
|
-
if: "!github.event.issue.pull_request"
|
|
157
|
-
runs-on: ubuntu-latest
|
|
158
|
-
steps:
|
|
159
|
-
- uses: actions/checkout@v4
|
|
160
|
-
- uses: Netwalls/contributor-agent@v2
|
|
161
|
-
with:
|
|
162
|
-
action: score-applicant
|
|
163
|
-
notify-user: 'your-username' # ā you get @mentioned
|
|
87
|
+
- uses: Netwalls/contributor-agent@v2
|
|
88
|
+
with:
|
|
89
|
+
action: score-applicant
|
|
164
90
|
```
|
|
165
91
|
|
|
166
|
-
|
|
167
|
-
|
|
92
|
+
**Auto-Review PRs:**
|
|
168
93
|
```yaml
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
pull_request:
|
|
173
|
-
types: [opened, synchronize, reopened]
|
|
174
|
-
permissions:
|
|
175
|
-
contents: read
|
|
176
|
-
pull-requests: write
|
|
177
|
-
jobs:
|
|
178
|
-
review:
|
|
179
|
-
runs-on: ubuntu-latest
|
|
180
|
-
steps:
|
|
181
|
-
- uses: actions/checkout@v4
|
|
182
|
-
- uses: Netwalls/contributor-agent@v2
|
|
183
|
-
with:
|
|
184
|
-
action: review-pr
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### š Bulk Create Issues
|
|
188
|
-
|
|
189
|
-
```yaml
|
|
190
|
-
# .github/workflows/create-issues.yml
|
|
191
|
-
name: Create Issues
|
|
192
|
-
on:
|
|
193
|
-
workflow_dispatch:
|
|
194
|
-
inputs:
|
|
195
|
-
issues-file:
|
|
196
|
-
description: 'Path to issues JSON'
|
|
197
|
-
default: 'issues.json'
|
|
198
|
-
dry-run:
|
|
199
|
-
description: 'Preview only'
|
|
200
|
-
default: 'true'
|
|
201
|
-
permissions:
|
|
202
|
-
issues: write
|
|
203
|
-
jobs:
|
|
204
|
-
create:
|
|
205
|
-
runs-on: ubuntu-latest
|
|
206
|
-
steps:
|
|
207
|
-
- uses: actions/checkout@v4
|
|
208
|
-
- uses: Netwalls/contributor-agent@v2
|
|
209
|
-
with:
|
|
210
|
-
action: create-issues
|
|
211
|
-
issues-file: ${{ github.event.inputs.issues-file }}
|
|
212
|
-
dry-run: ${{ github.event.inputs.dry-run }}
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
---
|
|
216
|
-
|
|
217
|
-
## š Scoring Algorithm
|
|
218
|
-
|
|
219
|
-
GitPadi scores contributors on **6 dimensions** (100 points total):
|
|
220
|
-
|
|
221
|
-
| Category | Max | What It Measures |
|
|
222
|
-
|----------|-----|-----------------|
|
|
223
|
-
| š§ **Repo Experience** | **30** | Merged PRs, open PRs, issues in YOUR specific repo |
|
|
224
|
-
| šļø Account Maturity | 15 | Account age |
|
|
225
|
-
| š GitHub Presence | 15 | Public repos, followers, profile README |
|
|
226
|
-
| ā” Activity Level | 15 | Recent public GitHub activity |
|
|
227
|
-
| š Application Quality | 15 | Comment depth ā mentions approach, experience, timeline |
|
|
228
|
-
| š» Language Relevance | 10 | User's languages match issue label languages |
|
|
229
|
-
|
|
230
|
-
### Tiers
|
|
231
|
-
|
|
232
|
-
| Tier | Score | Recommendation |
|
|
233
|
-
|------|-------|---------------|
|
|
234
|
-
| š **S** | 75+ | Strong ā assign immediately |
|
|
235
|
-
| š¢ **A** | 55-74 | Good ā assign with confidence |
|
|
236
|
-
| š” **B** | 40-54 | Decent ā review profile first |
|
|
237
|
-
| š **C** | 25-39 | Below average ā wait for better |
|
|
238
|
-
| š“ **D** | 0-24 | Low ā manual review needed |
|
|
239
|
-
|
|
240
|
-
### Detected Application Phrases
|
|
241
|
-
|
|
242
|
-
> *"I'd like to work on this"* Ā· *"Can I take this?"* Ā· *"Assign me"* Ā· *"I'm interested"* Ā· *"I want to contribute"* Ā· *"Let me handle this"* Ā· *"Picking this up"* Ā· *"Claiming this"*
|
|
243
|
-
|
|
244
|
-
---
|
|
245
|
-
|
|
246
|
-
## āļø Configuration
|
|
247
|
-
|
|
248
|
-
### Environment Variables
|
|
249
|
-
|
|
250
|
-
| Variable | Required | Description |
|
|
251
|
-
|----------|----------|-------------|
|
|
252
|
-
| `GITHUB_TOKEN` | ā
| GitHub PAT with `repo` scope |
|
|
253
|
-
| `GITHUB_OWNER` | For CLI | Org or username |
|
|
254
|
-
| `GITHUB_REPO` | For CLI | Repository name |
|
|
255
|
-
|
|
256
|
-
### CLI Flags
|
|
257
|
-
|
|
258
|
-
```bash
|
|
259
|
-
npx gitpadi --owner MyOrg --repo my-project --token ghp_xxx issues list
|
|
94
|
+
- uses: Netwalls/contributor-agent@v2
|
|
95
|
+
with:
|
|
96
|
+
action: review-pr
|
|
260
97
|
```
|
|
261
98
|
|
|
262
99
|
---
|
|
263
100
|
|
|
264
101
|
## š License
|
|
265
|
-
|
|
266
|
-
MIT ā Built by [Netwalls](https://github.com/Netwalls)
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
i noticed that a users have more issues but it is just bring out the top 10 issues that are open how do we bring out more issues
|
|
102
|
+
MIT ā Built by [Netwalls](https://github.com/Netwalls)
|
package/dist/cli.js
CHANGED
|
@@ -363,11 +363,10 @@ async function issueMenu() {
|
|
|
363
363
|
else if (action === 'bulk') {
|
|
364
364
|
const a = await ask([
|
|
365
365
|
{ type: 'input', name: 'file', message: yellow('š Path to issues file (JSON or MD)') + dim(' (q=back):') },
|
|
366
|
-
{ type: 'confirm', name: 'dryRun', message: 'Dry run first?', default: true },
|
|
367
366
|
{ type: 'number', name: 'start', message: dim('Start index:'), default: 1 },
|
|
368
367
|
{ type: 'number', name: 'end', message: dim('End index:'), default: 999 },
|
|
369
368
|
]);
|
|
370
|
-
await issues.createIssuesFromFile(a.file, {
|
|
369
|
+
await issues.createIssuesFromFile(a.file, { start: a.start, end: a.end });
|
|
371
370
|
}
|
|
372
371
|
else if (action === 'assign-best') {
|
|
373
372
|
const spinner = createSpinner(dim('Finding issues with applicants...')).start();
|
|
@@ -81,8 +81,23 @@ export async function forkAndClone(target) {
|
|
|
81
81
|
execSync(`git remote add upstream https://github.com/${owner}/${repo}.git`, { stdio: 'pipe' });
|
|
82
82
|
}
|
|
83
83
|
catch { /* exists */ }
|
|
84
|
+
// 1. Detect default branch
|
|
85
|
+
let defaultBranch = 'main';
|
|
86
|
+
try {
|
|
87
|
+
execSync('git rev-parse upstream/main', { stdio: 'pipe' });
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
defaultBranch = 'master';
|
|
91
|
+
}
|
|
92
|
+
// 2. Checkout default branch before syncing
|
|
93
|
+
try {
|
|
94
|
+
execSync(`git checkout ${defaultBranch}`, { stdio: 'pipe' });
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// If it fails, maybe it's not even a valid default branch locally, but we'll try to sync anyway
|
|
98
|
+
}
|
|
84
99
|
await syncBranch();
|
|
85
|
-
// Create or switch to issue branch
|
|
100
|
+
// 3. Create or switch to issue branch
|
|
86
101
|
const branchName = issue ? `fix/issue-${issue}` : null;
|
|
87
102
|
if (branchName) {
|
|
88
103
|
try {
|
|
@@ -246,13 +261,21 @@ export async function submitPR(opts) {
|
|
|
246
261
|
execSync(`git push origin ${branch}`, { stdio: 'pipe' });
|
|
247
262
|
spinner.text = 'Creating Pull Request...';
|
|
248
263
|
const body = opts.body || (linkedIssue ? `Fixes #${linkedIssue}` : 'Automated PR via GitPadi');
|
|
264
|
+
// Detect base branch
|
|
265
|
+
let baseBranch = 'main';
|
|
266
|
+
try {
|
|
267
|
+
execSync('git rev-parse origin/main', { stdio: 'pipe' });
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
baseBranch = 'master';
|
|
271
|
+
}
|
|
249
272
|
const { data: pr } = await getOctokit().pulls.create({
|
|
250
273
|
owner,
|
|
251
274
|
repo,
|
|
252
275
|
title: opts.title,
|
|
253
276
|
body,
|
|
254
277
|
head: `${await getAuthenticatedUser()}:${branch}`,
|
|
255
|
-
base:
|
|
278
|
+
base: baseBranch,
|
|
256
279
|
});
|
|
257
280
|
spinner.succeed(`PR Created: ${green(pr.html_url)}`);
|
|
258
281
|
}
|
package/dist/commands/issues.js
CHANGED
|
@@ -11,16 +11,29 @@ export async function listIssues(opts) {
|
|
|
11
11
|
const octokit = getOctokit();
|
|
12
12
|
const spinner = ora(`Fetching issues from ${chalk.cyan(getFullRepo())}...`).start();
|
|
13
13
|
try {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
const requestedLimit = opts.limit || 50;
|
|
15
|
+
const realIssues = [];
|
|
16
|
+
let page = 1;
|
|
17
|
+
// Fetch until we have enough real issues or run out of pages
|
|
18
|
+
while (realIssues.length < requestedLimit) {
|
|
19
|
+
const { data: issues } = await octokit.issues.listForRepo({
|
|
20
|
+
owner: getOwner(), repo: getRepo(),
|
|
21
|
+
state: opts.state || 'open',
|
|
22
|
+
labels: opts.labels || undefined,
|
|
23
|
+
per_page: 100, // Fetch max per page to be efficient
|
|
24
|
+
page: page++,
|
|
25
|
+
});
|
|
26
|
+
if (issues.length === 0)
|
|
27
|
+
break;
|
|
28
|
+
const batch = issues.filter((i) => !i.pull_request);
|
|
29
|
+
realIssues.push(...batch);
|
|
30
|
+
if (issues.length < 100)
|
|
31
|
+
break; // Last page
|
|
32
|
+
}
|
|
33
|
+
// Clip to requested limit
|
|
34
|
+
const finalIssues = realIssues.slice(0, requestedLimit);
|
|
22
35
|
spinner.stop();
|
|
23
|
-
if (
|
|
36
|
+
if (finalIssues.length === 0) {
|
|
24
37
|
console.log(chalk.yellow('\n No issues found.\n'));
|
|
25
38
|
return;
|
|
26
39
|
}
|
|
@@ -28,13 +41,13 @@ export async function listIssues(opts) {
|
|
|
28
41
|
head: ['#', 'Title', 'Labels', 'Assignee', 'State'].map((h) => chalk.cyan(h)),
|
|
29
42
|
style: { head: [], border: [] },
|
|
30
43
|
});
|
|
31
|
-
|
|
44
|
+
finalIssues.forEach((i) => {
|
|
32
45
|
const labels = i.labels.map((l) => typeof l === 'string' ? l : l.name || '').join(', ');
|
|
33
46
|
const assignee = i.assignee?.login || chalk.dim('unassigned');
|
|
34
47
|
const state = i.state === 'open' ? chalk.green('open') : chalk.red('closed');
|
|
35
48
|
table.push([`#${i.number}`, i.title.substring(0, 60), labels.substring(0, 30), assignee, state]);
|
|
36
49
|
});
|
|
37
|
-
console.log(`\n${chalk.bold(`š Issues ā ${getFullRepo()}`)} (${
|
|
50
|
+
console.log(`\n${chalk.bold(`š Issues ā ${getFullRepo()}`)} (${finalIssues.length})\n`);
|
|
38
51
|
console.log(table.toString());
|
|
39
52
|
console.log('');
|
|
40
53
|
}
|
|
@@ -137,16 +150,22 @@ export async function createIssuesFromFile(filePath, opts) {
|
|
|
137
150
|
console.log(`\n${chalk.bold('š GitPadi Issue Creator')}`);
|
|
138
151
|
console.log(chalk.dim(` Repo: ${getFullRepo()}`));
|
|
139
152
|
console.log(chalk.dim(` File: ${filePath} (${detectedFormat})`));
|
|
140
|
-
console.log(chalk.dim(`
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
153
|
+
console.log(chalk.dim(` Found: ${filtered.length} issues\n`));
|
|
154
|
+
// Always preview first
|
|
155
|
+
filtered.forEach((i) => {
|
|
156
|
+
console.log(` ${chalk.dim(`#${String(i.number).padStart(2, '0')}`)} ${i.title}`);
|
|
157
|
+
console.log(chalk.dim(` [${(i.labels || []).join(', ')}]`));
|
|
158
|
+
});
|
|
159
|
+
// Ask for confirmation
|
|
160
|
+
const inquirer = (await import('inquirer')).default;
|
|
161
|
+
const { proceed } = await inquirer.prompt([{
|
|
162
|
+
type: 'confirm',
|
|
163
|
+
name: 'proceed',
|
|
164
|
+
message: chalk.yellow(`Create these ${filtered.length} issues on GitHub?`),
|
|
165
|
+
default: true,
|
|
166
|
+
}]);
|
|
167
|
+
if (!proceed) {
|
|
168
|
+
console.log(chalk.dim('\n Cancelled.\n'));
|
|
150
169
|
return;
|
|
151
170
|
}
|
|
152
171
|
const octokit = getOctokit();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitpadi",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.3",
|
|
4
|
+
"description": "GitPadi ā Your AI-powered GitHub management CLI. Create repos, manage issues & PRs, score contributors, and automate everything.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"gitpadi": "./dist/cli.js"
|
package/src/cli.ts
CHANGED
|
@@ -393,11 +393,10 @@ async function issueMenu() {
|
|
|
393
393
|
} else if (action === 'bulk') {
|
|
394
394
|
const a = await ask([
|
|
395
395
|
{ type: 'input', name: 'file', message: yellow('š Path to issues file (JSON or MD)') + dim(' (q=back):') },
|
|
396
|
-
{ type: 'confirm', name: 'dryRun', message: 'Dry run first?', default: true },
|
|
397
396
|
{ type: 'number', name: 'start', message: dim('Start index:'), default: 1 },
|
|
398
397
|
{ type: 'number', name: 'end', message: dim('End index:'), default: 999 },
|
|
399
398
|
]);
|
|
400
|
-
await issues.createIssuesFromFile(a.file, {
|
|
399
|
+
await issues.createIssuesFromFile(a.file, { start: a.start, end: a.end });
|
|
401
400
|
} else if (action === 'assign-best') {
|
|
402
401
|
const spinner = createSpinner(dim('Finding issues with applicants...')).start();
|
|
403
402
|
const octokit = getOctokit();
|
|
@@ -100,9 +100,24 @@ export async function forkAndClone(target: string) {
|
|
|
100
100
|
// Ensure upstream remote exists
|
|
101
101
|
try { execSync(`git remote add upstream https://github.com/${owner}/${repo}.git`, { stdio: 'pipe' }); } catch { /* exists */ }
|
|
102
102
|
|
|
103
|
+
// 1. Detect default branch
|
|
104
|
+
let defaultBranch = 'main';
|
|
105
|
+
try {
|
|
106
|
+
execSync('git rev-parse upstream/main', { stdio: 'pipe' });
|
|
107
|
+
} catch {
|
|
108
|
+
defaultBranch = 'master';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 2. Checkout default branch before syncing
|
|
112
|
+
try {
|
|
113
|
+
execSync(`git checkout ${defaultBranch}`, { stdio: 'pipe' });
|
|
114
|
+
} catch {
|
|
115
|
+
// If it fails, maybe it's not even a valid default branch locally, but we'll try to sync anyway
|
|
116
|
+
}
|
|
117
|
+
|
|
103
118
|
await syncBranch();
|
|
104
119
|
|
|
105
|
-
// Create or switch to issue branch
|
|
120
|
+
// 3. Create or switch to issue branch
|
|
106
121
|
const branchName = issue ? `fix/issue-${issue}` : null;
|
|
107
122
|
if (branchName) {
|
|
108
123
|
try {
|
|
@@ -282,13 +297,21 @@ export async function submitPR(opts: { title: string, body?: string, issue?: num
|
|
|
282
297
|
spinner.text = 'Creating Pull Request...';
|
|
283
298
|
const body = opts.body || (linkedIssue ? `Fixes #${linkedIssue}` : 'Automated PR via GitPadi');
|
|
284
299
|
|
|
300
|
+
// Detect base branch
|
|
301
|
+
let baseBranch = 'main';
|
|
302
|
+
try {
|
|
303
|
+
execSync('git rev-parse origin/main', { stdio: 'pipe' });
|
|
304
|
+
} catch {
|
|
305
|
+
baseBranch = 'master';
|
|
306
|
+
}
|
|
307
|
+
|
|
285
308
|
const { data: pr } = await getOctokit().pulls.create({
|
|
286
309
|
owner,
|
|
287
310
|
repo,
|
|
288
311
|
title: opts.title,
|
|
289
312
|
body,
|
|
290
313
|
head: `${await getAuthenticatedUser()}:${branch}`,
|
|
291
|
-
base:
|
|
314
|
+
base: baseBranch,
|
|
292
315
|
});
|
|
293
316
|
|
|
294
317
|
spinner.succeed(`PR Created: ${green(pr.html_url)}`);
|
package/src/commands/issues.ts
CHANGED
|
@@ -14,18 +14,34 @@ export async function listIssues(opts: { state?: string; labels?: string; limit?
|
|
|
14
14
|
const spinner = ora(`Fetching issues from ${chalk.cyan(getFullRepo())}...`).start();
|
|
15
15
|
|
|
16
16
|
try {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
const requestedLimit = opts.limit || 50;
|
|
18
|
+
const realIssues: any[] = [];
|
|
19
|
+
let page = 1;
|
|
20
|
+
|
|
21
|
+
// Fetch until we have enough real issues or run out of pages
|
|
22
|
+
while (realIssues.length < requestedLimit) {
|
|
23
|
+
const { data: issues } = await octokit.issues.listForRepo({
|
|
24
|
+
owner: getOwner(), repo: getRepo(),
|
|
25
|
+
state: (opts.state as 'open' | 'closed' | 'all') || 'open',
|
|
26
|
+
labels: opts.labels || undefined,
|
|
27
|
+
per_page: 100, // Fetch max per page to be efficient
|
|
28
|
+
page: page++,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (issues.length === 0) break;
|
|
32
|
+
|
|
33
|
+
const batch = issues.filter((i) => !i.pull_request);
|
|
34
|
+
realIssues.push(...batch);
|
|
35
|
+
|
|
36
|
+
if (issues.length < 100) break; // Last page
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Clip to requested limit
|
|
40
|
+
const finalIssues = realIssues.slice(0, requestedLimit);
|
|
23
41
|
|
|
24
|
-
// Filter out PRs (GitHub API returns PRs in issues endpoint)
|
|
25
|
-
const realIssues = issues.filter((i) => !i.pull_request);
|
|
26
42
|
spinner.stop();
|
|
27
43
|
|
|
28
|
-
if (
|
|
44
|
+
if (finalIssues.length === 0) {
|
|
29
45
|
console.log(chalk.yellow('\n No issues found.\n'));
|
|
30
46
|
return;
|
|
31
47
|
}
|
|
@@ -35,14 +51,14 @@ export async function listIssues(opts: { state?: string; labels?: string; limit?
|
|
|
35
51
|
style: { head: [], border: [] },
|
|
36
52
|
});
|
|
37
53
|
|
|
38
|
-
|
|
39
|
-
const labels = i.labels.map((l) => typeof l === 'string' ? l : l.name || '').join(', ');
|
|
54
|
+
finalIssues.forEach((i) => {
|
|
55
|
+
const labels = i.labels.map((l: any) => typeof l === 'string' ? l : l.name || '').join(', ');
|
|
40
56
|
const assignee = i.assignee?.login || chalk.dim('unassigned');
|
|
41
57
|
const state = i.state === 'open' ? chalk.green('open') : chalk.red('closed');
|
|
42
58
|
table.push([`#${i.number}`, i.title.substring(0, 60), labels.substring(0, 30), assignee, state]);
|
|
43
59
|
});
|
|
44
60
|
|
|
45
|
-
console.log(`\n${chalk.bold(`š Issues ā ${getFullRepo()}`)} (${
|
|
61
|
+
console.log(`\n${chalk.bold(`š Issues ā ${getFullRepo()}`)} (${finalIssues.length})\n`);
|
|
46
62
|
console.log(table.toString());
|
|
47
63
|
console.log('');
|
|
48
64
|
} catch (e: any) {
|
|
@@ -153,16 +169,25 @@ export async function createIssuesFromFile(filePath: string, opts: { dryRun?: bo
|
|
|
153
169
|
console.log(`\n${chalk.bold('š GitPadi Issue Creator')}`);
|
|
154
170
|
console.log(chalk.dim(` Repo: ${getFullRepo()}`));
|
|
155
171
|
console.log(chalk.dim(` File: ${filePath} (${detectedFormat})`));
|
|
156
|
-
console.log(chalk.dim(`
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
172
|
+
console.log(chalk.dim(` Found: ${filtered.length} issues\n`));
|
|
173
|
+
|
|
174
|
+
// Always preview first
|
|
175
|
+
filtered.forEach((i: any) => {
|
|
176
|
+
console.log(` ${chalk.dim(`#${String(i.number).padStart(2, '0')}`)} ${i.title}`);
|
|
177
|
+
console.log(chalk.dim(` [${(i.labels || []).join(', ')}]`));
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Ask for confirmation
|
|
181
|
+
const inquirer = (await import('inquirer')).default;
|
|
182
|
+
const { proceed } = await inquirer.prompt([{
|
|
183
|
+
type: 'confirm',
|
|
184
|
+
name: 'proceed',
|
|
185
|
+
message: chalk.yellow(`Create these ${filtered.length} issues on GitHub?`),
|
|
186
|
+
default: true,
|
|
187
|
+
}]);
|
|
188
|
+
|
|
189
|
+
if (!proceed) {
|
|
190
|
+
console.log(chalk.dim('\n Cancelled.\n'));
|
|
166
191
|
return;
|
|
167
192
|
}
|
|
168
193
|
|