team-toon-tack 1.6.3 → 1.7.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/dist/scripts/done-job.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import prompts from "prompts";
|
|
2
2
|
import { buildCompletionComment, getLatestCommit } from "./lib/git.js";
|
|
3
3
|
import { addComment, getStatusTransitions, getWorkflowStates, updateIssueStatus, } from "./lib/linear.js";
|
|
4
|
-
import {
|
|
4
|
+
import { syncSingleIssue } from "./lib/sync.js";
|
|
5
|
+
import { getLinearClient, loadConfig, loadCycleData, loadLocalConfig, } from "./utils.js";
|
|
5
6
|
function parseArgs(args) {
|
|
6
7
|
let issueId;
|
|
7
8
|
let message;
|
|
@@ -144,10 +145,15 @@ Examples:
|
|
|
144
145
|
}
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
|
-
//
|
|
148
|
-
task.
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
// Sync full issue data from Linear (including new comment)
|
|
149
|
+
const syncedTask = await syncSingleIssue(task.id, {
|
|
150
|
+
config,
|
|
151
|
+
localConfig,
|
|
152
|
+
preserveLocalStatus: false, // Let remote status determine local status
|
|
153
|
+
});
|
|
154
|
+
if (syncedTask) {
|
|
155
|
+
console.log(`Synced: ${syncedTask.id} → ${syncedTask.status} (local: ${syncedTask.localStatus})`);
|
|
156
|
+
}
|
|
151
157
|
// Summary
|
|
152
158
|
console.log(`\n${"═".repeat(50)}`);
|
|
153
159
|
console.log(`✅ ${task.id}: ${task.title}`);
|
|
@@ -66,8 +66,7 @@ export function getDefaultStatusTransitions(states) {
|
|
|
66
66
|
const defaultDone = states.find((s) => s.type === "completed")?.name ||
|
|
67
67
|
findStatusByKeyword(states, ["done", "complete"]) ||
|
|
68
68
|
"Done";
|
|
69
|
-
const defaultTesting = findStatusByKeyword(states, ["testing", "review"]) ||
|
|
70
|
-
undefined;
|
|
69
|
+
const defaultTesting = findStatusByKeyword(states, ["testing", "review"]) || undefined;
|
|
71
70
|
return {
|
|
72
71
|
todo: defaultTodo,
|
|
73
72
|
in_progress: defaultInProgress,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LinearClient } from "@linear/sdk";
|
|
2
|
+
import { type Config, type LocalConfig, type Task } from "../utils.js";
|
|
3
|
+
export interface SyncIssueOptions {
|
|
4
|
+
config: Config;
|
|
5
|
+
localConfig: LocalConfig;
|
|
6
|
+
client?: LinearClient;
|
|
7
|
+
preserveLocalStatus?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Sync a single issue from Linear and update local cycle data
|
|
11
|
+
* Returns the updated task or null if issue not found
|
|
12
|
+
*/
|
|
13
|
+
export declare function syncSingleIssue(issueId: string, options: SyncIssueOptions): Promise<Task | null>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { getLinearClient, loadCycleData, saveCycleData, getPrioritySortIndex, } from "../utils.js";
|
|
2
|
+
import { getStatusTransitions } from "./linear.js";
|
|
3
|
+
/**
|
|
4
|
+
* Sync a single issue from Linear and update local cycle data
|
|
5
|
+
* Returns the updated task or null if issue not found
|
|
6
|
+
*/
|
|
7
|
+
export async function syncSingleIssue(issueId, options) {
|
|
8
|
+
const { config, localConfig: _localConfig, preserveLocalStatus = true, } = options;
|
|
9
|
+
const client = options.client ?? getLinearClient();
|
|
10
|
+
// Search for the issue
|
|
11
|
+
const searchResult = await client.searchIssues(issueId);
|
|
12
|
+
const matchingIssue = searchResult.nodes.find((i) => i.identifier === issueId);
|
|
13
|
+
if (!matchingIssue) {
|
|
14
|
+
console.error(`Issue ${issueId} not found in Linear.`);
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
// Fetch full issue data
|
|
18
|
+
const issue = await client.issue(matchingIssue.id);
|
|
19
|
+
const assignee = await issue.assignee;
|
|
20
|
+
const assigneeEmail = assignee?.email;
|
|
21
|
+
const labels = await issue.labels();
|
|
22
|
+
const labelNames = labels.nodes.map((l) => l.name);
|
|
23
|
+
const state = await issue.state;
|
|
24
|
+
const parent = await issue.parent;
|
|
25
|
+
const attachmentsData = await issue.attachments();
|
|
26
|
+
const commentsData = await issue.comments();
|
|
27
|
+
// Build attachments list
|
|
28
|
+
const attachments = attachmentsData.nodes.map((a) => ({
|
|
29
|
+
id: a.id,
|
|
30
|
+
title: a.title,
|
|
31
|
+
url: a.url,
|
|
32
|
+
sourceType: a.sourceType ?? undefined,
|
|
33
|
+
}));
|
|
34
|
+
// Build comments list
|
|
35
|
+
const comments = await Promise.all(commentsData.nodes.map(async (c) => {
|
|
36
|
+
const user = await c.user;
|
|
37
|
+
return {
|
|
38
|
+
id: c.id,
|
|
39
|
+
body: c.body,
|
|
40
|
+
createdAt: c.createdAt.toISOString(),
|
|
41
|
+
user: user?.displayName ?? user?.email,
|
|
42
|
+
};
|
|
43
|
+
}));
|
|
44
|
+
// Determine local status
|
|
45
|
+
let localStatus = "pending";
|
|
46
|
+
const existingData = await loadCycleData();
|
|
47
|
+
if (preserveLocalStatus && existingData) {
|
|
48
|
+
const existingTask = existingData.tasks.find((t) => t.id === issueId);
|
|
49
|
+
if (existingTask) {
|
|
50
|
+
localStatus = existingTask.localStatus;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Map remote status to local status if not preserving
|
|
54
|
+
if (!preserveLocalStatus && state) {
|
|
55
|
+
const transitions = getStatusTransitions(config);
|
|
56
|
+
if (state.name === transitions.done) {
|
|
57
|
+
localStatus = "completed";
|
|
58
|
+
}
|
|
59
|
+
else if (state.name === transitions.in_progress) {
|
|
60
|
+
localStatus = "in-progress";
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
localStatus = "pending";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const task = {
|
|
67
|
+
id: issue.identifier,
|
|
68
|
+
linearId: issue.id,
|
|
69
|
+
title: issue.title,
|
|
70
|
+
status: state ? state.name : "Unknown",
|
|
71
|
+
localStatus: localStatus,
|
|
72
|
+
assignee: assigneeEmail,
|
|
73
|
+
priority: issue.priority,
|
|
74
|
+
labels: labelNames,
|
|
75
|
+
branch: issue.branchName,
|
|
76
|
+
description: issue.description ?? undefined,
|
|
77
|
+
parentIssueId: parent ? parent.identifier : undefined,
|
|
78
|
+
url: issue.url,
|
|
79
|
+
attachments: attachments.length > 0 ? attachments : undefined,
|
|
80
|
+
comments: comments.length > 0 ? comments : undefined,
|
|
81
|
+
};
|
|
82
|
+
// Update cycle data
|
|
83
|
+
if (existingData) {
|
|
84
|
+
const existingTasks = existingData.tasks.filter((t) => t.id !== issueId);
|
|
85
|
+
const finalTasks = [...existingTasks, task];
|
|
86
|
+
// Sort by priority
|
|
87
|
+
finalTasks.sort((a, b) => {
|
|
88
|
+
const pa = getPrioritySortIndex(a.priority, config.priority_order);
|
|
89
|
+
const pb = getPrioritySortIndex(b.priority, config.priority_order);
|
|
90
|
+
return pa - pb;
|
|
91
|
+
});
|
|
92
|
+
existingData.tasks = finalTasks;
|
|
93
|
+
existingData.updatedAt = new Date().toISOString();
|
|
94
|
+
await saveCycleData(existingData);
|
|
95
|
+
}
|
|
96
|
+
return task;
|
|
97
|
+
}
|