team-toon-tack 3.7.1 → 3.7.3
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 +7 -1
- package/dist/scripts/lib/init/prompts.js +8 -1
- package/dist/scripts/sync.js +41 -41
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -110,8 +110,14 @@ async function main() {
|
|
|
110
110
|
process.env.TOON_DIR = dir;
|
|
111
111
|
// Load .ttt/.env (if present) and resolve configured Linear API key env
|
|
112
112
|
// var into LINEAR_API_KEY so downstream code is workspace-aware.
|
|
113
|
+
// Skip the resolver for `init` so the workspace picker sees the raw
|
|
114
|
+
// env — otherwise we'd mirror the previously-saved key over LINEAR_API_KEY
|
|
115
|
+
// and the user's shell-level key would appear to point to the saved
|
|
116
|
+
// workspace.
|
|
113
117
|
await loadDotEnv(join(dir, ".env"));
|
|
114
|
-
|
|
118
|
+
if (command !== "init") {
|
|
119
|
+
await resolveLinearApiKey(join(dir, "local.toon"));
|
|
120
|
+
}
|
|
115
121
|
if (!COMMANDS.includes(command)) {
|
|
116
122
|
console.error(`Unknown command: ${command}`);
|
|
117
123
|
console.error(`Run 'ttt help' for usage.`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generic prompt functions shared between Linear and Trello init
|
|
3
3
|
*/
|
|
4
|
-
import { checkbox, select } from "@inquirer/prompts";
|
|
4
|
+
import { checkbox, confirm, select } from "@inquirer/prompts";
|
|
5
5
|
export async function selectTaskSource(options) {
|
|
6
6
|
if (options.source) {
|
|
7
7
|
return options.source;
|
|
@@ -60,6 +60,13 @@ export async function selectLabelFilter(labels, options) {
|
|
|
60
60
|
return options.labels;
|
|
61
61
|
}
|
|
62
62
|
if (options.interactive && labels.length > 0) {
|
|
63
|
+
const filterByLabels = await confirm({
|
|
64
|
+
message: "Filter issues by labels? (No = fetch all, ignoring labels)",
|
|
65
|
+
default: false,
|
|
66
|
+
});
|
|
67
|
+
if (!filterByLabels) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
63
70
|
const labelChoices = labels.map((l) => ({ name: l.name, value: l.name }));
|
|
64
71
|
const selectedLabels = await checkbox({
|
|
65
72
|
message: "Select label filters (space to select, enter to confirm):",
|
package/dist/scripts/sync.js
CHANGED
|
@@ -86,51 +86,49 @@ Examples:
|
|
|
86
86
|
}
|
|
87
87
|
// Build excluded labels set
|
|
88
88
|
const excludedLabels = new Set(localConfig.exclude_labels ?? []);
|
|
89
|
-
// Phase 1: Fetch active cycle directly from team
|
|
89
|
+
// Phase 1: Fetch active cycle directly from team (if the team uses cycles)
|
|
90
90
|
console.log("Fetching latest cycle...");
|
|
91
91
|
const team = await client.team(teamId);
|
|
92
92
|
const activeCycle = await team.activeCycle;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
process.exit(1);
|
|
96
|
-
}
|
|
97
|
-
const cycleId = activeCycle.id;
|
|
98
|
-
const cycleName = activeCycle.name ?? `Cycle #${activeCycle.number}`;
|
|
99
|
-
const newCycleInfo = {
|
|
100
|
-
id: cycleId,
|
|
101
|
-
name: cycleName,
|
|
102
|
-
start_date: activeCycle.startsAt?.toISOString().split("T")[0] ?? "",
|
|
103
|
-
end_date: activeCycle.endsAt?.toISOString().split("T")[0] ?? "",
|
|
104
|
-
};
|
|
105
|
-
// Check if cycle changed and update config with history
|
|
93
|
+
let cycleId;
|
|
94
|
+
let cycleName;
|
|
106
95
|
const existingData = await loadCycleData();
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
96
|
+
if (activeCycle) {
|
|
97
|
+
cycleId = activeCycle.id;
|
|
98
|
+
cycleName = activeCycle.name ?? `Cycle #${activeCycle.number}`;
|
|
99
|
+
const newCycleInfo = {
|
|
100
|
+
id: cycleId,
|
|
101
|
+
name: cycleName,
|
|
102
|
+
start_date: activeCycle.startsAt?.toISOString().split("T")[0] ?? "",
|
|
103
|
+
end_date: activeCycle.endsAt?.toISOString().split("T")[0] ?? "",
|
|
104
|
+
};
|
|
105
|
+
const oldCycleId = config.current_cycle?.id ?? existingData?.cycleId;
|
|
106
|
+
if (oldCycleId && oldCycleId !== cycleId) {
|
|
107
|
+
const oldCycleName = config.current_cycle?.name ?? existingData?.cycleName ?? "Unknown";
|
|
108
|
+
console.log(`Cycle changed: ${oldCycleName} → ${cycleName}`);
|
|
109
|
+
if (config.current_cycle) {
|
|
110
|
+
config.cycle_history = config.cycle_history ?? [];
|
|
111
|
+
config.cycle_history = config.cycle_history.filter((c) => c.id !== config.current_cycle?.id);
|
|
112
|
+
config.cycle_history.unshift(config.current_cycle);
|
|
113
|
+
if (config.cycle_history.length > 10) {
|
|
114
|
+
config.cycle_history = config.cycle_history.slice(0, 10);
|
|
115
|
+
}
|
|
120
116
|
}
|
|
121
|
-
}
|
|
122
|
-
// Update current cycle
|
|
123
|
-
config.current_cycle = newCycleInfo;
|
|
124
|
-
await saveConfig(config);
|
|
125
|
-
console.log("Config updated with new cycle (old cycle saved to history).");
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
// Update current cycle info even if ID unchanged (dates might change)
|
|
129
|
-
if (!config.current_cycle || config.current_cycle.id !== cycleId) {
|
|
130
117
|
config.current_cycle = newCycleInfo;
|
|
131
118
|
await saveConfig(config);
|
|
119
|
+
console.log("Config updated with new cycle (old cycle saved to history).");
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
if (!config.current_cycle || config.current_cycle.id !== cycleId) {
|
|
123
|
+
config.current_cycle = newCycleInfo;
|
|
124
|
+
await saveConfig(config);
|
|
125
|
+
}
|
|
126
|
+
console.log(`Current cycle: ${cycleName}`);
|
|
132
127
|
}
|
|
133
|
-
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
cycleName = "No Cycle";
|
|
131
|
+
console.log("No active cycle on this team — syncing without cycle filter.");
|
|
134
132
|
}
|
|
135
133
|
// Phase 2: Fetch workflow states and get status mappings
|
|
136
134
|
const workflowStates = await client.workflowStates({
|
|
@@ -222,12 +220,14 @@ Examples:
|
|
|
222
220
|
? "all statuses"
|
|
223
221
|
: `${syncStatuses.join("/")} status`;
|
|
224
222
|
console.log(`Fetching issues (${statusDesc})${labelDesc}...`);
|
|
225
|
-
// Build filter - label is optional
|
|
223
|
+
// Build filter - label is optional; cycle is skipped if team has no
|
|
224
|
+
// active cycle (Linear lets teams disable cycles entirely).
|
|
226
225
|
const issueFilter = {
|
|
227
226
|
team: { id: { eq: teamId } },
|
|
228
|
-
cycle: { id: { eq: cycleId } },
|
|
229
227
|
};
|
|
230
|
-
|
|
228
|
+
if (cycleId) {
|
|
229
|
+
issueFilter.cycle = { id: { eq: cycleId } };
|
|
230
|
+
}
|
|
231
231
|
if (!syncAll) {
|
|
232
232
|
issueFilter.state = { name: { in: syncStatuses } };
|
|
233
233
|
}
|
|
@@ -372,7 +372,7 @@ Examples:
|
|
|
372
372
|
finalTasks = tasks;
|
|
373
373
|
}
|
|
374
374
|
const newData = {
|
|
375
|
-
cycleId: cycleId
|
|
375
|
+
cycleId: cycleId ?? `team:${teamId}`,
|
|
376
376
|
cycleName: cycleName,
|
|
377
377
|
updatedAt: new Date().toISOString(),
|
|
378
378
|
tasks: finalTasks,
|