team-toon-tack 1.0.3 → 1.0.5

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/bin/cli.ts CHANGED
@@ -52,7 +52,7 @@ function printVersion() {
52
52
  }
53
53
 
54
54
  function parseGlobalArgs(args: string[]): { dir: string; commandArgs: string[] } {
55
- let dir = process.env.TOON_DIR || process.cwd();
55
+ let dir = process.env.TOON_DIR || resolve(process.cwd(), '.ttt');
56
56
  const commandArgs: string[] = [];
57
57
 
58
58
  for (let i = 0; i < args.length; i++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "team-toon-tack",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Linear task sync & management CLI with TOON format",
5
5
  "type": "module",
6
6
  "bin": {
package/scripts/init.ts CHANGED
@@ -277,17 +277,7 @@ async function init() {
277
277
  complete: 'Done',
278
278
  need_review: 'In Review'
279
279
  },
280
- current_cycle: currentCycle ? {
281
- id: currentCycle.id,
282
- name: currentCycle.name || 'Cycle',
283
- start_date: currentCycle.startsAt?.toISOString().split('T')[0] || '',
284
- end_date: currentCycle.endsAt?.toISOString().split('T')[0] || ''
285
- } : {
286
- id: '',
287
- name: 'No active cycle',
288
- start_date: '',
289
- end_date: ''
290
- }
280
+ priority_order: ['urgent', 'high', 'medium', 'low', 'none']
291
281
  };
292
282
 
293
283
  // Find current user key
package/scripts/sync.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { getLinearClient, loadConfig, loadLocalConfig, saveConfig, loadCycleData, saveCycleData, getTeamId, CycleData, Task, Attachment, Comment } from './utils';
1
+ import { getLinearClient, loadConfig, loadLocalConfig, loadCycleData, saveCycleData, getTeamId, getPrioritySortIndex, CycleData, Task, Attachment, Comment } from './utils';
2
2
 
3
3
  async function sync() {
4
4
  const args = process.argv.slice(2);
@@ -47,24 +47,17 @@ Examples:
47
47
  }
48
48
 
49
49
  const activeCycle = cycles.nodes[0];
50
- const cycleChanged = config.current_cycle.id !== activeCycle.id;
51
-
52
- if (cycleChanged) {
53
- console.log(`Cycle changed: ${config.current_cycle.name} ${activeCycle.name}`);
54
- config.current_cycle = {
55
- id: activeCycle.id,
56
- name: activeCycle.name ?? `Cycle`,
57
- start_date: activeCycle.startsAt?.toISOString().split('T')[0] ?? '',
58
- end_date: activeCycle.endsAt?.toISOString().split('T')[0] ?? ''
59
- };
60
- await saveConfig(config);
61
- console.log('Config updated with new cycle.');
50
+ const cycleId = activeCycle.id;
51
+ const cycleName = activeCycle.name ?? 'Cycle';
52
+
53
+ // Check if cycle changed by comparing with existing cycle.toon
54
+ const existingData = await loadCycleData();
55
+ if (existingData && existingData.cycleId !== cycleId) {
56
+ console.log(`Cycle changed: ${existingData.cycleName} ${cycleName}`);
62
57
  } else {
63
- console.log(`Current cycle: ${config.current_cycle.name}`);
58
+ console.log(`Current cycle: ${cycleName}`);
64
59
  }
65
60
 
66
- const cycleId = config.current_cycle.id;
67
-
68
61
  // Phase 2: Fetch workflow states
69
62
  const workflowStates = await client.workflowStates({
70
63
  filter: { team: { id: { eq: teamId } } }
@@ -72,8 +65,7 @@ Examples:
72
65
  const stateMap = new Map(workflowStates.nodes.map(s => [s.name, s.id]));
73
66
  const testingStateId = stateMap.get('Testing');
74
67
 
75
- // Phase 3: Read existing local state
76
- const existingData = await loadCycleData();
68
+ // Phase 3: Build existing tasks map for preserving local status
77
69
  const existingTasksMap = new Map(existingData?.tasks.map(t => [t.id, t]));
78
70
 
79
71
  // Phase 4: Fetch current issues with full content
@@ -168,22 +160,22 @@ Examples:
168
160
  tasks.push(task);
169
161
  }
170
162
 
171
- // Sort by priority (urgent first)
163
+ // Sort by priority using config order
172
164
  tasks.sort((a, b) => {
173
- const pa = a.priority === 0 ? 5 : a.priority;
174
- const pb = b.priority === 0 ? 5 : b.priority;
165
+ const pa = getPrioritySortIndex(a.priority, config.priority_order);
166
+ const pb = getPrioritySortIndex(b.priority, config.priority_order);
175
167
  return pa - pb;
176
168
  });
177
169
 
178
170
  const newData: CycleData = {
179
171
  cycleId: cycleId,
180
- cycleName: config.current_cycle.name,
172
+ cycleName: cycleName,
181
173
  updatedAt: new Date().toISOString(),
182
174
  tasks: tasks
183
175
  };
184
176
 
185
177
  await saveCycleData(newData);
186
- console.log(`\n✅ Synced ${tasks.length} tasks for ${config.current_cycle.name}.`);
178
+ console.log(`\n✅ Synced ${tasks.length} tasks for ${cycleName}.`);
187
179
  if (updatedCount > 0) {
188
180
  console.log(` Updated ${updatedCount} issues to Testing in Linear.`);
189
181
  }
package/scripts/utils.ts CHANGED
@@ -55,11 +55,29 @@ export interface LabelConfig {
55
55
  export interface Config {
56
56
  teams: Record<string, TeamConfig>;
57
57
  users: Record<string, UserConfig>;
58
- labels: Record<string, LabelConfig>;
59
- priorities: Record<string, { value: number; name: string }>;
60
- statuses: Record<string, { name: string; type: string }>;
61
- status_transitions: Record<string, string>;
62
- current_cycle: { id: string; name: string; start_date: string; end_date: string };
58
+ labels?: Record<string, LabelConfig>;
59
+ priorities?: Record<string, { value: number; name: string }>;
60
+ statuses?: Record<string, { name: string; type: string }>;
61
+ status_transitions?: Record<string, string>;
62
+ priority_order?: string[]; // e.g., ['urgent', 'high', 'medium', 'low', 'none']
63
+ }
64
+
65
+ // Linear priority value to name mapping (fixed by Linear API)
66
+ export const PRIORITY_NAMES: Record<number, string> = {
67
+ 0: 'none',
68
+ 1: 'urgent',
69
+ 2: 'high',
70
+ 3: 'medium',
71
+ 4: 'low'
72
+ };
73
+
74
+ export const DEFAULT_PRIORITY_ORDER = ['urgent', 'high', 'medium', 'low', 'none'];
75
+
76
+ export function getPrioritySortIndex(priority: number, priorityOrder?: string[]): number {
77
+ const order = priorityOrder ?? DEFAULT_PRIORITY_ORDER;
78
+ const name = PRIORITY_NAMES[priority] ?? 'none';
79
+ const index = order.indexOf(name);
80
+ return index === -1 ? order.length : index;
63
81
  }
64
82
 
65
83
  export interface Attachment {
@@ -1,8 +1,8 @@
1
1
  import prompts from 'prompts';
2
- import { getLinearClient, loadConfig, loadLocalConfig, loadCycleData, saveCycleData, getUserEmail, getTeamId } from './utils';
2
+ import { getLinearClient, loadConfig, loadLocalConfig, loadCycleData, saveCycleData, getUserEmail, getTeamId, getPrioritySortIndex } from './utils';
3
3
 
4
4
  const PRIORITY_LABELS: Record<number, string> = {
5
- 0: '⚪',
5
+ 0: '⚪ None',
6
6
  1: '🔴 Urgent',
7
7
  2: '🟠 High',
8
8
  3: '🟡 Medium',
@@ -53,8 +53,8 @@ Examples:
53
53
  !excludedEmails.has(t.assignee ?? '')
54
54
  )
55
55
  .sort((a, b) => {
56
- const pa = a.priority === 0 ? 5 : a.priority;
57
- const pb = b.priority === 0 ? 5 : b.priority;
56
+ const pa = getPrioritySortIndex(a.priority, config.priority_order);
57
+ const pb = getPrioritySortIndex(b.priority, config.priority_order);
58
58
  return pa - pb;
59
59
  });
60
60