team-toon-tack 3.7.4 → 3.7.6
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/bin/cli.js +14 -4
- package/dist/scripts/create.js +40 -11
- package/dist/scripts/edit.js +5 -4
- package/dist/scripts/lib/adapters/trello-adapter.js +8 -1
- package/dist/scripts/lib/sync.js +1 -1
- package/dist/scripts/lib/trello.js +1 -1
- package/dist/scripts/show.js +3 -2
- package/package.json +1 -1
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Task management tools for Claude Code - supports Linear and Trello, efficient workflow without MCP overhead",
|
|
9
|
-
"version": "1.2.
|
|
9
|
+
"version": "1.2.1"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "team-toon-tack",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "Linear/Trello task sync & management CLI with commands and skills",
|
|
16
|
-
"version": "2.8.
|
|
16
|
+
"version": "2.8.1"
|
|
17
17
|
}
|
|
18
18
|
]
|
|
19
19
|
}
|
package/dist/bin/cli.js
CHANGED
|
@@ -49,8 +49,10 @@ COMMANDS:
|
|
|
49
49
|
version Show version
|
|
50
50
|
|
|
51
51
|
GLOBAL OPTIONS:
|
|
52
|
-
|
|
52
|
+
--dir <path> Config directory (default: .ttt)
|
|
53
53
|
Can also set via TOON_DIR environment variable
|
|
54
|
+
-d <path> Shortcut for --dir; ignored for create/edit/comment
|
|
55
|
+
(where -d means --description)
|
|
54
56
|
|
|
55
57
|
EXAMPLES:
|
|
56
58
|
ttt init # Initialize .ttt directory
|
|
@@ -76,12 +78,20 @@ More info: https://github.com/wayne930242/team-toon-tack
|
|
|
76
78
|
function printVersion() {
|
|
77
79
|
console.log(`team-toon-tack v${VERSION}`);
|
|
78
80
|
}
|
|
79
|
-
|
|
81
|
+
// Subcommands where `-d` is a subcommand flag (e.g. --description) — never
|
|
82
|
+
// consume `-d` as the global config-dir flag for these. `--dir` always works.
|
|
83
|
+
const SHORT_D_RESERVED_FOR_SUBCOMMAND = new Set([
|
|
84
|
+
"create",
|
|
85
|
+
"edit",
|
|
86
|
+
"comment",
|
|
87
|
+
]);
|
|
88
|
+
function parseGlobalArgs(command, args) {
|
|
80
89
|
let dir = process.env.TOON_DIR || resolve(process.cwd(), ".ttt");
|
|
81
90
|
const commandArgs = [];
|
|
91
|
+
const allowShortD = !SHORT_D_RESERVED_FOR_SUBCOMMAND.has(command);
|
|
82
92
|
for (let i = 0; i < args.length; i++) {
|
|
83
93
|
const arg = args[i];
|
|
84
|
-
if (arg === "
|
|
94
|
+
if (arg === "--dir" || (allowShortD && arg === "-d")) {
|
|
85
95
|
dir = resolve(args[++i] || ".");
|
|
86
96
|
}
|
|
87
97
|
else {
|
|
@@ -105,7 +115,7 @@ async function main() {
|
|
|
105
115
|
}
|
|
106
116
|
const command = args[0];
|
|
107
117
|
const restArgs = args.slice(1);
|
|
108
|
-
const { dir, commandArgs } = parseGlobalArgs(restArgs);
|
|
118
|
+
const { dir, commandArgs } = parseGlobalArgs(command, restArgs);
|
|
109
119
|
// Set TOON_DIR for scripts to use
|
|
110
120
|
process.env.TOON_DIR = dir;
|
|
111
121
|
// Load .ttt/.env (if present) and resolve configured Linear API key env
|
package/dist/scripts/create.js
CHANGED
|
@@ -59,11 +59,39 @@ function resolveAssigneeId(config, assigneeKey) {
|
|
|
59
59
|
}
|
|
60
60
|
return user.id;
|
|
61
61
|
}
|
|
62
|
-
function resolveStatusId(config, statusName) {
|
|
63
|
-
if (!statusName
|
|
62
|
+
async function resolveStatusId(config, adapter, teamId, statusName) {
|
|
63
|
+
if (!statusName)
|
|
64
64
|
return undefined;
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
// Resolve `status_transitions` aliases (e.g. "todo" → "Todo", "in_progress" → "In Progress")
|
|
66
|
+
const transitions = config.status_transitions;
|
|
67
|
+
let targetName = statusName;
|
|
68
|
+
if (transitions) {
|
|
69
|
+
const aliasKey = statusName.toLowerCase().replace(/[\s-]+/g, "_");
|
|
70
|
+
const aliased = aliasKey === "todo"
|
|
71
|
+
? Array.isArray(transitions.todo)
|
|
72
|
+
? transitions.todo[0]
|
|
73
|
+
: transitions.todo
|
|
74
|
+
: aliasKey === "in_progress"
|
|
75
|
+
? transitions.in_progress
|
|
76
|
+
: aliasKey === "done"
|
|
77
|
+
? transitions.done
|
|
78
|
+
: aliasKey === "testing"
|
|
79
|
+
? transitions.testing
|
|
80
|
+
: aliasKey === "blocked"
|
|
81
|
+
? transitions.blocked
|
|
82
|
+
: undefined;
|
|
83
|
+
if (aliased)
|
|
84
|
+
targetName = aliased;
|
|
85
|
+
}
|
|
86
|
+
const statuses = await adapter.getStatuses(teamId);
|
|
87
|
+
const target = statuses.find((s) => s.name.toLowerCase() === targetName.toLowerCase());
|
|
88
|
+
if (!target) {
|
|
89
|
+
console.error(`Status "${statusName}" not found. Available: ${statuses
|
|
90
|
+
.map((s) => s.name)
|
|
91
|
+
.join(", ")}`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
return target.id;
|
|
67
95
|
}
|
|
68
96
|
function resolveLabelIds(config, labelArg) {
|
|
69
97
|
if (!labelArg || !config.labels)
|
|
@@ -71,9 +99,11 @@ function resolveLabelIds(config, labelArg) {
|
|
|
71
99
|
const names = labelArg.split(",").map((n) => n.trim());
|
|
72
100
|
const ids = [];
|
|
73
101
|
for (const name of names) {
|
|
74
|
-
const
|
|
102
|
+
const lower = name.toLowerCase();
|
|
103
|
+
// Match by config key (e.g. "backend_v3") OR display name (e.g. "Backend-v3")
|
|
104
|
+
const entry = Object.entries(config.labels).find(([key, l]) => key.toLowerCase() === lower || l.name.toLowerCase() === lower);
|
|
75
105
|
if (entry) {
|
|
76
|
-
ids.push(entry[
|
|
106
|
+
ids.push(entry[1].id);
|
|
77
107
|
}
|
|
78
108
|
else {
|
|
79
109
|
console.error(`Warning: label "${name}" not found in config, skipping.`);
|
|
@@ -81,10 +111,9 @@ function resolveLabelIds(config, labelArg) {
|
|
|
81
111
|
}
|
|
82
112
|
return ids.length > 0 ? ids : undefined;
|
|
83
113
|
}
|
|
84
|
-
async function resolveParentSourceId(
|
|
114
|
+
async function resolveParentSourceId(adapter, parentIdentifier) {
|
|
85
115
|
if (!parentIdentifier)
|
|
86
116
|
return undefined;
|
|
87
|
-
const adapter = createAdapter(config);
|
|
88
117
|
const parent = await adapter.searchIssue(parentIdentifier);
|
|
89
118
|
if (!parent) {
|
|
90
119
|
console.error(`Parent issue "${parentIdentifier}" not found.`);
|
|
@@ -174,11 +203,11 @@ Examples:
|
|
|
174
203
|
}
|
|
175
204
|
}
|
|
176
205
|
console.log("Creating issue...");
|
|
206
|
+
const adapter = createAdapter(config);
|
|
177
207
|
const assigneeId = resolveAssigneeId(config, assigneeKey);
|
|
178
|
-
const statusId = resolveStatusId(config, statusName);
|
|
208
|
+
const statusId = await resolveStatusId(config, adapter, teamId, statusName);
|
|
179
209
|
const labelIds = resolveLabelIds(config, labelArg);
|
|
180
|
-
const parentSourceId = await resolveParentSourceId(
|
|
181
|
-
const adapter = createAdapter(config);
|
|
210
|
+
const parentSourceId = await resolveParentSourceId(adapter, parentId);
|
|
182
211
|
const currentCycle = await adapter.getCurrentCycle(teamId);
|
|
183
212
|
const options = {
|
|
184
213
|
teamId,
|
package/dist/scripts/edit.js
CHANGED
|
@@ -111,9 +111,10 @@ Examples:
|
|
|
111
111
|
const ids = [];
|
|
112
112
|
if (config.labels) {
|
|
113
113
|
for (const name of names) {
|
|
114
|
-
const
|
|
114
|
+
const lower = name.toLowerCase();
|
|
115
|
+
const entry = Object.entries(config.labels).find(([key, l]) => key.toLowerCase() === lower || l.name.toLowerCase() === lower);
|
|
115
116
|
if (entry) {
|
|
116
|
-
ids.push(entry[
|
|
117
|
+
ids.push(entry[1].id);
|
|
117
118
|
}
|
|
118
119
|
else {
|
|
119
120
|
console.error(`Warning: label "${name}" not found in config, skipping.`);
|
|
@@ -173,9 +174,9 @@ Examples:
|
|
|
173
174
|
}
|
|
174
175
|
case "labels": {
|
|
175
176
|
if (config.labels) {
|
|
176
|
-
const labelChoices = Object.entries(config.labels).map(([
|
|
177
|
+
const labelChoices = Object.entries(config.labels).map(([, l]) => ({
|
|
177
178
|
name: l.name,
|
|
178
|
-
value: id,
|
|
179
|
+
value: l.id,
|
|
179
180
|
checked: task?.labels.includes(l.name) ?? false,
|
|
180
181
|
}));
|
|
181
182
|
fields.labelIds = await checkbox({
|
|
@@ -134,6 +134,12 @@ export class TrelloAdapter {
|
|
|
134
134
|
const card = await this.client.getCard(issueId);
|
|
135
135
|
if (!card)
|
|
136
136
|
return null;
|
|
137
|
+
if (!Array.isArray(card.labels)) {
|
|
138
|
+
card.labels = [];
|
|
139
|
+
}
|
|
140
|
+
if (!Array.isArray(card.idMembers)) {
|
|
141
|
+
card.idMembers = [];
|
|
142
|
+
}
|
|
137
143
|
// Get lists for status name lookup
|
|
138
144
|
if (!this.listsCache.has(card.idBoard)) {
|
|
139
145
|
await this.getStatuses(card.idBoard);
|
|
@@ -185,7 +191,8 @@ export class TrelloAdapter {
|
|
|
185
191
|
comments: sourceComments.length > 0 ? sourceComments : undefined,
|
|
186
192
|
};
|
|
187
193
|
}
|
|
188
|
-
catch {
|
|
194
|
+
catch (error) {
|
|
195
|
+
console.warn(`[trello] getIssue(${issueId}) failed:`, error instanceof Error ? error.message : error);
|
|
189
196
|
return null;
|
|
190
197
|
}
|
|
191
198
|
}
|
package/dist/scripts/lib/sync.js
CHANGED
|
@@ -62,7 +62,7 @@ export async function fetchIssueDetail(issueId, options = {}) {
|
|
|
62
62
|
* Returns the updated task or null if issue not found
|
|
63
63
|
*/
|
|
64
64
|
export async function syncSingleIssue(issueId, options) {
|
|
65
|
-
const { config, localConfig, preserveLocalStatus = true
|
|
65
|
+
const { config, localConfig, preserveLocalStatus = true } = options;
|
|
66
66
|
const client = options.client ?? getLinearClient();
|
|
67
67
|
// Fetch issue details using shared function
|
|
68
68
|
let task = await fetchIssueDetail(issueId, { client });
|
|
@@ -98,7 +98,7 @@ export class TrelloClient {
|
|
|
98
98
|
* Get a specific card by ID
|
|
99
99
|
*/
|
|
100
100
|
async getCard(cardId) {
|
|
101
|
-
return this.request("GET", `/cards/${cardId}?fields=id,name,desc,url,shortUrl,shortLink,closed,pos,due,dueComplete,idBoard,idList,idMembers,idLabels,badges,dateLastActivity&attachments=false&members=false
|
|
101
|
+
return this.request("GET", `/cards/${cardId}?fields=id,name,desc,url,shortUrl,shortLink,closed,pos,due,dueComplete,idBoard,idList,idMembers,idLabels,labels,badges,dateLastActivity&attachments=false&members=false`);
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
104
104
|
* Get card attachments
|
package/dist/scripts/show.js
CHANGED
|
@@ -291,8 +291,9 @@ Examples:
|
|
|
291
291
|
}
|
|
292
292
|
// Check if this is a search (has filters) or single issue lookup
|
|
293
293
|
const hasFilters = Object.keys(filters).length > 0;
|
|
294
|
-
// Find issue ID (
|
|
295
|
-
const issueId = args.find((arg) => !arg.startsWith("-") &&
|
|
294
|
+
// Find issue ID: Linear-style (MP-123) or Trello shortLink (8+ alphanumeric)
|
|
295
|
+
const issueId = args.find((arg) => !arg.startsWith("-") &&
|
|
296
|
+
(arg.match(/^[A-Z]+-\d+$/i) || arg.match(/^[A-Za-z0-9]{8,}$/)));
|
|
296
297
|
// If no issue ID and no filters, show all local issues
|
|
297
298
|
if (!issueId && !hasFilters) {
|
|
298
299
|
const data = await loadCycleData();
|