linear-github-cli 1.0.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/.env.example ADDED
@@ -0,0 +1,3 @@
1
+ # Linear API Key
2
+ # Get your API key from: https://linear.app/settings/api
3
+ LINEAR_API_KEY=lin_api_...
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2025 negoth
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,336 @@
1
+ # Linear + GitHub CLI Tool
2
+
3
+ A CLI tool for creating GitHub issues with Linear integration, providing an interactive experience with autocomplete and dropdown selections.
4
+
5
+ ## Installation
6
+
7
+ ### Option 1: Install from npm (Recommended)
8
+
9
+ ```bash
10
+ npm install -g linear-github-cli
11
+ ```
12
+
13
+ After installation, use `lg` or `linear-github` from anywhere:
14
+
15
+ ```bash
16
+ lg create-parent
17
+ lg create-sub
18
+ lg --help
19
+ ```
20
+
21
+ ### Option 2: Install from Source
22
+
23
+ ```bash
24
+ git clone <repository-url>
25
+ cd linear-github-cli
26
+ npm install
27
+ npm install -g .
28
+ ```
29
+
30
+ ### Option 3: Development Mode
31
+
32
+ ```bash
33
+ git clone <repository-url>
34
+ cd linear-github-cli
35
+ npm install
36
+ npm run dev create-parent
37
+ npm run dev create-sub
38
+ ```
39
+
40
+ ## Setup
41
+
42
+ ### 1. Install Dependencies (if installing from source)
43
+
44
+ ```bash
45
+ npm install
46
+ ```
47
+
48
+ ### 2. Configure Environment Variables
49
+
50
+ **Option 1: Using .env file (Recommended)**
51
+
52
+ Create a `.env` file in the project root:
53
+
54
+ ```bash
55
+ cp .env.example .env
56
+ # Then edit .env and add your API key: LINEAR_API_KEY=lin_api_...
57
+ ```
58
+
59
+ Or create it directly:
60
+
61
+ ```bash
62
+ echo 'LINEAR_API_KEY=lin_api_...' > .env
63
+ ```
64
+
65
+ **Option 2: Export in shell (Temporary)**
66
+
67
+ ```bash
68
+ export LINEAR_API_KEY="lin_api_..."
69
+ ```
70
+
71
+ **Get your Linear API key:**
72
+ 1. Go to [Linear Settings > API](https://linear.app/settings/api)
73
+ 2. Create a new Personal API Key
74
+ 3. Copy the key (starts with `lin_api_`)
75
+
76
+ **Note:** The `.env` file is already in `.gitignore`, so it won't be committed to Git.
77
+
78
+ ### 3. Authenticate GitHub CLI
79
+
80
+ ```bash
81
+ gh auth login
82
+ gh auth status # Verify
83
+ ```
84
+
85
+ ## Usage
86
+
87
+ ### Create Parent Issue
88
+
89
+ ```bash
90
+ lg create-parent
91
+ # or
92
+ lg parent
93
+ ```
94
+
95
+ Follow the interactive prompts:
96
+ 1. Select repository from dropdown
97
+ 2. Enter issue title
98
+ 3. Enter description (opens in editor)
99
+ 4. Optionally set due date (YYYY-MM-DD)
100
+ 5. Select GitHub labels (checkboxes). Choices: `feat`, `fix`, `chore`, `docs`, `refactor`, `test`, `research`
101
+ 6. Optionally select GitHub project
102
+ 7. Optionally select Linear project (after sync)
103
+
104
+ ### Create Sub-Issue
105
+
106
+ ```bash
107
+ lg create-sub
108
+ # or
109
+ lg sub
110
+ ```
111
+
112
+ Follow the interactive prompts:
113
+ 1. Select repository from dropdown
114
+ 2. Select parent issue from list
115
+ 3. Enter sub-issue title
116
+ 4. Enter description (opens in editor)
117
+ 5. Optionally set due date (YYYY-MM-DD)
118
+ 6. Select GitHub labels (same predefined list as above)
119
+ 7. Optionally select Linear project (after sync)
120
+
121
+ ### Show Help
122
+
123
+ ```bash
124
+ lg --help
125
+ lg create-parent --help
126
+ lg create-sub --help
127
+ ```
128
+
129
+ ## Features
130
+
131
+ - ✅ **Interactive repository selection**: Choose from accessible GitHub repositories
132
+ - ✅ **Project autocomplete**: Select GitHub and Linear projects from dropdowns
133
+ - ✅ **Parent issue selection**: Browse and select parent issues when creating sub-issues
134
+ - ✅ **GitHub label sync**: Multi-select from the seven standard labels (feat, fix, chore, docs, refactor, test, research); selections are mirrored to matching Linear team labels
135
+ - ✅ **Due date input**: Optional date picker with validation
136
+ - ✅ **Automatic Linear sync**: Waits for Linear sync and updates metadata (due date, project, labels)
137
+ - ✅ **Parent-child relationships**: Automatically links sub-issues to parent issues
138
+ - ✅ **Status automation**: Issues start in Linear backlog; rely on the Linear × GitHub PR automation for status changes
139
+
140
+ ## Examples
141
+
142
+ ### Basic Usage
143
+
144
+ ```bash
145
+ # Create a parent issue
146
+ lg create-parent
147
+
148
+ # Create a sub-issue
149
+ lg create-sub
150
+ ```
151
+
152
+ ### With Environment Variable
153
+
154
+ ```bash
155
+ LINEAR_API_KEY="lin_api_..." lg create-parent
156
+ ```
157
+
158
+ ## Requirements
159
+
160
+ - **Node.js** 18+ and npm
161
+ - **GitHub CLI** (`gh`) installed and authenticated
162
+ - **Linear API key** (get from [Linear Settings](https://linear.app/settings/api))
163
+
164
+ ## Building from Source
165
+
166
+ ```bash
167
+ npm install
168
+ npm run build
169
+ ```
170
+
171
+ The compiled JavaScript will be in the `dist/` directory.
172
+
173
+ ## Troubleshooting
174
+
175
+ ### "LINEAR_API_KEY environment variable is required"
176
+
177
+ Make sure you've set the environment variable:
178
+
179
+ ```bash
180
+ export LINEAR_API_KEY="lin_api_..."
181
+ ```
182
+
183
+ ### "lg: command not found"
184
+
185
+ If you installed globally, make sure npm's global bin directory is in your PATH:
186
+
187
+ ```bash
188
+ # Check npm global prefix
189
+ npm config get prefix
190
+
191
+ # Add to PATH (macOS/Linux)
192
+ export PATH="$(npm config get prefix)/bin:$PATH"
193
+ ```
194
+
195
+ ### "gh: command not found"
196
+
197
+ Install GitHub CLI:
198
+ - **macOS**: `brew install gh`
199
+ - **Linux/Windows**: See [GitHub CLI installation](https://cli.github.com/manual/installation)
200
+
201
+ ### Repository list is empty
202
+
203
+ Make sure you're authenticated:
204
+
205
+ ```bash
206
+ gh auth status
207
+ gh auth login # if not authenticated
208
+ ```
209
+
210
+ ### Projects not showing
211
+
212
+ GitHub Projects might not be available via CLI in all repositories. This is optional - you can skip project selection.
213
+
214
+ ### Linear issue not found after sync
215
+
216
+ The tool waits 5 seconds for Linear sync. If the issue still isn't found:
217
+ - Check Linear GitHub integration is enabled
218
+ - Wait a bit longer and manually update metadata in Linear
219
+ - The GitHub Actions workflow will also set metadata automatically
220
+
221
+ ## Architecture
222
+
223
+ ```
224
+ lg (CLI)
225
+ ├── cli.ts # CLI entry point (Commander.js)
226
+ ├── commands/
227
+ │ ├── create-parent.ts # Parent issue command
228
+ │ └── create-sub.ts # Sub-issue command
229
+ ├── linear-client.ts # Linear SDK wrapper
230
+ ├── github-client.ts # GitHub CLI/API wrapper
231
+ └── input-handler.ts # Interactive prompts (Inquirer.js)
232
+ ```
233
+
234
+ ## Development
235
+
236
+ ### Run in Development Mode
237
+
238
+ ```bash
239
+ npm run dev create-parent
240
+ npm run dev create-sub
241
+ ```
242
+
243
+ ### Build
244
+
245
+ ```bash
246
+ npm run build
247
+ ```
248
+
249
+ ## Label Behaviour
250
+
251
+ - The CLI surfaces the seven standard GitHub labels: `feat`, `fix`, `chore`, `docs`, `refactor`, `test`, `research`. (Custom GitHub labels can still be added manually after creation.)
252
+ - When an issue syncs to Linear, the CLI ensures team-scoped Linear labels exist. New labels are created for the linked team if necessary and then attached to the issue.
253
+ - Linear issues are always created in the backlog ("No status"). Move them forward by opening PRs and letting the Linear × GitHub integration handle status updates automatically.
254
+
255
+ ## Future Enhancements
256
+
257
+ - Caching of repositories and projects
258
+ - Configuration file for defaults
259
+ - Better error handling and retry logic
260
+ - Additional commands (list, update, close issues)
261
+ - Template support for issue creation
262
+
263
+ ## PR Creation Workflow
264
+
265
+ After creating issues with `lg`, use the Linear-GitHub integration workflow to manage PRs and track progress.
266
+
267
+ ### Recommended Approach: Aliases
268
+
269
+ The simplest approach is to use `gh` aliases or interactive mode:
270
+
271
+ ```bash
272
+ # Set up aliases (optional)
273
+ gh alias set prd 'pr create --draft --title "$1" --body "solve: #$2"'
274
+ gh alias set prms 'pr merge --squash --delete-branch'
275
+
276
+ # Or use interactive mode (recommended)
277
+ gh pr create --draft --fill
278
+ gh pr ready # Standard command, use directly when starting work
279
+ gh prms # Merge with squash and delete branch
280
+ ```
281
+
282
+ ### Workflow Overview
283
+
284
+ 1. **Create issue** - Use `lg parent/sub` command
285
+ 2. **Create branch** - Include issue number (e.g., `feat/LEA-123-task`)
286
+ 3. **Create draft PR** - Right after branch creation, before work begins
287
+ - Include Linear issue ID in title (copy with `Cmd + .` in Linear)
288
+ - Include `solve: #123` or `Closes #123` in body
289
+ - Linear status: `Todo`
290
+ 4. **Start work** - Begin actual development
291
+ 5. **Mark PR ready** - Use `gh pr ready` when ready for review
292
+ - Linear status: `In Progress`
293
+ 6. **Continue work** - More commits, add PR/issue comments for progress
294
+ 7. **Merge** - When task is complete
295
+ - Linear status: `Done`
296
+ - GitHub issue: Closed automatically
297
+
298
+ ### Two PR Types
299
+
300
+ **Completing PRs (Issue Completion):**
301
+ - Include Linear issue ID in title: `LEA-123 Implement login`
302
+ - Use `solve: #123` or `Closes #123` in body
303
+ - Merging sets Linear status to `Done` and closes GitHub issue
304
+
305
+ **Partial Progress PRs (Non-Completing):**
306
+ - Do NOT include Linear issue ID in title: `Add login form`
307
+ - Use `Ref: #123` in body
308
+ - Merging keeps Linear status unchanged and doesn't close GitHub issue
309
+ - Useful for tracking incremental work on large issues
310
+
311
+ ### Linear Settings
312
+
313
+ Configure Linear's GitHub integration:
314
+
315
+ 1. Linear Settings → Integrations → GitHub
316
+ 2. Open "Pull request and commit automations"
317
+ 3. Configure:
318
+ - **On draft PR open** → `Todo` ✅
319
+ - **On PR open (ready)** → `In Progress` ✅ (triggered by `gh pr ready`)
320
+ - **On PR review request** → `No Action`
321
+ - **On PR ready for merge** → `No Action`
322
+ - **On PR merge** → `Done` ✅
323
+
324
+ ### Additional Notes
325
+
326
+ For branch name auto-extraction or more complex logic, you can create custom shell functions or use `gh pr create --fill` interactively, which allows you to manually enter the issue number.
327
+
328
+ For most cases, aliases or `gh pr create --fill` are simpler and sufficient.
329
+
330
+ ### Documentation
331
+
332
+ See `workflow.md` for complete workflow documentation, examples, and troubleshooting.
333
+
334
+ ## License
335
+
336
+ ISC
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.sanitizeBranchName = sanitizeBranchName;
7
+ exports.selectBranchPrefix = selectBranchPrefix;
8
+ exports.generateBranchName = generateBranchName;
9
+ exports.extractLinearIssueId = extractLinearIssueId;
10
+ exports.extractBranchPrefix = extractBranchPrefix;
11
+ exports.checkUnpushedCommitsOnCurrentBranch = checkUnpushedCommitsOnCurrentBranch;
12
+ exports.createGitBranch = createGitBranch;
13
+ const child_process_1 = require("child_process");
14
+ const inquirer_1 = __importDefault(require("inquirer"));
15
+ /**
16
+ * Valid branch prefix types (must match commit_typed.sh)
17
+ */
18
+ const VALID_BRANCH_PREFIXES = ['feat', 'fix', 'chore', 'docs', 'refactor', 'test', 'research'];
19
+ /**
20
+ * Maps GitHub labels to branch prefix types
21
+ * Since GitHub labels are already updated to match the standard list,
22
+ * labels should directly map to branch prefixes.
23
+ * @param label - GitHub label name (should be one of: feat, fix, chore, docs, refactor, test, research)
24
+ * @returns Branch prefix (same as label if valid, otherwise returns label as-is)
25
+ */
26
+ function mapLabelToBranchPrefix(label) {
27
+ const labelLower = label.toLowerCase();
28
+ // If label is already a valid branch prefix, return it as-is
29
+ if (VALID_BRANCH_PREFIXES.includes(labelLower)) {
30
+ return labelLower;
31
+ }
32
+ // Fallback: return label as-is (shouldn't happen if labels are properly configured)
33
+ return labelLower;
34
+ }
35
+ /**
36
+ * Sanitizes a title string to be used as part of a git branch name
37
+ * - Converts to lowercase
38
+ * - Replaces spaces with hyphens
39
+ * - Removes special characters (keeps alphanumeric and hyphens)
40
+ * - Removes leading/trailing hyphens
41
+ * - Collapses multiple consecutive hyphens
42
+ * - Limits length to 50 characters
43
+ *
44
+ * @param title - The title to sanitize
45
+ * @returns Sanitized branch name portion
46
+ */
47
+ function sanitizeBranchName(title) {
48
+ if (!title || title.trim().length === 0) {
49
+ return '';
50
+ }
51
+ // Convert to lowercase and replace spaces with hyphens
52
+ let sanitized = title.toLowerCase().replace(/\s+/g, '-');
53
+ // Remove special characters except hyphens and alphanumeric
54
+ sanitized = sanitized.replace(/[^a-z0-9-]/g, '');
55
+ // Collapse multiple consecutive hyphens
56
+ sanitized = sanitized.replace(/-+/g, '-');
57
+ // Remove leading and trailing hyphens
58
+ sanitized = sanitized.replace(/^-+|-+$/g, '');
59
+ // Limit length to 50 characters
60
+ if (sanitized.length > 50) {
61
+ sanitized = sanitized.substring(0, 50);
62
+ // Remove trailing hyphen if truncated
63
+ sanitized = sanitized.replace(/-+$/, '');
64
+ }
65
+ return sanitized;
66
+ }
67
+ /**
68
+ * Standard branch prefix options when no labels are available
69
+ */
70
+ const STANDARD_PREFIXES = [
71
+ { name: 'feat', value: 'feat' },
72
+ { name: 'fix', value: 'fix' },
73
+ { name: 'chore', value: 'chore' },
74
+ { name: 'docs', value: 'docs' },
75
+ { name: 'refactor', value: 'refactor' },
76
+ { name: 'test', value: 'test' },
77
+ { name: 'research', value: 'research' },
78
+ ];
79
+ /**
80
+ * Selects a branch prefix from GitHub labels
81
+ * - If no labels: prompts user to select from standard prefixes
82
+ * - If one label: maps and returns branch prefix
83
+ * - If multiple labels: prompts user to select one
84
+ *
85
+ * @param labels - Array of GitHub label names
86
+ * @returns Branch prefix string or null if user cancels
87
+ */
88
+ async function selectBranchPrefix(labels) {
89
+ // Map all labels to branch prefixes
90
+ const mappedPrefixes = labels && labels.length > 0
91
+ ? labels.map(label => ({
92
+ label,
93
+ prefix: mapLabelToBranchPrefix(label),
94
+ }))
95
+ : [];
96
+ // If no labels: prompt user to select from standard prefixes
97
+ if (mappedPrefixes.length === 0) {
98
+ const { selectedPrefix } = await inquirer_1.default.prompt([
99
+ {
100
+ type: 'list',
101
+ name: 'selectedPrefix',
102
+ message: 'Select branch prefix (no labels selected):',
103
+ choices: STANDARD_PREFIXES,
104
+ },
105
+ ]);
106
+ return selectedPrefix;
107
+ }
108
+ // If only one label, return its mapped prefix
109
+ if (mappedPrefixes.length === 1) {
110
+ return mappedPrefixes[0].prefix;
111
+ }
112
+ // Multiple labels: prompt user to select
113
+ const choices = mappedPrefixes.map(({ label, prefix }) => ({
114
+ name: `${label} → ${prefix}`,
115
+ value: prefix,
116
+ }));
117
+ const { selectedPrefix } = await inquirer_1.default.prompt([
118
+ {
119
+ type: 'list',
120
+ name: 'selectedPrefix',
121
+ message: 'Select branch prefix (multiple labels selected):',
122
+ choices,
123
+ },
124
+ ]);
125
+ return selectedPrefix;
126
+ }
127
+ /**
128
+ * Generates a branch name from prefix, Linear ID, and title
129
+ * Format: prefix/LinearID-sanitized-title
130
+ *
131
+ * @param prefix - Branch prefix (e.g., 'feat', 'docs')
132
+ * @param linearId - Linear issue ID (e.g., 'LEA-123')
133
+ * @param title - Issue title to sanitize
134
+ * @returns Full branch name
135
+ */
136
+ function generateBranchName(prefix, linearId, title) {
137
+ const sanitizedTitle = sanitizeBranchName(title);
138
+ if (!sanitizedTitle) {
139
+ // If title is empty after sanitization, just use prefix and ID
140
+ return `${prefix}/${linearId}`;
141
+ }
142
+ return `${prefix}/${linearId}-${sanitizedTitle}`;
143
+ }
144
+ /**
145
+ * Extracts Linear issue ID from a branch name
146
+ * - Matches pattern: prefix/LEA-123-title or prefix/LEA-123
147
+ * - Uses regex to find Linear issue ID format: [A-Z]+-\d+
148
+ *
149
+ * @param branchName - Branch name (e.g., 'feat/LEA-123-implement-login')
150
+ * @returns Linear issue ID (e.g., 'LEA-123') or null if not found
151
+ */
152
+ function extractLinearIssueId(branchName) {
153
+ if (!branchName || branchName.trim().length === 0) {
154
+ return null;
155
+ }
156
+ // Match Linear issue ID pattern: [A-Z]+-\d+ (e.g., LEA-123)
157
+ const match = branchName.match(/([A-Z]+-\d+)/);
158
+ return match ? match[1] : null;
159
+ }
160
+ /**
161
+ * Extracts branch prefix (commit type) from a branch name
162
+ * - Extracts the part before the first '/' (e.g., 'research' from 'research/LEA-75-probit-model')
163
+ * - Validates against VALID_BRANCH_PREFIXES
164
+ * - Returns 'feat' as default if prefix is not found or invalid
165
+ *
166
+ * @param branchName - Branch name (e.g., 'research/LEA-75-probit-model')
167
+ * @returns Branch prefix (e.g., 'research') or 'feat' as default
168
+ */
169
+ function extractBranchPrefix(branchName) {
170
+ if (!branchName || branchName.trim().length === 0) {
171
+ return 'feat';
172
+ }
173
+ // Extract prefix before first '/'
174
+ const parts = branchName.split('/');
175
+ if (parts.length === 0 || !parts[0]) {
176
+ return 'feat';
177
+ }
178
+ const prefix = parts[0].toLowerCase();
179
+ // Validate against valid prefixes
180
+ if (VALID_BRANCH_PREFIXES.includes(prefix)) {
181
+ return prefix;
182
+ }
183
+ // Return 'feat' as default if prefix is invalid
184
+ return 'feat';
185
+ }
186
+ /**
187
+ * Checks for unpushed commits on the current branch
188
+ * - Gets current branch name
189
+ * - Checks if remote branch exists
190
+ * - Counts unpushed commits
191
+ * - Returns information about unpushed commits
192
+ *
193
+ * @returns Object with hasUnpushed flag, count, and commit list
194
+ */
195
+ function checkUnpushedCommitsOnCurrentBranch() {
196
+ try {
197
+ // Check if we're in a git repository
198
+ try {
199
+ (0, child_process_1.execSync)('git rev-parse --git-dir', { stdio: 'pipe' });
200
+ }
201
+ catch (error) {
202
+ // Not in a git repository, return no unpushed commits
203
+ return { hasUnpushed: false, count: 0, commits: [] };
204
+ }
205
+ // Get current branch
206
+ const currentBranch = (0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf-8' }).trim();
207
+ if (!currentBranch) {
208
+ return { hasUnpushed: false, count: 0, commits: [] };
209
+ }
210
+ // Check if remote branch exists
211
+ try {
212
+ (0, child_process_1.execSync)(`git rev-parse --verify origin/${currentBranch}`, { stdio: 'pipe' });
213
+ }
214
+ catch (error) {
215
+ // Remote branch doesn't exist, no unpushed commits
216
+ return { hasUnpushed: false, count: 0, commits: [] };
217
+ }
218
+ // Count unpushed commits
219
+ const countOutput = (0, child_process_1.execSync)(`git rev-list --count origin/${currentBranch}..${currentBranch}`, { encoding: 'utf-8' }).trim();
220
+ const count = parseInt(countOutput, 10) || 0;
221
+ if (count === 0) {
222
+ return { hasUnpushed: false, count: 0, commits: [] };
223
+ }
224
+ // Get commit list
225
+ const commitsOutput = (0, child_process_1.execSync)(`git log origin/${currentBranch}..${currentBranch} --oneline`, { encoding: 'utf-8' }).trim();
226
+ const commits = commitsOutput ? commitsOutput.split('\n') : [];
227
+ return { hasUnpushed: true, count, commits };
228
+ }
229
+ catch (error) {
230
+ // On error, assume no unpushed commits to avoid blocking workflow
231
+ return { hasUnpushed: false, count: 0, commits: [] };
232
+ }
233
+ }
234
+ /**
235
+ * Creates a git branch and switches to it
236
+ * - Checks if in git repository
237
+ * - Checks if branch already exists
238
+ * - Creates branch using 'git switch -c'
239
+ *
240
+ * @param branchName - Name of the branch to create
241
+ * @returns true if successful, false otherwise
242
+ */
243
+ async function createGitBranch(branchName) {
244
+ try {
245
+ // Check if we're in a git repository
246
+ try {
247
+ (0, child_process_1.execSync)('git rev-parse --git-dir', { stdio: 'pipe' });
248
+ }
249
+ catch (error) {
250
+ console.log('⚠️ Not in a git repository. Branch creation skipped.');
251
+ return false;
252
+ }
253
+ // Check if branch already exists
254
+ try {
255
+ (0, child_process_1.execSync)(`git show-ref --verify --quiet refs/heads/${branchName}`, { stdio: 'pipe' });
256
+ console.log(`⚠️ Branch '${branchName}' already exists. Branch creation skipped.`);
257
+ return false;
258
+ }
259
+ catch (error) {
260
+ // Branch doesn't exist, which is what we want
261
+ }
262
+ // Create and switch to branch
263
+ (0, child_process_1.execSync)(`git switch -c ${branchName}`, { stdio: 'inherit' });
264
+ return true;
265
+ }
266
+ catch (error) {
267
+ console.error(`❌ Failed to create branch '${branchName}':`, error instanceof Error ? error.message : error);
268
+ return false;
269
+ }
270
+ }
package/dist/cli.js ADDED
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const dotenv_1 = require("dotenv");
6
+ const path_1 = require("path");
7
+ const create_parent_1 = require("./commands/create-parent");
8
+ const create_sub_1 = require("./commands/create-sub");
9
+ // Load .env file from the project root
10
+ // __dirname points to dist/ in compiled output, so we go up one level
11
+ (0, dotenv_1.config)({ path: (0, path_1.resolve)(__dirname, '..', '.env') });
12
+ const program = new commander_1.Command();
13
+ program
14
+ .name('lg')
15
+ .description('Linear + GitHub Integration CLI - Create GitHub issues with Linear sync')
16
+ .version('1.0.0');
17
+ program
18
+ .command('create-parent')
19
+ .alias('parent')
20
+ .description('Create a parent GitHub issue with Linear integration')
21
+ .action(async () => {
22
+ try {
23
+ await (0, create_parent_1.createParentIssue)();
24
+ }
25
+ catch (error) {
26
+ console.error('❌ Error:', error instanceof Error ? error.message : error);
27
+ process.exit(1);
28
+ }
29
+ });
30
+ program
31
+ .command('create-sub')
32
+ .alias('sub')
33
+ .description('Create a sub-issue linked to a parent issue')
34
+ .action(async () => {
35
+ try {
36
+ await (0, create_sub_1.createSubIssue)();
37
+ }
38
+ catch (error) {
39
+ console.error('❌ Error:', error instanceof Error ? error.message : error);
40
+ process.exit(1);
41
+ }
42
+ });
43
+ program.parse();