team-toon-tack 2.0.0 → 2.0.2

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.
@@ -1,6 +1,9 @@
1
1
  #!/usr/bin/env bun
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
4
7
  import { decode, encode } from "@toon-format/toon";
5
8
  import prompts from "prompts";
6
9
  import { buildConfig, buildLocalConfig, findTeamKey, findUserKey, getDefaultStatusTransitions, } from "./lib/config-builder.js";
@@ -217,13 +220,20 @@ async function selectStatusSource(options) {
217
220
  });
218
221
  return response.statusSource || "remote";
219
222
  }
220
- async function selectStatusMappings(states, options) {
221
- const defaults = getDefaultStatusTransitions(states);
222
- if (!options.interactive || states.length === 0) {
223
- return defaults;
223
+ async function selectStatusMappings(devStates, qaStates, options) {
224
+ // Use dev team states for todo, in_progress, done, blocked
225
+ // Use qa team states for testing (fallback to dev team if not set)
226
+ const devDefaults = getDefaultStatusTransitions(devStates);
227
+ const testingStates = qaStates && qaStates.length > 0 ? qaStates : devStates;
228
+ const testingDefaults = getDefaultStatusTransitions(testingStates);
229
+ if (!options.interactive || devStates.length === 0) {
230
+ return {
231
+ ...devDefaults,
232
+ testing: testingDefaults.testing,
233
+ };
224
234
  }
225
235
  console.log("\nšŸ“Š Configure status mappings:");
226
- const stateChoices = states.map((s) => ({
236
+ const devStateChoices = devStates.map((s) => ({
227
237
  title: `${s.name} (${s.type})`,
228
238
  value: s.name,
229
239
  }));
@@ -231,53 +241,61 @@ async function selectStatusMappings(states, options) {
231
241
  type: "select",
232
242
  name: "todo",
233
243
  message: 'Select status for "Todo" (pending tasks):',
234
- choices: stateChoices,
235
- initial: stateChoices.findIndex((c) => c.value === defaults.todo),
244
+ choices: devStateChoices,
245
+ initial: devStateChoices.findIndex((c) => c.value === devDefaults.todo),
236
246
  });
237
247
  const inProgressResponse = await prompts({
238
248
  type: "select",
239
249
  name: "in_progress",
240
250
  message: 'Select status for "In Progress" (working tasks):',
241
- choices: stateChoices,
242
- initial: stateChoices.findIndex((c) => c.value === defaults.in_progress),
251
+ choices: devStateChoices,
252
+ initial: devStateChoices.findIndex((c) => c.value === devDefaults.in_progress),
243
253
  });
244
254
  const doneResponse = await prompts({
245
255
  type: "select",
246
256
  name: "done",
247
257
  message: 'Select status for "Done" (completed tasks):',
248
- choices: stateChoices,
249
- initial: stateChoices.findIndex((c) => c.value === defaults.done),
258
+ choices: devStateChoices,
259
+ initial: devStateChoices.findIndex((c) => c.value === devDefaults.done),
250
260
  });
261
+ // Testing uses qa team states (or dev team if qa not set)
262
+ const testingStateChoices = testingStates.map((s) => ({
263
+ title: `${s.name} (${s.type})`,
264
+ value: s.name,
265
+ }));
251
266
  const testingChoices = [
252
267
  { title: "(None)", value: undefined },
253
- ...stateChoices,
268
+ ...testingStateChoices,
254
269
  ];
270
+ const testingMessage = qaStates && qaStates.length > 0
271
+ ? 'Select status for "Testing" (from QA team, for parent tasks):'
272
+ : 'Select status for "Testing" (optional, for parent tasks):';
255
273
  const testingResponse = await prompts({
256
274
  type: "select",
257
275
  name: "testing",
258
- message: 'Select status for "Testing" (optional, for parent tasks):',
276
+ message: testingMessage,
259
277
  choices: testingChoices,
260
- initial: defaults.testing
261
- ? testingChoices.findIndex((c) => c.value === defaults.testing)
278
+ initial: testingDefaults.testing
279
+ ? testingChoices.findIndex((c) => c.value === testingDefaults.testing)
262
280
  : 0,
263
281
  });
264
282
  const blockedChoices = [
265
283
  { title: "(None)", value: undefined },
266
- ...stateChoices,
284
+ ...devStateChoices,
267
285
  ];
268
286
  const blockedResponse = await prompts({
269
287
  type: "select",
270
288
  name: "blocked",
271
289
  message: 'Select status for "Blocked" (optional, for blocked tasks):',
272
290
  choices: blockedChoices,
273
- initial: defaults.blocked
274
- ? blockedChoices.findIndex((c) => c.value === defaults.blocked)
291
+ initial: devDefaults.blocked
292
+ ? blockedChoices.findIndex((c) => c.value === devDefaults.blocked)
275
293
  : 0,
276
294
  });
277
295
  return {
278
- todo: todoResponse.todo || defaults.todo,
279
- in_progress: inProgressResponse.in_progress || defaults.in_progress,
280
- done: doneResponse.done || defaults.done,
296
+ todo: todoResponse.todo || devDefaults.todo,
297
+ in_progress: inProgressResponse.in_progress || devDefaults.in_progress,
298
+ done: doneResponse.done || devDefaults.done,
281
299
  testing: testingResponse.testing,
282
300
  blocked: blockedResponse.blocked,
283
301
  };
@@ -526,10 +544,12 @@ async function init() {
526
544
  }
527
545
  // Fetch data from ALL teams (not just primary) to support cross-team operations
528
546
  console.log(` Fetching data from ${teams.length} teams...`);
529
- // Collect users, labels, states from all teams
547
+ // Collect users from all teams, but labels only from primary team
548
+ // States are stored per-team for status mapping selection
530
549
  const allUsers = [];
531
550
  const allLabels = [];
532
551
  const allStates = [];
552
+ const teamStatesMap = new Map(); // team.id -> states
533
553
  const seenUserIds = new Set();
534
554
  const seenLabelIds = new Set();
535
555
  const seenStateIds = new Set();
@@ -543,24 +563,31 @@ async function init() {
543
563
  allUsers.push(user);
544
564
  }
545
565
  }
546
- const labelsData = await client.issueLabels({
547
- filter: { team: { id: { eq: team.id } } },
548
- });
549
- for (const label of labelsData.nodes) {
550
- if (!seenLabelIds.has(label.id)) {
551
- seenLabelIds.add(label.id);
552
- allLabels.push(label);
566
+ // Labels: only from primary team (dev team)
567
+ if (team.id === primaryTeam.id) {
568
+ const labelsData = await client.issueLabels({
569
+ filter: { team: { id: { eq: team.id } } },
570
+ });
571
+ for (const label of labelsData.nodes) {
572
+ if (!seenLabelIds.has(label.id)) {
573
+ seenLabelIds.add(label.id);
574
+ allLabels.push(label);
575
+ }
553
576
  }
554
577
  }
578
+ // States: store per-team and also collect all
555
579
  const statesData = await client.workflowStates({
556
580
  filter: { team: { id: { eq: team.id } } },
557
581
  });
582
+ const teamStates = [];
558
583
  for (const state of statesData.nodes) {
584
+ teamStates.push(state);
559
585
  if (!seenStateIds.has(state.id)) {
560
586
  seenStateIds.add(state.id);
561
587
  allStates.push(state);
562
588
  }
563
589
  }
590
+ teamStatesMap.set(team.id, teamStates);
564
591
  }
565
592
  catch (error) {
566
593
  console.warn(` ⚠ Could not fetch data for team ${team.name}, skipping...`);
@@ -569,8 +596,10 @@ async function init() {
569
596
  const users = allUsers;
570
597
  const labels = allLabels;
571
598
  const states = allStates;
599
+ // Get team-specific states for status mapping
600
+ const primaryTeamStates = teamStatesMap.get(primaryTeam.id) || [];
572
601
  console.log(` Users: ${users.length}`);
573
- console.log(` Labels: ${labels.length}`);
602
+ console.log(` Labels: ${labels.length} (from ${primaryTeam.name})`);
574
603
  console.log(` Workflow states: ${states.length}`);
575
604
  // Get cycle from primary team (for current work tracking)
576
605
  const selectedTeam = await client.team(primaryTeam.id);
@@ -580,7 +609,9 @@ async function init() {
580
609
  const defaultLabel = await selectLabelFilter(labels, options);
581
610
  const statusSource = await selectStatusSource(options);
582
611
  const qaPmTeam = await selectQaPmTeam(teams, primaryTeam, options);
583
- const statusTransitions = await selectStatusMappings(states, options);
612
+ // Get qa team states for testing status mapping (fallback to primary team if not set)
613
+ const qaTeamStates = qaPmTeam ? teamStatesMap.get(qaPmTeam.id) : undefined;
614
+ const statusTransitions = await selectStatusMappings(primaryTeamStates, qaTeamStates, options);
584
615
  // Build config
585
616
  const config = buildConfig(teams, users, labels, states, statusTransitions, currentCycle ?? undefined);
586
617
  // Find keys
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "team-toon-tack",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Linear task sync & management CLI with TOON format",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,7 +26,8 @@
26
26
  "dependencies": {
27
27
  "@linear/sdk": "^69.0.0",
28
28
  "@toon-format/toon": "^2.1.0",
29
- "prompts": "^2.4.2"
29
+ "prompts": "^2.4.2",
30
+ "team-toon-tack": "^2.0.1"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@biomejs/biome": "^2.3.11",