team-toon-tack 3.7.5 → 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/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({
@@ -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 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "team-toon-tack",
3
- "version": "3.7.5",
3
+ "version": "3.7.6",
4
4
  "description": "Linear & Trello task sync & management CLI with TOON format",
5
5
  "type": "module",
6
6
  "bin": {