linear-github-cli 1.3.6 → 1.3.8
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/dist/cli.js +3 -3
- package/dist/input-handler.js +111 -22
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -19,15 +19,15 @@ const pkgPathCandidates = [
|
|
|
19
19
|
(0, path_1.resolve)(__dirname, '..', 'package.json'),
|
|
20
20
|
];
|
|
21
21
|
const pkgPath = pkgPathCandidates.find(candidate => (0, fs_1.existsSync)(candidate));
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const pkg = pkgPath ? JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf-8')) : null;
|
|
23
|
+
if (pkg) {
|
|
24
24
|
(0, update_notifier_1.default)({ pkg }).notify();
|
|
25
25
|
}
|
|
26
26
|
const program = new commander_1.Command();
|
|
27
27
|
program
|
|
28
28
|
.name('lg')
|
|
29
29
|
.description('Linear + GitHub Integration CLI - Create GitHub issues with Linear sync')
|
|
30
|
-
.version('
|
|
30
|
+
.version(pkg?.version ?? 'unknown');
|
|
31
31
|
program
|
|
32
32
|
.command('create-parent')
|
|
33
33
|
.alias('parent')
|
package/dist/input-handler.js
CHANGED
|
@@ -5,7 +5,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.InputHandler = void 0;
|
|
7
7
|
const child_process_1 = require("child_process");
|
|
8
|
+
const fs_1 = require("fs");
|
|
8
9
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
10
|
+
const path_1 = require("path");
|
|
11
|
+
const readline_1 = __importDefault(require("readline"));
|
|
12
|
+
const os_1 = require("os");
|
|
9
13
|
const isValidDateYmd = (input) => {
|
|
10
14
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(input))
|
|
11
15
|
return false;
|
|
@@ -15,6 +19,87 @@ const isValidDateYmd = (input) => {
|
|
|
15
19
|
date.getUTCMonth() === month - 1 &&
|
|
16
20
|
date.getUTCDate() === day);
|
|
17
21
|
};
|
|
22
|
+
const WAIT_FLAG_EDITORS = {
|
|
23
|
+
code: '--wait',
|
|
24
|
+
'code-insiders': '--wait',
|
|
25
|
+
cursor: '--wait',
|
|
26
|
+
'cursor-insiders': '--wait',
|
|
27
|
+
subl: '-w',
|
|
28
|
+
sublime_text: '-w',
|
|
29
|
+
atom: '--wait',
|
|
30
|
+
};
|
|
31
|
+
const hasWaitFlag = (command) => /\s--wait\b/.test(command) || /\s-w\b/.test(command);
|
|
32
|
+
const resolveEditorCommand = () => {
|
|
33
|
+
const envEditor = process.env.LG_EDITOR || process.env.VISUAL || process.env.EDITOR;
|
|
34
|
+
const base = (envEditor && envEditor.trim().length > 0 ? envEditor : 'vim').trim();
|
|
35
|
+
const binary = base.split(/\s+/)[0];
|
|
36
|
+
const waitFlag = WAIT_FLAG_EDITORS[binary];
|
|
37
|
+
if (waitFlag && !hasWaitFlag(base)) {
|
|
38
|
+
return `${base} ${waitFlag}`;
|
|
39
|
+
}
|
|
40
|
+
return base;
|
|
41
|
+
};
|
|
42
|
+
const openEditorForText = (editorCommand, initialText = '') => {
|
|
43
|
+
const tempDir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'lg-editor-'));
|
|
44
|
+
const tempFilePath = (0, path_1.join)(tempDir, 'issue-body.md');
|
|
45
|
+
try {
|
|
46
|
+
(0, fs_1.writeFileSync)(tempFilePath, initialText, 'utf-8');
|
|
47
|
+
const quotedPath = `"${tempFilePath.replace(/"/g, '\\"')}"`;
|
|
48
|
+
const command = `${editorCommand} ${quotedPath}`;
|
|
49
|
+
const result = (0, child_process_1.spawnSync)(command, { stdio: 'inherit', shell: true });
|
|
50
|
+
if (result.error) {
|
|
51
|
+
throw result.error;
|
|
52
|
+
}
|
|
53
|
+
return (0, fs_1.readFileSync)(tempFilePath, 'utf-8');
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
(0, fs_1.rmSync)(tempDir, { recursive: true, force: true });
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const promptDescriptionAction = async () => {
|
|
60
|
+
const stdin = process.stdin;
|
|
61
|
+
const stdout = process.stdout;
|
|
62
|
+
if (!stdin.isTTY || !stdout.isTTY) {
|
|
63
|
+
return 'skip';
|
|
64
|
+
}
|
|
65
|
+
stdout.write('Body [press e to launch editor, Enter to skip]: ');
|
|
66
|
+
const rl = readline_1.default.createInterface({ input: stdin, output: stdout });
|
|
67
|
+
readline_1.default.emitKeypressEvents(stdin, rl);
|
|
68
|
+
if (typeof stdin.setRawMode === 'function') {
|
|
69
|
+
stdin.setRawMode(true);
|
|
70
|
+
}
|
|
71
|
+
stdin.resume();
|
|
72
|
+
return new Promise(resolve => {
|
|
73
|
+
const cleanup = () => {
|
|
74
|
+
stdin.removeListener('keypress', onKeypress);
|
|
75
|
+
if (typeof stdin.setRawMode === 'function') {
|
|
76
|
+
stdin.setRawMode(false);
|
|
77
|
+
}
|
|
78
|
+
rl.close();
|
|
79
|
+
};
|
|
80
|
+
const onKeypress = (_, key) => {
|
|
81
|
+
const keyName = key?.name ?? '';
|
|
82
|
+
const keySeq = key?.sequence ?? '';
|
|
83
|
+
if (key?.ctrl && keyName === 'c') {
|
|
84
|
+
stdout.write('\n');
|
|
85
|
+
cleanup();
|
|
86
|
+
process.exit(130);
|
|
87
|
+
}
|
|
88
|
+
if (keyName === 'return' || keySeq === '\r') {
|
|
89
|
+
stdout.write('\n');
|
|
90
|
+
cleanup();
|
|
91
|
+
resolve('skip');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (keyName.toLowerCase() === 'e' || keySeq.toLowerCase() === 'e') {
|
|
95
|
+
stdout.write('\n');
|
|
96
|
+
cleanup();
|
|
97
|
+
resolve('edit');
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
stdin.on('keypress', onKeypress);
|
|
101
|
+
});
|
|
102
|
+
};
|
|
18
103
|
class InputHandler {
|
|
19
104
|
linearClient;
|
|
20
105
|
githubClient;
|
|
@@ -155,22 +240,37 @@ class InputHandler {
|
|
|
155
240
|
'research',
|
|
156
241
|
];
|
|
157
242
|
}
|
|
158
|
-
const
|
|
243
|
+
const baseAnswers = await inquirer_1.default.prompt([
|
|
159
244
|
{
|
|
160
245
|
type: 'input',
|
|
161
246
|
name: 'title',
|
|
162
247
|
message: 'Issue title (required):',
|
|
163
248
|
validate: (input) => input.length > 0 || 'Title is required',
|
|
164
249
|
},
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
250
|
+
]);
|
|
251
|
+
let description = '';
|
|
252
|
+
const descriptionAction = await promptDescriptionAction();
|
|
253
|
+
if (descriptionAction === 'edit') {
|
|
254
|
+
const editorCommand = resolveEditorCommand();
|
|
255
|
+
try {
|
|
256
|
+
description = openEditorForText(editorCommand).trimEnd();
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
console.error('\n⚠️ Failed to open editor for issue description.');
|
|
260
|
+
console.error(` Editor command: ${editorCommand}`);
|
|
261
|
+
console.error(' Tip: set $EDITOR or $VISUAL to a terminal editor (e.g. "vim")');
|
|
262
|
+
console.error(' or a GUI editor with wait flag (e.g. "code --wait").\n');
|
|
263
|
+
const { description: fallbackDescription } = await inquirer_1.default.prompt([
|
|
264
|
+
{
|
|
265
|
+
type: 'input',
|
|
266
|
+
name: 'description',
|
|
267
|
+
message: 'Issue description (single line or paste text):',
|
|
268
|
+
},
|
|
269
|
+
]);
|
|
270
|
+
description = fallbackDescription || '';
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const answers = await inquirer_1.default.prompt([
|
|
174
274
|
{
|
|
175
275
|
type: 'input',
|
|
176
276
|
name: 'startDate',
|
|
@@ -198,19 +298,8 @@ class InputHandler {
|
|
|
198
298
|
choices: labelChoices,
|
|
199
299
|
},
|
|
200
300
|
]);
|
|
201
|
-
let description = '';
|
|
202
|
-
if (answers.descriptionAction.trim().toLowerCase() === 'e') {
|
|
203
|
-
const { description: editedDescription } = await inquirer_1.default.prompt([
|
|
204
|
-
{
|
|
205
|
-
type: 'editor',
|
|
206
|
-
name: 'description',
|
|
207
|
-
message: 'Issue description:',
|
|
208
|
-
},
|
|
209
|
-
]);
|
|
210
|
-
description = editedDescription || '';
|
|
211
|
-
}
|
|
212
301
|
return {
|
|
213
|
-
title:
|
|
302
|
+
title: baseAnswers.title,
|
|
214
303
|
description,
|
|
215
304
|
dueDate: answers.dueDate || '',
|
|
216
305
|
startDate: answers.startDate || '',
|