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 +14 -4
- package/dist/scripts/create.js +40 -11
- package/dist/scripts/edit.js +5 -4
- package/dist/scripts/lib/sync.js +1 -1
- package/package.json +1 -1
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({
|
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 });
|