@fink-andreas/pi-linear-tools 0.1.0 → 0.2.1

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/src/linear.js CHANGED
@@ -1136,14 +1136,9 @@ export async function updateProjectMilestone(client, milestoneId, patch = {}) {
1136
1136
  updateInput.targetDate = patch.targetDate;
1137
1137
  }
1138
1138
 
1139
- if (patch.status !== undefined) {
1140
- const validStatuses = ['backlogged', 'planned', 'inProgress', 'paused', 'completed', 'done', 'cancelled'];
1141
- const status = String(patch.status);
1142
- if (!validStatuses.includes(status)) {
1143
- throw new Error(`Invalid status: ${status}. Valid values: ${validStatuses.join(', ')}`);
1144
- }
1145
- updateInput.status = status;
1146
- }
1139
+ // Note: status is a computed/read-only field in Linear's API (ProjectMilestoneStatus enum)
1140
+ // It cannot be set via ProjectMilestoneUpdateInput. The status values (done, next, overdue, unstarted)
1141
+ // are automatically determined by Linear based on milestone progress and dates.
1147
1142
 
1148
1143
  if (Object.keys(updateInput).length === 0) {
1149
1144
  throw new Error('No update fields provided');
@@ -1166,14 +1161,26 @@ export async function updateProjectMilestone(client, milestoneId, patch = {}) {
1166
1161
  * Delete a project milestone
1167
1162
  * @param {LinearClient} client - Linear SDK client
1168
1163
  * @param {string} milestoneId - Milestone ID
1169
- * @returns {Promise<{success: boolean, milestoneId: string}>}
1164
+ * @returns {Promise<{success: boolean, milestoneId: string, name: string|null}>}
1170
1165
  */
1171
1166
  export async function deleteProjectMilestone(client, milestoneId) {
1167
+ let milestoneName = null;
1168
+
1169
+ try {
1170
+ const existing = await client.projectMilestone(milestoneId);
1171
+ if (existing?.name) {
1172
+ milestoneName = existing.name;
1173
+ }
1174
+ } catch {
1175
+ milestoneName = null;
1176
+ }
1177
+
1172
1178
  const result = await client.deleteProjectMilestone(milestoneId);
1173
1179
 
1174
1180
  return {
1175
1181
  success: result.success,
1176
1182
  milestoneId,
1183
+ name: milestoneName,
1177
1184
  };
1178
1185
  }
1179
1186
 
package/src/settings.js CHANGED
@@ -10,8 +10,13 @@ import { debug, warn, error as logError } from './logger.js';
10
10
 
11
11
  export function getDefaultSettings() {
12
12
  return {
13
- schemaVersion: 1,
14
- linearApiKey: null,
13
+ schemaVersion: 2,
14
+ authMethod: 'api-key', // 'api-key' or 'oauth'
15
+ apiKey: null, // Legacy API key (migrated from linearApiKey)
16
+ oauth: {
17
+ clientId: 'a3e177176c6697611367f1a2405d4a34',
18
+ redirectUri: 'http://localhost:34711/callback',
19
+ },
15
20
  defaultTeam: null,
16
21
  defaultWorkspace: null,
17
22
  projects: {},
@@ -21,12 +26,70 @@ export function getDefaultSettings() {
21
26
  function migrateSettings(settings) {
22
27
  const migrated = { ...(settings || {}) };
23
28
 
29
+ // Set default schema version if not set
24
30
  if (migrated.schemaVersion === undefined) {
25
31
  migrated.schemaVersion = 1;
26
32
  }
27
33
 
28
- if (migrated.linearApiKey === undefined) {
29
- migrated.linearApiKey = null;
34
+ // Migration from schema version 1 to 2
35
+ if (migrated.schemaVersion === 1) {
36
+ debug('Migrating settings from schema version 1 to 2');
37
+
38
+ // Migrate linearApiKey to apiKey
39
+ if (migrated.linearApiKey !== undefined) {
40
+ migrated.apiKey = migrated.linearApiKey;
41
+ delete migrated.linearApiKey;
42
+ } else {
43
+ migrated.apiKey = null;
44
+ }
45
+
46
+ // Set default auth method
47
+ migrated.authMethod = 'api-key';
48
+
49
+ // Add OAuth config
50
+ if (!migrated.oauth || typeof migrated.oauth !== 'object') {
51
+ migrated.oauth = {
52
+ clientId: 'a3e177176c6697611367f1a2405d4a34',
53
+ redirectUri: 'http://localhost:34711/callback',
54
+ };
55
+ }
56
+
57
+ // Update schema version
58
+ migrated.schemaVersion = 2;
59
+
60
+ debug('Settings migration to version 2 complete');
61
+ }
62
+
63
+ // Ensure authMethod is set
64
+ if (migrated.authMethod === undefined) {
65
+ migrated.authMethod = 'api-key';
66
+ }
67
+
68
+ // Ensure apiKey is set (for backward compatibility)
69
+ if (migrated.apiKey === undefined) {
70
+ migrated.apiKey = null;
71
+ }
72
+
73
+ // Ensure oauth config exists
74
+ if (!migrated.oauth || typeof migrated.oauth !== 'object') {
75
+ migrated.oauth = {
76
+ clientId: 'a3e177176c6697611367f1a2405d4a34',
77
+ redirectUri: 'http://localhost:34711/callback',
78
+ };
79
+ }
80
+
81
+ // Ensure oauth has clientId and redirectUri
82
+ if (migrated.oauth.clientId === undefined) {
83
+ migrated.oauth.clientId = 'a3e177176c6697611367f1a2405d4a34';
84
+ }
85
+
86
+ if (migrated.oauth.redirectUri === undefined) {
87
+ migrated.oauth.redirectUri = 'http://localhost:34711/callback';
88
+ }
89
+
90
+ // Legacy: ensure linearApiKey is null (for validation)
91
+ if (migrated.linearApiKey !== undefined) {
92
+ delete migrated.linearApiKey;
30
93
  }
31
94
 
32
95
  if (migrated.defaultTeam === undefined) {
@@ -37,10 +100,17 @@ function migrateSettings(settings) {
37
100
  migrated.defaultWorkspace = null;
38
101
  }
39
102
 
103
+ // Remove deprecated debug_reload field
104
+ if (Object.prototype.hasOwnProperty.call(migrated, 'debug_reload')) {
105
+ delete migrated.debug_reload;
106
+ }
107
+
108
+ // Ensure projects is an object
40
109
  if (!migrated.projects || typeof migrated.projects !== 'object' || Array.isArray(migrated.projects)) {
41
110
  migrated.projects = {};
42
111
  }
43
112
 
113
+ // Migrate project scopes
44
114
  for (const [projectId, cfg] of Object.entries(migrated.projects)) {
45
115
  if (!cfg || typeof cfg !== 'object' || Array.isArray(cfg)) {
46
116
  migrated.projects[projectId] = { scope: { team: null } };
@@ -71,8 +141,39 @@ export function validateSettings(settings) {
71
141
  errors.push('settings.schemaVersion must be a positive number');
72
142
  }
73
143
 
74
- if (settings.linearApiKey !== null && settings.linearApiKey !== undefined && typeof settings.linearApiKey !== 'string') {
75
- errors.push('settings.linearApiKey must be a string or null');
144
+ // Validate authMethod
145
+ if (settings.authMethod !== undefined && settings.authMethod !== null) {
146
+ if (typeof settings.authMethod !== 'string') {
147
+ errors.push('settings.authMethod must be a string');
148
+ } else if (
149
+ !['api-key', 'oauth'].includes(settings.authMethod)
150
+ ) {
151
+ errors.push('settings.authMethod must be "api-key" or "oauth"');
152
+ }
153
+ }
154
+
155
+ // Validate apiKey (for backward compatibility)
156
+ if (settings.apiKey !== null && settings.apiKey !== undefined && typeof settings.apiKey !== 'string') {
157
+ errors.push('settings.apiKey must be a string or null');
158
+ }
159
+
160
+ // Reject legacy linearApiKey field
161
+ if (settings.linearApiKey !== undefined) {
162
+ errors.push('settings.linearApiKey is deprecated. Use settings.apiKey instead.');
163
+ }
164
+
165
+ // Validate oauth config
166
+ if (settings.oauth !== undefined && settings.oauth !== null) {
167
+ if (typeof settings.oauth !== 'object' || Array.isArray(settings.oauth)) {
168
+ errors.push('settings.oauth must be an object');
169
+ } else {
170
+ if (settings.oauth.clientId !== undefined && typeof settings.oauth.clientId !== 'string') {
171
+ errors.push('settings.oauth.clientId must be a string');
172
+ }
173
+ if (settings.oauth.redirectUri !== undefined && typeof settings.oauth.redirectUri !== 'string') {
174
+ errors.push('settings.oauth.redirectUri must be a string');
175
+ }
176
+ }
76
177
  }
77
178
 
78
179
  if (settings.defaultTeam !== null && settings.defaultTeam !== undefined && typeof settings.defaultTeam !== 'string') {