@asagiri-design/labels-config 0.2.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.
- package/LICENSE +21 -0
- package/README.ja.md +387 -0
- package/README.md +387 -0
- package/dist/chunk-4ZUJQMV7.mjs +285 -0
- package/dist/chunk-DGUMSQAI.mjs +496 -0
- package/dist/chunk-DSI7SDAM.mjs +161 -0
- package/dist/chunk-QJLMZSVA.mjs +496 -0
- package/dist/chunk-QZ7TP4HQ.mjs +7 -0
- package/dist/chunk-VU2JB66N.mjs +103 -0
- package/dist/chunk-ZYHIDOG2.mjs +247 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1417 -0
- package/dist/cli.mjs +436 -0
- package/dist/config/index.d.mts +49 -0
- package/dist/config/index.d.ts +49 -0
- package/dist/config/index.js +554 -0
- package/dist/config/index.mjs +10 -0
- package/dist/github/index.d.mts +113 -0
- package/dist/github/index.d.ts +113 -0
- package/dist/github/index.js +310 -0
- package/dist/github/index.mjs +9 -0
- package/dist/index.d.mts +309 -0
- package/dist/index.d.ts +309 -0
- package/dist/index.js +306 -0
- package/dist/index.mjs +44 -0
- package/dist/types-CkwsO1Iu.d.mts +50 -0
- package/dist/types-CkwsO1Iu.d.ts +50 -0
- package/docs/API.md +309 -0
- package/docs/GETTING_STARTED.md +305 -0
- package/package.json +87 -0
- package/templates/prod-labels.json +106 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1417 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
6
|
+
|
|
7
|
+
// src/cli.ts
|
|
8
|
+
var import_fs = require("fs");
|
|
9
|
+
|
|
10
|
+
// src/types.ts
|
|
11
|
+
function isCategorized(labels) {
|
|
12
|
+
return Array.isArray(labels) && labels.length > 0 && "category" in labels[0] && "labels" in labels[0];
|
|
13
|
+
}
|
|
14
|
+
function flattenLabels(labels) {
|
|
15
|
+
if (isCategorized(labels)) {
|
|
16
|
+
return labels.flatMap((cat) => cat.labels);
|
|
17
|
+
}
|
|
18
|
+
return labels;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/validation.ts
|
|
22
|
+
var import_zod = require("zod");
|
|
23
|
+
|
|
24
|
+
// src/utils/color.ts
|
|
25
|
+
function normalizeHexColor(input) {
|
|
26
|
+
const color = input.toLowerCase();
|
|
27
|
+
if (color.length === 3) {
|
|
28
|
+
return color.split("").map((c) => c + c).join("");
|
|
29
|
+
}
|
|
30
|
+
return color;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/validation.ts
|
|
34
|
+
var hexColorSchema = import_zod.z.string().regex(/^[0-9a-fA-F]{6}$|^[0-9a-fA-F]{3}$/, "Invalid hex color format").transform((color) => normalizeHexColor(color));
|
|
35
|
+
var labelConfigSchema = import_zod.z.object({
|
|
36
|
+
name: import_zod.z.string().min(1, "Label name is required").max(50, "Label name must be 50 characters or less").regex(
|
|
37
|
+
/^[a-zA-Z0-9\-\s\/\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF]+$/,
|
|
38
|
+
"Label name contains invalid characters"
|
|
39
|
+
),
|
|
40
|
+
color: hexColorSchema,
|
|
41
|
+
description: import_zod.z.string().min(1, "Description is required").max(200, "Description must be 200 characters or less")
|
|
42
|
+
});
|
|
43
|
+
var labelCategorySchema = import_zod.z.object({
|
|
44
|
+
category: import_zod.z.string().min(1, "Category name is required").max(50, "Category name must be 50 characters or less"),
|
|
45
|
+
labels: import_zod.z.array(labelConfigSchema)
|
|
46
|
+
});
|
|
47
|
+
var labelRegistrySchema = import_zod.z.object({
|
|
48
|
+
version: import_zod.z.string(),
|
|
49
|
+
timestamp: import_zod.z.string().datetime().optional(),
|
|
50
|
+
labels: import_zod.z.union([
|
|
51
|
+
import_zod.z.array(labelConfigSchema),
|
|
52
|
+
import_zod.z.array(labelCategorySchema)
|
|
53
|
+
]),
|
|
54
|
+
metadata: import_zod.z.record(import_zod.z.unknown()).optional()
|
|
55
|
+
});
|
|
56
|
+
function validateLabels(labels) {
|
|
57
|
+
const schema = import_zod.z.array(labelConfigSchema);
|
|
58
|
+
return schema.parse(labels);
|
|
59
|
+
}
|
|
60
|
+
function findDuplicateStrings(values) {
|
|
61
|
+
const seen = /* @__PURE__ */ new Map();
|
|
62
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
63
|
+
for (const value of values) {
|
|
64
|
+
const count = seen.get(value) || 0;
|
|
65
|
+
seen.set(value, count + 1);
|
|
66
|
+
if (count === 1) {
|
|
67
|
+
duplicates.add(value);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return Array.from(duplicates);
|
|
71
|
+
}
|
|
72
|
+
function checkDuplicateNames(labels) {
|
|
73
|
+
const names = labels.map((label) => label.name);
|
|
74
|
+
return findDuplicateStrings(names);
|
|
75
|
+
}
|
|
76
|
+
function checkDuplicateColors(labels) {
|
|
77
|
+
const colors2 = labels.map((label) => normalizeHexColor(label.color));
|
|
78
|
+
return findDuplicateStrings(colors2);
|
|
79
|
+
}
|
|
80
|
+
function validateWithDetails(labels) {
|
|
81
|
+
try {
|
|
82
|
+
const parsed = validateLabels(labels);
|
|
83
|
+
const duplicateNames = checkDuplicateNames(parsed);
|
|
84
|
+
const duplicateColors = checkDuplicateColors(parsed);
|
|
85
|
+
return {
|
|
86
|
+
valid: duplicateNames.length === 0 && duplicateColors.length === 0,
|
|
87
|
+
labels: parsed,
|
|
88
|
+
errors: {
|
|
89
|
+
duplicateNames,
|
|
90
|
+
duplicateColors
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
} catch (error2) {
|
|
94
|
+
if (error2 instanceof import_zod.z.ZodError) {
|
|
95
|
+
return {
|
|
96
|
+
valid: false,
|
|
97
|
+
labels: [],
|
|
98
|
+
errors: {
|
|
99
|
+
validationErrors: error2.errors
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
throw error2;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// src/manager.ts
|
|
108
|
+
var LabelManager = class {
|
|
109
|
+
constructor(options = {}) {
|
|
110
|
+
__publicField(this, "labels", /* @__PURE__ */ new Map());
|
|
111
|
+
__publicField(this, "strict", false);
|
|
112
|
+
this.strict = options.strict ?? false;
|
|
113
|
+
if (options.labels) {
|
|
114
|
+
this.loadLabels(options.labels);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Load labels from array
|
|
119
|
+
*/
|
|
120
|
+
loadLabels(labels) {
|
|
121
|
+
const validation = validateWithDetails(labels);
|
|
122
|
+
if (!validation.valid && this.strict) {
|
|
123
|
+
throw new Error(`Label validation failed: ${JSON.stringify(validation.errors)}`);
|
|
124
|
+
}
|
|
125
|
+
this.labels.clear();
|
|
126
|
+
validation.labels.forEach((label) => {
|
|
127
|
+
this.labels.set(label.name.toLowerCase(), label);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Load labels from registry object
|
|
132
|
+
*/
|
|
133
|
+
loadRegistry(registry) {
|
|
134
|
+
const labels = flattenLabels(registry.labels);
|
|
135
|
+
this.loadLabels(labels);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Add a single label
|
|
139
|
+
*/
|
|
140
|
+
addLabel(label) {
|
|
141
|
+
const validation = validateLabels([label]);
|
|
142
|
+
const newLabel = validation[0];
|
|
143
|
+
if (this.labels.has(newLabel.name.toLowerCase())) {
|
|
144
|
+
throw new Error(`Label "${newLabel.name}" already exists`);
|
|
145
|
+
}
|
|
146
|
+
this.labels.set(newLabel.name.toLowerCase(), newLabel);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Update an existing label
|
|
150
|
+
*/
|
|
151
|
+
updateLabel(name, updates) {
|
|
152
|
+
const key = name.toLowerCase();
|
|
153
|
+
const existing = this.labels.get(key);
|
|
154
|
+
if (!existing) {
|
|
155
|
+
throw new Error(`Label "${name}" not found`);
|
|
156
|
+
}
|
|
157
|
+
const updated = { ...existing, ...updates };
|
|
158
|
+
const validation = validateLabels([updated]);
|
|
159
|
+
this.labels.set(key, validation[0]);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Remove a label
|
|
163
|
+
*/
|
|
164
|
+
removeLabel(name) {
|
|
165
|
+
this.labels.delete(name.toLowerCase());
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get a label by name
|
|
169
|
+
*/
|
|
170
|
+
getLabel(name) {
|
|
171
|
+
return this.labels.get(name.toLowerCase());
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Check if label exists
|
|
175
|
+
*/
|
|
176
|
+
hasLabel(name) {
|
|
177
|
+
return this.labels.has(name.toLowerCase());
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get all labels
|
|
181
|
+
*/
|
|
182
|
+
getAllLabels() {
|
|
183
|
+
return Array.from(this.labels.values());
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Get labels count
|
|
187
|
+
*/
|
|
188
|
+
count() {
|
|
189
|
+
return this.labels.size;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Export labels as array
|
|
193
|
+
*/
|
|
194
|
+
export() {
|
|
195
|
+
return this.getAllLabels();
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Export as registry object
|
|
199
|
+
*/
|
|
200
|
+
exportRegistry(version = "1.0.0", metadata) {
|
|
201
|
+
return {
|
|
202
|
+
version,
|
|
203
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
204
|
+
labels: this.getAllLabels(),
|
|
205
|
+
metadata
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Search labels by name or description
|
|
210
|
+
*/
|
|
211
|
+
search(query) {
|
|
212
|
+
const lowerQuery = query.toLowerCase();
|
|
213
|
+
return this.getAllLabels().filter(
|
|
214
|
+
(label) => label.name.toLowerCase().includes(lowerQuery) || label.description.toLowerCase().includes(lowerQuery)
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Find labels by color
|
|
219
|
+
*/
|
|
220
|
+
findByColor(color) {
|
|
221
|
+
const normalizedColor = color.toLowerCase();
|
|
222
|
+
return this.getAllLabels().filter((label) => label.color.toLowerCase() === normalizedColor);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Clear all labels
|
|
226
|
+
*/
|
|
227
|
+
clear() {
|
|
228
|
+
this.labels.clear();
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Validate current state
|
|
232
|
+
*/
|
|
233
|
+
validate() {
|
|
234
|
+
const labels = this.getAllLabels();
|
|
235
|
+
const duplicates = checkDuplicateNames(labels);
|
|
236
|
+
return {
|
|
237
|
+
valid: duplicates.length === 0,
|
|
238
|
+
duplicates
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// src/config/loader.ts
|
|
244
|
+
var ConfigLoader = class {
|
|
245
|
+
constructor(options = {}) {
|
|
246
|
+
__publicField(this, "strict");
|
|
247
|
+
this.strict = options.strict ?? true;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Load labels from JSON object
|
|
251
|
+
*/
|
|
252
|
+
loadFromJSON(data) {
|
|
253
|
+
if (typeof data === "object" && data !== null && "labels" in data && Array.isArray(data.labels)) {
|
|
254
|
+
const labels = data.labels;
|
|
255
|
+
if (labels.length > 0 && "category" in labels[0]) {
|
|
256
|
+
return labels.flatMap((cat) => cat.labels);
|
|
257
|
+
}
|
|
258
|
+
return validateLabels(labels);
|
|
259
|
+
}
|
|
260
|
+
return validateLabels(data);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Load from JSON string
|
|
264
|
+
*/
|
|
265
|
+
loadFromString(jsonString) {
|
|
266
|
+
try {
|
|
267
|
+
const data = JSON.parse(jsonString);
|
|
268
|
+
return this.loadFromJSON(data);
|
|
269
|
+
} catch (error2) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
`Failed to parse JSON: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Load from registry object
|
|
277
|
+
*/
|
|
278
|
+
loadRegistry(registry) {
|
|
279
|
+
return this.loadFromJSON({ labels: registry.labels });
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// src/github/client.ts
|
|
284
|
+
var import_child_process = require("child_process");
|
|
285
|
+
var GitHubClient = class {
|
|
286
|
+
constructor(options) {
|
|
287
|
+
__publicField(this, "owner");
|
|
288
|
+
__publicField(this, "repo");
|
|
289
|
+
__publicField(this, "repoPath");
|
|
290
|
+
this.owner = options.owner;
|
|
291
|
+
this.repo = options.repo;
|
|
292
|
+
this.repoPath = `${this.owner}/${this.repo}`;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Execute gh CLI command
|
|
296
|
+
*/
|
|
297
|
+
exec(command) {
|
|
298
|
+
try {
|
|
299
|
+
return (0, import_child_process.execSync)(command, {
|
|
300
|
+
encoding: "utf-8",
|
|
301
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
302
|
+
}).trim();
|
|
303
|
+
} catch (error2) {
|
|
304
|
+
throw new Error(`gh CLI command failed: ${error2.message}`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Fetch all labels from repository
|
|
309
|
+
*/
|
|
310
|
+
async fetchLabels() {
|
|
311
|
+
try {
|
|
312
|
+
const output = this.exec(
|
|
313
|
+
`gh label list --repo ${this.repoPath} --json name,color,description --limit 1000`
|
|
314
|
+
);
|
|
315
|
+
if (!output) {
|
|
316
|
+
return [];
|
|
317
|
+
}
|
|
318
|
+
const labels = JSON.parse(output);
|
|
319
|
+
return labels.map((label) => ({
|
|
320
|
+
name: label.name,
|
|
321
|
+
color: label.color,
|
|
322
|
+
description: label.description || ""
|
|
323
|
+
}));
|
|
324
|
+
} catch (error2) {
|
|
325
|
+
throw new Error(`Failed to fetch labels from ${this.repoPath}: ${error2}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Create a new label
|
|
330
|
+
*/
|
|
331
|
+
async createLabel(label) {
|
|
332
|
+
try {
|
|
333
|
+
const name = label.name.replace(/"/g, '\\"');
|
|
334
|
+
const description = label.description.replace(/"/g, '\\"');
|
|
335
|
+
const color = label.color.replace("#", "");
|
|
336
|
+
this.exec(
|
|
337
|
+
`gh label create "${name}" --color "${color}" --description "${description}" --repo ${this.repoPath}`
|
|
338
|
+
);
|
|
339
|
+
return {
|
|
340
|
+
name: label.name,
|
|
341
|
+
color: label.color,
|
|
342
|
+
description: label.description
|
|
343
|
+
};
|
|
344
|
+
} catch (error2) {
|
|
345
|
+
throw new Error(`Failed to create label "${label.name}": ${error2}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Update an existing label
|
|
350
|
+
*/
|
|
351
|
+
async updateLabel(currentName, label) {
|
|
352
|
+
try {
|
|
353
|
+
const escapedCurrentName = currentName.replace(/"/g, '\\"');
|
|
354
|
+
const args = [];
|
|
355
|
+
if (label.name && label.name !== currentName) {
|
|
356
|
+
args.push(`--name "${label.name.replace(/"/g, '\\"')}"`);
|
|
357
|
+
}
|
|
358
|
+
if (label.color) {
|
|
359
|
+
args.push(`--color "${label.color.replace("#", "")}"`);
|
|
360
|
+
}
|
|
361
|
+
if (label.description !== void 0) {
|
|
362
|
+
args.push(`--description "${label.description.replace(/"/g, '\\"')}"`);
|
|
363
|
+
}
|
|
364
|
+
if (args.length === 0) {
|
|
365
|
+
return {
|
|
366
|
+
name: currentName,
|
|
367
|
+
color: label.color || "",
|
|
368
|
+
description: label.description || ""
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
this.exec(
|
|
372
|
+
`gh label edit "${escapedCurrentName}" ${args.join(" ")} --repo ${this.repoPath}`
|
|
373
|
+
);
|
|
374
|
+
return {
|
|
375
|
+
name: label.name || currentName,
|
|
376
|
+
color: label.color || "",
|
|
377
|
+
description: label.description || ""
|
|
378
|
+
};
|
|
379
|
+
} catch (error2) {
|
|
380
|
+
throw new Error(`Failed to update label "${currentName}": ${error2}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Delete a label
|
|
385
|
+
*/
|
|
386
|
+
async deleteLabel(name) {
|
|
387
|
+
try {
|
|
388
|
+
const escapedName = name.replace(/"/g, '\\"');
|
|
389
|
+
this.exec(`gh label delete "${escapedName}" --repo ${this.repoPath} --yes`);
|
|
390
|
+
} catch (error2) {
|
|
391
|
+
throw new Error(`Failed to delete label "${name}": ${error2}`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Check if label exists
|
|
396
|
+
*/
|
|
397
|
+
async hasLabel(name) {
|
|
398
|
+
try {
|
|
399
|
+
const labels = await this.fetchLabels();
|
|
400
|
+
return labels.some((label) => label.name.toLowerCase() === name.toLowerCase());
|
|
401
|
+
} catch (error2) {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
// src/github/sync.ts
|
|
408
|
+
var _GitHubLabelSync = class _GitHubLabelSync {
|
|
409
|
+
constructor(options) {
|
|
410
|
+
__publicField(this, "client");
|
|
411
|
+
__publicField(this, "options");
|
|
412
|
+
this.client = new GitHubClient(options);
|
|
413
|
+
this.options = options;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Log message if verbose mode is enabled
|
|
417
|
+
*/
|
|
418
|
+
log(message) {
|
|
419
|
+
if (this.options.verbose) {
|
|
420
|
+
console.log(`[labels-config] ${message}`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Sync labels to GitHub repository with batch operations for better performance
|
|
425
|
+
*/
|
|
426
|
+
async syncLabels(localLabels) {
|
|
427
|
+
this.log("Fetching remote labels...");
|
|
428
|
+
const remoteLabels = await this.client.fetchLabels();
|
|
429
|
+
const result = {
|
|
430
|
+
created: [],
|
|
431
|
+
updated: [],
|
|
432
|
+
deleted: [],
|
|
433
|
+
unchanged: [],
|
|
434
|
+
errors: []
|
|
435
|
+
};
|
|
436
|
+
const remoteLabelMap = new Map(remoteLabels.map((label) => [label.name.toLowerCase(), label]));
|
|
437
|
+
const localLabelMap = new Map(localLabels.map((label) => [label.name.toLowerCase(), label]));
|
|
438
|
+
const toCreate = [];
|
|
439
|
+
const toUpdate = [];
|
|
440
|
+
const toDelete = [];
|
|
441
|
+
for (const label of localLabels) {
|
|
442
|
+
const remoteLabel = remoteLabelMap.get(label.name.toLowerCase());
|
|
443
|
+
if (!remoteLabel) {
|
|
444
|
+
toCreate.push(label);
|
|
445
|
+
} else if (this.hasChanges(label, remoteLabel)) {
|
|
446
|
+
toUpdate.push({ current: remoteLabel.name, updated: label });
|
|
447
|
+
} else {
|
|
448
|
+
result.unchanged.push(label);
|
|
449
|
+
this.log(`Unchanged label: ${label.name}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (this.options.deleteExtra) {
|
|
453
|
+
for (const remoteLabel of remoteLabels) {
|
|
454
|
+
if (!localLabelMap.has(remoteLabel.name.toLowerCase())) {
|
|
455
|
+
toDelete.push(remoteLabel.name);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (!this.options.dryRun) {
|
|
460
|
+
for (let i = 0; i < toCreate.length; i += _GitHubLabelSync.BATCH_SIZE) {
|
|
461
|
+
const batch = toCreate.slice(i, i + _GitHubLabelSync.BATCH_SIZE);
|
|
462
|
+
const promises = batch.map(async (label) => {
|
|
463
|
+
try {
|
|
464
|
+
await this.client.createLabel(label);
|
|
465
|
+
result.created.push(label);
|
|
466
|
+
this.log(`Created label: ${label.name}`);
|
|
467
|
+
} catch (error2) {
|
|
468
|
+
result.errors.push({
|
|
469
|
+
name: label.name,
|
|
470
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
471
|
+
});
|
|
472
|
+
this.log(`Error creating label "${label.name}": ${error2}`);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
await Promise.all(promises);
|
|
476
|
+
}
|
|
477
|
+
for (let i = 0; i < toUpdate.length; i += _GitHubLabelSync.BATCH_SIZE) {
|
|
478
|
+
const batch = toUpdate.slice(i, i + _GitHubLabelSync.BATCH_SIZE);
|
|
479
|
+
const promises = batch.map(async ({ current, updated }) => {
|
|
480
|
+
try {
|
|
481
|
+
await this.client.updateLabel(current, updated);
|
|
482
|
+
result.updated.push(updated);
|
|
483
|
+
this.log(`Updated label: ${updated.name}`);
|
|
484
|
+
} catch (error2) {
|
|
485
|
+
result.errors.push({
|
|
486
|
+
name: updated.name,
|
|
487
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
488
|
+
});
|
|
489
|
+
this.log(`Error updating label "${updated.name}": ${error2}`);
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
await Promise.all(promises);
|
|
493
|
+
}
|
|
494
|
+
for (let i = 0; i < toDelete.length; i += _GitHubLabelSync.BATCH_SIZE) {
|
|
495
|
+
const batch = toDelete.slice(i, i + _GitHubLabelSync.BATCH_SIZE);
|
|
496
|
+
const promises = batch.map(async (name) => {
|
|
497
|
+
try {
|
|
498
|
+
await this.client.deleteLabel(name);
|
|
499
|
+
result.deleted.push(name);
|
|
500
|
+
this.log(`Deleted label: ${name}`);
|
|
501
|
+
} catch (error2) {
|
|
502
|
+
result.errors.push({
|
|
503
|
+
name,
|
|
504
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
505
|
+
});
|
|
506
|
+
this.log(`Error deleting label "${name}": ${error2}`);
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
await Promise.all(promises);
|
|
510
|
+
}
|
|
511
|
+
} else {
|
|
512
|
+
result.created.push(...toCreate);
|
|
513
|
+
result.updated.push(...toUpdate.map((op) => op.updated));
|
|
514
|
+
result.deleted.push(...toDelete);
|
|
515
|
+
toCreate.forEach((label) => this.log(`[DRY RUN] Would create label: ${label.name}`));
|
|
516
|
+
toUpdate.forEach(({ updated }) => this.log(`[DRY RUN] Would update label: ${updated.name}`));
|
|
517
|
+
toDelete.forEach((name) => this.log(`[DRY RUN] Would delete label: ${name}`));
|
|
518
|
+
}
|
|
519
|
+
return result;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Check if label has changes
|
|
523
|
+
*/
|
|
524
|
+
hasChanges(local, remote) {
|
|
525
|
+
return local.color.toLowerCase() !== remote.color.toLowerCase() || local.description !== (remote.description || "");
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Fetch labels from GitHub
|
|
529
|
+
*/
|
|
530
|
+
async fetchLabels() {
|
|
531
|
+
const labels = await this.client.fetchLabels();
|
|
532
|
+
return labels.map((label) => ({
|
|
533
|
+
name: label.name,
|
|
534
|
+
color: label.color,
|
|
535
|
+
description: label.description || ""
|
|
536
|
+
}));
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Delete a single label
|
|
540
|
+
*/
|
|
541
|
+
async deleteLabel(name) {
|
|
542
|
+
if (!this.options.dryRun) {
|
|
543
|
+
await this.client.deleteLabel(name);
|
|
544
|
+
}
|
|
545
|
+
this.log(`Deleted label: ${name}`);
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Update a single label
|
|
549
|
+
*/
|
|
550
|
+
async updateLabel(name, updates) {
|
|
551
|
+
if (!this.options.dryRun) {
|
|
552
|
+
await this.client.updateLabel(name, updates);
|
|
553
|
+
}
|
|
554
|
+
this.log(`Updated label: ${name}`);
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
__publicField(_GitHubLabelSync, "BATCH_SIZE", 5);
|
|
558
|
+
var GitHubLabelSync = _GitHubLabelSync;
|
|
559
|
+
|
|
560
|
+
// src/config/templates.ts
|
|
561
|
+
var MINIMAL_TEMPLATE = [
|
|
562
|
+
{
|
|
563
|
+
name: "bug",
|
|
564
|
+
color: "ff0000",
|
|
565
|
+
description: "Something is broken"
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
name: "feature",
|
|
569
|
+
color: "00ff00",
|
|
570
|
+
description: "New feature or request"
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
name: "documentation",
|
|
574
|
+
color: "0000ff",
|
|
575
|
+
description: "Improvements or additions to documentation"
|
|
576
|
+
}
|
|
577
|
+
];
|
|
578
|
+
var createLabels = (labels) => labels;
|
|
579
|
+
var GITHUB_STANDARD_TEMPLATE = createLabels([
|
|
580
|
+
{
|
|
581
|
+
name: "bug",
|
|
582
|
+
color: "d73a4a",
|
|
583
|
+
description: "Something is broken"
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
name: "documentation",
|
|
587
|
+
color: "0075ca",
|
|
588
|
+
description: "Improvements or additions to documentation"
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
name: "duplicate",
|
|
592
|
+
color: "cfd3d7",
|
|
593
|
+
description: "This issue or pull request already exists"
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
name: "enhancement",
|
|
597
|
+
color: "a2eeef",
|
|
598
|
+
description: "New feature or request"
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
name: "good first issue",
|
|
602
|
+
color: "7057ff",
|
|
603
|
+
description: "Good for newcomers"
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
name: "help wanted",
|
|
607
|
+
color: "008672",
|
|
608
|
+
description: "Extra attention is needed"
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
name: "invalid",
|
|
612
|
+
color: "e4e669",
|
|
613
|
+
description: "This does not seem right"
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
name: "question",
|
|
617
|
+
color: "d876e3",
|
|
618
|
+
description: "Further information is requested"
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
name: "wontfix",
|
|
622
|
+
color: "ffffff",
|
|
623
|
+
description: "This will not be worked on"
|
|
624
|
+
}
|
|
625
|
+
]);
|
|
626
|
+
var PROD_EN_TEMPLATE = createLabels([
|
|
627
|
+
{
|
|
628
|
+
name: "API",
|
|
629
|
+
color: "ffb300",
|
|
630
|
+
description: "API integration and external services"
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
name: "BFF",
|
|
634
|
+
color: "7057ff",
|
|
635
|
+
description: "Backend for Frontend integration"
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
name: "CI/CD",
|
|
639
|
+
color: "00ced1",
|
|
640
|
+
description: "CI/CD and automation"
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
name: "Component",
|
|
644
|
+
color: "008672",
|
|
645
|
+
description: "Component addition and updates"
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
name: "Documentation",
|
|
649
|
+
color: "332412",
|
|
650
|
+
description: "Documentation additions and updates"
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
name: "Hotfix",
|
|
654
|
+
color: "ff6347",
|
|
655
|
+
description: "Emergency fixes and hotfixes"
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
name: "Refactoring",
|
|
659
|
+
color: "a9a9a9",
|
|
660
|
+
description: "Code refactoring and performance optimization"
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
name: "Testing",
|
|
664
|
+
color: "08a4d6",
|
|
665
|
+
description: "Testing, E2E, and unit tests"
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
name: "Type Definition",
|
|
669
|
+
color: "e91e63",
|
|
670
|
+
description: "TypeScript type definitions and type safety"
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
name: "Design",
|
|
674
|
+
color: "d876e3",
|
|
675
|
+
description: "UI/UX design"
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
name: "Specification",
|
|
679
|
+
color: "d93f0b",
|
|
680
|
+
description: "Specification changes"
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
name: "Feature",
|
|
684
|
+
color: "b3fa11",
|
|
685
|
+
description: "New feature addition"
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
name: "Environment",
|
|
689
|
+
color: "c5def5",
|
|
690
|
+
description: "Development environment and package updates"
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
name: "Page",
|
|
694
|
+
color: "16c9f5",
|
|
695
|
+
description: "Page additions and updates"
|
|
696
|
+
}
|
|
697
|
+
]);
|
|
698
|
+
var PROD_JA_TEMPLATE = createLabels([
|
|
699
|
+
{
|
|
700
|
+
name: "API\u9023\u643A",
|
|
701
|
+
color: "ffb300",
|
|
702
|
+
description: "API\u30FB\u5916\u90E8\u30B5\u30FC\u30D3\u30B9\u9023\u643A"
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
name: "BFF",
|
|
706
|
+
color: "7057ff",
|
|
707
|
+
description: "BFF\u30FB\u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u9023\u643A"
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
name: "CI/CD",
|
|
711
|
+
color: "00ced1",
|
|
712
|
+
description: "CI/CD\u30FB\u81EA\u52D5\u5316"
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
name: "\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8",
|
|
716
|
+
color: "008672",
|
|
717
|
+
description: "\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\u306E\u8FFD\u52A0\u30FB\u66F4\u65B0"
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
name: "\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
|
|
721
|
+
color: "332412",
|
|
722
|
+
description: "\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u306E\u8FFD\u52A0\u30FB\u66F4\u65B0"
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
name: "\u7DCA\u6025\u5BFE\u5FDC",
|
|
726
|
+
color: "ff6347",
|
|
727
|
+
description: "\u7DCA\u6025\u4FEE\u6B63\u30FBHotfix"
|
|
728
|
+
},
|
|
729
|
+
{
|
|
730
|
+
name: "\u30EA\u30D5\u30A1\u30AF\u30BF",
|
|
731
|
+
color: "a9a9a9",
|
|
732
|
+
description: "\u30EA\u30D5\u30A1\u30AF\u30BF\u30EA\u30F3\u30B0\u30FB\u30D1\u30D5\u30A9\u30FC\u30DE\u30F3\u30B9\u6700\u9069\u5316\u30FB\u30B3\u30FC\u30C9\u6574\u7406"
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
name: "\u30C6\u30B9\u30C8",
|
|
736
|
+
color: "08a4d6",
|
|
737
|
+
description: "\u30C6\u30B9\u30C8\u30FBE2E\u30FB\u30E6\u30CB\u30C3\u30C8\u30C6\u30B9\u30C8"
|
|
738
|
+
},
|
|
739
|
+
{
|
|
740
|
+
name: "\u578B\u5B9A\u7FA9",
|
|
741
|
+
color: "e91e63",
|
|
742
|
+
description: "TypeScript\u578B\u5B9A\u7FA9\u30FB\u578B\u5B89\u5168\u6027"
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
name: "\u30C7\u30B6\u30A4\u30F3",
|
|
746
|
+
color: "d876e3",
|
|
747
|
+
description: "UIUX\u30C7\u30B6\u30A4\u30F3"
|
|
748
|
+
},
|
|
749
|
+
{
|
|
750
|
+
name: "\u4ED5\u69D8\u5909\u66F4",
|
|
751
|
+
color: "d93f0b",
|
|
752
|
+
description: "\u4ED5\u69D8\u5909\u66F4"
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
name: "\u6A5F\u80FD\u8FFD\u52A0",
|
|
756
|
+
color: "b3fa11",
|
|
757
|
+
description: "\u6A5F\u80FD\u8FFD\u52A0"
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
name: "\u74B0\u5883\u69CB\u7BC9",
|
|
761
|
+
color: "c5def5",
|
|
762
|
+
description: "\u958B\u767A\u74B0\u5883\u30FB\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u8FFD\u52A0\u30FB\u5909\u66F4\u30FB\u66F4\u65B0"
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
name: "\u753B\u9762\u8FFD\u52A0",
|
|
766
|
+
color: "16c9f5",
|
|
767
|
+
description: "\u753B\u9762\u306E\u8FFD\u52A0\u30FB\u66F4\u65B0"
|
|
768
|
+
}
|
|
769
|
+
]);
|
|
770
|
+
var PROD_TEMPLATE = PROD_JA_TEMPLATE;
|
|
771
|
+
var REACT_TEMPLATE = createLabels([
|
|
772
|
+
{
|
|
773
|
+
name: "component",
|
|
774
|
+
color: "61dafb",
|
|
775
|
+
description: "React component development"
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
name: "hook",
|
|
779
|
+
color: "20232a",
|
|
780
|
+
description: "Custom hooks implementation"
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
name: "state-management",
|
|
784
|
+
color: "764abc",
|
|
785
|
+
description: "Redux, Zustand, Context API"
|
|
786
|
+
},
|
|
787
|
+
{
|
|
788
|
+
name: "performance",
|
|
789
|
+
color: "ffc107",
|
|
790
|
+
description: "Performance optimization, memoization"
|
|
791
|
+
},
|
|
792
|
+
{
|
|
793
|
+
name: "typescript",
|
|
794
|
+
color: "3178c6",
|
|
795
|
+
description: "TypeScript types and interfaces"
|
|
796
|
+
},
|
|
797
|
+
{
|
|
798
|
+
name: "styling",
|
|
799
|
+
color: "ff69b4",
|
|
800
|
+
description: "CSS-in-JS, Tailwind, styled-components"
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
name: "testing",
|
|
804
|
+
color: "15c213",
|
|
805
|
+
description: "Unit tests, React Testing Library"
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
name: "bug",
|
|
809
|
+
color: "d73a4a",
|
|
810
|
+
description: "Bug fix required"
|
|
811
|
+
},
|
|
812
|
+
{
|
|
813
|
+
name: "refactor",
|
|
814
|
+
color: "fbca04",
|
|
815
|
+
description: "Code refactoring"
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
name: "accessibility",
|
|
819
|
+
color: "0e8a16",
|
|
820
|
+
description: "A11y improvements"
|
|
821
|
+
}
|
|
822
|
+
]);
|
|
823
|
+
var VUE_TEMPLATE = createLabels([
|
|
824
|
+
{
|
|
825
|
+
name: "component",
|
|
826
|
+
color: "42b883",
|
|
827
|
+
description: "Vue component development"
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
name: "composable",
|
|
831
|
+
color: "35495e",
|
|
832
|
+
description: "Composition API, composables"
|
|
833
|
+
},
|
|
834
|
+
{
|
|
835
|
+
name: "pinia",
|
|
836
|
+
color: "ffd859",
|
|
837
|
+
description: "Pinia state management"
|
|
838
|
+
},
|
|
839
|
+
{
|
|
840
|
+
name: "vue-router",
|
|
841
|
+
color: "3ba57a",
|
|
842
|
+
description: "Vue Router navigation"
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
name: "typescript",
|
|
846
|
+
color: "3178c6",
|
|
847
|
+
description: "TypeScript integration"
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
name: "styling",
|
|
851
|
+
color: "ff69b4",
|
|
852
|
+
description: "Scoped CSS, CSS modules"
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
name: "testing",
|
|
856
|
+
color: "15c213",
|
|
857
|
+
description: "Vitest, Vue Test Utils"
|
|
858
|
+
},
|
|
859
|
+
{
|
|
860
|
+
name: "bug",
|
|
861
|
+
color: "d73a4a",
|
|
862
|
+
description: "Bug fix required"
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
name: "performance",
|
|
866
|
+
color: "ffc107",
|
|
867
|
+
description: "Performance optimization"
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
name: "accessibility",
|
|
871
|
+
color: "0e8a16",
|
|
872
|
+
description: "A11y improvements"
|
|
873
|
+
}
|
|
874
|
+
]);
|
|
875
|
+
var FRONTEND_TEMPLATE = createLabels([
|
|
876
|
+
{
|
|
877
|
+
name: "feature",
|
|
878
|
+
color: "0e8a16",
|
|
879
|
+
description: "New feature implementation"
|
|
880
|
+
},
|
|
881
|
+
{
|
|
882
|
+
name: "bug",
|
|
883
|
+
color: "d73a4a",
|
|
884
|
+
description: "Bug fix required"
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
name: "ui",
|
|
888
|
+
color: "ff69b4",
|
|
889
|
+
description: "UI/UX improvements"
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
name: "styling",
|
|
893
|
+
color: "c5def5",
|
|
894
|
+
description: "CSS, styling updates"
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
name: "responsive",
|
|
898
|
+
color: "1d76db",
|
|
899
|
+
description: "Responsive design, mobile support"
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
name: "performance",
|
|
903
|
+
color: "ffc107",
|
|
904
|
+
description: "Performance optimization"
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
name: "accessibility",
|
|
908
|
+
color: "0052cc",
|
|
909
|
+
description: "A11y improvements"
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
name: "testing",
|
|
913
|
+
color: "15c213",
|
|
914
|
+
description: "Testing, E2E, unit tests"
|
|
915
|
+
},
|
|
916
|
+
{
|
|
917
|
+
name: "dependencies",
|
|
918
|
+
color: "0366d6",
|
|
919
|
+
description: "Dependencies update"
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
name: "documentation",
|
|
923
|
+
color: "0075ca",
|
|
924
|
+
description: "Documentation updates"
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
name: "build",
|
|
928
|
+
color: "fbca04",
|
|
929
|
+
description: "Build system, bundler"
|
|
930
|
+
},
|
|
931
|
+
{
|
|
932
|
+
name: "seo",
|
|
933
|
+
color: "b60205",
|
|
934
|
+
description: "SEO optimization"
|
|
935
|
+
}
|
|
936
|
+
]);
|
|
937
|
+
var AGILE_TEMPLATE = createLabels([
|
|
938
|
+
{
|
|
939
|
+
name: "story",
|
|
940
|
+
color: "0e7490",
|
|
941
|
+
description: "User story"
|
|
942
|
+
},
|
|
943
|
+
{
|
|
944
|
+
name: "task",
|
|
945
|
+
color: "4c72a0",
|
|
946
|
+
description: "Implementation task"
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
name: "spike",
|
|
950
|
+
color: "ab47bc",
|
|
951
|
+
description: "Research and exploration"
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
name: "bug",
|
|
955
|
+
color: "d32f2f",
|
|
956
|
+
description: "Bug fix"
|
|
957
|
+
},
|
|
958
|
+
{
|
|
959
|
+
name: "debt",
|
|
960
|
+
color: "f57c00",
|
|
961
|
+
description: "Technical debt"
|
|
962
|
+
},
|
|
963
|
+
{
|
|
964
|
+
name: "blocked",
|
|
965
|
+
color: "616161",
|
|
966
|
+
description: "Blocked by another issue"
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
name: "priority:critical",
|
|
970
|
+
color: "ff0000",
|
|
971
|
+
description: "Critical priority"
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
name: "priority:high",
|
|
975
|
+
color: "ff9800",
|
|
976
|
+
description: "High priority"
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
name: "priority:medium",
|
|
980
|
+
color: "ffc107",
|
|
981
|
+
description: "Medium priority"
|
|
982
|
+
},
|
|
983
|
+
{
|
|
984
|
+
name: "priority:low",
|
|
985
|
+
color: "cddc39",
|
|
986
|
+
description: "Low priority"
|
|
987
|
+
}
|
|
988
|
+
]);
|
|
989
|
+
var CONFIG_TEMPLATES = {
|
|
990
|
+
minimal: MINIMAL_TEMPLATE,
|
|
991
|
+
github: GITHUB_STANDARD_TEMPLATE,
|
|
992
|
+
react: REACT_TEMPLATE,
|
|
993
|
+
vue: VUE_TEMPLATE,
|
|
994
|
+
frontend: FRONTEND_TEMPLATE,
|
|
995
|
+
"prod-en": PROD_EN_TEMPLATE,
|
|
996
|
+
"prod-ja": PROD_JA_TEMPLATE,
|
|
997
|
+
prod: PROD_TEMPLATE,
|
|
998
|
+
agile: AGILE_TEMPLATE
|
|
999
|
+
};
|
|
1000
|
+
function listTemplates() {
|
|
1001
|
+
return Object.keys(CONFIG_TEMPLATES);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// src/utils/args.ts
|
|
1005
|
+
function parseArgs(argv) {
|
|
1006
|
+
const result = {
|
|
1007
|
+
command: void 0,
|
|
1008
|
+
flags: /* @__PURE__ */ new Set(),
|
|
1009
|
+
options: /* @__PURE__ */ new Map(),
|
|
1010
|
+
positional: []
|
|
1011
|
+
};
|
|
1012
|
+
let i = 0;
|
|
1013
|
+
if (i < argv.length && !argv[i].startsWith("-")) {
|
|
1014
|
+
result.command = argv[i];
|
|
1015
|
+
i++;
|
|
1016
|
+
}
|
|
1017
|
+
while (i < argv.length) {
|
|
1018
|
+
const arg = argv[i];
|
|
1019
|
+
if (arg.startsWith("--")) {
|
|
1020
|
+
const parts = arg.split("=", 2);
|
|
1021
|
+
if (parts.length === 2) {
|
|
1022
|
+
result.options.set(parts[0], parts[1]);
|
|
1023
|
+
i++;
|
|
1024
|
+
} else {
|
|
1025
|
+
const nextArg = argv[i + 1];
|
|
1026
|
+
if (!nextArg || nextArg.startsWith("-")) {
|
|
1027
|
+
result.flags.add(arg);
|
|
1028
|
+
i++;
|
|
1029
|
+
} else {
|
|
1030
|
+
result.options.set(arg, nextArg);
|
|
1031
|
+
i += 2;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
} else if (arg.startsWith("-")) {
|
|
1035
|
+
result.flags.add(arg);
|
|
1036
|
+
i++;
|
|
1037
|
+
} else {
|
|
1038
|
+
result.positional.push(arg);
|
|
1039
|
+
i++;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
return result;
|
|
1043
|
+
}
|
|
1044
|
+
function getRequiredOption(args, name, errorMessage) {
|
|
1045
|
+
const value = args.options.get(name);
|
|
1046
|
+
if (!value) {
|
|
1047
|
+
throw new Error(errorMessage || `Missing required option: ${name}`);
|
|
1048
|
+
}
|
|
1049
|
+
return value;
|
|
1050
|
+
}
|
|
1051
|
+
function getOption(args, name, defaultValue) {
|
|
1052
|
+
return args.options.get(name) || defaultValue;
|
|
1053
|
+
}
|
|
1054
|
+
function hasFlag(args, ...names) {
|
|
1055
|
+
return names.some((name) => args.flags.has(name));
|
|
1056
|
+
}
|
|
1057
|
+
function getPositional(args, index) {
|
|
1058
|
+
return args.positional[index];
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// src/utils/ui.ts
|
|
1062
|
+
var colors = {
|
|
1063
|
+
reset: "\x1B[0m",
|
|
1064
|
+
bright: "\x1B[1m",
|
|
1065
|
+
dim: "\x1B[2m",
|
|
1066
|
+
// Foreground colors
|
|
1067
|
+
red: "\x1B[31m",
|
|
1068
|
+
green: "\x1B[32m",
|
|
1069
|
+
yellow: "\x1B[33m",
|
|
1070
|
+
blue: "\x1B[34m",
|
|
1071
|
+
magenta: "\x1B[35m",
|
|
1072
|
+
cyan: "\x1B[36m",
|
|
1073
|
+
white: "\x1B[37m",
|
|
1074
|
+
gray: "\x1B[90m",
|
|
1075
|
+
// Background colors
|
|
1076
|
+
bgRed: "\x1B[41m",
|
|
1077
|
+
bgGreen: "\x1B[42m",
|
|
1078
|
+
bgYellow: "\x1B[43m",
|
|
1079
|
+
bgBlue: "\x1B[44m"
|
|
1080
|
+
};
|
|
1081
|
+
function supportsColor() {
|
|
1082
|
+
if (process.env.NO_COLOR) {
|
|
1083
|
+
return false;
|
|
1084
|
+
}
|
|
1085
|
+
if (process.env.FORCE_COLOR) {
|
|
1086
|
+
return true;
|
|
1087
|
+
}
|
|
1088
|
+
if (!process.stdout.isTTY) {
|
|
1089
|
+
return false;
|
|
1090
|
+
}
|
|
1091
|
+
if (process.platform === "win32") {
|
|
1092
|
+
return true;
|
|
1093
|
+
}
|
|
1094
|
+
return true;
|
|
1095
|
+
}
|
|
1096
|
+
function colorize(text, color) {
|
|
1097
|
+
if (!supportsColor()) {
|
|
1098
|
+
return text;
|
|
1099
|
+
}
|
|
1100
|
+
return `${colors[color]}${text}${colors.reset}`;
|
|
1101
|
+
}
|
|
1102
|
+
function success(message) {
|
|
1103
|
+
return colorize("\u2713", "green") + " " + message;
|
|
1104
|
+
}
|
|
1105
|
+
function error(message) {
|
|
1106
|
+
return colorize("\u2717", "red") + " " + message;
|
|
1107
|
+
}
|
|
1108
|
+
function warning(message) {
|
|
1109
|
+
return colorize("\u26A0", "yellow") + " " + message;
|
|
1110
|
+
}
|
|
1111
|
+
function info(message) {
|
|
1112
|
+
return colorize("\u2139", "blue") + " " + message;
|
|
1113
|
+
}
|
|
1114
|
+
function header(text) {
|
|
1115
|
+
return "\n" + colorize(text, "bright") + "\n" + "\u2500".repeat(Math.min(text.length, 50));
|
|
1116
|
+
}
|
|
1117
|
+
var Spinner = class {
|
|
1118
|
+
constructor() {
|
|
1119
|
+
__publicField(this, "frames", ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"]);
|
|
1120
|
+
__publicField(this, "interval", null);
|
|
1121
|
+
__publicField(this, "frameIndex", 0);
|
|
1122
|
+
__publicField(this, "message", "");
|
|
1123
|
+
}
|
|
1124
|
+
start(message) {
|
|
1125
|
+
this.message = message;
|
|
1126
|
+
if (!process.stdout.isTTY || !supportsColor()) {
|
|
1127
|
+
console.log(message + "...");
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
this.interval = setInterval(() => {
|
|
1131
|
+
const frame = this.frames[this.frameIndex];
|
|
1132
|
+
process.stdout.write(`\r${colorize(frame, "cyan")} ${this.message}`);
|
|
1133
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
1134
|
+
}, 80);
|
|
1135
|
+
}
|
|
1136
|
+
succeed(message) {
|
|
1137
|
+
this.stop();
|
|
1138
|
+
console.log(success(message || this.message));
|
|
1139
|
+
}
|
|
1140
|
+
fail(message) {
|
|
1141
|
+
this.stop();
|
|
1142
|
+
console.log(error(message || this.message));
|
|
1143
|
+
}
|
|
1144
|
+
warn(message) {
|
|
1145
|
+
this.stop();
|
|
1146
|
+
console.log(warning(message || this.message));
|
|
1147
|
+
}
|
|
1148
|
+
stop() {
|
|
1149
|
+
if (this.interval) {
|
|
1150
|
+
clearInterval(this.interval);
|
|
1151
|
+
this.interval = null;
|
|
1152
|
+
if (process.stdout.isTTY) {
|
|
1153
|
+
process.stdout.write("\r\x1B[K");
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1159
|
+
// src/cli.ts
|
|
1160
|
+
var rawArgs = process.argv.slice(2);
|
|
1161
|
+
var parsedArgs = parseArgs(rawArgs);
|
|
1162
|
+
function printUsage() {
|
|
1163
|
+
console.log(colorize("\nlabels-config", "bright") + " - Terminal-first GitHub label management\n");
|
|
1164
|
+
console.log(header("Commands"));
|
|
1165
|
+
console.log(" " + colorize("validate", "cyan") + " <file> Validate label configuration file");
|
|
1166
|
+
console.log(" " + colorize("sync", "cyan") + " Sync labels to GitHub repository");
|
|
1167
|
+
console.log(" " + colorize("export", "cyan") + " Export labels from GitHub repository");
|
|
1168
|
+
console.log(" " + colorize("init", "cyan") + " <template> Initialize new configuration");
|
|
1169
|
+
console.log(" " + colorize("help", "cyan") + " Show this help message");
|
|
1170
|
+
console.log(header("Prerequisites"));
|
|
1171
|
+
console.log(" " + info("gh CLI must be installed and authenticated"));
|
|
1172
|
+
console.log(" Run: " + colorize("gh auth login", "yellow"));
|
|
1173
|
+
console.log(" No GitHub token required - uses gh CLI authentication");
|
|
1174
|
+
console.log(header("Options"));
|
|
1175
|
+
console.log(" " + colorize("--owner", "green") + " <owner> Repository owner (required for sync/export)");
|
|
1176
|
+
console.log(" " + colorize("--repo", "green") + " <repo> Repository name (required for sync/export)");
|
|
1177
|
+
console.log(" " + colorize("--file", "green") + " <file> Configuration file path");
|
|
1178
|
+
console.log(" " + colorize("--dry-run", "green") + " Dry run mode (don't make changes)");
|
|
1179
|
+
console.log(" " + colorize("--delete-extra", "green") + " Replace mode: delete labels not in config");
|
|
1180
|
+
console.log(" " + colorize("--verbose", "green") + " Verbose output");
|
|
1181
|
+
console.log(header("Available Templates"));
|
|
1182
|
+
console.log(" " + colorize("minimal", "magenta") + " Basic 3-label set (bug, feature, documentation)");
|
|
1183
|
+
console.log(" " + colorize("github", "magenta") + " GitHub standard labels");
|
|
1184
|
+
console.log(" " + colorize("react", "magenta") + " React ecosystem (components, hooks, state)");
|
|
1185
|
+
console.log(" " + colorize("vue", "magenta") + " Vue.js ecosystem (composables, Pinia, router)");
|
|
1186
|
+
console.log(" " + colorize("frontend", "magenta") + " General frontend development");
|
|
1187
|
+
console.log(" " + colorize("prod-en", "magenta") + " Production project (14 labels, English)");
|
|
1188
|
+
console.log(" " + colorize("prod-ja", "magenta") + " Production project (14 labels, Japanese)");
|
|
1189
|
+
console.log(" " + colorize("prod", "magenta") + " Production project (alias for prod-ja)");
|
|
1190
|
+
console.log(" " + colorize("agile", "magenta") + " Agile/Scrum workflow");
|
|
1191
|
+
console.log(header("Examples"));
|
|
1192
|
+
console.log(" # Validate configuration");
|
|
1193
|
+
console.log(" " + colorize("labels-config validate ./labels.json", "gray"));
|
|
1194
|
+
console.log("");
|
|
1195
|
+
console.log(" # Initialize from template");
|
|
1196
|
+
console.log(" " + colorize("labels-config init react --file ./labels.json", "gray"));
|
|
1197
|
+
console.log("");
|
|
1198
|
+
console.log(" # Sync labels (append mode)");
|
|
1199
|
+
console.log(" " + colorize("labels-config sync --owner user --repo repo --file labels.json", "gray"));
|
|
1200
|
+
console.log("");
|
|
1201
|
+
console.log(" # Sync with dry run");
|
|
1202
|
+
console.log(" " + colorize("labels-config sync --owner user --repo repo --file labels.json --dry-run", "gray"));
|
|
1203
|
+
console.log("");
|
|
1204
|
+
}
|
|
1205
|
+
async function validateCommand() {
|
|
1206
|
+
const file = getPositional(parsedArgs, 0);
|
|
1207
|
+
if (!file) {
|
|
1208
|
+
console.error(error("File path required"));
|
|
1209
|
+
console.error("Usage: labels-config validate <file>");
|
|
1210
|
+
process.exit(1);
|
|
1211
|
+
}
|
|
1212
|
+
const spinner = new Spinner();
|
|
1213
|
+
try {
|
|
1214
|
+
try {
|
|
1215
|
+
await import_fs.promises.access(file);
|
|
1216
|
+
} catch {
|
|
1217
|
+
console.error(error(`File not found: ${file}`));
|
|
1218
|
+
process.exit(1);
|
|
1219
|
+
}
|
|
1220
|
+
spinner.start(`Reading ${file}`);
|
|
1221
|
+
const content = await import_fs.promises.readFile(file, "utf-8");
|
|
1222
|
+
let data;
|
|
1223
|
+
try {
|
|
1224
|
+
data = JSON.parse(content);
|
|
1225
|
+
} catch (err) {
|
|
1226
|
+
spinner.fail(`Invalid JSON in file: ${file}`);
|
|
1227
|
+
console.error(error(err instanceof Error ? err.message : String(err)));
|
|
1228
|
+
process.exit(1);
|
|
1229
|
+
}
|
|
1230
|
+
spinner.stop();
|
|
1231
|
+
spinner.start("Validating configuration");
|
|
1232
|
+
const result = validateWithDetails(data);
|
|
1233
|
+
spinner.stop();
|
|
1234
|
+
console.log(info(`Validating: ${file}`));
|
|
1235
|
+
console.log(info(`Total labels: ${result.labels.length}`));
|
|
1236
|
+
if (result.valid) {
|
|
1237
|
+
console.log(success("Configuration is valid!"));
|
|
1238
|
+
return;
|
|
1239
|
+
} else {
|
|
1240
|
+
console.log(error("Validation errors found:"));
|
|
1241
|
+
const errors = result.errors;
|
|
1242
|
+
if (errors.duplicateNames && errors.duplicateNames.length > 0) {
|
|
1243
|
+
console.log(colorize(` \u2022 Duplicate names: ${errors.duplicateNames.join(", ")}`, "red"));
|
|
1244
|
+
}
|
|
1245
|
+
if (errors.duplicateColors && errors.duplicateColors.length > 0) {
|
|
1246
|
+
console.log(colorize(` \u2022 Duplicate colors: ${errors.duplicateColors.join(", ")}`, "red"));
|
|
1247
|
+
}
|
|
1248
|
+
if (errors.validationErrors && errors.validationErrors.length > 0) {
|
|
1249
|
+
console.log(colorize(" \u2022 Validation errors:", "red"));
|
|
1250
|
+
errors.validationErrors.forEach((err) => {
|
|
1251
|
+
console.log(` ${err.path.join(".")}: ${err.message}`);
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
process.exit(1);
|
|
1255
|
+
}
|
|
1256
|
+
} catch (err) {
|
|
1257
|
+
spinner.fail("Validation failed");
|
|
1258
|
+
console.error(error(err instanceof Error ? err.message : String(err)));
|
|
1259
|
+
process.exit(1);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
async function syncCommand() {
|
|
1263
|
+
const spinner = new Spinner();
|
|
1264
|
+
try {
|
|
1265
|
+
const owner = getRequiredOption(parsedArgs, "--owner", "Error: --owner is required for sync command");
|
|
1266
|
+
const repo = getRequiredOption(parsedArgs, "--repo", "Error: --repo is required for sync command");
|
|
1267
|
+
const file = getRequiredOption(parsedArgs, "--file", "Error: --file is required for sync command");
|
|
1268
|
+
const dryRun = hasFlag(parsedArgs, "--dry-run");
|
|
1269
|
+
const deleteExtra = hasFlag(parsedArgs, "--delete-extra");
|
|
1270
|
+
const verbose = hasFlag(parsedArgs, "--verbose");
|
|
1271
|
+
try {
|
|
1272
|
+
await import_fs.promises.access(file);
|
|
1273
|
+
} catch {
|
|
1274
|
+
console.error(error(`File not found: ${file}`));
|
|
1275
|
+
process.exit(1);
|
|
1276
|
+
}
|
|
1277
|
+
spinner.start(`Loading labels from ${file}`);
|
|
1278
|
+
const content = await import_fs.promises.readFile(file, "utf-8");
|
|
1279
|
+
let labels;
|
|
1280
|
+
try {
|
|
1281
|
+
const loader = new ConfigLoader();
|
|
1282
|
+
labels = loader.loadFromString(content);
|
|
1283
|
+
spinner.succeed(`Loaded ${labels.length} labels`);
|
|
1284
|
+
} catch (err) {
|
|
1285
|
+
spinner.fail(`Failed to load labels from file: ${file}`);
|
|
1286
|
+
console.error(error(err instanceof Error ? err.message : String(err)));
|
|
1287
|
+
process.exit(1);
|
|
1288
|
+
}
|
|
1289
|
+
const sync = new GitHubLabelSync({
|
|
1290
|
+
owner,
|
|
1291
|
+
repo,
|
|
1292
|
+
dryRun,
|
|
1293
|
+
deleteExtra,
|
|
1294
|
+
verbose
|
|
1295
|
+
});
|
|
1296
|
+
const modeText = dryRun ? colorize("[DRY RUN]", "yellow") : "";
|
|
1297
|
+
console.log(`
|
|
1298
|
+
${modeText} ${info(`Syncing to ${owner}/${repo}`)}`);
|
|
1299
|
+
if (deleteExtra) {
|
|
1300
|
+
console.log(warning("Replace mode: Will delete labels not in config"));
|
|
1301
|
+
}
|
|
1302
|
+
spinner.start("Syncing labels");
|
|
1303
|
+
const result = await sync.syncLabels(labels);
|
|
1304
|
+
spinner.succeed("Sync complete");
|
|
1305
|
+
console.log(header("Results"));
|
|
1306
|
+
if (result.created.length > 0) {
|
|
1307
|
+
console.log(success(`Created: ${result.created.length}`));
|
|
1308
|
+
if (verbose) {
|
|
1309
|
+
result.created.forEach((label) => console.log(` \u2022 ${label.name}`));
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
if (result.updated.length > 0) {
|
|
1313
|
+
console.log(info(`Updated: ${result.updated.length}`));
|
|
1314
|
+
if (verbose) {
|
|
1315
|
+
result.updated.forEach((label) => console.log(` \u2022 ${label.name}`));
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
if (result.deleted.length > 0) {
|
|
1319
|
+
console.log(warning(`Deleted: ${result.deleted.length}`));
|
|
1320
|
+
if (verbose) {
|
|
1321
|
+
result.deleted.forEach((name) => console.log(` \u2022 ${name}`));
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
if (result.unchanged.length > 0) {
|
|
1325
|
+
console.log(colorize(`Unchanged: ${result.unchanged.length}`, "gray"));
|
|
1326
|
+
}
|
|
1327
|
+
if (result.errors.length > 0) {
|
|
1328
|
+
console.log(header("Errors"));
|
|
1329
|
+
result.errors.forEach((err) => {
|
|
1330
|
+
console.log(error(`${err.name}: ${err.error}`));
|
|
1331
|
+
});
|
|
1332
|
+
process.exit(1);
|
|
1333
|
+
}
|
|
1334
|
+
} catch (err) {
|
|
1335
|
+
spinner.fail("Sync failed");
|
|
1336
|
+
console.error(error(err instanceof Error ? err.message : String(err)));
|
|
1337
|
+
process.exit(1);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
async function exportCommand() {
|
|
1341
|
+
const spinner = new Spinner();
|
|
1342
|
+
try {
|
|
1343
|
+
const owner = getRequiredOption(parsedArgs, "--owner", "Error: --owner is required for export command");
|
|
1344
|
+
const repo = getRequiredOption(parsedArgs, "--repo", "Error: --repo is required for export command");
|
|
1345
|
+
const file = getOption(parsedArgs, "--file") || "labels.json";
|
|
1346
|
+
spinner.start(`Fetching labels from ${owner}/${repo}`);
|
|
1347
|
+
const sync = new GitHubLabelSync({ owner, repo });
|
|
1348
|
+
const labels = await sync.fetchLabels();
|
|
1349
|
+
spinner.succeed(`Fetched ${labels.length} labels`);
|
|
1350
|
+
spinner.start("Creating registry");
|
|
1351
|
+
const manager = new LabelManager({ labels });
|
|
1352
|
+
const registry = manager.exportRegistry("1.0.0", { source: `${owner}/${repo}` });
|
|
1353
|
+
await import_fs.promises.writeFile(file, JSON.stringify(registry, null, 2));
|
|
1354
|
+
spinner.succeed(`Exported to ${file}`);
|
|
1355
|
+
console.log(success(`Successfully exported ${labels.length} labels`));
|
|
1356
|
+
} catch (err) {
|
|
1357
|
+
spinner.fail("Export failed");
|
|
1358
|
+
console.error(error(err instanceof Error ? err.message : String(err)));
|
|
1359
|
+
process.exit(1);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
async function initCommand() {
|
|
1363
|
+
const template = getPositional(parsedArgs, 0);
|
|
1364
|
+
const file = getOption(parsedArgs, "--file") || "labels.json";
|
|
1365
|
+
if (!template || !listTemplates().includes(template)) {
|
|
1366
|
+
console.log(error(`Invalid template "${template || ""}"`));
|
|
1367
|
+
console.log(info("Available templates: ") + listTemplates().map((t) => colorize(t, "magenta")).join(", "));
|
|
1368
|
+
process.exit(1);
|
|
1369
|
+
}
|
|
1370
|
+
const spinner = new Spinner();
|
|
1371
|
+
try {
|
|
1372
|
+
spinner.start(`Creating configuration from "${template}" template`);
|
|
1373
|
+
const labels = CONFIG_TEMPLATES[template];
|
|
1374
|
+
const manager = new LabelManager({ labels });
|
|
1375
|
+
const registry = manager.exportRegistry("1.0.0");
|
|
1376
|
+
await import_fs.promises.writeFile(file, JSON.stringify(registry, null, 2));
|
|
1377
|
+
spinner.succeed(`Created ${file}`);
|
|
1378
|
+
console.log(success(`Initialized with ${labels.length} labels from "${template}" template`));
|
|
1379
|
+
console.log(info("Next steps:"));
|
|
1380
|
+
console.log(` 1. Review and edit: ${colorize(file, "cyan")}`);
|
|
1381
|
+
console.log(` 2. Validate: ${colorize(`labels-config validate ${file}`, "gray")}`);
|
|
1382
|
+
console.log(` 3. Sync to repo: ${colorize(`labels-config sync --owner <owner> --repo <repo> --file ${file}`, "gray")}`);
|
|
1383
|
+
} catch (err) {
|
|
1384
|
+
spinner.fail("Initialization failed");
|
|
1385
|
+
console.error(error(err instanceof Error ? err.message : String(err)));
|
|
1386
|
+
process.exit(1);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
async function main() {
|
|
1390
|
+
const command = parsedArgs.command;
|
|
1391
|
+
if (!command || command === "help" || hasFlag(parsedArgs, "--help", "-h")) {
|
|
1392
|
+
printUsage();
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1395
|
+
switch (command) {
|
|
1396
|
+
case "validate":
|
|
1397
|
+
await validateCommand();
|
|
1398
|
+
break;
|
|
1399
|
+
case "sync":
|
|
1400
|
+
await syncCommand();
|
|
1401
|
+
break;
|
|
1402
|
+
case "export":
|
|
1403
|
+
await exportCommand();
|
|
1404
|
+
break;
|
|
1405
|
+
case "init":
|
|
1406
|
+
await initCommand();
|
|
1407
|
+
break;
|
|
1408
|
+
default:
|
|
1409
|
+
console.error(`Unknown command: ${command}`);
|
|
1410
|
+
printUsage();
|
|
1411
|
+
process.exit(1);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
main().catch((error2) => {
|
|
1415
|
+
console.error("Fatal error:", error2);
|
|
1416
|
+
process.exit(1);
|
|
1417
|
+
});
|