linear-github-cli 1.1.4 → 1.2.1

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.
@@ -53,13 +53,10 @@ async function createParentIssue() {
53
53
  const githubProject = await inputHandler.selectProject(repo);
54
54
  // Step 4: Create GitHub issue
55
55
  console.log('\nšŸš€ Creating GitHub issue...');
56
- const body = details.dueDate
57
- ? `Due Date: ${details.dueDate}\n\n${details.description}`
58
- : details.description;
59
56
  const issue = await githubClient.createIssue({
60
57
  repo,
61
58
  title: details.title,
62
- body,
59
+ body: details.description,
63
60
  labels: details.labels,
64
61
  assignees: ['@me'],
65
62
  project: githubProject || undefined,
@@ -71,9 +68,19 @@ async function createParentIssue() {
71
68
  await githubClient.setProjectDateFields(repo, githubProject, issue.id, details.dueDate || undefined, details.startDate || undefined);
72
69
  }
73
70
  // Step 5: Wait for Linear sync, then update metadata
74
- console.log('\nā³ Waiting for Linear sync (5 seconds)...');
75
- await new Promise(resolve => setTimeout(resolve, 5000));
76
- const linearIssueId = await linearClient.findIssueByGitHubUrl(issue.url);
71
+ const linearSyncDelayMs = 500;
72
+ const linearSyncMaxWaitMs = 10000;
73
+ const linearSyncMaxAttempts = Math.floor(linearSyncMaxWaitMs / linearSyncDelayMs) + 1;
74
+ console.log('\nā³ Waiting for Linear sync (polling for up to 10s)...');
75
+ const linearIssueId = await linearClient.waitForIssueByGitHubUrl(issue.url, {
76
+ maxAttempts: linearSyncMaxAttempts,
77
+ delayMs: linearSyncDelayMs,
78
+ onRetry: (attempt, maxAttempts, delayMs) => {
79
+ if (attempt % 5 === 0) {
80
+ console.log(` ā³ Linear issue not found yet, retrying in ${delayMs}ms... (${attempt}/${maxAttempts - 1})`);
81
+ }
82
+ },
83
+ });
77
84
  if (linearIssueId) {
78
85
  console.log('āœ… Found Linear issue, updating metadata...');
79
86
  // Auto-find Linear project if GitHub project was selected
@@ -54,29 +54,44 @@ async function createSubIssue() {
54
54
  const details = await inputHandler.promptIssueDetails(repo);
55
55
  // Step 4: Create sub-issue
56
56
  console.log('\nšŸš€ Creating sub-issue...');
57
- const body = details.dueDate
58
- ? `Due Date: ${details.dueDate}\n\n${details.description}`
59
- : details.description;
60
57
  const subIssue = await githubClient.createSubIssue({
61
58
  repo,
62
59
  parentIssueNumber,
63
60
  title: details.title,
64
- body,
61
+ body: details.description,
65
62
  labels: details.labels,
66
63
  assignees: ['@me'],
67
64
  });
68
65
  console.log(`āœ… Sub-Issue #${subIssue.number} created: ${subIssue.url}`);
69
66
  console.log(` Parent: #${parentIssueNumber}`);
70
67
  // Step 5: Wait for Linear sync, then update metadata
71
- console.log('\nā³ Waiting for Linear sync (5 seconds)...');
72
- await new Promise(resolve => setTimeout(resolve, 5000));
73
- const linearIssueId = await linearClient.findIssueByGitHubUrl(subIssue.url);
68
+ const linearSyncDelayMs = 500;
69
+ const linearSyncMaxWaitMs = 10000;
70
+ const linearSyncMaxAttempts = Math.floor(linearSyncMaxWaitMs / linearSyncDelayMs) + 1;
71
+ console.log('\nā³ Waiting for Linear sync (polling for up to 10s)...');
72
+ const linearIssueId = await linearClient.waitForIssueByGitHubUrl(subIssue.url, {
73
+ maxAttempts: linearSyncMaxAttempts,
74
+ delayMs: linearSyncDelayMs,
75
+ onRetry: (attempt, maxAttempts, delayMs) => {
76
+ if (attempt % 5 === 0) {
77
+ console.log(` ā³ Linear issue not found yet, retrying in ${delayMs}ms... (${attempt}/${maxAttempts - 1})`);
78
+ }
79
+ },
80
+ });
74
81
  if (linearIssueId) {
75
82
  console.log('āœ… Found Linear issue, updating metadata...');
76
83
  // Get parent issue to check if it has a project
77
84
  const parentIssueUrl = `https://github.com/${repo}/issues/${parentIssueNumber}`;
78
85
  console.log(` Looking for parent issue's Linear project...`);
79
- const parentLinearIssueId = await linearClient.findIssueByGitHubUrl(parentIssueUrl);
86
+ const parentLinearIssueId = await linearClient.waitForIssueByGitHubUrl(parentIssueUrl, {
87
+ maxAttempts: 6,
88
+ delayMs: 500,
89
+ onRetry: (attempt, maxAttempts, delayMs) => {
90
+ if (attempt % 3 === 0) {
91
+ console.log(` ā³ Parent Linear issue not found yet, retrying in ${delayMs}ms... (${attempt}/${maxAttempts - 1})`);
92
+ }
93
+ },
94
+ });
80
95
  let linearProjectId = null;
81
96
  let parentProjectName = null;
82
97
  if (parentLinearIssueId) {
@@ -46,7 +46,16 @@ class GitHubClientWrapper {
46
46
  (params.labels && params.labels.length > 0 ? `--label "${params.labels.join(',')}" ` : '') +
47
47
  (params.assignees && params.assignees.length > 0 ? `--assignee "${params.assignees.join(',')}" ` : '') +
48
48
  (params.project ? `--project "${params.project}" ` : '');
49
- const output = (0, child_process_1.execSync)(command, { encoding: 'utf-8' });
49
+ let output;
50
+ try {
51
+ output = (0, child_process_1.execSync)(command, { encoding: 'utf-8' });
52
+ }
53
+ catch (error) {
54
+ const errorMessage = error?.stderr?.toString().trim() || error?.message || String(error);
55
+ throw new Error(`Failed to create GitHub issue.\n` +
56
+ `Reason: ${errorMessage}\n` +
57
+ `Please review the issue body and try again.`);
58
+ }
50
59
  // Parse URL from output: "https://github.com/owner/repo/issues/123"
51
60
  const urlMatch = output.match(/https:\/\/github\.com\/[^\/]+\/[^\/]+\/issues\/(\d+)/);
52
61
  if (!urlMatch) {
@@ -154,9 +154,13 @@ class InputHandler {
154
154
  validate: (input) => input.length > 0 || 'Title is required',
155
155
  },
156
156
  {
157
- type: 'editor',
158
- name: 'description',
159
- message: 'Issue description (opens editor):',
157
+ type: 'input',
158
+ name: 'descriptionAction',
159
+ message: 'Body [(e) to launch vim, enter to skip]:',
160
+ validate: (input) => {
161
+ const value = input.trim().toLowerCase();
162
+ return value === '' || value === 'e' || 'Enter "e" to edit or press enter to skip';
163
+ },
160
164
  },
161
165
  {
162
166
  type: 'input',
@@ -190,9 +194,20 @@ class InputHandler {
190
194
  },
191
195
  },
192
196
  ]);
197
+ let description = '';
198
+ if (answers.descriptionAction.trim().toLowerCase() === 'e') {
199
+ const { description: editedDescription } = await inquirer_1.default.prompt([
200
+ {
201
+ type: 'editor',
202
+ name: 'description',
203
+ message: 'Issue description:',
204
+ },
205
+ ]);
206
+ description = editedDescription || '';
207
+ }
193
208
  return {
194
209
  title: answers.title,
195
- description: answers.description || '',
210
+ description,
196
211
  dueDate: answers.dueDate || '',
197
212
  startDate: answers.startDate || '',
198
213
  labels: answers.labels || [],
@@ -184,6 +184,26 @@ class LinearClientWrapper {
184
184
  });
185
185
  return issues.nodes[0]?.id || null;
186
186
  }
187
+ async waitForIssueByGitHubUrl(githubUrl, options = {}) {
188
+ const delayMs = options.delayMs ?? 5000;
189
+ const maxAttempts = options.maxAttempts;
190
+ for (let attempt = 1;; attempt++) {
191
+ const issueId = await this.findIssueByGitHubUrl(githubUrl);
192
+ if (issueId) {
193
+ return issueId;
194
+ }
195
+ if (maxAttempts !== undefined && attempt >= maxAttempts) {
196
+ return null;
197
+ }
198
+ options.onRetry?.(attempt, maxAttempts ?? Number.POSITIVE_INFINITY, delayMs);
199
+ if (delayMs > 0) {
200
+ await new Promise(resolve => setTimeout(resolve, delayMs));
201
+ }
202
+ else {
203
+ await new Promise(resolve => setTimeout(resolve, 0));
204
+ }
205
+ }
206
+ }
187
207
  async getIssueIdentifier(issueId) {
188
208
  try {
189
209
  const issue = await this.client.issue(issueId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linear-github-cli",
3
- "version": "1.1.4",
3
+ "version": "1.2.1",
4
4
  "description": "CLI tool for creating GitHub issues with Linear integration",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {