linear-github-cli 1.2.1 → 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/README.md +1 -1
- package/dist/branch-utils.js +29 -91
- package/dist/cli.js +10 -3
- package/dist/commands/commit-first.js +18 -3
- package/dist/commands/create-parent.js +30 -18
- package/dist/commands/create-sub.js +16 -15
- package/dist/env-utils.js +25 -3
- package/dist/github-client.js +12 -0
- package/dist/input-handler.js +1 -4
- package/dist/linear-client.js +35 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -301,7 +301,7 @@ gh prms # Merge with squash and delete branch
|
|
|
301
301
|
### Workflow Overview
|
|
302
302
|
|
|
303
303
|
1. **Create issue** - Use `lg parent/sub` command
|
|
304
|
-
2. **Create branch** - Include issue number (e.g., `
|
|
304
|
+
2. **Create branch** - Include issue number (e.g., `username/LEA-123-task`)
|
|
305
305
|
3. **Create draft PR** - Right after branch creation, before work begins
|
|
306
306
|
- Include Linear issue ID in title (copy with `Cmd + .` in Linear)
|
|
307
307
|
- Include `solve: #123` or `Closes #123` in body
|
package/dist/branch-utils.js
CHANGED
|
@@ -1,37 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.sanitizeBranchName = sanitizeBranchName;
|
|
7
|
-
exports.selectBranchPrefix = selectBranchPrefix;
|
|
8
4
|
exports.generateBranchName = generateBranchName;
|
|
9
5
|
exports.extractLinearIssueId = extractLinearIssueId;
|
|
10
6
|
exports.extractBranchPrefix = extractBranchPrefix;
|
|
11
7
|
exports.checkUnpushedCommitsOnCurrentBranch = checkUnpushedCommitsOnCurrentBranch;
|
|
12
8
|
exports.createGitBranch = createGitBranch;
|
|
13
9
|
const child_process_1 = require("child_process");
|
|
14
|
-
const inquirer_1 = __importDefault(require("inquirer"));
|
|
15
10
|
/**
|
|
16
11
|
* Valid branch prefix types (must match commit_typed.sh)
|
|
17
12
|
*/
|
|
18
13
|
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
14
|
/**
|
|
36
15
|
* Sanitizes a title string to be used as part of a git branch name
|
|
37
16
|
* - Converts to lowercase
|
|
@@ -65,88 +44,49 @@ function sanitizeBranchName(title) {
|
|
|
65
44
|
return sanitized;
|
|
66
45
|
}
|
|
67
46
|
/**
|
|
68
|
-
*
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
47
|
+
* Sanitizes a branch owner (username) to be used in a git branch name.
|
|
48
|
+
* - Converts to lowercase
|
|
49
|
+
* - Removes special characters (keeps alphanumeric and hyphens)
|
|
50
|
+
* - Collapses multiple consecutive hyphens
|
|
51
|
+
* - Removes leading/trailing hyphens
|
|
84
52
|
*
|
|
85
|
-
* @param
|
|
86
|
-
* @returns
|
|
53
|
+
* @param owner - The branch owner to sanitize
|
|
54
|
+
* @returns Sanitized branch owner portion
|
|
87
55
|
*/
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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;
|
|
56
|
+
function sanitizeBranchOwner(owner) {
|
|
57
|
+
if (!owner || owner.trim().length === 0) {
|
|
58
|
+
return '';
|
|
111
59
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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;
|
|
60
|
+
let sanitized = owner.toLowerCase().replace(/[^a-z0-9-]/g, '');
|
|
61
|
+
sanitized = sanitized.replace(/-+/g, '-');
|
|
62
|
+
sanitized = sanitized.replace(/^-+|-+$/g, '');
|
|
63
|
+
return sanitized;
|
|
126
64
|
}
|
|
127
65
|
/**
|
|
128
|
-
* Generates a branch name from
|
|
129
|
-
* Format:
|
|
66
|
+
* Generates a branch name from owner, Linear ID, and title
|
|
67
|
+
* Format: owner/LinearID-sanitized-title
|
|
130
68
|
*
|
|
131
|
-
* @param
|
|
69
|
+
* @param owner - Branch owner (e.g., GitHub username)
|
|
132
70
|
* @param linearId - Linear issue ID (e.g., 'LEA-123')
|
|
133
71
|
* @param title - Issue title to sanitize
|
|
134
72
|
* @returns Full branch name
|
|
135
73
|
*/
|
|
136
|
-
function generateBranchName(
|
|
74
|
+
function generateBranchName(owner, linearId, title) {
|
|
75
|
+
const sanitizedOwner = sanitizeBranchOwner(owner);
|
|
137
76
|
const sanitizedTitle = sanitizeBranchName(title);
|
|
77
|
+
const ownerSegment = sanitizedOwner || 'user';
|
|
138
78
|
if (!sanitizedTitle) {
|
|
139
|
-
// If title is empty after sanitization, just use
|
|
140
|
-
return `${
|
|
79
|
+
// If title is empty after sanitization, just use owner and ID
|
|
80
|
+
return `${ownerSegment}/${linearId}`;
|
|
141
81
|
}
|
|
142
|
-
return `${
|
|
82
|
+
return `${ownerSegment}/${linearId}-${sanitizedTitle}`;
|
|
143
83
|
}
|
|
144
84
|
/**
|
|
145
85
|
* Extracts Linear issue ID from a branch name
|
|
146
|
-
* - Matches pattern:
|
|
86
|
+
* - Matches pattern: owner/LEA-123-title or owner/LEA-123
|
|
147
87
|
* - Uses regex to find Linear issue ID format: [A-Z]+-\d+
|
|
148
88
|
*
|
|
149
|
-
* @param branchName - Branch name (e.g., '
|
|
89
|
+
* @param branchName - Branch name (e.g., 'negoth/LEA-123-implement-login')
|
|
150
90
|
* @returns Linear issue ID (e.g., 'LEA-123') or null if not found
|
|
151
91
|
*/
|
|
152
92
|
function extractLinearIssueId(branchName) {
|
|
@@ -161,27 +101,25 @@ function extractLinearIssueId(branchName) {
|
|
|
161
101
|
* Extracts branch prefix (commit type) from a branch name
|
|
162
102
|
* - Extracts the part before the first '/' (e.g., 'research' from 'research/LEA-75-probit-model')
|
|
163
103
|
* - Validates against VALID_BRANCH_PREFIXES
|
|
164
|
-
* - Returns 'feat' as default if prefix is not found or invalid
|
|
165
104
|
*
|
|
166
105
|
* @param branchName - Branch name (e.g., 'research/LEA-75-probit-model')
|
|
167
|
-
* @returns Branch prefix (e.g., 'research') or
|
|
106
|
+
* @returns Branch prefix (e.g., 'research') or null if not found/invalid
|
|
168
107
|
*/
|
|
169
108
|
function extractBranchPrefix(branchName) {
|
|
170
109
|
if (!branchName || branchName.trim().length === 0) {
|
|
171
|
-
return
|
|
110
|
+
return null;
|
|
172
111
|
}
|
|
173
112
|
// Extract prefix before first '/'
|
|
174
113
|
const parts = branchName.split('/');
|
|
175
114
|
if (parts.length === 0 || !parts[0]) {
|
|
176
|
-
return
|
|
115
|
+
return null;
|
|
177
116
|
}
|
|
178
117
|
const prefix = parts[0].toLowerCase();
|
|
179
118
|
// Validate against valid prefixes
|
|
180
119
|
if (VALID_BRANCH_PREFIXES.includes(prefix)) {
|
|
181
120
|
return prefix;
|
|
182
121
|
}
|
|
183
|
-
|
|
184
|
-
return 'feat';
|
|
122
|
+
return null;
|
|
185
123
|
}
|
|
186
124
|
/**
|
|
187
125
|
* Checks for unpushed commits on the current branch
|
package/dist/cli.js
CHANGED
|
@@ -13,9 +13,16 @@ const create_sub_1 = require("./commands/create-sub");
|
|
|
13
13
|
const env_utils_1 = require("./env-utils");
|
|
14
14
|
// Load .env file from current working directory, parent directories, or home directory
|
|
15
15
|
(0, env_utils_1.loadEnvFile)();
|
|
16
|
-
// Check for updates
|
|
17
|
-
const
|
|
18
|
-
(0,
|
|
16
|
+
// Check for updates (support both dev and built paths)
|
|
17
|
+
const pkgPathCandidates = [
|
|
18
|
+
(0, path_1.resolve)(__dirname, 'package.json'),
|
|
19
|
+
(0, path_1.resolve)(__dirname, '..', 'package.json'),
|
|
20
|
+
];
|
|
21
|
+
const pkgPath = pkgPathCandidates.find(candidate => (0, fs_1.existsSync)(candidate));
|
|
22
|
+
if (pkgPath) {
|
|
23
|
+
const pkg = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf-8'));
|
|
24
|
+
(0, update_notifier_1.default)({ pkg }).notify();
|
|
25
|
+
}
|
|
19
26
|
const program = new commander_1.Command();
|
|
20
27
|
program
|
|
21
28
|
.name('lg')
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.commitFirst = commitFirst;
|
|
4
7
|
const child_process_1 = require("child_process");
|
|
8
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
5
9
|
const branch_utils_1 = require("../branch-utils");
|
|
6
10
|
const linear_client_1 = require("../linear-client");
|
|
7
11
|
const env_utils_1 = require("../env-utils");
|
|
@@ -37,14 +41,25 @@ async function commitFirst() {
|
|
|
37
41
|
console.error('❌ Error: Not in a git repository or unable to get branch name');
|
|
38
42
|
process.exit(1);
|
|
39
43
|
}
|
|
40
|
-
// Step 2: Extract branch prefix and Linear issue ID from branch name
|
|
41
|
-
|
|
44
|
+
// Step 2: Extract branch prefix (if any) and Linear issue ID from branch name
|
|
45
|
+
let prefix = (0, branch_utils_1.extractBranchPrefix)(branchName);
|
|
42
46
|
const linearId = (0, branch_utils_1.extractLinearIssueId)(branchName);
|
|
43
47
|
if (!linearId) {
|
|
44
48
|
console.error(`❌ Error: Could not extract Linear issue ID from branch name: ${branchName}`);
|
|
45
|
-
console.error(' Branch name should follow pattern:
|
|
49
|
+
console.error(' Branch name should follow pattern: username/LEA-123-title');
|
|
46
50
|
process.exit(1);
|
|
47
51
|
}
|
|
52
|
+
if (!prefix) {
|
|
53
|
+
const { selectedPrefix } = await inquirer_1.default.prompt([
|
|
54
|
+
{
|
|
55
|
+
type: 'list',
|
|
56
|
+
name: 'selectedPrefix',
|
|
57
|
+
message: 'Select commit type:',
|
|
58
|
+
choices: ['feat', 'fix', 'chore', 'docs', 'refactor', 'test', 'research'],
|
|
59
|
+
},
|
|
60
|
+
]);
|
|
61
|
+
prefix = selectedPrefix;
|
|
62
|
+
}
|
|
48
63
|
console.log(`📋 Found Linear issue ID: ${linearId}`);
|
|
49
64
|
console.log(`📋 Using commit type: ${prefix}`);
|
|
50
65
|
// Step 3: Initialize Linear client and fetch issue data
|
|
@@ -83,22 +83,30 @@ async function createParentIssue() {
|
|
|
83
83
|
});
|
|
84
84
|
if (linearIssueId) {
|
|
85
85
|
console.log('✅ Found Linear issue, updating metadata...');
|
|
86
|
-
// Auto-find Linear project if GitHub project was selected
|
|
86
|
+
// Auto-find/create Linear project if GitHub project was selected
|
|
87
87
|
let linearProjectId = null;
|
|
88
|
+
let linearProjectName = null;
|
|
88
89
|
if (githubProject) {
|
|
89
90
|
console.log(` Looking for Linear project matching "${githubProject}"...`);
|
|
90
91
|
linearProjectId = await linearClient.findProjectByName(githubProject);
|
|
91
92
|
if (linearProjectId) {
|
|
93
|
+
linearProjectName = githubProject;
|
|
92
94
|
console.log(` ✅ Found matching Linear project: ${githubProject}`);
|
|
93
95
|
}
|
|
94
96
|
else {
|
|
95
|
-
console.log(` ⚠️ No matching Linear project found.
|
|
97
|
+
console.log(` ⚠️ No matching Linear project found. Creating "${githubProject}"...`);
|
|
98
|
+
const teamId = await linearClient.getIssueTeamId(linearIssueId);
|
|
99
|
+
const createdProject = await linearClient.createProject(githubProject, teamId || undefined);
|
|
100
|
+
if (createdProject) {
|
|
101
|
+
linearProjectId = createdProject.id;
|
|
102
|
+
linearProjectName = createdProject.name;
|
|
103
|
+
console.log(` ✅ Created Linear project: ${createdProject.name}`);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
console.log(` ⚠️ Failed to create Linear project. You can set it manually.`);
|
|
107
|
+
}
|
|
96
108
|
}
|
|
97
109
|
}
|
|
98
|
-
// If no auto-match, ask user (only if GitHub project wasn't selected)
|
|
99
|
-
if (!linearProjectId && !githubProject) {
|
|
100
|
-
linearProjectId = await inputHandler.selectLinearProject();
|
|
101
|
-
}
|
|
102
110
|
// Set labels on Linear issue
|
|
103
111
|
if (details.labels && details.labels.length > 0) {
|
|
104
112
|
console.log(` Setting labels: ${details.labels.join(', ')}`);
|
|
@@ -115,7 +123,7 @@ async function createParentIssue() {
|
|
|
115
123
|
if (success) {
|
|
116
124
|
console.log('✅ Linear issue metadata updated!');
|
|
117
125
|
if (linearProjectId) {
|
|
118
|
-
console.log(` Project: ${githubProject || '
|
|
126
|
+
console.log(` Project: ${linearProjectName || githubProject || 'linked'}`);
|
|
119
127
|
}
|
|
120
128
|
if (details.dueDate) {
|
|
121
129
|
console.log(` Due date: ${details.dueDate}`);
|
|
@@ -144,18 +152,22 @@ async function createParentIssue() {
|
|
|
144
152
|
console.log(` GitHub issue #${issue.number}`);
|
|
145
153
|
}
|
|
146
154
|
else {
|
|
147
|
-
|
|
148
|
-
if (
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
let branchOwner = await githubClient.getCurrentUsername();
|
|
156
|
+
if (!branchOwner) {
|
|
157
|
+
const { ownerInput } = await inquirer_1.default.prompt([
|
|
158
|
+
{
|
|
159
|
+
type: 'input',
|
|
160
|
+
name: 'ownerInput',
|
|
161
|
+
message: 'Branch username for naming (e.g., your GitHub login):',
|
|
162
|
+
validate: (input) => input.trim().length > 0 || 'Username is required',
|
|
163
|
+
},
|
|
164
|
+
]);
|
|
165
|
+
branchOwner = ownerInput.trim();
|
|
156
166
|
}
|
|
157
|
-
|
|
158
|
-
|
|
167
|
+
const branchName = (0, branch_utils_1.generateBranchName)(branchOwner ?? 'user', linearIssueIdentifier, details.title);
|
|
168
|
+
const success = await (0, branch_utils_1.createGitBranch)(branchName);
|
|
169
|
+
if (success) {
|
|
170
|
+
console.log(`✅ Branch created: ${branchName}`);
|
|
159
171
|
console.log(` Linear issue ID: ${linearIssueIdentifier}`);
|
|
160
172
|
console.log(` GitHub issue #${issue.number}`);
|
|
161
173
|
}
|
|
@@ -110,10 +110,7 @@ async function createSubIssue() {
|
|
|
110
110
|
else {
|
|
111
111
|
console.log(` ⚠️ Parent Linear issue not found yet (may need more time to sync)`);
|
|
112
112
|
}
|
|
113
|
-
// If no parent project,
|
|
114
|
-
if (!linearProjectId) {
|
|
115
|
-
linearProjectId = await inputHandler.selectLinearProject();
|
|
116
|
-
}
|
|
113
|
+
// If no parent project, leave project unset (no prompt)
|
|
117
114
|
// Set labels on Linear issue
|
|
118
115
|
if (details.labels && details.labels.length > 0) {
|
|
119
116
|
console.log(` Setting labels: ${details.labels.join(', ')}`);
|
|
@@ -172,18 +169,22 @@ async function createSubIssue() {
|
|
|
172
169
|
console.log(` GitHub issue #${subIssue.number}`);
|
|
173
170
|
}
|
|
174
171
|
else {
|
|
175
|
-
|
|
176
|
-
if (
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
172
|
+
let branchOwner = await githubClient.getCurrentUsername();
|
|
173
|
+
if (!branchOwner) {
|
|
174
|
+
const { ownerInput } = await inquirer_1.default.prompt([
|
|
175
|
+
{
|
|
176
|
+
type: 'input',
|
|
177
|
+
name: 'ownerInput',
|
|
178
|
+
message: 'Branch username for naming (e.g., your GitHub login):',
|
|
179
|
+
validate: (input) => input.trim().length > 0 || 'Username is required',
|
|
180
|
+
},
|
|
181
|
+
]);
|
|
182
|
+
branchOwner = ownerInput.trim();
|
|
184
183
|
}
|
|
185
|
-
|
|
186
|
-
|
|
184
|
+
const branchName = (0, branch_utils_1.generateBranchName)(branchOwner ?? 'user', linearIssueIdentifier, details.title);
|
|
185
|
+
const success = await (0, branch_utils_1.createGitBranch)(branchName);
|
|
186
|
+
if (success) {
|
|
187
|
+
console.log(`✅ Branch created: ${branchName}`);
|
|
187
188
|
console.log(` Linear issue ID: ${linearIssueIdentifier}`);
|
|
188
189
|
console.log(` GitHub issue #${subIssue.number}`);
|
|
189
190
|
}
|
package/dist/env-utils.js
CHANGED
|
@@ -14,6 +14,22 @@ const os_1 = require("os");
|
|
|
14
14
|
* - Installed globally (user can create .env in their project or home directory)
|
|
15
15
|
* - Installed locally (works with project's .env file)
|
|
16
16
|
*/
|
|
17
|
+
function hasLinearApiKey(envPath) {
|
|
18
|
+
const contents = (0, fs_1.readFileSync)(envPath, 'utf-8');
|
|
19
|
+
const match = contents.match(/^\s*LINEAR_API_KEY\s*=\s*(.+)\s*$/m);
|
|
20
|
+
if (!match) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const rawValue = match[1].trim();
|
|
24
|
+
const unquoted = rawValue.replace(/^['"]|['"]$/g, '').trim();
|
|
25
|
+
return unquoted.length > 0;
|
|
26
|
+
}
|
|
27
|
+
function exitMissingLinearApiKey() {
|
|
28
|
+
console.error('❌ LINEAR_API_KEY not found in .env');
|
|
29
|
+
console.error(' Add it to your .env file:');
|
|
30
|
+
console.error(' echo "LINEAR_API_KEY=lin_api_..." > .env');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
17
33
|
function loadEnvFile() {
|
|
18
34
|
// First, try to find .env in current working directory and parent directories
|
|
19
35
|
let currentDir = process.cwd();
|
|
@@ -21,6 +37,10 @@ function loadEnvFile() {
|
|
|
21
37
|
while (currentDir !== root) {
|
|
22
38
|
const envPath = (0, path_1.resolve)(currentDir, '.env');
|
|
23
39
|
if ((0, fs_1.existsSync)(envPath)) {
|
|
40
|
+
if (!hasLinearApiKey(envPath)) {
|
|
41
|
+
exitMissingLinearApiKey();
|
|
42
|
+
}
|
|
43
|
+
console.log(`✅ LINEAR_API_KEY found in ${envPath}`);
|
|
24
44
|
(0, dotenv_1.config)({ path: envPath });
|
|
25
45
|
return;
|
|
26
46
|
}
|
|
@@ -34,10 +54,12 @@ function loadEnvFile() {
|
|
|
34
54
|
// Fallback: try home directory
|
|
35
55
|
const homeEnvPath = (0, path_1.resolve)((0, os_1.homedir)(), '.env');
|
|
36
56
|
if ((0, fs_1.existsSync)(homeEnvPath)) {
|
|
57
|
+
if (!hasLinearApiKey(homeEnvPath)) {
|
|
58
|
+
exitMissingLinearApiKey();
|
|
59
|
+
}
|
|
60
|
+
console.log(`✅ LINEAR_API_KEY found in ${homeEnvPath}`);
|
|
37
61
|
(0, dotenv_1.config)({ path: homeEnvPath });
|
|
38
62
|
return;
|
|
39
63
|
}
|
|
40
|
-
|
|
41
|
-
// This is fine - we don't need to throw an error here
|
|
42
|
-
(0, dotenv_1.config)();
|
|
64
|
+
exitMissingLinearApiKey();
|
|
43
65
|
}
|
package/dist/github-client.js
CHANGED
|
@@ -12,6 +12,18 @@ class GitHubClientWrapper {
|
|
|
12
12
|
this.octokit = new rest_1.Octokit({ auth: token });
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
+
async getCurrentUsername() {
|
|
16
|
+
try {
|
|
17
|
+
const output = (0, child_process_1.execSync)('gh api user -q .login', {
|
|
18
|
+
encoding: 'utf-8',
|
|
19
|
+
stdio: 'pipe',
|
|
20
|
+
}).trim();
|
|
21
|
+
return output || null;
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
15
27
|
async getRepositories() {
|
|
16
28
|
// Use gh CLI to get accessible repos
|
|
17
29
|
const output = (0, child_process_1.execSync)('gh repo list --limit 100 --json nameWithOwner', {
|
package/dist/input-handler.js
CHANGED
|
@@ -187,11 +187,8 @@ class InputHandler {
|
|
|
187
187
|
{
|
|
188
188
|
type: 'checkbox',
|
|
189
189
|
name: 'labels',
|
|
190
|
-
message: 'Select GitHub labels (
|
|
190
|
+
message: 'Select GitHub labels (optional):',
|
|
191
191
|
choices: labelChoices,
|
|
192
|
-
validate: (input) => {
|
|
193
|
-
return input.length > 0 || 'At least one label is required';
|
|
194
|
-
},
|
|
195
192
|
},
|
|
196
193
|
]);
|
|
197
194
|
let description = '';
|
package/dist/linear-client.js
CHANGED
|
@@ -28,6 +28,41 @@ class LinearClientWrapper {
|
|
|
28
28
|
return project?.id || null;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
async createProject(projectName, teamId) {
|
|
32
|
+
try {
|
|
33
|
+
const mutation = `mutation CreateProject($input: ProjectCreateInput!) {
|
|
34
|
+
projectCreate(input: $input) {
|
|
35
|
+
success
|
|
36
|
+
project { id name }
|
|
37
|
+
}
|
|
38
|
+
}`;
|
|
39
|
+
const input = { name: projectName };
|
|
40
|
+
if (teamId) {
|
|
41
|
+
input.teamIds = [teamId];
|
|
42
|
+
}
|
|
43
|
+
const response = await fetch('https://api.linear.app/graphql', {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: {
|
|
46
|
+
'Content-Type': 'application/json',
|
|
47
|
+
'Authorization': linearApiKey,
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify({
|
|
50
|
+
query: mutation,
|
|
51
|
+
variables: { input },
|
|
52
|
+
}),
|
|
53
|
+
});
|
|
54
|
+
const result = await response.json();
|
|
55
|
+
if (result.errors || !result.data?.projectCreate?.success) {
|
|
56
|
+
console.error(`❌ Failed to create project "${projectName}":`, result.errors);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return result.data.projectCreate.project ?? null;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error(`❌ Error creating project "${projectName}":`, error);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
31
66
|
async getWorkflowStates(teamId) {
|
|
32
67
|
try {
|
|
33
68
|
// Get workflow states from teams
|