team-toon-tack 1.6.2 → 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 +11 -5
- package/dist/scripts/lib/config-builder.js +1 -2
- package/dist/scripts/lib/sync.d.ts +13 -0
- package/dist/scripts/lib/sync.js +97 -0
- package/dist/scripts/status.js +18 -10
- package/dist/scripts/work-on.js +3 -2
- package/package.json +1 -1
- package/templates/claude-code-commands/work-on.md +14 -3
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
|
+
}
|
package/dist/scripts/status.js
CHANGED
|
@@ -135,17 +135,13 @@ Examples:
|
|
|
135
135
|
console.error(`Unknown status: ${setStatus}`);
|
|
136
136
|
process.exit(1);
|
|
137
137
|
}
|
|
138
|
+
// Track if we need to save
|
|
139
|
+
let needsSave = false;
|
|
140
|
+
const oldLocalStatus = task.localStatus;
|
|
138
141
|
// Update local status
|
|
139
|
-
if (newLocalStatus) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
task.localStatus = newLocalStatus;
|
|
143
|
-
await saveCycleData(data);
|
|
144
|
-
console.log(`Local: ${task.id} ${oldStatus} → ${newLocalStatus}`);
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
console.log(`Local: ${task.id} already ${newLocalStatus}`);
|
|
148
|
-
}
|
|
142
|
+
if (newLocalStatus && newLocalStatus !== task.localStatus) {
|
|
143
|
+
task.localStatus = newLocalStatus;
|
|
144
|
+
needsSave = true;
|
|
149
145
|
}
|
|
150
146
|
// Update Linear status
|
|
151
147
|
if (newLinearStatus || newLocalStatus) {
|
|
@@ -156,10 +152,22 @@ Examples:
|
|
|
156
152
|
if (targetStateName) {
|
|
157
153
|
const success = await updateIssueStatus(task.linearId, targetStateName, config, localConfig.team);
|
|
158
154
|
if (success) {
|
|
155
|
+
task.status = targetStateName;
|
|
156
|
+
needsSave = true;
|
|
159
157
|
console.log(`Linear: ${task.id} → ${targetStateName}`);
|
|
160
158
|
}
|
|
161
159
|
}
|
|
162
160
|
}
|
|
161
|
+
// Save if anything changed
|
|
162
|
+
if (needsSave) {
|
|
163
|
+
await saveCycleData(data);
|
|
164
|
+
if (newLocalStatus && newLocalStatus !== oldLocalStatus) {
|
|
165
|
+
console.log(`Local: ${task.id} ${oldLocalStatus} → ${newLocalStatus}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else if (newLocalStatus) {
|
|
169
|
+
console.log(`Local: ${task.id} already ${newLocalStatus}`);
|
|
170
|
+
}
|
|
163
171
|
}
|
|
164
172
|
// Display task info using shared function
|
|
165
173
|
displayTaskWithStatus(task);
|
package/dist/scripts/work-on.js
CHANGED
|
@@ -83,16 +83,17 @@ Examples:
|
|
|
83
83
|
// Mark as In Progress
|
|
84
84
|
if (task.localStatus === "pending") {
|
|
85
85
|
task.localStatus = "in-progress";
|
|
86
|
-
await saveCycleData(data);
|
|
87
|
-
console.log(`Local: ${task.id} → in-progress`);
|
|
88
86
|
// Update Linear
|
|
89
87
|
if (task.linearId && process.env.LINEAR_API_KEY) {
|
|
90
88
|
const transitions = getStatusTransitions(config);
|
|
91
89
|
const success = await updateIssueStatus(task.linearId, transitions.in_progress, config, localConfig.team);
|
|
92
90
|
if (success) {
|
|
91
|
+
task.status = transitions.in_progress;
|
|
93
92
|
console.log(`Linear: ${task.id} → ${transitions.in_progress}`);
|
|
94
93
|
}
|
|
95
94
|
}
|
|
95
|
+
await saveCycleData(data);
|
|
96
|
+
console.log(`Local: ${task.id} → in-progress`);
|
|
96
97
|
}
|
|
97
98
|
// Display task info
|
|
98
99
|
displayTaskFull(task, "👷");
|
package/package.json
CHANGED
|
@@ -28,9 +28,20 @@ Script displays title, description, priority, labels, and attachments.
|
|
|
28
28
|
1. Read the issue description carefully
|
|
29
29
|
2. Explore related code
|
|
30
30
|
3. Implement the fix/feature
|
|
31
|
-
4.
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
4. Commit with conventional format
|
|
32
|
+
|
|
33
|
+
### 4. Verify
|
|
34
|
+
|
|
35
|
+
Run project-required verification before completing:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Run verification procedure defined in project
|
|
39
|
+
# (e.g., type-check, lint, test, build)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 5. Complete
|
|
43
|
+
|
|
44
|
+
Use `/done-job` to mark task as completed
|
|
34
45
|
|
|
35
46
|
## Example Usage
|
|
36
47
|
|