linear-github-cli 1.0.3 → 1.1.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 +19 -0
- package/dist/cli.js +1 -1
- package/dist/commands/create-parent.js +5 -0
- package/dist/commands/create-sub.js +12 -0
- package/dist/github-client.js +448 -0
- package/dist/input-handler.js +12 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -180,6 +180,25 @@ Make sure you've set the environment variable:
|
|
|
180
180
|
export LINEAR_API_KEY="lin_api_..."
|
|
181
181
|
```
|
|
182
182
|
|
|
183
|
+
### GitHub Project date fields not being set
|
|
184
|
+
|
|
185
|
+
If start date or target date fields are not being set in GitHub Projects:
|
|
186
|
+
|
|
187
|
+
1. **Enable debug mode** to see detailed logs:
|
|
188
|
+
```bash
|
|
189
|
+
DEBUG=true lg parent
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
2. **Check the logs** for:
|
|
193
|
+
- Whether the project item was found (may require retries due to timing)
|
|
194
|
+
- Whether the date fields exist in the project
|
|
195
|
+
- Any GraphQL errors
|
|
196
|
+
|
|
197
|
+
3. **Common issues**:
|
|
198
|
+
- The issue may not be indexed in the project yet (the tool will retry automatically)
|
|
199
|
+
- The project may not have "Target" or "Start" date fields configured
|
|
200
|
+
- Network or API rate limiting issues
|
|
201
|
+
|
|
183
202
|
### "lg: command not found"
|
|
184
203
|
|
|
185
204
|
If you installed globally, make sure npm's global bin directory is in your PATH:
|
package/dist/cli.js
CHANGED
|
@@ -20,7 +20,7 @@ const program = new commander_1.Command();
|
|
|
20
20
|
program
|
|
21
21
|
.name('lg')
|
|
22
22
|
.description('Linear + GitHub Integration CLI - Create GitHub issues with Linear sync')
|
|
23
|
-
.version('1.0
|
|
23
|
+
.version('1.1.0');
|
|
24
24
|
program
|
|
25
25
|
.command('create-parent')
|
|
26
26
|
.alias('parent')
|
|
@@ -65,6 +65,11 @@ async function createParentIssue() {
|
|
|
65
65
|
project: githubProject || undefined,
|
|
66
66
|
});
|
|
67
67
|
console.log(`✅ GitHub Issue #${issue.number} created: ${issue.url}`);
|
|
68
|
+
// Set GitHub Project date fields if project is selected
|
|
69
|
+
if (githubProject && (details.dueDate || details.startDate)) {
|
|
70
|
+
console.log('\n📅 Setting GitHub Project date fields...');
|
|
71
|
+
await githubClient.setProjectDateFields(repo, githubProject, issue.id, details.dueDate || undefined, details.startDate || undefined);
|
|
72
|
+
}
|
|
68
73
|
// Step 5: Wait for Linear sync, then update metadata
|
|
69
74
|
console.log('\n⏳ Waiting for Linear sync (5 seconds)...');
|
|
70
75
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
@@ -67,6 +67,18 @@ async function createSubIssue() {
|
|
|
67
67
|
});
|
|
68
68
|
console.log(`✅ Sub-Issue #${subIssue.number} created: ${subIssue.url}`);
|
|
69
69
|
console.log(` Parent: #${parentIssueNumber}`);
|
|
70
|
+
// Set GitHub Project date fields if dates are provided
|
|
71
|
+
if (details.dueDate || details.startDate) {
|
|
72
|
+
console.log('\n📅 Setting GitHub Project date fields...');
|
|
73
|
+
// sub-issueから直接プロジェクト情報を取得(Auto-add sub-issues to projectが有効な場合)
|
|
74
|
+
const projectName = await githubClient.getIssueProject(repo, subIssue.id);
|
|
75
|
+
if (projectName) {
|
|
76
|
+
await githubClient.setProjectDateFields(repo, projectName, subIssue.id, details.dueDate || undefined, details.startDate || undefined);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.log(' ⚠️ Sub-issue has no GitHub Project. Skipping date field setting.');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
70
82
|
// Step 5: Wait for Linear sync, then update metadata
|
|
71
83
|
console.log('\n⏳ Waiting for Linear sync (5 seconds)...');
|
|
72
84
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
package/dist/github-client.js
CHANGED
|
@@ -119,5 +119,453 @@ class GitHubClientWrapper {
|
|
|
119
119
|
];
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Get project node ID by project name
|
|
124
|
+
* Supports both Organization and User accounts
|
|
125
|
+
*/
|
|
126
|
+
async getProjectNodeId(repo, projectName) {
|
|
127
|
+
try {
|
|
128
|
+
const [owner] = repo.split('/');
|
|
129
|
+
let projects = [];
|
|
130
|
+
// First try Organization
|
|
131
|
+
try {
|
|
132
|
+
const orgQuery = `query {
|
|
133
|
+
organization(login: "${owner}") {
|
|
134
|
+
projectsV2(first: 50) {
|
|
135
|
+
nodes {
|
|
136
|
+
id
|
|
137
|
+
title
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}`;
|
|
142
|
+
const orgOutput = (0, child_process_1.execSync)(`gh api graphql -f query="${orgQuery.replace(/"/g, '\\"')}"`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
143
|
+
const orgResult = JSON.parse(orgOutput);
|
|
144
|
+
if (orgResult.data?.organization) {
|
|
145
|
+
projects = orgResult.data.organization.projectsV2?.nodes || [];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (orgError) {
|
|
149
|
+
// Organization not found or error occurred, will try User below
|
|
150
|
+
}
|
|
151
|
+
// If no projects found from Organization, try User
|
|
152
|
+
if (projects.length === 0) {
|
|
153
|
+
try {
|
|
154
|
+
const userQuery = `query {
|
|
155
|
+
user(login: "${owner}") {
|
|
156
|
+
projectsV2(first: 50) {
|
|
157
|
+
nodes {
|
|
158
|
+
id
|
|
159
|
+
title
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}`;
|
|
164
|
+
const userOutput = (0, child_process_1.execSync)(`gh api graphql -f query="${userQuery.replace(/"/g, '\\"')}"`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
165
|
+
const userResult = JSON.parse(userOutput);
|
|
166
|
+
if (userResult.data?.user) {
|
|
167
|
+
projects = userResult.data.user.projectsV2?.nodes || [];
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (userError) {
|
|
171
|
+
// User not found or error occurred
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const project = projects.find((p) => p.title === projectName);
|
|
175
|
+
return project?.id || null;
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
console.error('⚠️ Failed to get project node ID:', error);
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get project field ID by field name
|
|
184
|
+
*/
|
|
185
|
+
async getProjectFieldId(projectId, fieldName) {
|
|
186
|
+
const debug = process.env.DEBUG === 'true';
|
|
187
|
+
try {
|
|
188
|
+
const query = `query {
|
|
189
|
+
node(id: "${projectId}") {
|
|
190
|
+
... on ProjectV2 {
|
|
191
|
+
fields(first: 50) {
|
|
192
|
+
nodes {
|
|
193
|
+
... on ProjectV2FieldCommon {
|
|
194
|
+
id
|
|
195
|
+
name
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}`;
|
|
202
|
+
if (debug) {
|
|
203
|
+
console.log(` [DEBUG] Looking up field "${fieldName}" in project ${projectId}`);
|
|
204
|
+
}
|
|
205
|
+
const output = (0, child_process_1.execSync)(`gh api graphql -f query="${query.replace(/"/g, '\\"')}"`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
206
|
+
const result = JSON.parse(output);
|
|
207
|
+
if (debug) {
|
|
208
|
+
console.log(` [DEBUG] Fields query response:`, JSON.stringify(result, null, 2));
|
|
209
|
+
}
|
|
210
|
+
if (result.errors) {
|
|
211
|
+
console.error(` ⚠️ GraphQL errors when fetching fields:`, result.errors);
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
const fields = result.data?.node?.fields?.nodes || [];
|
|
215
|
+
const field = fields.find((f) => f.name === fieldName);
|
|
216
|
+
if (field) {
|
|
217
|
+
if (debug) {
|
|
218
|
+
console.log(` [DEBUG] Found field "${fieldName}" with ID: ${field.id}`);
|
|
219
|
+
}
|
|
220
|
+
return field.id;
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
if (debug) {
|
|
224
|
+
const availableFields = fields.map((f) => f.name);
|
|
225
|
+
console.log(` [DEBUG] Field "${fieldName}" not found. Available fields:`, availableFields);
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
const errorMessage = error.message || error.stderr || String(error);
|
|
232
|
+
console.error(` ⚠️ Failed to get field ID for "${fieldName}": ${errorMessage}`);
|
|
233
|
+
if (debug) {
|
|
234
|
+
console.error(` [DEBUG] Full error:`, error);
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Get project item ID by issue ID with retry and pagination support
|
|
241
|
+
*/
|
|
242
|
+
async getProjectItemId(projectId, issueId, maxRetries = 3) {
|
|
243
|
+
const debug = process.env.DEBUG === 'true';
|
|
244
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
245
|
+
try {
|
|
246
|
+
let cursor = null;
|
|
247
|
+
let hasNextPage = true;
|
|
248
|
+
let allItems = [];
|
|
249
|
+
// Paginate through all items
|
|
250
|
+
while (hasNextPage) {
|
|
251
|
+
const query = cursor
|
|
252
|
+
? `query {
|
|
253
|
+
node(id: "${projectId}") {
|
|
254
|
+
... on ProjectV2 {
|
|
255
|
+
items(first: 100, after: "${cursor}") {
|
|
256
|
+
pageInfo {
|
|
257
|
+
hasNextPage
|
|
258
|
+
endCursor
|
|
259
|
+
}
|
|
260
|
+
nodes {
|
|
261
|
+
id
|
|
262
|
+
content {
|
|
263
|
+
... on Issue {
|
|
264
|
+
id
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}`
|
|
272
|
+
: `query {
|
|
273
|
+
node(id: "${projectId}") {
|
|
274
|
+
... on ProjectV2 {
|
|
275
|
+
items(first: 100) {
|
|
276
|
+
pageInfo {
|
|
277
|
+
hasNextPage
|
|
278
|
+
endCursor
|
|
279
|
+
}
|
|
280
|
+
nodes {
|
|
281
|
+
id
|
|
282
|
+
content {
|
|
283
|
+
... on Issue {
|
|
284
|
+
id
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}`;
|
|
292
|
+
if (debug) {
|
|
293
|
+
console.log(` [DEBUG] Attempt ${attempt}/${maxRetries}: Querying project items${cursor ? ` (cursor: ${cursor})` : ''}`);
|
|
294
|
+
}
|
|
295
|
+
const output = (0, child_process_1.execSync)(`gh api graphql -f query="${query.replace(/"/g, '\\"')}"`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
296
|
+
const result = JSON.parse(output);
|
|
297
|
+
if (debug) {
|
|
298
|
+
console.log(` [DEBUG] GraphQL response:`, JSON.stringify(result, null, 2));
|
|
299
|
+
}
|
|
300
|
+
if (result.errors) {
|
|
301
|
+
console.error(` ⚠️ GraphQL errors on attempt ${attempt}:`, result.errors);
|
|
302
|
+
throw new Error(`GraphQL errors: ${JSON.stringify(result.errors)}`);
|
|
303
|
+
}
|
|
304
|
+
const itemsData = result.data?.node?.items;
|
|
305
|
+
if (!itemsData) {
|
|
306
|
+
console.error(` ⚠️ Invalid response structure on attempt ${attempt}`);
|
|
307
|
+
if (debug) {
|
|
308
|
+
console.error(` [DEBUG] Full response:`, JSON.stringify(result, null, 2));
|
|
309
|
+
}
|
|
310
|
+
throw new Error('Invalid response structure');
|
|
311
|
+
}
|
|
312
|
+
const items = itemsData.nodes || [];
|
|
313
|
+
allItems = allItems.concat(items);
|
|
314
|
+
hasNextPage = itemsData.pageInfo?.hasNextPage || false;
|
|
315
|
+
cursor = itemsData.pageInfo?.endCursor || null;
|
|
316
|
+
if (debug) {
|
|
317
|
+
console.log(` [DEBUG] Found ${items.length} items in this page (total: ${allItems.length}), hasNextPage: ${hasNextPage}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// Search for the item with matching issue ID
|
|
321
|
+
const item = allItems.find((i) => i.content?.id === issueId);
|
|
322
|
+
if (item) {
|
|
323
|
+
if (attempt > 1) {
|
|
324
|
+
console.log(` ✅ Found project item after ${attempt} attempt(s)`);
|
|
325
|
+
}
|
|
326
|
+
if (debug) {
|
|
327
|
+
console.log(` [DEBUG] Found item ID: ${item.id} for issue ID: ${issueId}`);
|
|
328
|
+
}
|
|
329
|
+
return item.id;
|
|
330
|
+
}
|
|
331
|
+
// If not found and not last attempt, wait and retry
|
|
332
|
+
if (attempt < maxRetries) {
|
|
333
|
+
const waitTime = attempt * 1000; // 1s, 2s, 3s
|
|
334
|
+
console.log(` ⏳ Project item not found (searched ${allItems.length} items), retrying in ${waitTime}ms... (attempt ${attempt}/${maxRetries})`);
|
|
335
|
+
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
console.error(` ⚠️ Project item not found after ${maxRetries} attempts (searched ${allItems.length} total items)`);
|
|
339
|
+
if (debug) {
|
|
340
|
+
console.error(` [DEBUG] Issue ID being searched: ${issueId}`);
|
|
341
|
+
console.error(` [DEBUG] Available issue IDs in project:`, allItems.map((i) => i.content?.id).slice(0, 10));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
const errorMessage = error.message || error.stderr || String(error);
|
|
347
|
+
console.error(` ⚠️ Failed to get project item ID on attempt ${attempt}/${maxRetries}:`, errorMessage);
|
|
348
|
+
if (debug) {
|
|
349
|
+
console.error(` [DEBUG] Full error:`, error);
|
|
350
|
+
}
|
|
351
|
+
// If not last attempt, wait and retry
|
|
352
|
+
if (attempt < maxRetries) {
|
|
353
|
+
const waitTime = attempt * 1000;
|
|
354
|
+
console.log(` ⏳ Retrying in ${waitTime}ms...`);
|
|
355
|
+
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Set date field value for a project item
|
|
363
|
+
*/
|
|
364
|
+
async setProjectItemDateField(projectId, itemId, fieldId, date) {
|
|
365
|
+
const debug = process.env.DEBUG === 'true';
|
|
366
|
+
try {
|
|
367
|
+
const mutation = `mutation {
|
|
368
|
+
updateProjectV2ItemFieldValue(
|
|
369
|
+
input: {
|
|
370
|
+
projectId: "${projectId}"
|
|
371
|
+
itemId: "${itemId}"
|
|
372
|
+
fieldId: "${fieldId}"
|
|
373
|
+
value: {
|
|
374
|
+
date: "${date}"
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
) {
|
|
378
|
+
projectV2Item {
|
|
379
|
+
id
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}`;
|
|
383
|
+
if (debug) {
|
|
384
|
+
console.log(` [DEBUG] Executing mutation:`);
|
|
385
|
+
console.log(` [DEBUG] Project ID: ${projectId}`);
|
|
386
|
+
console.log(` [DEBUG] Item ID: ${itemId}`);
|
|
387
|
+
console.log(` [DEBUG] Field ID: ${fieldId}`);
|
|
388
|
+
console.log(` [DEBUG] Date: ${date}`);
|
|
389
|
+
}
|
|
390
|
+
const output = (0, child_process_1.execSync)(`gh api graphql -f query="${mutation.replace(/"/g, '\\"')}"`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
391
|
+
const result = JSON.parse(output);
|
|
392
|
+
if (debug) {
|
|
393
|
+
console.log(` [DEBUG] Mutation response:`, JSON.stringify(result, null, 2));
|
|
394
|
+
}
|
|
395
|
+
if (result.errors) {
|
|
396
|
+
console.error(` ⚠️ GraphQL errors:`, result.errors);
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
const success = !!result.data?.updateProjectV2ItemFieldValue?.projectV2Item;
|
|
400
|
+
if (!success && debug) {
|
|
401
|
+
console.error(` [DEBUG] Mutation returned no projectV2Item in response`);
|
|
402
|
+
}
|
|
403
|
+
return success;
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
const errorMessage = error.message || error.stderr || String(error);
|
|
407
|
+
console.error(` ⚠️ Failed to set project date field: ${errorMessage}`);
|
|
408
|
+
if (debug) {
|
|
409
|
+
console.error(` [DEBUG] Full error:`, error);
|
|
410
|
+
console.error(` [DEBUG] Stack trace:`, error.stack);
|
|
411
|
+
}
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Set GitHub Project date fields (Target and Start) for an issue
|
|
417
|
+
*/
|
|
418
|
+
async setProjectDateFields(repo, projectName, issueId, targetDate, startDate) {
|
|
419
|
+
const debug = process.env.DEBUG === 'true';
|
|
420
|
+
try {
|
|
421
|
+
if (debug) {
|
|
422
|
+
console.log(` [DEBUG] Setting project date fields:`);
|
|
423
|
+
console.log(` [DEBUG] Repo: ${repo}`);
|
|
424
|
+
console.log(` [DEBUG] Project: ${projectName}`);
|
|
425
|
+
console.log(` [DEBUG] Issue ID: ${issueId}`);
|
|
426
|
+
console.log(` [DEBUG] Target date: ${targetDate || 'not set'}`);
|
|
427
|
+
console.log(` [DEBUG] Start date: ${startDate || 'not set'}`);
|
|
428
|
+
}
|
|
429
|
+
// Get project node ID
|
|
430
|
+
console.log(` Looking up project "${projectName}"...`);
|
|
431
|
+
const projectId = await this.getProjectNodeId(repo, projectName);
|
|
432
|
+
if (!projectId) {
|
|
433
|
+
console.error(` ⚠️ Project "${projectName}" not found`);
|
|
434
|
+
if (debug) {
|
|
435
|
+
console.error(` [DEBUG] Tried to find project in repo: ${repo}`);
|
|
436
|
+
}
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
if (debug) {
|
|
440
|
+
console.log(` [DEBUG] Found project ID: ${projectId}`);
|
|
441
|
+
}
|
|
442
|
+
// Get project item ID (with retry mechanism)
|
|
443
|
+
console.log(` Looking up issue in project...`);
|
|
444
|
+
const itemId = await this.getProjectItemId(projectId, issueId);
|
|
445
|
+
if (!itemId) {
|
|
446
|
+
console.error(` ⚠️ Issue not found in project "${projectName}"`);
|
|
447
|
+
console.error(` ⚠️ This may be due to a timing issue. The issue may appear in the project shortly.`);
|
|
448
|
+
if (debug) {
|
|
449
|
+
console.error(` [DEBUG] Project ID: ${projectId}`);
|
|
450
|
+
console.error(` [DEBUG] Issue ID: ${issueId}`);
|
|
451
|
+
}
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
if (debug) {
|
|
455
|
+
console.log(` [DEBUG] Found project item ID: ${itemId}`);
|
|
456
|
+
}
|
|
457
|
+
let success = true;
|
|
458
|
+
const results = {};
|
|
459
|
+
// Set Target date field if provided
|
|
460
|
+
if (targetDate) {
|
|
461
|
+
console.log(` Setting Target date field...`);
|
|
462
|
+
const targetFieldId = await this.getProjectFieldId(projectId, 'Target');
|
|
463
|
+
if (targetFieldId) {
|
|
464
|
+
if (debug) {
|
|
465
|
+
console.log(` [DEBUG] Target field ID: ${targetFieldId}`);
|
|
466
|
+
}
|
|
467
|
+
const result = await this.setProjectItemDateField(projectId, itemId, targetFieldId, targetDate);
|
|
468
|
+
results.target = result;
|
|
469
|
+
if (result) {
|
|
470
|
+
console.log(` ✅ Set Target date: ${targetDate}`);
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
console.log(` ⚠️ Failed to set Target date: ${targetDate}`);
|
|
474
|
+
if (debug) {
|
|
475
|
+
console.error(` [DEBUG] Mutation may have failed. Check GraphQL response above.`);
|
|
476
|
+
}
|
|
477
|
+
success = false;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
console.log(` ⚠️ Target field not found in project "${projectName}"`);
|
|
482
|
+
if (debug) {
|
|
483
|
+
console.error(` [DEBUG] Make sure the project has a "Target" date field configured.`);
|
|
484
|
+
}
|
|
485
|
+
success = false;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
// Set Start date field if provided
|
|
489
|
+
if (startDate) {
|
|
490
|
+
console.log(` Setting Start date field...`);
|
|
491
|
+
const startFieldId = await this.getProjectFieldId(projectId, 'Start');
|
|
492
|
+
if (startFieldId) {
|
|
493
|
+
if (debug) {
|
|
494
|
+
console.log(` [DEBUG] Start field ID: ${startFieldId}`);
|
|
495
|
+
}
|
|
496
|
+
const result = await this.setProjectItemDateField(projectId, itemId, startFieldId, startDate);
|
|
497
|
+
results.start = result;
|
|
498
|
+
if (result) {
|
|
499
|
+
console.log(` ✅ Set Start date: ${startDate}`);
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
console.log(` ⚠️ Failed to set Start date: ${startDate}`);
|
|
503
|
+
if (debug) {
|
|
504
|
+
console.error(` [DEBUG] Mutation may have failed. Check GraphQL response above.`);
|
|
505
|
+
}
|
|
506
|
+
success = false;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
console.log(` ⚠️ Start field not found in project "${projectName}"`);
|
|
511
|
+
if (debug) {
|
|
512
|
+
console.error(` [DEBUG] Make sure the project has a "Start" date field configured.`);
|
|
513
|
+
}
|
|
514
|
+
success = false;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
// Summary
|
|
518
|
+
if (debug) {
|
|
519
|
+
console.log(` [DEBUG] Date field setting results:`, results);
|
|
520
|
+
}
|
|
521
|
+
if (!success) {
|
|
522
|
+
console.log(` ⚠️ Some date fields failed to set. Check the messages above for details.`);
|
|
523
|
+
}
|
|
524
|
+
return success;
|
|
525
|
+
}
|
|
526
|
+
catch (error) {
|
|
527
|
+
const errorMessage = error.message || error.stderr || String(error);
|
|
528
|
+
console.error(` ⚠️ Failed to set project date fields: ${errorMessage}`);
|
|
529
|
+
if (debug) {
|
|
530
|
+
console.error(` [DEBUG] Full error:`, error);
|
|
531
|
+
console.error(` [DEBUG] Stack trace:`, error.stack);
|
|
532
|
+
}
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Get GitHub Project name from issue ID
|
|
538
|
+
*/
|
|
539
|
+
async getIssueProject(repo, issueId) {
|
|
540
|
+
try {
|
|
541
|
+
const [owner, name] = repo.split('/');
|
|
542
|
+
const query = `query {
|
|
543
|
+
repository(owner: "${owner}", name: "${name}") {
|
|
544
|
+
issue(id: "${issueId}") {
|
|
545
|
+
projectItems(first: 10) {
|
|
546
|
+
nodes {
|
|
547
|
+
project {
|
|
548
|
+
... on ProjectV2 {
|
|
549
|
+
title
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}`;
|
|
557
|
+
const output = (0, child_process_1.execSync)(`gh api graphql -f query="${query.replace(/"/g, '\\"')}"`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
558
|
+
const result = JSON.parse(output);
|
|
559
|
+
const projectItems = result.data?.repository?.issue?.projectItems?.nodes || [];
|
|
560
|
+
if (projectItems.length > 0) {
|
|
561
|
+
return projectItems[0].project?.title || null;
|
|
562
|
+
}
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
catch (error) {
|
|
566
|
+
console.error('⚠️ Failed to get issue project:', error);
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
122
570
|
}
|
|
123
571
|
exports.GitHubClientWrapper = GitHubClientWrapper;
|
package/dist/input-handler.js
CHANGED
|
@@ -158,6 +158,17 @@ class InputHandler {
|
|
|
158
158
|
name: 'description',
|
|
159
159
|
message: 'Issue description (opens editor):',
|
|
160
160
|
},
|
|
161
|
+
{
|
|
162
|
+
type: 'input',
|
|
163
|
+
name: 'startDate',
|
|
164
|
+
message: 'Start date (YYYY-MM-DD, optional):',
|
|
165
|
+
validate: (input) => {
|
|
166
|
+
if (!input)
|
|
167
|
+
return true; // Optional
|
|
168
|
+
const date = new Date(input);
|
|
169
|
+
return !isNaN(date.getTime()) || 'Invalid date format';
|
|
170
|
+
},
|
|
171
|
+
},
|
|
161
172
|
{
|
|
162
173
|
type: 'input',
|
|
163
174
|
name: 'dueDate',
|
|
@@ -183,6 +194,7 @@ class InputHandler {
|
|
|
183
194
|
title: answers.title,
|
|
184
195
|
description: answers.description || '',
|
|
185
196
|
dueDate: answers.dueDate || '',
|
|
197
|
+
startDate: answers.startDate || '',
|
|
186
198
|
labels: answers.labels || [],
|
|
187
199
|
};
|
|
188
200
|
}
|