hyouji 0.0.7 → 0.0.9
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/README.md +94 -8
- package/dist/index.js +241 -42
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -18,7 +18,6 @@ https://levelup.gitconnected.com/create-github-labels-from-terminal-158d4868fab
|
|
|
18
18
|
|
|
19
19
|
https://github.com/user-attachments/assets/739f185a-1bd0-411b-8947-dd4600c452c8
|
|
20
20
|
|
|
21
|
-
|
|
22
21
|
### Labels API
|
|
23
22
|
|
|
24
23
|
https://docs.github.com/en/rest/reference/issues#labels
|
|
@@ -60,7 +59,7 @@ This tool provides the following functionality:
|
|
|
60
59
|
2. Create multiple labels on a specific repo
|
|
61
60
|
3. Delete a single label from a specific repo
|
|
62
61
|
4. Delete all labels from a specific repo
|
|
63
|
-
5. Import labels from JSON file
|
|
62
|
+
5. Import labels from JSON or YAML file
|
|
64
63
|
6. **Display your saved settings** - View your stored GitHub configuration
|
|
65
64
|
7. **Persistent configuration** - Save your GitHub token and username for future use
|
|
66
65
|
|
|
@@ -77,9 +76,7 @@ hyouji
|
|
|
77
76
|
On your first run, you'll be prompted to enter:
|
|
78
77
|
|
|
79
78
|
- **GitHub Personal Token** - Generate one [here](https://github.com/settings/tokens) with `repo` scope
|
|
80
|
-
<img width="792" height="564" alt="github_token" src="https://github.com/user-attachments/assets/e460738f-833a-4158-a8ba-61752beaad72" />
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
<img width="792" height="564" alt="github_token" src="https://github.com/user-attachments/assets/e460738f-833a-4158-a8ba-61752beaad72" />
|
|
83
80
|
- **GitHub Username** - Your GitHub account name
|
|
84
81
|
|
|
85
82
|
These credentials will be securely saved and reused for future sessions.
|
|
@@ -90,9 +87,11 @@ These credentials will be securely saved and reused for future sessions.
|
|
|
90
87
|
2. **Create multiple labels on a specific repo**
|
|
91
88
|
3. **Delete a single label from a specific repo**
|
|
92
89
|
4. **Delete all labels from a specific repo**
|
|
93
|
-
5. **Import labels from JSON file**
|
|
94
|
-
6. **
|
|
95
|
-
7. **
|
|
90
|
+
5. **Import labels from JSON or YAML file**
|
|
91
|
+
6. **Generate sample JSON** - Create a sample JSON file with predefined labels
|
|
92
|
+
7. **Generate sample YAML** - Create a sample YAML file with predefined labels
|
|
93
|
+
8. **Display your settings** - View your saved configuration
|
|
94
|
+
9. **Exit**
|
|
96
95
|
|
|
97
96
|
### Settings Management
|
|
98
97
|
|
|
@@ -200,6 +199,93 @@ npm start
|
|
|
200
199
|
|
|
201
200
|
You can use `pnpm`, `yarn` or `bun`.
|
|
202
201
|
|
|
202
|
+
### File Import Support
|
|
203
|
+
|
|
204
|
+
The "Import labels from JSON or YAML file" option allows you to import multiple labels from external files. Both JSON and YAML formats are supported.
|
|
205
|
+
|
|
206
|
+
#### Supported File Formats
|
|
207
|
+
|
|
208
|
+
- **JSON files** (`.json` extension)
|
|
209
|
+
- **YAML files** (`.yaml` or `.yml` extension)
|
|
210
|
+
|
|
211
|
+
#### Label Structure
|
|
212
|
+
|
|
213
|
+
Both formats support the same label structure:
|
|
214
|
+
|
|
215
|
+
- `name` (required): The label name
|
|
216
|
+
- `color` (optional): Hex color code without the `#` symbol
|
|
217
|
+
- `description` (optional): Label description
|
|
218
|
+
|
|
219
|
+
#### JSON Example
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
[
|
|
223
|
+
{
|
|
224
|
+
"name": "bug",
|
|
225
|
+
"color": "d73a4a",
|
|
226
|
+
"description": "Something isn't working"
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"name": "enhancement",
|
|
230
|
+
"color": "a2eeef",
|
|
231
|
+
"description": "New feature or request"
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"name": "documentation",
|
|
235
|
+
"color": "0075ca",
|
|
236
|
+
"description": "Improvements or additions to documentation"
|
|
237
|
+
}
|
|
238
|
+
]
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### YAML Example
|
|
242
|
+
|
|
243
|
+
```yaml
|
|
244
|
+
# Sample YAML file for importing GitHub labels
|
|
245
|
+
- name: 'bug'
|
|
246
|
+
color: 'd73a4a'
|
|
247
|
+
description: "Something isn't working"
|
|
248
|
+
|
|
249
|
+
- name: 'enhancement'
|
|
250
|
+
color: 'a2eeef'
|
|
251
|
+
description: 'New feature or request'
|
|
252
|
+
|
|
253
|
+
- name: 'documentation'
|
|
254
|
+
color: '0075ca'
|
|
255
|
+
description: 'Improvements or additions to documentation'
|
|
256
|
+
|
|
257
|
+
# Labels with minimal configuration (name only)
|
|
258
|
+
- name: 'good first issue'
|
|
259
|
+
color: '7057ff'
|
|
260
|
+
description: 'Good for newcomers'
|
|
261
|
+
|
|
262
|
+
# Labels without description (optional field)
|
|
263
|
+
- name: 'wontfix'
|
|
264
|
+
color: 'ffffff'
|
|
265
|
+
|
|
266
|
+
# Labels without color (will use GitHub default)
|
|
267
|
+
- name: 'question'
|
|
268
|
+
description: 'Further information is requested'
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
#### Sample Files
|
|
272
|
+
|
|
273
|
+
You can find complete example files in the `examples/` directory:
|
|
274
|
+
|
|
275
|
+
- `examples/labels.json` - Basic label examples in JSON format
|
|
276
|
+
- `examples/labels.yaml` - Basic label examples in YAML format
|
|
277
|
+
- `examples/project-labels.json` - Project management labels in JSON format
|
|
278
|
+
- `examples/project-labels.yaml` - Project management labels in YAML format
|
|
279
|
+
|
|
280
|
+
#### Generate Sample Files
|
|
281
|
+
|
|
282
|
+
The tool can generate sample files for you:
|
|
283
|
+
|
|
284
|
+
- **Generate sample JSON** - Creates `hyouji.json` with predefined labels
|
|
285
|
+
- **Generate sample YAML** - Creates `hyouji.yaml` with predefined labels
|
|
286
|
+
|
|
287
|
+
Both generated files contain the same predefined labels and can be used as starting points for your own label configurations.
|
|
288
|
+
|
|
203
289
|
### Predefined Labels
|
|
204
290
|
|
|
205
291
|
The "Create multiple labels" option uses predefined labels from `src/constant.ts`. These include common labels for project management:
|
package/dist/index.js
CHANGED
|
@@ -4,9 +4,12 @@ import { renderFilled } from "oh-my-logo";
|
|
|
4
4
|
import * as fs from "fs";
|
|
5
5
|
import { promises, existsSync } from "fs";
|
|
6
6
|
import { homedir } from "os";
|
|
7
|
+
import * as path from "path";
|
|
7
8
|
import { join, dirname } from "path";
|
|
8
9
|
import { createHash, randomBytes, createCipheriv, createDecipheriv } from "crypto";
|
|
9
10
|
import prompts from "prompts";
|
|
11
|
+
import * as yaml from "js-yaml";
|
|
12
|
+
import yaml__default from "js-yaml";
|
|
10
13
|
import { Octokit } from "@octokit/core";
|
|
11
14
|
import { exec } from "child_process";
|
|
12
15
|
import { promisify } from "util";
|
|
@@ -49,10 +52,10 @@ const deleteLabel$1 = {
|
|
|
49
52
|
name: "name",
|
|
50
53
|
message: "Please type label name you want to delete"
|
|
51
54
|
};
|
|
52
|
-
const
|
|
55
|
+
const labelFilePath = {
|
|
53
56
|
type: "text",
|
|
54
57
|
name: "filePath",
|
|
55
|
-
message: "Please type the path to your JSON file"
|
|
58
|
+
message: "Please type the path to your JSON or YAML file"
|
|
56
59
|
};
|
|
57
60
|
const actionSelector = {
|
|
58
61
|
type: "multiselect",
|
|
@@ -63,9 +66,11 @@ const actionSelector = {
|
|
|
63
66
|
{ title: "create multiple labels", value: 1 },
|
|
64
67
|
{ title: "delete a label", value: 2 },
|
|
65
68
|
{ title: "delete all labels", value: 3 },
|
|
66
|
-
{ title: "import JSON", value: 4 },
|
|
67
|
-
{ title: "
|
|
68
|
-
{ title: "
|
|
69
|
+
{ title: "import labels from JSON or YAML", value: 4 },
|
|
70
|
+
{ title: "Generate sample JSON", value: 5 },
|
|
71
|
+
{ title: "Generate sample YAML", value: 6 },
|
|
72
|
+
{ title: "Display your settings", value: 7 },
|
|
73
|
+
{ title: "exit", value: 8 }
|
|
69
74
|
]
|
|
70
75
|
};
|
|
71
76
|
const holdToken = {
|
|
@@ -74,6 +79,23 @@ const holdToken = {
|
|
|
74
79
|
message: "Do you have a personal token?",
|
|
75
80
|
initial: true
|
|
76
81
|
};
|
|
82
|
+
const sampleData = [
|
|
83
|
+
{
|
|
84
|
+
name: "Type: Bug Fix",
|
|
85
|
+
color: "FF8A65",
|
|
86
|
+
description: "Fix features that are not working"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "Type: Enhancement",
|
|
90
|
+
color: "64B5F7",
|
|
91
|
+
description: "Add new features"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "Type: Improvement",
|
|
95
|
+
color: "4DB6AC",
|
|
96
|
+
description: "Improve existing functionality"
|
|
97
|
+
}
|
|
98
|
+
];
|
|
77
99
|
const labels = (
|
|
78
100
|
// the following labels are based on this post
|
|
79
101
|
// https://qiita.com/willow-micro/items/51eeb3efe5b4192a4abd
|
|
@@ -247,7 +269,7 @@ Thank you!
|
|
|
247
269
|
};
|
|
248
270
|
const extraGuideText = `If you don't see action selector, please hit space key.`;
|
|
249
271
|
const linkToPersonalToken = "https://github.com/settings/tokens";
|
|
250
|
-
const log$
|
|
272
|
+
const log$4 = console.log;
|
|
251
273
|
const createLabel = async (configs2, label) => {
|
|
252
274
|
const resp = await configs2.octokit.request(
|
|
253
275
|
"POST /repos/{owner}/{repo}/labels",
|
|
@@ -262,16 +284,16 @@ const createLabel = async (configs2, label) => {
|
|
|
262
284
|
const status = resp.status;
|
|
263
285
|
switch (status) {
|
|
264
286
|
case 201:
|
|
265
|
-
log$
|
|
287
|
+
log$4(chalk.green(`${resp.status}: Created ${label.name}`));
|
|
266
288
|
break;
|
|
267
289
|
case 404:
|
|
268
|
-
log$
|
|
290
|
+
log$4(chalk.red(`${resp.status}: Resource not found`));
|
|
269
291
|
break;
|
|
270
292
|
case 422:
|
|
271
|
-
log$
|
|
293
|
+
log$4(chalk.red(`${resp.status}: Validation failed`));
|
|
272
294
|
break;
|
|
273
295
|
default:
|
|
274
|
-
log$
|
|
296
|
+
log$4(chalk.yellow(`${resp.status}: Something wrong`));
|
|
275
297
|
break;
|
|
276
298
|
}
|
|
277
299
|
};
|
|
@@ -279,8 +301,8 @@ const createLabels = async (configs2) => {
|
|
|
279
301
|
labels.forEach(async (label) => {
|
|
280
302
|
createLabel(configs2, label);
|
|
281
303
|
});
|
|
282
|
-
log$
|
|
283
|
-
log$
|
|
304
|
+
log$4("Created all labels");
|
|
305
|
+
log$4(chalk.bgBlueBright(extraGuideText));
|
|
284
306
|
};
|
|
285
307
|
const deleteLabel = async (configs2, labelNames) => {
|
|
286
308
|
for (const labelName of labelNames) {
|
|
@@ -294,15 +316,15 @@ const deleteLabel = async (configs2, labelNames) => {
|
|
|
294
316
|
}
|
|
295
317
|
);
|
|
296
318
|
if (resp.status === 204) {
|
|
297
|
-
log$
|
|
319
|
+
log$4(chalk.green(`${resp.status}: Deleted ${labelName}`));
|
|
298
320
|
} else {
|
|
299
|
-
log$
|
|
321
|
+
log$4(chalk.yellow(`${resp.status}: Something wrong with ${labelName}`));
|
|
300
322
|
}
|
|
301
323
|
} catch (error) {
|
|
302
324
|
if (error && typeof error === "object" && "status" in error && error.status === 404) {
|
|
303
|
-
log$
|
|
325
|
+
log$4(chalk.red(`404: Label "${labelName}" not found`));
|
|
304
326
|
} else {
|
|
305
|
-
log$
|
|
327
|
+
log$4(
|
|
306
328
|
chalk.red(
|
|
307
329
|
`Error deleting label "${labelName}": ${error instanceof Error ? error.message : "Unknown error"}`
|
|
308
330
|
)
|
|
@@ -323,17 +345,17 @@ const getLabels = async (configs2) => {
|
|
|
323
345
|
const names = await resp.data.map((label) => label.name);
|
|
324
346
|
return names;
|
|
325
347
|
} else {
|
|
326
|
-
log$
|
|
348
|
+
log$4(chalk.red("something wrong"));
|
|
327
349
|
return [];
|
|
328
350
|
}
|
|
329
351
|
};
|
|
330
352
|
const deleteLabels = async (configs2) => {
|
|
331
353
|
const names = await getLabels(configs2);
|
|
332
354
|
if (names.length === 0) {
|
|
333
|
-
log$
|
|
355
|
+
log$4(chalk.yellow("No labels found to delete"));
|
|
334
356
|
return;
|
|
335
357
|
}
|
|
336
|
-
log$
|
|
358
|
+
log$4(chalk.blue(`Deleting ${names.length} labels...`));
|
|
337
359
|
for (const name of names) {
|
|
338
360
|
try {
|
|
339
361
|
const resp = await configs2.octokit.request(
|
|
@@ -345,15 +367,15 @@ const deleteLabels = async (configs2) => {
|
|
|
345
367
|
}
|
|
346
368
|
);
|
|
347
369
|
if (resp.status === 204) {
|
|
348
|
-
log$
|
|
370
|
+
log$4(chalk.green(`${resp.status}: Deleted ${name}`));
|
|
349
371
|
} else {
|
|
350
|
-
log$
|
|
372
|
+
log$4(chalk.yellow(`${resp.status}: Something wrong with ${name}`));
|
|
351
373
|
}
|
|
352
374
|
} catch (error) {
|
|
353
375
|
if (error && typeof error === "object" && "status" in error && error.status === 404) {
|
|
354
|
-
log$
|
|
376
|
+
log$4(chalk.red(`404: Label "${name}" not found`));
|
|
355
377
|
} else {
|
|
356
|
-
log$
|
|
378
|
+
log$4(
|
|
357
379
|
chalk.red(
|
|
358
380
|
`Error deleting label "${name}": ${error instanceof Error ? error.message : "Unknown error"}`
|
|
359
381
|
)
|
|
@@ -361,8 +383,8 @@ const deleteLabels = async (configs2) => {
|
|
|
361
383
|
}
|
|
362
384
|
}
|
|
363
385
|
}
|
|
364
|
-
log$
|
|
365
|
-
log$
|
|
386
|
+
log$4(chalk.blue("Finished deleting labels"));
|
|
387
|
+
log$4(chalk.bgBlueBright(extraGuideText));
|
|
366
388
|
};
|
|
367
389
|
const _CryptoUtils = class _CryptoUtils {
|
|
368
390
|
/**
|
|
@@ -912,9 +934,9 @@ class ConfigManager {
|
|
|
912
934
|
/**
|
|
913
935
|
* Check if file exists
|
|
914
936
|
*/
|
|
915
|
-
async fileExists(
|
|
937
|
+
async fileExists(path2) {
|
|
916
938
|
try {
|
|
917
|
-
await promises.access(
|
|
939
|
+
await promises.access(path2);
|
|
918
940
|
return true;
|
|
919
941
|
} catch {
|
|
920
942
|
return false;
|
|
@@ -956,19 +978,170 @@ const getConfirmation = async () => {
|
|
|
956
978
|
const response = await prompts(holdToken);
|
|
957
979
|
return response.value;
|
|
958
980
|
};
|
|
981
|
+
const log$3 = console.log;
|
|
982
|
+
const generateSampleJson = async () => {
|
|
983
|
+
try {
|
|
984
|
+
const outputPath = "./hyouji.json";
|
|
985
|
+
const jsonContent = JSON.stringify(sampleData, null, 2);
|
|
986
|
+
log$3(chalk.blue("Generating sample JSON file..."));
|
|
987
|
+
fs.writeFileSync(outputPath, jsonContent, "utf8");
|
|
988
|
+
log$3(
|
|
989
|
+
chalk.green(
|
|
990
|
+
"✅ Sample JSON file generated successfully at ./hyouji.json"
|
|
991
|
+
)
|
|
992
|
+
);
|
|
993
|
+
} catch (error) {
|
|
994
|
+
if (error instanceof Error) {
|
|
995
|
+
const nodeError = error;
|
|
996
|
+
if (nodeError.code === "EACCES") {
|
|
997
|
+
log$3(
|
|
998
|
+
chalk.red(
|
|
999
|
+
"❌ Error generating sample JSON file: Permission denied. Please check write permissions for the current directory."
|
|
1000
|
+
)
|
|
1001
|
+
);
|
|
1002
|
+
} else if (nodeError.code === "ENOSPC") {
|
|
1003
|
+
log$3(
|
|
1004
|
+
chalk.red(
|
|
1005
|
+
"❌ Error generating sample JSON file: Insufficient disk space."
|
|
1006
|
+
)
|
|
1007
|
+
);
|
|
1008
|
+
} else if (nodeError.code === "EROFS") {
|
|
1009
|
+
log$3(
|
|
1010
|
+
chalk.red(
|
|
1011
|
+
"❌ Error generating sample JSON file: Read-only file system."
|
|
1012
|
+
)
|
|
1013
|
+
);
|
|
1014
|
+
} else {
|
|
1015
|
+
log$3(
|
|
1016
|
+
chalk.red(`❌ Error generating sample JSON file: ${error.message}`)
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
} else {
|
|
1020
|
+
log$3(
|
|
1021
|
+
chalk.red(
|
|
1022
|
+
"❌ An unexpected error occurred while generating the sample JSON file"
|
|
1023
|
+
)
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
1028
|
+
const log$2 = console.log;
|
|
1029
|
+
const generateSampleYaml = async () => {
|
|
1030
|
+
try {
|
|
1031
|
+
const outputPath = "./hyouji.yaml";
|
|
1032
|
+
const yamlContent = yaml__default.dump(sampleData, {
|
|
1033
|
+
indent: 2,
|
|
1034
|
+
lineWidth: -1,
|
|
1035
|
+
// Disable line wrapping
|
|
1036
|
+
noRefs: true,
|
|
1037
|
+
// Disable references
|
|
1038
|
+
quotingType: '"',
|
|
1039
|
+
// Use double quotes for strings
|
|
1040
|
+
forceQuotes: false
|
|
1041
|
+
// Only quote when necessary
|
|
1042
|
+
});
|
|
1043
|
+
log$2(chalk.blue("Generating sample YAML file..."));
|
|
1044
|
+
fs.writeFileSync(outputPath, yamlContent, "utf8");
|
|
1045
|
+
log$2(
|
|
1046
|
+
chalk.green(
|
|
1047
|
+
"✅ Sample YAML file generated successfully at ./hyouji.yaml"
|
|
1048
|
+
)
|
|
1049
|
+
);
|
|
1050
|
+
} catch (error) {
|
|
1051
|
+
if (error instanceof Error) {
|
|
1052
|
+
const nodeError = error;
|
|
1053
|
+
if (nodeError.code === "EACCES") {
|
|
1054
|
+
log$2(
|
|
1055
|
+
chalk.red(
|
|
1056
|
+
"❌ Error generating sample YAML file: Permission denied. Please check write permissions for the current directory."
|
|
1057
|
+
)
|
|
1058
|
+
);
|
|
1059
|
+
} else if (nodeError.code === "ENOSPC") {
|
|
1060
|
+
log$2(
|
|
1061
|
+
chalk.red(
|
|
1062
|
+
"❌ Error generating sample YAML file: Insufficient disk space."
|
|
1063
|
+
)
|
|
1064
|
+
);
|
|
1065
|
+
} else if (nodeError.code === "EROFS") {
|
|
1066
|
+
log$2(
|
|
1067
|
+
chalk.red(
|
|
1068
|
+
"❌ Error generating sample YAML file: Read-only file system."
|
|
1069
|
+
)
|
|
1070
|
+
);
|
|
1071
|
+
} else {
|
|
1072
|
+
log$2(
|
|
1073
|
+
chalk.red(`❌ Error generating sample YAML file: ${error.message}`)
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
} else {
|
|
1077
|
+
log$2(
|
|
1078
|
+
chalk.red(
|
|
1079
|
+
"❌ An unexpected error occurred while generating the sample YAML file"
|
|
1080
|
+
)
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
const detectFileFormat = (filePath) => {
|
|
1086
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
1087
|
+
switch (extension) {
|
|
1088
|
+
case ".json":
|
|
1089
|
+
return "json";
|
|
1090
|
+
case ".yaml":
|
|
1091
|
+
case ".yml":
|
|
1092
|
+
return "yaml";
|
|
1093
|
+
default:
|
|
1094
|
+
return null;
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
const parseJsonContent = (content) => {
|
|
1098
|
+
return JSON.parse(content);
|
|
1099
|
+
};
|
|
1100
|
+
const parseYamlContent = (content) => {
|
|
1101
|
+
try {
|
|
1102
|
+
return yaml.load(content);
|
|
1103
|
+
} catch (error) {
|
|
1104
|
+
if (error instanceof yaml.YAMLException) {
|
|
1105
|
+
throw new Error(`YAMLException: ${error.message}`);
|
|
1106
|
+
}
|
|
1107
|
+
throw error;
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1110
|
+
const getSupportedExtensions = () => {
|
|
1111
|
+
return [".json", ".yaml", ".yml"];
|
|
1112
|
+
};
|
|
1113
|
+
const formatSupportedExtensions = () => {
|
|
1114
|
+
return getSupportedExtensions().join(", ");
|
|
1115
|
+
};
|
|
959
1116
|
const log$1 = console.log;
|
|
960
|
-
const
|
|
1117
|
+
const importLabelsFromFile = async (configs2, filePath) => {
|
|
961
1118
|
try {
|
|
962
1119
|
if (!fs.existsSync(filePath)) {
|
|
963
1120
|
log$1(chalk.red(`Error: File not found at path: ${filePath}`));
|
|
964
1121
|
return;
|
|
965
1122
|
}
|
|
1123
|
+
const format = detectFileFormat(filePath);
|
|
1124
|
+
if (!format) {
|
|
1125
|
+
log$1(
|
|
1126
|
+
chalk.red(
|
|
1127
|
+
`Error: Unsupported file format. Supported formats: ${formatSupportedExtensions()}`
|
|
1128
|
+
)
|
|
1129
|
+
);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
966
1132
|
const fileContent = fs.readFileSync(filePath, "utf8");
|
|
967
|
-
let
|
|
1133
|
+
let parsedData;
|
|
968
1134
|
try {
|
|
969
|
-
|
|
1135
|
+
if (format === "json") {
|
|
1136
|
+
parsedData = parseJsonContent(fileContent);
|
|
1137
|
+
} else if (format === "yaml") {
|
|
1138
|
+
parsedData = parseYamlContent(fileContent);
|
|
1139
|
+
}
|
|
970
1140
|
} catch (parseError) {
|
|
971
|
-
|
|
1141
|
+
const formatName = format.toUpperCase();
|
|
1142
|
+
log$1(
|
|
1143
|
+
chalk.red(`Error: Invalid ${formatName} syntax in file: ${filePath}`)
|
|
1144
|
+
);
|
|
972
1145
|
log$1(
|
|
973
1146
|
chalk.red(
|
|
974
1147
|
`Parse error: ${parseError instanceof Error ? parseError.message : "Unknown error"}`
|
|
@@ -976,13 +1149,13 @@ const importLabelsFromJson = async (configs2, filePath) => {
|
|
|
976
1149
|
);
|
|
977
1150
|
return;
|
|
978
1151
|
}
|
|
979
|
-
if (!Array.isArray(
|
|
980
|
-
log$1(chalk.red("Error:
|
|
1152
|
+
if (!Array.isArray(parsedData)) {
|
|
1153
|
+
log$1(chalk.red("Error: File must contain an array of label objects"));
|
|
981
1154
|
return;
|
|
982
1155
|
}
|
|
983
1156
|
const validLabels = [];
|
|
984
|
-
for (let i = 0; i <
|
|
985
|
-
const item =
|
|
1157
|
+
for (let i = 0; i < parsedData.length; i++) {
|
|
1158
|
+
const item = parsedData[i];
|
|
986
1159
|
if (typeof item !== "object" || item === null) {
|
|
987
1160
|
log$1(chalk.red(`Error: Item at index ${i} is not a valid object`));
|
|
988
1161
|
continue;
|
|
@@ -1063,7 +1236,7 @@ const importLabelsFromJson = async (configs2, filePath) => {
|
|
|
1063
1236
|
validLabels.push(validLabel);
|
|
1064
1237
|
}
|
|
1065
1238
|
if (validLabels.length === 0) {
|
|
1066
|
-
log$1(chalk.red("Error: No valid labels found in
|
|
1239
|
+
log$1(chalk.red("Error: No valid labels found in file"));
|
|
1067
1240
|
return;
|
|
1068
1241
|
}
|
|
1069
1242
|
log$1(chalk.blue(`Starting import of ${validLabels.length} labels...`));
|
|
@@ -1420,8 +1593,8 @@ const getGitHubConfigs = async () => {
|
|
|
1420
1593
|
detectionMethod: "manual"
|
|
1421
1594
|
};
|
|
1422
1595
|
};
|
|
1423
|
-
const
|
|
1424
|
-
const response = await prompts(
|
|
1596
|
+
const getLabelFilePath = async () => {
|
|
1597
|
+
const response = await prompts(labelFilePath);
|
|
1425
1598
|
return response.filePath;
|
|
1426
1599
|
};
|
|
1427
1600
|
const getNewLabel = async () => {
|
|
@@ -1600,16 +1773,16 @@ const main = async () => {
|
|
|
1600
1773
|
}
|
|
1601
1774
|
case 4: {
|
|
1602
1775
|
try {
|
|
1603
|
-
const filePath = await
|
|
1776
|
+
const filePath = await getLabelFilePath();
|
|
1604
1777
|
if (filePath) {
|
|
1605
|
-
await
|
|
1778
|
+
await importLabelsFromFile(configs, filePath);
|
|
1606
1779
|
} else {
|
|
1607
1780
|
log(chalk.yellow("No file path provided. Returning to main menu."));
|
|
1608
1781
|
}
|
|
1609
1782
|
} catch (error) {
|
|
1610
1783
|
log(
|
|
1611
1784
|
chalk.red(
|
|
1612
|
-
`Error during
|
|
1785
|
+
`Error during label import: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1613
1786
|
)
|
|
1614
1787
|
);
|
|
1615
1788
|
}
|
|
@@ -1617,11 +1790,37 @@ const main = async () => {
|
|
|
1617
1790
|
break;
|
|
1618
1791
|
}
|
|
1619
1792
|
case 5: {
|
|
1620
|
-
|
|
1793
|
+
try {
|
|
1794
|
+
await generateSampleJson();
|
|
1795
|
+
} catch (error) {
|
|
1796
|
+
log(
|
|
1797
|
+
chalk.red(
|
|
1798
|
+
`Error generating sample JSON: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1799
|
+
)
|
|
1800
|
+
);
|
|
1801
|
+
}
|
|
1621
1802
|
firstStart = firstStart && false;
|
|
1622
1803
|
break;
|
|
1623
1804
|
}
|
|
1624
1805
|
case 6: {
|
|
1806
|
+
try {
|
|
1807
|
+
await generateSampleYaml();
|
|
1808
|
+
} catch (error) {
|
|
1809
|
+
log(
|
|
1810
|
+
chalk.red(
|
|
1811
|
+
`Error generating sample YAML: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1812
|
+
)
|
|
1813
|
+
);
|
|
1814
|
+
}
|
|
1815
|
+
firstStart = firstStart && false;
|
|
1816
|
+
break;
|
|
1817
|
+
}
|
|
1818
|
+
case 7: {
|
|
1819
|
+
await displaySettings();
|
|
1820
|
+
firstStart = firstStart && false;
|
|
1821
|
+
break;
|
|
1822
|
+
}
|
|
1823
|
+
case 8: {
|
|
1625
1824
|
console.log("exit");
|
|
1626
1825
|
process.exit(0);
|
|
1627
1826
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hyouji",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "Hyouji (表示) — A command-line tool for organizing and displaying GitHub labels with clarity and harmony.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -20,7 +20,10 @@
|
|
|
20
20
|
"hyouji",
|
|
21
21
|
"cli",
|
|
22
22
|
"表示",
|
|
23
|
-
"Hyouji(表示)"
|
|
23
|
+
"Hyouji(表示)",
|
|
24
|
+
"JSON",
|
|
25
|
+
"yaml",
|
|
26
|
+
"yml"
|
|
24
27
|
],
|
|
25
28
|
"scripts": {
|
|
26
29
|
"start": "node dist/index.js",
|
|
@@ -60,6 +63,7 @@
|
|
|
60
63
|
"prompts": "^2.4.2"
|
|
61
64
|
},
|
|
62
65
|
"devDependencies": {
|
|
66
|
+
"@types/js-yaml": "^4.0.9",
|
|
63
67
|
"@types/node": "^24.0.13",
|
|
64
68
|
"@vitest/coverage-v8": "^3.2.4",
|
|
65
69
|
"@vitest/ui": "^3.2.4",
|