@lansisdev/gh-createpr 1.3.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 lansisDev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,457 @@
1
+ # gh-createpr
2
+
3
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
4
+ [![GitHub CLI Extension](https://img.shields.io/badge/GitHub_CLI-Extension-blue.svg)](https://cli.github.com/)
5
+
6
+ A powerful GitHub CLI extension that streamlines the process of creating GitHub Pull Requests directly from Jira tickets. This extension automates the entire workflow from ticket information extraction to PR creation, making development workflows more efficient and consistent.
7
+
8
+ ## 🚀 Features
9
+
10
+ - **🎫 Jira Integration**: Automatically fetches ticket information including title, description, and team details
11
+ - **🌿 Branch Management**: Creates appropriately named feature branches based on ticket information
12
+ - **📝 Smart PR Creation**: Generates well-formatted Pull Requests with proper titles and descriptions
13
+ - **⚡ Workflow Automation**: Handles git operations including branch creation, commits, and pushes
14
+ - **🏷️ Team Detection**: Automatically detects and includes team information in PR titles
15
+ - **🔗 Cross-linking**: Links PRs back to their corresponding Jira tickets
16
+
17
+ ## 📋 Prerequisites
18
+
19
+ Before using this extension, make sure you have:
20
+
21
+ - **GitHub CLI** (v2.0.0 or higher) - [Install here](https://cli.github.com/)
22
+ - **GitHub CLI authenticated** - Run `gh auth login` if not already done
23
+ - **Node.js** (v16 or higher) - Required for the extension runtime
24
+ - **Git** configured with your credentials
25
+ - **Jira API access** with appropriate permissions
26
+ - A repository with a `develop` branch (default base branch for PRs)
27
+
28
+ ### Quick Setup Check
29
+
30
+ ```bash
31
+ # Verify GitHub CLI is installed and authenticated
32
+ gh --version
33
+ gh auth status
34
+
35
+ # Verify Git is configured
36
+ git config --global user.name
37
+ git config --global user.email
38
+
39
+ # Verify Node.js version
40
+ node --version
41
+ ```
42
+
43
+ ## Prerequisites
44
+
45
+ - GitHub CLI (`gh`) installed and authenticated
46
+ - **Node.js 18 or higher** (required for native fetch support)
47
+ - Access to Jira REST API with appropriate permissions
48
+
49
+ ## Installation
50
+
51
+ ### Quick Install (Recommended)
52
+
53
+ ```bash
54
+ # Install the extension directly from GitHub
55
+ gh extension install lansisDev/gh-createpr
56
+ ```
57
+
58
+ That's it! The extension is now available as `gh createpr`.
59
+
60
+ ### Verify Installation
61
+
62
+ ```bash
63
+ # Check if the extension is installed
64
+ gh extension list
65
+
66
+ # Test the extension
67
+ gh createpr --help
68
+ ```
69
+
70
+ ### Prerequisites for GitHub CLI Extensions
71
+
72
+ Before installing, ensure you have:
73
+
74
+ 1. **GitHub CLI installed**: [Download here](https://cli.github.com/) or install via:
75
+ ```bash
76
+ # macOS
77
+ brew install gh
78
+
79
+ # Windows
80
+ winget install --id GitHub.cli
81
+
82
+ # Linux (Ubuntu/Debian)
83
+ sudo apt install gh
84
+ ```
85
+
86
+ 2. **GitHub CLI authenticated**:
87
+ ```bash
88
+ gh auth login
89
+ ```
90
+
91
+ ### Manual Installation (for Development)
92
+
93
+ If you want to contribute or modify the extension:
94
+
95
+ ```bash
96
+ # Clone the repository
97
+ git clone https://github.com/lansisDev/gh-createpr.git
98
+ cd gh-createpr
99
+
100
+ # Install dependencies
101
+ npm install
102
+
103
+ # Build the project
104
+ npm run build
105
+
106
+ # Install as local GitHub CLI extension
107
+ gh extension install .
108
+ ```
109
+
110
+ ### Troubleshooting Installation
111
+
112
+ If you encounter issues:
113
+
114
+ ```bash
115
+ # Uninstall and reinstall
116
+ gh extension remove createpr
117
+ gh extension install lansisDev/gh-createpr
118
+
119
+ # Check GitHub CLI version (requires v2.0.0+)
120
+ gh --version
121
+
122
+ # Verify GitHub CLI authentication
123
+ gh auth status
124
+ ```
125
+
126
+ ## 🚀 Quick Start
127
+
128
+ 1. **Install the extension**:
129
+ ```bash
130
+ gh extension install lansisDev/gh-createpr
131
+ ```
132
+
133
+ 2. **Set up Jira credentials** (see [Configuration](#%EF%B8%8F-configuration) section below):
134
+ ```bash
135
+ export JIRA_BASE_URL="https://your-company.atlassian.net"
136
+ export JIRA_EMAIL="your-email@company.com"
137
+ export JIRA_API_TOKEN="your-jira-api-token"
138
+ ```
139
+
140
+ 3. **Navigate to your git repository** and run:
141
+ ```bash
142
+ gh createpr LAN-123
143
+ ```
144
+
145
+ That's it! The extension will create a branch, commit, and pull request automatically.
146
+
147
+ ## ⚙️ Configuration
148
+
149
+ ### Required Environment Variables
150
+
151
+ The extension requires these Jira credentials to fetch ticket information:
152
+
153
+ ```bash
154
+ export JIRA_BASE_URL="https://your-company.atlassian.net"
155
+ export JIRA_EMAIL="your-email@company.com"
156
+ export JIRA_API_TOKEN="your-jira-api-token"
157
+ ```
158
+
159
+ ### Step-by-Step Configuration
160
+
161
+ #### 1. Get Your Jira API Token
162
+
163
+ 1. Go to [Atlassian Account Settings](https://id.atlassian.com/manage-profile/security/api-tokens)
164
+ 2. Click "Create API token"
165
+ 3. Give it a descriptive name (e.g., "gh-createpr-extension")
166
+ 4. Copy the generated token (save it securely!)
167
+
168
+ #### 2. Find Your Jira Details
169
+
170
+ - **JIRA_BASE_URL**: Your company's Jira URL (e.g., `https://mycompany.atlassian.net`)
171
+ - **JIRA_EMAIL**: The email address associated with your Jira account
172
+
173
+ #### 3. Set Environment Variables
174
+
175
+ **Option A: Temporary (current session only)**
176
+ ```bash
177
+ export JIRA_BASE_URL="https://your-company.atlassian.net"
178
+ export JIRA_EMAIL="your-email@company.com"
179
+ export JIRA_API_TOKEN="your-jira-api-token"
180
+ ```
181
+
182
+ **Option B: Permanent (recommended)**
183
+
184
+ Add to your shell profile file (`~/.zshrc`, `~/.bashrc`, or `~/.bash_profile`):
185
+
186
+ ```bash
187
+ # GitHub CLI createpr extension - Jira Configuration
188
+ export JIRA_BASE_URL="https://your-company.atlassian.net"
189
+ export JIRA_EMAIL="your-email@company.com"
190
+ export JIRA_API_TOKEN="your-jira-api-token"
191
+ ```
192
+
193
+ Then reload your shell:
194
+ ```bash
195
+ source ~/.zshrc # or ~/.bashrc
196
+ ```
197
+
198
+ #### 4. Verify Configuration
199
+
200
+ ```bash
201
+ # Test that environment variables are set
202
+ echo $JIRA_BASE_URL
203
+ echo $JIRA_EMAIL
204
+ echo $JIRA_API_TOKEN
205
+
206
+ # Test the extension with a real ticket
207
+ gh createpr YOUR-TICKET-123
208
+ ```
209
+
210
+ ## 🎯 Usage
211
+
212
+ ### Basic Usage
213
+
214
+ ```bash
215
+ gh createpr <JIRA_TICKET>
216
+ ```
217
+
218
+ ### Examples
219
+
220
+ ```bash
221
+ # Create PR for ticket LAN-123
222
+ gh createpr LAN-123
223
+
224
+ # Create PR for ticket PROJ-456
225
+ gh createpr PROJ-456
226
+ ```
227
+
228
+ ### What the tool does:
229
+
230
+ 1. **Fetches Jira ticket data** including title, description, and team information
231
+ 2. **Switches to develop branch** and pulls latest changes
232
+ 3. **Creates a new feature branch** with format: `{ticket-id}-{slugified-title}`
233
+ 4. **Makes an initial commit** with a conventional commit message
234
+ 5. **Pushes the branch** to the remote repository
235
+ 6. **Creates a Pull Request** with proper title and description linking back to Jira
236
+
237
+ ### Example Output
238
+
239
+ ```bash
240
+ 🔍 Fetching data for LAN-123 from Jira...
241
+ 🔍 Validating data obtained from Jira...
242
+ ✅ Title: Add user authentication feature
243
+ 📝 Description: Implement OAuth2 authentication for user login
244
+ 👥 Team: Frontend
245
+ 🌿 New branch: lan-123-add-user-authentication-feature
246
+ 🔄 Switching to develop and updating...
247
+ 🚧 Creating new branch: lan-123-add-user-authentication-feature
248
+ 📝 Creating initial commit...
249
+ ⬆️ Pushing branch to origin...
250
+ 🚀 Creating Pull Request from lan-123-add-user-authentication-feature to develop...
251
+ 🎉 Pull Request created from 'lan-123-add-user-authentication-feature' to 'develop'
252
+ ✅ You are now on branch 'lan-123-add-user-authentication-feature' with initial commit pushed
253
+ 🔗 The PR is ready on GitHub
254
+ ```
255
+
256
+ ## 📁 Project Structure
257
+
258
+ ```
259
+ gh-createpr/
260
+ ├── src/
261
+ │ └── index.ts # Main CLI application
262
+ ├── dist/ # Compiled JavaScript output
263
+ ├── manifest.yml # GitHub CLI extension manifest
264
+ ├── package.json # Project configuration
265
+ ├── tsconfig.json # TypeScript configuration
266
+ ├── LICENSE # ISC License
267
+ └── README.md # This file
268
+ ```
269
+
270
+ ## 🛠️ Development
271
+
272
+ ### Available Scripts
273
+
274
+ ```bash
275
+ # Build the project
276
+ npm run build
277
+
278
+ # Run in development mode
279
+ npm run dev
280
+
281
+ # Start the built version
282
+ npm start
283
+
284
+ # Prepare for publishing
285
+ npm run prepare
286
+ ```
287
+
288
+ ### Building from Source
289
+
290
+ ```bash
291
+ # Clone and setup
292
+ git clone https://github.com/lansisDev/gh-createpr.git
293
+ cd gh-createpr
294
+ npm install
295
+
296
+ # Build
297
+ npm run build
298
+
299
+ # Test the extension locally
300
+ gh createpr LAN-123
301
+ ```
302
+
303
+ ## 🏗️ Architecture
304
+
305
+ This GitHub CLI extension is built with:
306
+
307
+ - **TypeScript** for type safety and modern JavaScript features
308
+ - **Commander.js** for CLI argument parsing and command structure
309
+ - **Node-fetch** for HTTP requests to Jira API
310
+ - **Node.js Child Process** for Git operations
311
+ - **GitHub CLI** as the platform for the extension
312
+
313
+ ### Key Components
314
+
315
+ - **Jira API Integration**: Fetches ticket data using REST API
316
+ - **Git Operations**: Automated branch management and commits
317
+ - **GitHub CLI Integration**: Leverages `gh pr create` for PR creation
318
+ - **Data Processing**: Intelligent parsing of ticket information and team detection
319
+
320
+ ## 🔧 Configuration Options
321
+
322
+ ### Branch Naming
323
+
324
+ Branches are automatically named using the format:
325
+ ```
326
+ {ticket-id-lowercase}-{slugified-title}
327
+ ```
328
+
329
+ Example: `lan-123-add-user-authentication-feature`
330
+
331
+ ### PR Title Format
332
+
333
+ ```
334
+ [{JIRA_TICKET}][{TEAM}] {TITLE}
335
+ ```
336
+
337
+ Example: `[LAN-123][Frontend] Add user authentication feature`
338
+
339
+ ### Commit Message Format
340
+
341
+ ```
342
+ feat({JIRA_TICKET}): initial commit for {TITLE}
343
+ ```
344
+
345
+ Example: `feat(LAN-123): initial commit for Add user authentication feature`
346
+
347
+ ## 🚨 Error Handling
348
+
349
+ The extension includes comprehensive error handling for:
350
+
351
+ - **Missing environment variables**
352
+ - **Invalid Jira tickets**
353
+ - **Network connectivity issues**
354
+ - **Git operation failures**
355
+ - **GitHub CLI authentication problems**
356
+
357
+ ## 📦 Extension Management
358
+
359
+ ```bash
360
+ # List installed extensions
361
+ gh extension list
362
+
363
+ # Upgrade the extension
364
+ gh extension upgrade createpr
365
+
366
+ # Uninstall the extension
367
+ gh extension remove createpr
368
+ ```
369
+
370
+ ## 🚀 Publishing the Extension
371
+
372
+ To make the extension available for installation via `gh extension install lansisDev/gh-createpr`:
373
+
374
+ ### 1. Build and Prepare
375
+
376
+ ```bash
377
+ # Build the project
378
+ npm run build
379
+
380
+ # Make the binary executable
381
+ chmod +x dist/index.js
382
+ ```
383
+
384
+ ### 2. Create a Release
385
+
386
+ ```bash
387
+ # Create and push a tag
388
+ git tag v1.1.0
389
+ git push origin v1.1.0
390
+
391
+ # Create a GitHub release
392
+ gh release create v1.1.0 --title "v1.1.0" --notes "English translation release"
393
+ ```
394
+
395
+ ### 3. Update Manifest
396
+
397
+ Ensure `manifest.yml` has the correct tag version:
398
+
399
+ ```yaml
400
+ name: createpr
401
+ owner: lansisDev
402
+ host: github.com
403
+ tag: v1.1.0
404
+ ```
405
+
406
+ ### 4. Test Installation
407
+
408
+ ```bash
409
+ # Test the installation
410
+ gh extension install lansisDev/gh-createpr
411
+
412
+ # Verify it works
413
+ gh createpr --help
414
+ ```
415
+
416
+ ## 🤝 Contributing
417
+
418
+ 1. Fork the repository
419
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
420
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
421
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
422
+ 5. Open a Pull Request
423
+
424
+ ## 📄 License
425
+
426
+ This project is licensed under the ISC License - see the [LICENSE](LICENSE) file for details.
427
+
428
+ ## 👤 Author
429
+
430
+ **Gonzalo Buasso**
431
+
432
+ ## 🆘 Support
433
+
434
+ If you encounter any issues or have questions:
435
+
436
+ 1. Check the [Issues](https://github.com/lansisDev/gh-createpr/issues) page
437
+ 2. Create a new issue with detailed information about your problem
438
+ 3. Include relevant error messages and environment details
439
+
440
+ ## 🔄 Changelog
441
+
442
+ ### Version 1.1.0
443
+ - **English Translation**: Translated all Spanish text to English for international accessibility
444
+ - **Improved User Interface**: All console messages, error messages, and help text now in English
445
+ - **Better Documentation**: Example output updated to reflect English interface
446
+ - **Enhanced Usability**: More professional appearance for global developer community
447
+
448
+ ### Version 1.0.0
449
+ - Initial release
450
+ - Jira integration for ticket data fetching
451
+ - Automated branch creation and PR generation
452
+ - Team detection and proper PR formatting
453
+ - Comprehensive error handling
454
+
455
+ ---
456
+
457
+ Made with ❤️ by [lansisDev](https://github.com/lansisDev)
@@ -0,0 +1,2 @@
1
+ export declare const createPrFromGithubIssue: (issueArg: string) => Promise<void>;
2
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../src/github.ts"],"names":[],"mappings":"AAsCA,eAAO,MAAM,uBAAuB,GAAU,UAAU,MAAM,kBA8M7D,CAAC"}
package/dist/github.js ADDED
@@ -0,0 +1,185 @@
1
+ import { execSync } from "child_process";
2
+ import * as p from "@clack/prompts";
3
+ import pc from "picocolors";
4
+ // Use native fetch (available in Node.js 18+)
5
+ // @ts-ignore
6
+ const fetch = globalThis.fetch;
7
+ const GITHUB_GRAPHQL_URL = "https://api.github.com/graphql";
8
+ export const createPrFromGithubIssue = async (issueArg) => {
9
+ const issueRegex = /^(?:([a-zA-Z0-9-]+)\/([a-zA-Z0-9._-]+)#(\d+)|#(\d+)|(\d+))$/;
10
+ const match = issueArg.match(issueRegex);
11
+ let owner;
12
+ let repo;
13
+ let issueNumber;
14
+ if (match) {
15
+ // owner/repo#number format
16
+ if (match[1] && match[2] && match[3]) {
17
+ owner = match[1];
18
+ repo = match[2];
19
+ issueNumber = parseInt(match[3], 10);
20
+ }
21
+ else if (match[4] || match[5]) {
22
+ // #number or just number format - use current repo from git
23
+ try {
24
+ const currentRemote = execSync("git remote get-url origin", { encoding: "utf-8" }).trim();
25
+ const remoteRegex = /github\.com[:/](?:[^/]+)\/([^/.]+)|github\.com[:/]([^/]+)\/([^/.]+)/;
26
+ const remoteMatch = currentRemote.match(remoteRegex);
27
+ if (!remoteMatch) {
28
+ throw new Error("No se pudo determinar el repositorio actual desde el remote de git");
29
+ }
30
+ owner = remoteMatch[1] || remoteMatch[2];
31
+ repo = remoteMatch[3] || remoteMatch[1];
32
+ const numStr = match[4] || match[5];
33
+ issueNumber = parseInt(numStr, 10);
34
+ // Strip out .git from repo name if it exists
35
+ repo = repo.replace(/\.git$/, '');
36
+ }
37
+ catch (e) {
38
+ p.cancel(pc.red("Error obteniendo el repo actual. Asegurate de estar en un repo de git con un remote origin."));
39
+ process.exit(1);
40
+ }
41
+ }
42
+ else {
43
+ p.cancel(pc.red("Formato de issue inválido. Usá owner/repo#number o #number o simplemente el número."));
44
+ process.exit(1);
45
+ }
46
+ }
47
+ else {
48
+ p.cancel(pc.red("Formato de issue inválido. Usá owner/repo#number o #number o simplemente el número."));
49
+ process.exit(1);
50
+ }
51
+ const GITHUB_TOKEN = process.env.GITHUB_TOKEN || "";
52
+ if (!GITHUB_TOKEN) {
53
+ p.cancel(pc.red("Falta la variable de entorno GITHUB_TOKEN"));
54
+ process.exit(1);
55
+ }
56
+ const s = p.spinner();
57
+ s.start(`Buscando issue #${issueNumber} en ${owner}/${repo}...`);
58
+ try {
59
+ const query = `
60
+ query GetIssue($owner: String!, $repo: String!, $number: Int!) {
61
+ repository(owner: $owner, name: $repo) {
62
+ issue(number: $number) {
63
+ number
64
+ title
65
+ body
66
+ labels(first: 10) {
67
+ nodes {
68
+ name
69
+ }
70
+ }
71
+ assignees(first: 5) {
72
+ nodes {
73
+ login
74
+ }
75
+ }
76
+ }
77
+ }
78
+ }
79
+ `;
80
+ const res = await fetch(GITHUB_GRAPHQL_URL, {
81
+ method: "POST",
82
+ headers: {
83
+ "Authorization": `Bearer ${GITHUB_TOKEN}`,
84
+ "Content-Type": "application/json",
85
+ },
86
+ body: JSON.stringify({
87
+ query,
88
+ variables: { owner, repo, number: issueNumber },
89
+ }),
90
+ });
91
+ if (!res.ok) {
92
+ throw new Error(`Error en la API de GitHub: ${res.status} ${res.statusText}`);
93
+ }
94
+ const response = (await res.json());
95
+ if (response.errors && response.errors.length > 0) {
96
+ throw new Error(`Error de GraphQL: ${response.errors.map((e) => e.message).join(", ")}`);
97
+ }
98
+ const issue = response.repository?.issue;
99
+ if (!issue) {
100
+ throw new Error(`No se encontró la issue #${issueNumber} en ${owner}/${repo}`);
101
+ }
102
+ const title = issue.title || "";
103
+ const description = issue.body || "";
104
+ const label = issue.labels?.nodes?.[0]?.name;
105
+ if (!title) {
106
+ throw new Error(`No se pudo obtener el título para la issue #${issueNumber}`);
107
+ }
108
+ s.stop(`Datos de la issue obtenidos correctamente`);
109
+ // Create slug for branch
110
+ const slugTitle = title
111
+ .normalize("NFD").replace(/[\u0300-\u036f]/g, "")
112
+ .toLowerCase()
113
+ .replace(/[^a-z0-9]+/g, "-")
114
+ .replace(/^-+|-+$/g, "");
115
+ const ticketRef = `${owner}-${repo}-${issueNumber}`;
116
+ const branchName = `${ticketRef}-${slugTitle}`.substring(0, 100);
117
+ let summaryText = `${pc.bold("Issue:")} #${issueNumber}\n` +
118
+ `${pc.bold("Título:")} ${title}\n`;
119
+ if (label)
120
+ summaryText += `${pc.bold("Label:")} ${label}\n`;
121
+ summaryText += `${pc.bold("Rama:")} ${pc.cyan(branchName)}`;
122
+ p.note(summaryText, "Resumen del PR");
123
+ const shouldContinue = await p.confirm({
124
+ message: '¿Continuar con la creación de la rama y PR?',
125
+ initialValue: true,
126
+ });
127
+ if (p.isCancel(shouldContinue) || !shouldContinue) {
128
+ p.cancel('Operación cancelada por el usuario.');
129
+ process.exit(0);
130
+ }
131
+ s.start('Validando estado de git...');
132
+ // Validate: Check for uncommitted changes
133
+ const statusOutput = execSync("git status --porcelain", { encoding: "utf-8" }).trim();
134
+ if (statusOutput) {
135
+ throw new Error("Tenés cambios sin commitear. Por favor hacé commit o stash antes de correr esto.");
136
+ }
137
+ // Validate: Check for unpushed commits on current branch
138
+ const currentBranch = execSync("git branch --show-current", { encoding: "utf-8" }).trim();
139
+ if (currentBranch) {
140
+ // Allow it to fail gracefully if there is no upstream
141
+ try {
142
+ const unpushedOutput = execSync(`git log @{u}..HEAD --oneline`, { encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"] }).toString().trim();
143
+ if (unpushedOutput) {
144
+ throw new Error(`Tenés commits sin pushear en la rama '${currentBranch}'.\nPor favor hacé push antes de correr este script.`);
145
+ }
146
+ }
147
+ catch (e) {
148
+ // No upstream branch, ignore
149
+ }
150
+ }
151
+ s.stop('Estado de git validado');
152
+ s.start('Cambiando a develop y actualizando...');
153
+ execSync(`git checkout develop`, { stdio: "ignore" });
154
+ execSync(`git pull origin develop`, { stdio: "ignore" });
155
+ s.stop('Rama develop actualizada');
156
+ s.start(`Creando nueva rama: ${branchName}`);
157
+ execSync(`git checkout -b ${branchName}`, { stdio: "ignore" });
158
+ s.stop(`Rama ${pc.cyan(branchName)} creada`);
159
+ s.start('Creando commit inicial...');
160
+ execSync(`git add .`, { stdio: "ignore" });
161
+ execSync(`git commit -m "feat(${ticketRef}): initial commit for ${title}" --allow-empty --no-verify`, { stdio: "ignore" });
162
+ s.stop('Commit inicial creado');
163
+ s.start('Pusheando rama al remoto...');
164
+ execSync(`git push origin ${branchName}`, { stdio: "ignore" });
165
+ s.stop('Rama pusheada');
166
+ s.start('Creando Pull Request en GitHub...');
167
+ const prTitle = label
168
+ ? `[${ticketRef}][${label}] ${title}`
169
+ : `[${ticketRef}] ${title}`;
170
+ const issueUrl = `https://github.com/${owner}/${repo}/issues/${issueNumber}`;
171
+ const prBody = description
172
+ ? `**Relates to GitHub issue [${issueNumber}](${issueUrl})**\n\n${description}`
173
+ : `**Relates to GitHub issue [${issueNumber}](${issueUrl})**`;
174
+ execSync(`gh pr create --title "${prTitle}" --body "${prBody}" --base develop --head "${branchName}"`, { stdio: "ignore" });
175
+ s.stop('Pull Request creado');
176
+ execSync(`git push --set-upstream origin "${branchName}"`, { stdio: "ignore" });
177
+ p.outro(`¡Todo listo! Estás en la rama ${pc.cyan(branchName)} y la PR ya está subida.`);
178
+ }
179
+ catch (err) {
180
+ s.stop('Ocurrió un error');
181
+ p.cancel(pc.red(`Error: ${err.message}`));
182
+ process.exit(1);
183
+ }
184
+ };
185
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../src/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,393 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+ import * as p3 from "@clack/prompts";
6
+ import pc3 from "picocolors";
7
+
8
+ // src/jira.ts
9
+ import { execSync } from "child_process";
10
+ import * as p from "@clack/prompts";
11
+ import pc from "picocolors";
12
+ var fetch = globalThis.fetch;
13
+ var createPrFromJira = async (jiraTicket) => {
14
+ const JIRA_BASE_URL = process.env.JIRA_BASE_URL || "";
15
+ const JIRA_EMAIL = process.env.JIRA_EMAIL || "";
16
+ const JIRA_API_TOKEN = process.env.JIRA_API_TOKEN || "";
17
+ if (!JIRA_BASE_URL || !JIRA_EMAIL || !JIRA_API_TOKEN) {
18
+ p.cancel(pc.red("Faltan variables de entorno (JIRA_BASE_URL, JIRA_EMAIL, JIRA_API_TOKEN)"));
19
+ process.exit(1);
20
+ }
21
+ const s = p.spinner();
22
+ s.start(`Buscando datos del ticket ${pc.cyan(jiraTicket)} en Jira...`);
23
+ try {
24
+ const res = await fetch(`${JIRA_BASE_URL}/rest/api/3/issue/${jiraTicket}`, {
25
+ headers: {
26
+ "Authorization": "Basic " + Buffer.from(`${JIRA_EMAIL}:${JIRA_API_TOKEN}`).toString("base64"),
27
+ "Accept": "application/json"
28
+ }
29
+ });
30
+ if (!res.ok) {
31
+ throw new Error(`Error de Jira: ${res.status} ${res.statusText}`);
32
+ }
33
+ const response = await res.json();
34
+ if (response.errorMessages && response.errorMessages.length > 0) {
35
+ throw new Error(`Error de Jira: ${response.errorMessages.join(", ")}`);
36
+ }
37
+ const title = response.fields?.summary || "";
38
+ const description = response.fields?.description?.content?.[0]?.content?.[0]?.text || "";
39
+ const teamFields = [
40
+ response.fields?.customfield_10001?.name,
41
+ response.fields?.customfield_10001,
42
+ response.fields?.customfield_10037?.value,
43
+ response.fields?.customfield_10038?.value,
44
+ response.fields?.components?.[0]?.name,
45
+ response.fields?.labels?.[0]
46
+ ].filter(Boolean);
47
+ let team = teamFields.length > 0 ? teamFields[0] : response.fields?.project?.key || "";
48
+ if (!title) {
49
+ throw new Error(`No se pudo obtener el t\xEDtulo para el ticket ${jiraTicket}`);
50
+ }
51
+ if (!team) {
52
+ throw new Error(`No se pudo obtener el equipo para el ticket ${jiraTicket}`);
53
+ }
54
+ s.stop(`Datos de Jira obtenidos correctamente`);
55
+ const slugTitle = title.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
56
+ const ticketLower = jiraTicket.toLowerCase();
57
+ const branchName = `${ticketLower}-${slugTitle}`;
58
+ p.note(
59
+ `${pc.bold("Ticket:")} ${jiraTicket}
60
+ ${pc.bold("T\xEDtulo:")} ${title}
61
+ ${pc.bold("Equipo:")} ${team}
62
+ ${pc.bold("Rama:")} ${pc.cyan(branchName)}`,
63
+ "Resumen del PR"
64
+ );
65
+ const shouldContinue = await p.confirm({
66
+ message: "\xBFContinuar con la creaci\xF3n de la rama y PR?",
67
+ initialValue: true
68
+ });
69
+ if (p.isCancel(shouldContinue) || !shouldContinue) {
70
+ p.cancel("Operaci\xF3n cancelada por el usuario.");
71
+ process.exit(0);
72
+ }
73
+ s.start("Validando estado de git...");
74
+ const statusOutput = execSync("git status --porcelain", { encoding: "utf-8" }).trim();
75
+ if (statusOutput) {
76
+ throw new Error("Ten\xE9s cambios sin commitear. Por favor hac\xE9 commit o stash antes de correr esto.");
77
+ }
78
+ s.stop("Estado de git validado");
79
+ s.start("Cambiando a develop y actualizando...");
80
+ execSync(`git checkout develop`, { stdio: "ignore" });
81
+ execSync(`git pull origin develop`, { stdio: "ignore" });
82
+ s.stop("Rama develop actualizada");
83
+ s.start(`Creando nueva rama: ${branchName}`);
84
+ execSync(`git checkout -b ${branchName}`, { stdio: "ignore" });
85
+ s.stop(`Rama ${pc.cyan(branchName)} creada`);
86
+ s.start("Creando commit inicial...");
87
+ execSync(`git add .`, { stdio: "ignore" });
88
+ execSync(`git commit -m "feat(${jiraTicket}): initial commit for ${title}" --allow-empty --no-verify`, { stdio: "ignore" });
89
+ s.stop("Commit inicial creado");
90
+ s.start("Pusheando rama al remoto...");
91
+ execSync(`git push origin ${branchName}`, { stdio: "ignore" });
92
+ s.stop("Rama pusheada");
93
+ s.start("Creando Pull Request en GitHub...");
94
+ const prTitle = team ? `[${jiraTicket}][${team}] ${title}` : `[${jiraTicket}] ${title}`;
95
+ const prBody = `**Relates to Jira ticket [${jiraTicket}](${JIRA_BASE_URL}/browse/${jiraTicket})**
96
+
97
+ ${description}`;
98
+ execSync(`gh pr create --title "${prTitle}" --body "${prBody}" --base develop --head "${branchName}"`, { stdio: "ignore" });
99
+ s.stop("Pull Request creado");
100
+ s.start(`Moviendo ticket ${jiraTicket} a 'In Progress'...`);
101
+ try {
102
+ const transitionsRes = await fetch(`${JIRA_BASE_URL}/rest/api/3/issue/${jiraTicket}/transitions`, {
103
+ method: "GET",
104
+ headers: {
105
+ "Authorization": "Basic " + Buffer.from(`${JIRA_EMAIL}:${JIRA_API_TOKEN}`).toString("base64"),
106
+ "Accept": "application/json"
107
+ }
108
+ });
109
+ if (transitionsRes.ok) {
110
+ const transitionsData = await transitionsRes.json();
111
+ const transitions = transitionsData.transitions || [];
112
+ const inProgress = transitions.find((t) => t.name.toLowerCase() === "in progress");
113
+ if (inProgress) {
114
+ const doTransitionRes = await fetch(`${JIRA_BASE_URL}/rest/api/3/issue/${jiraTicket}/transitions`, {
115
+ method: "POST",
116
+ headers: {
117
+ "Authorization": "Basic " + Buffer.from(`${JIRA_EMAIL}:${JIRA_API_TOKEN}`).toString("base64"),
118
+ "Accept": "application/json",
119
+ "Content-Type": "application/json"
120
+ },
121
+ body: JSON.stringify({ transition: { id: inProgress.id } })
122
+ });
123
+ if (!doTransitionRes.ok) {
124
+ p.log.warn(`No se pudo mover a In Progress: ${doTransitionRes.statusText}`);
125
+ } else {
126
+ p.log.success(`Ticket ${jiraTicket} movido a 'In Progress' en Jira`);
127
+ }
128
+ } else {
129
+ p.log.warn("No se encontr\xF3 el estado 'In Progress' para este ticket.");
130
+ }
131
+ }
132
+ } catch (jiraTransitionError) {
133
+ p.log.warn(`Error al intentar mover en Jira: ${jiraTransitionError.message}`);
134
+ }
135
+ s.stop("Transici\xF3n de Jira procesada");
136
+ execSync(`git push --set-upstream origin "${branchName}"`, { stdio: "ignore" });
137
+ p.outro(`\xA1Todo listo! Est\xE1s en la rama ${pc.cyan(branchName)} y la PR ya est\xE1 subida.`);
138
+ } catch (err) {
139
+ s.stop("Ocurri\xF3 un error");
140
+ p.cancel(pc.red(`Error: ${err.message}`));
141
+ process.exit(1);
142
+ }
143
+ };
144
+
145
+ // src/github.ts
146
+ import { execSync as execSync2 } from "child_process";
147
+ import * as p2 from "@clack/prompts";
148
+ import pc2 from "picocolors";
149
+ var fetch2 = globalThis.fetch;
150
+ var GITHUB_GRAPHQL_URL = "https://api.github.com/graphql";
151
+ var createPrFromGithubIssue = async (issueArg) => {
152
+ const issueRegex = /^(?:([a-zA-Z0-9-]+)\/([a-zA-Z0-9._-]+)#(\d+)|#(\d+)|(\d+))$/;
153
+ const match = issueArg.match(issueRegex);
154
+ let owner;
155
+ let repo;
156
+ let issueNumber;
157
+ if (match) {
158
+ if (match[1] && match[2] && match[3]) {
159
+ owner = match[1];
160
+ repo = match[2];
161
+ issueNumber = parseInt(match[3], 10);
162
+ } else if (match[4] || match[5]) {
163
+ try {
164
+ const currentRemote = execSync2("git remote get-url origin", { encoding: "utf-8" }).trim();
165
+ const remoteRegex = /github\.com[:/](?:[^/]+)\/([^/.]+)|github\.com[:/]([^/]+)\/([^/.]+)/;
166
+ const remoteMatch = currentRemote.match(remoteRegex);
167
+ if (!remoteMatch) {
168
+ throw new Error("No se pudo determinar el repositorio actual desde el remote de git");
169
+ }
170
+ owner = remoteMatch[1] || remoteMatch[2];
171
+ repo = remoteMatch[3] || remoteMatch[1];
172
+ const numStr = match[4] || match[5];
173
+ issueNumber = parseInt(numStr, 10);
174
+ repo = repo.replace(/\.git$/, "");
175
+ } catch (e) {
176
+ p2.cancel(pc2.red("Error obteniendo el repo actual. Asegurate de estar en un repo de git con un remote origin."));
177
+ process.exit(1);
178
+ }
179
+ } else {
180
+ p2.cancel(pc2.red("Formato de issue inv\xE1lido. Us\xE1 owner/repo#number o #number o simplemente el n\xFAmero."));
181
+ process.exit(1);
182
+ }
183
+ } else {
184
+ p2.cancel(pc2.red("Formato de issue inv\xE1lido. Us\xE1 owner/repo#number o #number o simplemente el n\xFAmero."));
185
+ process.exit(1);
186
+ }
187
+ const GITHUB_TOKEN = process.env.GITHUB_TOKEN || "";
188
+ if (!GITHUB_TOKEN) {
189
+ p2.cancel(pc2.red("Falta la variable de entorno GITHUB_TOKEN"));
190
+ process.exit(1);
191
+ }
192
+ const s = p2.spinner();
193
+ s.start(`Buscando issue #${issueNumber} en ${owner}/${repo}...`);
194
+ try {
195
+ const query = `
196
+ query GetIssue($owner: String!, $repo: String!, $number: Int!) {
197
+ repository(owner: $owner, name: $repo) {
198
+ issue(number: $number) {
199
+ number
200
+ title
201
+ body
202
+ labels(first: 10) {
203
+ nodes {
204
+ name
205
+ }
206
+ }
207
+ assignees(first: 5) {
208
+ nodes {
209
+ login
210
+ }
211
+ }
212
+ }
213
+ }
214
+ }
215
+ `;
216
+ const res = await fetch2(GITHUB_GRAPHQL_URL, {
217
+ method: "POST",
218
+ headers: {
219
+ "Authorization": `Bearer ${GITHUB_TOKEN}`,
220
+ "Content-Type": "application/json"
221
+ },
222
+ body: JSON.stringify({
223
+ query,
224
+ variables: { owner, repo, number: issueNumber }
225
+ })
226
+ });
227
+ if (!res.ok) {
228
+ throw new Error(`Error en la API de GitHub: ${res.status} ${res.statusText}`);
229
+ }
230
+ const response = await res.json();
231
+ if (response.errors && response.errors.length > 0) {
232
+ throw new Error(`Error de GraphQL: ${response.errors.map((e) => e.message).join(", ")}`);
233
+ }
234
+ const issue = response.repository?.issue;
235
+ if (!issue) {
236
+ throw new Error(`No se encontr\xF3 la issue #${issueNumber} en ${owner}/${repo}`);
237
+ }
238
+ const title = issue.title || "";
239
+ const description = issue.body || "";
240
+ const label = issue.labels?.nodes?.[0]?.name;
241
+ if (!title) {
242
+ throw new Error(`No se pudo obtener el t\xEDtulo para la issue #${issueNumber}`);
243
+ }
244
+ s.stop(`Datos de la issue obtenidos correctamente`);
245
+ const slugTitle = title.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
246
+ const ticketRef = `${owner}-${repo}-${issueNumber}`;
247
+ const branchName = `${ticketRef}-${slugTitle}`.substring(0, 100);
248
+ let summaryText = `${pc2.bold("Issue:")} #${issueNumber}
249
+ ${pc2.bold("T\xEDtulo:")} ${title}
250
+ `;
251
+ if (label) summaryText += `${pc2.bold("Label:")} ${label}
252
+ `;
253
+ summaryText += `${pc2.bold("Rama:")} ${pc2.cyan(branchName)}`;
254
+ p2.note(summaryText, "Resumen del PR");
255
+ const shouldContinue = await p2.confirm({
256
+ message: "\xBFContinuar con la creaci\xF3n de la rama y PR?",
257
+ initialValue: true
258
+ });
259
+ if (p2.isCancel(shouldContinue) || !shouldContinue) {
260
+ p2.cancel("Operaci\xF3n cancelada por el usuario.");
261
+ process.exit(0);
262
+ }
263
+ s.start("Validando estado de git...");
264
+ const statusOutput = execSync2("git status --porcelain", { encoding: "utf-8" }).trim();
265
+ if (statusOutput) {
266
+ throw new Error("Ten\xE9s cambios sin commitear. Por favor hac\xE9 commit o stash antes de correr esto.");
267
+ }
268
+ const currentBranch = execSync2("git branch --show-current", { encoding: "utf-8" }).trim();
269
+ if (currentBranch) {
270
+ try {
271
+ const unpushedOutput = execSync2(`git log @{u}..HEAD --oneline`, { encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"] }).toString().trim();
272
+ if (unpushedOutput) {
273
+ throw new Error(`Ten\xE9s commits sin pushear en la rama '${currentBranch}'.
274
+ Por favor hac\xE9 push antes de correr este script.`);
275
+ }
276
+ } catch (e) {
277
+ }
278
+ }
279
+ s.stop("Estado de git validado");
280
+ s.start("Cambiando a develop y actualizando...");
281
+ execSync2(`git checkout develop`, { stdio: "ignore" });
282
+ execSync2(`git pull origin develop`, { stdio: "ignore" });
283
+ s.stop("Rama develop actualizada");
284
+ s.start(`Creando nueva rama: ${branchName}`);
285
+ execSync2(`git checkout -b ${branchName}`, { stdio: "ignore" });
286
+ s.stop(`Rama ${pc2.cyan(branchName)} creada`);
287
+ s.start("Creando commit inicial...");
288
+ execSync2(`git add .`, { stdio: "ignore" });
289
+ execSync2(`git commit -m "feat(${ticketRef}): initial commit for ${title}" --allow-empty --no-verify`, { stdio: "ignore" });
290
+ s.stop("Commit inicial creado");
291
+ s.start("Pusheando rama al remoto...");
292
+ execSync2(`git push origin ${branchName}`, { stdio: "ignore" });
293
+ s.stop("Rama pusheada");
294
+ s.start("Creando Pull Request en GitHub...");
295
+ const prTitle = label ? `[${ticketRef}][${label}] ${title}` : `[${ticketRef}] ${title}`;
296
+ const issueUrl = `https://github.com/${owner}/${repo}/issues/${issueNumber}`;
297
+ const prBody = description ? `**Relates to GitHub issue [${issueNumber}](${issueUrl})**
298
+
299
+ ${description}` : `**Relates to GitHub issue [${issueNumber}](${issueUrl})**`;
300
+ execSync2(`gh pr create --title "${prTitle}" --body "${prBody}" --base develop --head "${branchName}"`, { stdio: "ignore" });
301
+ s.stop("Pull Request creado");
302
+ execSync2(`git push --set-upstream origin "${branchName}"`, { stdio: "ignore" });
303
+ p2.outro(`\xA1Todo listo! Est\xE1s en la rama ${pc2.cyan(branchName)} y la PR ya est\xE1 subida.`);
304
+ } catch (err) {
305
+ s.stop("Ocurri\xF3 un error");
306
+ p2.cancel(pc2.red(`Error: ${err.message}`));
307
+ process.exit(1);
308
+ }
309
+ };
310
+
311
+ // src/index.ts
312
+ var program = new Command();
313
+ program.name("gh-createpr").description("CLI interactiva para crear GitHub Pull Requests desde Jira o GitHub Issues").version("1.3.2");
314
+ program.command("jira").description("Crear PR desde un ticket de Jira").argument("[ticket]", "ID del ticket de Jira (ej: LAN-3)").action(async (ticket) => {
315
+ let finalTicket = ticket;
316
+ if (!finalTicket) {
317
+ p3.intro(pc3.bgBlue(pc3.white(" gh-createpr: Jira ")));
318
+ const result = await p3.text({
319
+ message: "Ingres\xE1 el ID del ticket de Jira (ej: LAN-3):",
320
+ placeholder: "LAN-3",
321
+ validate(value) {
322
+ if (!value) return "Por favor ingres\xE1 un ticket.";
323
+ }
324
+ });
325
+ if (p3.isCancel(result)) {
326
+ p3.cancel("Operaci\xF3n cancelada.");
327
+ process.exit(0);
328
+ }
329
+ finalTicket = result;
330
+ }
331
+ await createPrFromJira(finalTicket);
332
+ });
333
+ program.command("github").description("Crear PR desde una GitHub Issue").argument("[issue]", "ID de la Issue (ej: owner/repo#123 o #123)").action(async (issue) => {
334
+ let finalIssue = issue;
335
+ if (!finalIssue) {
336
+ p3.intro(pc3.bgMagenta(pc3.white(" gh-createpr: GitHub Issue ")));
337
+ const result = await p3.text({
338
+ message: "Ingres\xE1 el ID de la Issue (ej: owner/repo#123 o #123):",
339
+ placeholder: "#123",
340
+ validate(value) {
341
+ if (!value) return "Por favor ingres\xE1 un identificador.";
342
+ }
343
+ });
344
+ if (p3.isCancel(result)) {
345
+ p3.cancel("Operaci\xF3n cancelada.");
346
+ process.exit(0);
347
+ }
348
+ finalIssue = result;
349
+ }
350
+ await createPrFromGithubIssue(finalIssue);
351
+ });
352
+ program.action(async () => {
353
+ p3.intro(pc3.bgCyan(pc3.black(" gh-createpr: CLI Interactiva ")));
354
+ const sourceType = await p3.select({
355
+ message: "\xBFDesde d\xF3nde quer\xE9s crear tu Pull Request?",
356
+ options: [
357
+ { value: "jira", label: "Jira Ticket", hint: "Ej: LAN-3" },
358
+ { value: "github", label: "GitHub Issue", hint: "Ej: #123 o owner/repo#123" }
359
+ ]
360
+ });
361
+ if (p3.isCancel(sourceType)) {
362
+ p3.cancel("Operaci\xF3n cancelada por el usuario.");
363
+ process.exit(0);
364
+ }
365
+ if (sourceType === "jira") {
366
+ const ticketId = await p3.text({
367
+ message: "Ingres\xE1 el ID del ticket de Jira:",
368
+ placeholder: "LAN-3",
369
+ validate: (value) => {
370
+ if (!value) return "Por favor ingres\xE1 un ticket v\xE1lido.";
371
+ }
372
+ });
373
+ if (p3.isCancel(ticketId)) {
374
+ p3.cancel("Operaci\xF3n cancelada.");
375
+ process.exit(0);
376
+ }
377
+ await createPrFromJira(ticketId);
378
+ } else if (sourceType === "github") {
379
+ const issueId = await p3.text({
380
+ message: "Ingres\xE1 el ID de la Issue:",
381
+ placeholder: "#123",
382
+ validate: (value) => {
383
+ if (!value) return "Por favor ingres\xE1 un ID v\xE1lido.";
384
+ }
385
+ });
386
+ if (p3.isCancel(issueId)) {
387
+ p3.cancel("Operaci\xF3n cancelada.");
388
+ process.exit(0);
389
+ }
390
+ await createPrFromGithubIssue(issueId);
391
+ }
392
+ });
393
+ program.parse(process.argv);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,aAAa,CAAC;KACnB,WAAW,CAAC,4EAA4E,CAAC;KACzF,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,kCAAkC,CAAC;KAC/C,QAAQ,CAAC,UAAU,EAAE,mCAAmC,CAAC;KACzD,MAAM,CAAC,KAAK,EAAE,MAA0B,EAAE,EAAE;IAC3C,IAAI,WAAW,GAAG,MAAM,CAAC;IAEzB,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,+CAA+C;YACxD,WAAW,EAAE,OAAO;YACpB,QAAQ,CAAC,KAAK;gBACZ,IAAI,CAAC,KAAK;oBAAE,OAAO,8BAA8B,CAAC;YACpD,CAAC;SACF,CAAC,CAAC;QACH,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACvB,CAAC,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,WAAW,GAAG,MAAgB,CAAC;IACjC,CAAC;IAED,MAAM,gBAAgB,CAAC,WAAW,CAAC,CAAC;AACtC,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,iCAAiC,CAAC;KAC9C,QAAQ,CAAC,SAAS,EAAE,4CAA4C,CAAC;KACjE,MAAM,CAAC,KAAK,EAAE,KAAyB,EAAE,EAAE;IAC1C,IAAI,UAAU,GAAG,KAAK,CAAC;IAEvB,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC,CAAC;QAC/D,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,wDAAwD;YACjE,WAAW,EAAE,MAAM;YACnB,QAAQ,CAAC,KAAK;gBACZ,IAAI,CAAC,KAAK;oBAAE,OAAO,qCAAqC,CAAC;YAC3D,CAAC;SACF,CAAC,CAAC;QACH,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACvB,CAAC,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,UAAU,GAAG,MAAgB,CAAC;IAChC,CAAC;IAED,MAAM,uBAAuB,CAAC,UAAU,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE;IACxB,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC,CAAC;IAE/D,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,MAAM,CAAC;QAChC,OAAO,EAAE,4CAA4C;QACrD,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,WAAW,EAAE;YAC1D,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,2BAA2B,EAAE;SAC9E;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,CAAC,CAAC,MAAM,CAAC,qCAAqC,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC;YAC5B,OAAO,EAAE,mCAAmC;YAC5C,WAAW,EAAE,OAAO;YACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;gBAClB,IAAI,CAAC,KAAK;oBAAE,OAAO,qCAAqC,CAAC;YAC3D,CAAC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,CAAC,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,gBAAgB,CAAC,QAAkB,CAAC,CAAC;IAC7C,CAAC;SAAM,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC;YAC3B,OAAO,EAAE,4BAA4B;YACrC,WAAW,EAAE,MAAM;YACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;gBAClB,IAAI,CAAC,KAAK;oBAAE,OAAO,iCAAiC,CAAC;YACvD,CAAC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,CAAC,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,uBAAuB,CAAC,OAAiB,CAAC,CAAC;IACnD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
package/dist/jira.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare const createPrFromJira: (jiraTicket: string) => Promise<void>;
2
+ //# sourceMappingURL=jira.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jira.d.ts","sourceRoot":"","sources":["../src/jira.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,gBAAgB,GAAU,YAAY,MAAM,kBAkKxD,CAAC"}
package/dist/jira.js ADDED
@@ -0,0 +1,145 @@
1
+ import { execSync } from "child_process";
2
+ import * as p from "@clack/prompts";
3
+ import pc from "picocolors";
4
+ // Use native fetch (available in Node.js 18+)
5
+ // @ts-ignore
6
+ const fetch = globalThis.fetch;
7
+ export const createPrFromJira = async (jiraTicket) => {
8
+ const JIRA_BASE_URL = process.env.JIRA_BASE_URL || "";
9
+ const JIRA_EMAIL = process.env.JIRA_EMAIL || "";
10
+ const JIRA_API_TOKEN = process.env.JIRA_API_TOKEN || "";
11
+ if (!JIRA_BASE_URL || !JIRA_EMAIL || !JIRA_API_TOKEN) {
12
+ p.cancel(pc.red("Faltan variables de entorno (JIRA_BASE_URL, JIRA_EMAIL, JIRA_API_TOKEN)"));
13
+ process.exit(1);
14
+ }
15
+ const s = p.spinner();
16
+ s.start(`Buscando datos del ticket ${pc.cyan(jiraTicket)} en Jira...`);
17
+ try {
18
+ const res = await fetch(`${JIRA_BASE_URL}/rest/api/3/issue/${jiraTicket}`, {
19
+ headers: {
20
+ "Authorization": "Basic " + Buffer.from(`${JIRA_EMAIL}:${JIRA_API_TOKEN}`).toString("base64"),
21
+ "Accept": "application/json"
22
+ }
23
+ });
24
+ if (!res.ok) {
25
+ throw new Error(`Error de Jira: ${res.status} ${res.statusText}`);
26
+ }
27
+ const response = await res.json();
28
+ if (response.errorMessages && response.errorMessages.length > 0) {
29
+ throw new Error(`Error de Jira: ${response.errorMessages.join(", ")}`);
30
+ }
31
+ const title = response.fields?.summary || "";
32
+ const description = response.fields?.description?.content?.[0]?.content?.[0]?.text || "";
33
+ // Get team
34
+ const teamFields = [
35
+ response.fields?.customfield_10001?.name,
36
+ response.fields?.customfield_10001,
37
+ response.fields?.customfield_10037?.value,
38
+ response.fields?.customfield_10038?.value,
39
+ response.fields?.components?.[0]?.name,
40
+ response.fields?.labels?.[0],
41
+ ].filter(Boolean);
42
+ let team = teamFields.length > 0 ? teamFields[0] : response.fields?.project?.key || "";
43
+ if (!title) {
44
+ throw new Error(`No se pudo obtener el título para el ticket ${jiraTicket}`);
45
+ }
46
+ if (!team) {
47
+ throw new Error(`No se pudo obtener el equipo para el ticket ${jiraTicket}`);
48
+ }
49
+ s.stop(`Datos de Jira obtenidos correctamente`);
50
+ // Create slug for branch
51
+ const slugTitle = title
52
+ .normalize("NFD").replace(/[\u0300-\u036f]/g, "")
53
+ .toLowerCase()
54
+ .replace(/[^a-z0-9]+/g, "-")
55
+ .replace(/^-+|-+$/g, "");
56
+ const ticketLower = jiraTicket.toLowerCase();
57
+ const branchName = `${ticketLower}-${slugTitle}`;
58
+ p.note(`${pc.bold("Ticket:")} ${jiraTicket}\n` +
59
+ `${pc.bold("Título:")} ${title}\n` +
60
+ `${pc.bold("Equipo:")} ${team}\n` +
61
+ `${pc.bold("Rama:")} ${pc.cyan(branchName)}`, "Resumen del PR");
62
+ const shouldContinue = await p.confirm({
63
+ message: '¿Continuar con la creación de la rama y PR?',
64
+ initialValue: true,
65
+ });
66
+ if (p.isCancel(shouldContinue) || !shouldContinue) {
67
+ p.cancel('Operación cancelada por el usuario.');
68
+ process.exit(0);
69
+ }
70
+ s.start('Validando estado de git...');
71
+ // Validate: Check for uncommitted changes
72
+ const statusOutput = execSync("git status --porcelain", { encoding: "utf-8" }).trim();
73
+ if (statusOutput) {
74
+ throw new Error("Tenés cambios sin commitear. Por favor hacé commit o stash antes de correr esto.");
75
+ }
76
+ s.stop('Estado de git validado');
77
+ s.start('Cambiando a develop y actualizando...');
78
+ execSync(`git checkout develop`, { stdio: "ignore" });
79
+ execSync(`git pull origin develop`, { stdio: "ignore" });
80
+ s.stop('Rama develop actualizada');
81
+ s.start(`Creando nueva rama: ${branchName}`);
82
+ execSync(`git checkout -b ${branchName}`, { stdio: "ignore" });
83
+ s.stop(`Rama ${pc.cyan(branchName)} creada`);
84
+ s.start('Creando commit inicial...');
85
+ execSync(`git add .`, { stdio: "ignore" });
86
+ execSync(`git commit -m "feat(${jiraTicket}): initial commit for ${title}" --allow-empty --no-verify`, { stdio: "ignore" });
87
+ s.stop('Commit inicial creado');
88
+ s.start('Pusheando rama al remoto...');
89
+ execSync(`git push origin ${branchName}`, { stdio: "ignore" });
90
+ s.stop('Rama pusheada');
91
+ s.start('Creando Pull Request en GitHub...');
92
+ const prTitle = team ? `[${jiraTicket}][${team}] ${title}` : `[${jiraTicket}] ${title}`;
93
+ const prBody = `**Relates to Jira ticket [${jiraTicket}](${JIRA_BASE_URL}/browse/${jiraTicket})**\n\n${description}`;
94
+ execSync(`gh pr create --title "${prTitle}" --body "${prBody}" --base develop --head "${branchName}"`, { stdio: "ignore" });
95
+ s.stop('Pull Request creado');
96
+ // JIRA TRANSITION TO IN PROGRESS
97
+ s.start(`Moviendo ticket ${jiraTicket} a 'In Progress'...`);
98
+ try {
99
+ const transitionsRes = await fetch(`${JIRA_BASE_URL}/rest/api/3/issue/${jiraTicket}/transitions`, {
100
+ method: "GET",
101
+ headers: {
102
+ "Authorization": "Basic " + Buffer.from(`${JIRA_EMAIL}:${JIRA_API_TOKEN}`).toString("base64"),
103
+ "Accept": "application/json"
104
+ }
105
+ });
106
+ if (transitionsRes.ok) {
107
+ const transitionsData = await transitionsRes.json();
108
+ const transitions = transitionsData.transitions || [];
109
+ const inProgress = transitions.find((t) => t.name.toLowerCase() === "in progress");
110
+ if (inProgress) {
111
+ const doTransitionRes = await fetch(`${JIRA_BASE_URL}/rest/api/3/issue/${jiraTicket}/transitions`, {
112
+ method: "POST",
113
+ headers: {
114
+ "Authorization": "Basic " + Buffer.from(`${JIRA_EMAIL}:${JIRA_API_TOKEN}`).toString("base64"),
115
+ "Accept": "application/json",
116
+ "Content-Type": "application/json"
117
+ },
118
+ body: JSON.stringify({ transition: { id: inProgress.id } })
119
+ });
120
+ if (!doTransitionRes.ok) {
121
+ p.log.warn(`No se pudo mover a In Progress: ${doTransitionRes.statusText}`);
122
+ }
123
+ else {
124
+ p.log.success(`Ticket ${jiraTicket} movido a 'In Progress' en Jira`);
125
+ }
126
+ }
127
+ else {
128
+ p.log.warn("No se encontró el estado 'In Progress' para este ticket.");
129
+ }
130
+ }
131
+ }
132
+ catch (jiraTransitionError) {
133
+ p.log.warn(`Error al intentar mover en Jira: ${jiraTransitionError.message}`);
134
+ }
135
+ s.stop('Transición de Jira procesada');
136
+ execSync(`git push --set-upstream origin "${branchName}"`, { stdio: "ignore" });
137
+ p.outro(`¡Todo listo! Estás en la rama ${pc.cyan(branchName)} y la PR ya está subida.`);
138
+ }
139
+ catch (err) {
140
+ s.stop('Ocurrió un error');
141
+ p.cancel(pc.red(`Error: ${err.message}`));
142
+ process.exit(1);
143
+ }
144
+ };
145
+ //# sourceMappingURL=jira.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jira.js","sourceRoot":"","sources":["../src/jira.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@lansisdev/gh-createpr",
3
+ "version": "1.3.2",
4
+ "main": "dist/index.js",
5
+ "bin": {
6
+ "gh-createpr": "./dist/index.js"
7
+ },
8
+ "scripts": {
9
+ "build": "rm -rf dist && tsc && npm run bundle",
10
+ "bundle": "esbuild src/index.ts --bundle --platform=node --target=node18 --outfile=dist/index.js --format=esm --packages=external --allow-overwrite",
11
+ "start": "node dist/index.js",
12
+ "dev": "ts-node --esm src/index.ts",
13
+ "prepare": "npm run build && chmod +x dist/index.js",
14
+ "build:bin": "rm -rf dist/bin && mkdir -p dist/bin && bun build src/index.ts --compile --target=bun-darwin-x64 --outfile dist/bin/gh-createpr-darwin-amd64 && bun build src/index.ts --compile --target=bun-darwin-arm64 --outfile dist/bin/gh-createpr-darwin-arm64",
15
+ "test": "echo \"Error: no test specified\" && exit 1"
16
+ },
17
+ "keywords": [],
18
+ "author": "Gonzalo Buasso",
19
+ "license": "ISC",
20
+ "description": "CLI tool to create GitHub PRs from Jira tickets",
21
+ "type": "module",
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "dependencies": {
26
+ "@clack/prompts": "^1.1.0",
27
+ "@types/node": "^24.2.1",
28
+ "commander": "^14.0.0",
29
+ "picocolors": "^1.1.1",
30
+ "ts-node": "^10.9.2",
31
+ "typescript": "^5.9.2"
32
+ },
33
+ "devDependencies": {
34
+ "esbuild": "^0.25.8"
35
+ }
36
+ }