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.
@@ -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.0"
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.0"
16
+ "version": "2.8.1"
17
17
  }
18
18
  ]
19
19
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "team-toon-tack",
3
3
  "description": "Linear/Trello task sync & management CLI for Claude Code - saves tokens vs MCP",
4
- "version": "2.8.0",
4
+ "version": "2.8.1",
5
5
  "author": {
6
6
  "name": "wayne930242",
7
7
  "email": "wayne930242@gmail.com"
package/dist/bin/cli.js CHANGED
@@ -49,8 +49,10 @@ COMMANDS:
49
49
  version Show version
50
50
 
51
51
  GLOBAL OPTIONS:
52
- -d, --dir <path> Config directory (default: .ttt)
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
- function parseGlobalArgs(args) {
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 === "-d" || arg === "--dir") {
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
@@ -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 || !config.statuses)
62
+ async function resolveStatusId(config, adapter, teamId, statusName) {
63
+ if (!statusName)
64
64
  return undefined;
65
- const entry = Object.entries(config.statuses).find(([, s]) => s.name.toLowerCase() === statusName.toLowerCase());
66
- return entry ? entry[0] : undefined;
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 entry = Object.entries(config.labels).find(([, l]) => l.name.toLowerCase() === name.toLowerCase());
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[0]);
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(config, parentIdentifier) {
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(config, parentId);
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,
@@ -111,9 +111,10 @@ Examples:
111
111
  const ids = [];
112
112
  if (config.labels) {
113
113
  for (const name of names) {
114
- const entry = Object.entries(config.labels).find(([, l]) => l.name.toLowerCase() === name.toLowerCase());
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[0]);
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(([id, l]) => ({
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
  }
@@ -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, } = options;
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&labels=true`);
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
@@ -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 (argument that doesn't start with -)
295
- const issueId = args.find((arg) => !arg.startsWith("-") && arg.match(/^[A-Z]+-\d+$/i));
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "team-toon-tack",
3
- "version": "3.7.4",
3
+ "version": "3.7.6",
4
4
  "description": "Linear & Trello task sync & management CLI with TOON format",
5
5
  "type": "module",
6
6
  "bin": {