hyouji 0.0.8 → 0.0.10
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 +96 -9
- package/dist/index.js +166 -50
- package/package.json +12 -19
package/README.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
### article
|
|
4
4
|
|
|
5
|
-
https://levelup.gitconnected.com/create-github-labels-from-terminal-158d4868fab
|
|
5
|
+
https://levelup.gitconnected.com/create-github-labels-from-terminal-158d4868fab
|
|
6
|
+
https://github.com/koji/Hyouji/blob/main/blog/article.md
|
|
6
7
|
|
|
7
8
|
[](https://github.com/koji/github-label-manager/actions/workflows/ci.yml)
|
|
8
9
|
[](https://github.com/koji/github-label-manager/actions/workflows/publish.yml)
|
|
@@ -18,7 +19,6 @@ https://levelup.gitconnected.com/create-github-labels-from-terminal-158d4868fab
|
|
|
18
19
|
|
|
19
20
|
https://github.com/user-attachments/assets/739f185a-1bd0-411b-8947-dd4600c452c8
|
|
20
21
|
|
|
21
|
-
|
|
22
22
|
### Labels API
|
|
23
23
|
|
|
24
24
|
https://docs.github.com/en/rest/reference/issues#labels
|
|
@@ -60,7 +60,7 @@ This tool provides the following functionality:
|
|
|
60
60
|
2. Create multiple labels on a specific repo
|
|
61
61
|
3. Delete a single label from a specific repo
|
|
62
62
|
4. Delete all labels from a specific repo
|
|
63
|
-
5. Import labels from JSON file
|
|
63
|
+
5. Import labels from JSON or YAML file
|
|
64
64
|
6. **Display your saved settings** - View your stored GitHub configuration
|
|
65
65
|
7. **Persistent configuration** - Save your GitHub token and username for future use
|
|
66
66
|
|
|
@@ -77,9 +77,7 @@ hyouji
|
|
|
77
77
|
On your first run, you'll be prompted to enter:
|
|
78
78
|
|
|
79
79
|
- **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
|
-
|
|
80
|
+
<img width="792" height="564" alt="github_token" src="https://github.com/user-attachments/assets/e460738f-833a-4158-a8ba-61752beaad72" />
|
|
83
81
|
- **GitHub Username** - Your GitHub account name
|
|
84
82
|
|
|
85
83
|
These credentials will be securely saved and reused for future sessions.
|
|
@@ -90,9 +88,11 @@ These credentials will be securely saved and reused for future sessions.
|
|
|
90
88
|
2. **Create multiple labels on a specific repo**
|
|
91
89
|
3. **Delete a single label from a specific repo**
|
|
92
90
|
4. **Delete all labels from a specific repo**
|
|
93
|
-
5. **Import labels from JSON file**
|
|
94
|
-
6. **
|
|
95
|
-
7. **
|
|
91
|
+
5. **Import labels from JSON or YAML file**
|
|
92
|
+
6. **Generate sample JSON** - Create a sample JSON file with predefined labels
|
|
93
|
+
7. **Generate sample YAML** - Create a sample YAML file with predefined labels
|
|
94
|
+
8. **Display your settings** - View your saved configuration
|
|
95
|
+
9. **Exit**
|
|
96
96
|
|
|
97
97
|
### Settings Management
|
|
98
98
|
|
|
@@ -200,6 +200,93 @@ npm start
|
|
|
200
200
|
|
|
201
201
|
You can use `pnpm`, `yarn` or `bun`.
|
|
202
202
|
|
|
203
|
+
### File Import Support
|
|
204
|
+
|
|
205
|
+
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.
|
|
206
|
+
|
|
207
|
+
#### Supported File Formats
|
|
208
|
+
|
|
209
|
+
- **JSON files** (`.json` extension)
|
|
210
|
+
- **YAML files** (`.yaml` or `.yml` extension)
|
|
211
|
+
|
|
212
|
+
#### Label Structure
|
|
213
|
+
|
|
214
|
+
Both formats support the same label structure:
|
|
215
|
+
|
|
216
|
+
- `name` (required): The label name
|
|
217
|
+
- `color` (optional): Hex color code without the `#` symbol
|
|
218
|
+
- `description` (optional): Label description
|
|
219
|
+
|
|
220
|
+
#### JSON Example
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
[
|
|
224
|
+
{
|
|
225
|
+
"name": "bug",
|
|
226
|
+
"color": "d73a4a",
|
|
227
|
+
"description": "Something isn't working"
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"name": "enhancement",
|
|
231
|
+
"color": "a2eeef",
|
|
232
|
+
"description": "New feature or request"
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"name": "documentation",
|
|
236
|
+
"color": "0075ca",
|
|
237
|
+
"description": "Improvements or additions to documentation"
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
#### YAML Example
|
|
243
|
+
|
|
244
|
+
```yaml
|
|
245
|
+
# Sample YAML file for importing GitHub labels
|
|
246
|
+
- name: 'bug'
|
|
247
|
+
color: 'd73a4a'
|
|
248
|
+
description: "Something isn't working"
|
|
249
|
+
|
|
250
|
+
- name: 'enhancement'
|
|
251
|
+
color: 'a2eeef'
|
|
252
|
+
description: 'New feature or request'
|
|
253
|
+
|
|
254
|
+
- name: 'documentation'
|
|
255
|
+
color: '0075ca'
|
|
256
|
+
description: 'Improvements or additions to documentation'
|
|
257
|
+
|
|
258
|
+
# Labels with minimal configuration (name only)
|
|
259
|
+
- name: 'good first issue'
|
|
260
|
+
color: '7057ff'
|
|
261
|
+
description: 'Good for newcomers'
|
|
262
|
+
|
|
263
|
+
# Labels without description (optional field)
|
|
264
|
+
- name: 'wontfix'
|
|
265
|
+
color: 'ffffff'
|
|
266
|
+
|
|
267
|
+
# Labels without color (will use GitHub default)
|
|
268
|
+
- name: 'question'
|
|
269
|
+
description: 'Further information is requested'
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### Sample Files
|
|
273
|
+
|
|
274
|
+
You can find complete example files in the `examples/` directory:
|
|
275
|
+
|
|
276
|
+
- `examples/labels.json` - Basic label examples in JSON format
|
|
277
|
+
- `examples/labels.yaml` - Basic label examples in YAML format
|
|
278
|
+
- `examples/project-labels.json` - Project management labels in JSON format
|
|
279
|
+
- `examples/project-labels.yaml` - Project management labels in YAML format
|
|
280
|
+
|
|
281
|
+
#### Generate Sample Files
|
|
282
|
+
|
|
283
|
+
The tool can generate sample files for you:
|
|
284
|
+
|
|
285
|
+
- **Generate sample JSON** - Creates `hyouji.json` with predefined labels
|
|
286
|
+
- **Generate sample YAML** - Creates `hyouji.yaml` with predefined labels
|
|
287
|
+
|
|
288
|
+
Both generated files contain the same predefined labels and can be used as starting points for your own label configurations.
|
|
289
|
+
|
|
203
290
|
### Predefined Labels
|
|
204
291
|
|
|
205
292
|
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,10 +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 },
|
|
69
|
+
{ title: "import labels from JSON or YAML", value: 4 },
|
|
67
70
|
{ title: "Generate sample JSON", value: 5 },
|
|
68
|
-
{ title: "
|
|
69
|
-
{ title: "
|
|
71
|
+
{ title: "Generate sample YAML", value: 6 },
|
|
72
|
+
{ title: "Display your settings", value: 7 },
|
|
73
|
+
{ title: "exit", value: 8 }
|
|
70
74
|
]
|
|
71
75
|
};
|
|
72
76
|
const holdToken = {
|
|
@@ -265,7 +269,7 @@ Thank you!
|
|
|
265
269
|
};
|
|
266
270
|
const extraGuideText = `If you don't see action selector, please hit space key.`;
|
|
267
271
|
const linkToPersonalToken = "https://github.com/settings/tokens";
|
|
268
|
-
const log$
|
|
272
|
+
const log$4 = console.log;
|
|
269
273
|
const createLabel = async (configs2, label) => {
|
|
270
274
|
const resp = await configs2.octokit.request(
|
|
271
275
|
"POST /repos/{owner}/{repo}/labels",
|
|
@@ -280,16 +284,16 @@ const createLabel = async (configs2, label) => {
|
|
|
280
284
|
const status = resp.status;
|
|
281
285
|
switch (status) {
|
|
282
286
|
case 201:
|
|
283
|
-
log$
|
|
287
|
+
log$4(chalk.green(`${resp.status}: Created ${label.name}`));
|
|
284
288
|
break;
|
|
285
289
|
case 404:
|
|
286
|
-
log$
|
|
290
|
+
log$4(chalk.red(`${resp.status}: Resource not found`));
|
|
287
291
|
break;
|
|
288
292
|
case 422:
|
|
289
|
-
log$
|
|
293
|
+
log$4(chalk.red(`${resp.status}: Validation failed`));
|
|
290
294
|
break;
|
|
291
295
|
default:
|
|
292
|
-
log$
|
|
296
|
+
log$4(chalk.yellow(`${resp.status}: Something wrong`));
|
|
293
297
|
break;
|
|
294
298
|
}
|
|
295
299
|
};
|
|
@@ -297,8 +301,8 @@ const createLabels = async (configs2) => {
|
|
|
297
301
|
labels.forEach(async (label) => {
|
|
298
302
|
createLabel(configs2, label);
|
|
299
303
|
});
|
|
300
|
-
log$
|
|
301
|
-
log$
|
|
304
|
+
log$4("Created all labels");
|
|
305
|
+
log$4(chalk.bgBlueBright(extraGuideText));
|
|
302
306
|
};
|
|
303
307
|
const deleteLabel = async (configs2, labelNames) => {
|
|
304
308
|
for (const labelName of labelNames) {
|
|
@@ -312,15 +316,15 @@ const deleteLabel = async (configs2, labelNames) => {
|
|
|
312
316
|
}
|
|
313
317
|
);
|
|
314
318
|
if (resp.status === 204) {
|
|
315
|
-
log$
|
|
319
|
+
log$4(chalk.green(`${resp.status}: Deleted ${labelName}`));
|
|
316
320
|
} else {
|
|
317
|
-
log$
|
|
321
|
+
log$4(chalk.yellow(`${resp.status}: Something wrong with ${labelName}`));
|
|
318
322
|
}
|
|
319
323
|
} catch (error) {
|
|
320
324
|
if (error && typeof error === "object" && "status" in error && error.status === 404) {
|
|
321
|
-
log$
|
|
325
|
+
log$4(chalk.red(`404: Label "${labelName}" not found`));
|
|
322
326
|
} else {
|
|
323
|
-
log$
|
|
327
|
+
log$4(
|
|
324
328
|
chalk.red(
|
|
325
329
|
`Error deleting label "${labelName}": ${error instanceof Error ? error.message : "Unknown error"}`
|
|
326
330
|
)
|
|
@@ -341,17 +345,17 @@ const getLabels = async (configs2) => {
|
|
|
341
345
|
const names = await resp.data.map((label) => label.name);
|
|
342
346
|
return names;
|
|
343
347
|
} else {
|
|
344
|
-
log$
|
|
348
|
+
log$4(chalk.red("something wrong"));
|
|
345
349
|
return [];
|
|
346
350
|
}
|
|
347
351
|
};
|
|
348
352
|
const deleteLabels = async (configs2) => {
|
|
349
353
|
const names = await getLabels(configs2);
|
|
350
354
|
if (names.length === 0) {
|
|
351
|
-
log$
|
|
355
|
+
log$4(chalk.yellow("No labels found to delete"));
|
|
352
356
|
return;
|
|
353
357
|
}
|
|
354
|
-
log$
|
|
358
|
+
log$4(chalk.blue(`Deleting ${names.length} labels...`));
|
|
355
359
|
for (const name of names) {
|
|
356
360
|
try {
|
|
357
361
|
const resp = await configs2.octokit.request(
|
|
@@ -363,15 +367,15 @@ const deleteLabels = async (configs2) => {
|
|
|
363
367
|
}
|
|
364
368
|
);
|
|
365
369
|
if (resp.status === 204) {
|
|
366
|
-
log$
|
|
370
|
+
log$4(chalk.green(`${resp.status}: Deleted ${name}`));
|
|
367
371
|
} else {
|
|
368
|
-
log$
|
|
372
|
+
log$4(chalk.yellow(`${resp.status}: Something wrong with ${name}`));
|
|
369
373
|
}
|
|
370
374
|
} catch (error) {
|
|
371
375
|
if (error && typeof error === "object" && "status" in error && error.status === 404) {
|
|
372
|
-
log$
|
|
376
|
+
log$4(chalk.red(`404: Label "${name}" not found`));
|
|
373
377
|
} else {
|
|
374
|
-
log$
|
|
378
|
+
log$4(
|
|
375
379
|
chalk.red(
|
|
376
380
|
`Error deleting label "${name}": ${error instanceof Error ? error.message : "Unknown error"}`
|
|
377
381
|
)
|
|
@@ -379,8 +383,8 @@ const deleteLabels = async (configs2) => {
|
|
|
379
383
|
}
|
|
380
384
|
}
|
|
381
385
|
}
|
|
382
|
-
log$
|
|
383
|
-
log$
|
|
386
|
+
log$4(chalk.blue("Finished deleting labels"));
|
|
387
|
+
log$4(chalk.bgBlueBright(extraGuideText));
|
|
384
388
|
};
|
|
385
389
|
const _CryptoUtils = class _CryptoUtils {
|
|
386
390
|
/**
|
|
@@ -930,9 +934,9 @@ class ConfigManager {
|
|
|
930
934
|
/**
|
|
931
935
|
* Check if file exists
|
|
932
936
|
*/
|
|
933
|
-
async fileExists(
|
|
937
|
+
async fileExists(path2) {
|
|
934
938
|
try {
|
|
935
|
-
await promises.access(
|
|
939
|
+
await promises.access(path2);
|
|
936
940
|
return true;
|
|
937
941
|
} catch {
|
|
938
942
|
return false;
|
|
@@ -974,14 +978,14 @@ const getConfirmation = async () => {
|
|
|
974
978
|
const response = await prompts(holdToken);
|
|
975
979
|
return response.value;
|
|
976
980
|
};
|
|
977
|
-
const log$
|
|
981
|
+
const log$3 = console.log;
|
|
978
982
|
const generateSampleJson = async () => {
|
|
979
983
|
try {
|
|
980
984
|
const outputPath = "./hyouji.json";
|
|
981
985
|
const jsonContent = JSON.stringify(sampleData, null, 2);
|
|
982
|
-
log$
|
|
986
|
+
log$3(chalk.blue("Generating sample JSON file..."));
|
|
983
987
|
fs.writeFileSync(outputPath, jsonContent, "utf8");
|
|
984
|
-
log$
|
|
988
|
+
log$3(
|
|
985
989
|
chalk.green(
|
|
986
990
|
"✅ Sample JSON file generated successfully at ./hyouji.json"
|
|
987
991
|
)
|
|
@@ -990,50 +994,148 @@ const generateSampleJson = async () => {
|
|
|
990
994
|
if (error instanceof Error) {
|
|
991
995
|
const nodeError = error;
|
|
992
996
|
if (nodeError.code === "EACCES") {
|
|
993
|
-
log$
|
|
997
|
+
log$3(
|
|
994
998
|
chalk.red(
|
|
995
999
|
"❌ Error generating sample JSON file: Permission denied. Please check write permissions for the current directory."
|
|
996
1000
|
)
|
|
997
1001
|
);
|
|
998
1002
|
} else if (nodeError.code === "ENOSPC") {
|
|
999
|
-
log$
|
|
1003
|
+
log$3(
|
|
1000
1004
|
chalk.red(
|
|
1001
1005
|
"❌ Error generating sample JSON file: Insufficient disk space."
|
|
1002
1006
|
)
|
|
1003
1007
|
);
|
|
1004
1008
|
} else if (nodeError.code === "EROFS") {
|
|
1005
|
-
log$
|
|
1009
|
+
log$3(
|
|
1006
1010
|
chalk.red(
|
|
1007
1011
|
"❌ Error generating sample JSON file: Read-only file system."
|
|
1008
1012
|
)
|
|
1009
1013
|
);
|
|
1010
1014
|
} else {
|
|
1015
|
+
log$3(chalk.red(`❌ Error generating sample JSON file: ${error.message}`));
|
|
1016
|
+
}
|
|
1017
|
+
} else {
|
|
1018
|
+
log$3(
|
|
1019
|
+
chalk.red(
|
|
1020
|
+
"❌ An unexpected error occurred while generating the sample JSON file"
|
|
1021
|
+
)
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
const log$2 = console.log;
|
|
1027
|
+
const generateSampleYaml = async () => {
|
|
1028
|
+
try {
|
|
1029
|
+
const outputPath = "./hyouji.yaml";
|
|
1030
|
+
const yamlContent = yaml__default.dump(sampleData, {
|
|
1031
|
+
indent: 2,
|
|
1032
|
+
lineWidth: -1,
|
|
1033
|
+
// Disable line wrapping
|
|
1034
|
+
noRefs: true,
|
|
1035
|
+
// Disable references
|
|
1036
|
+
quotingType: '"',
|
|
1037
|
+
// Use double quotes for strings
|
|
1038
|
+
forceQuotes: false
|
|
1039
|
+
// Only quote when necessary
|
|
1040
|
+
});
|
|
1041
|
+
log$2(chalk.blue("Generating sample YAML file..."));
|
|
1042
|
+
fs.writeFileSync(outputPath, yamlContent, "utf8");
|
|
1043
|
+
log$2(
|
|
1044
|
+
chalk.green(
|
|
1045
|
+
"✅ Sample YAML file generated successfully at ./hyouji.yaml"
|
|
1046
|
+
)
|
|
1047
|
+
);
|
|
1048
|
+
} catch (error) {
|
|
1049
|
+
if (error instanceof Error) {
|
|
1050
|
+
const nodeError = error;
|
|
1051
|
+
if (nodeError.code === "EACCES") {
|
|
1052
|
+
log$2(
|
|
1053
|
+
chalk.red(
|
|
1054
|
+
"❌ Error generating sample YAML file: Permission denied. Please check write permissions for the current directory."
|
|
1055
|
+
)
|
|
1056
|
+
);
|
|
1057
|
+
} else if (nodeError.code === "ENOSPC") {
|
|
1058
|
+
log$2(
|
|
1059
|
+
chalk.red(
|
|
1060
|
+
"❌ Error generating sample YAML file: Insufficient disk space."
|
|
1061
|
+
)
|
|
1062
|
+
);
|
|
1063
|
+
} else if (nodeError.code === "EROFS") {
|
|
1011
1064
|
log$2(
|
|
1012
|
-
chalk.red(
|
|
1065
|
+
chalk.red(
|
|
1066
|
+
"❌ Error generating sample YAML file: Read-only file system."
|
|
1067
|
+
)
|
|
1013
1068
|
);
|
|
1069
|
+
} else {
|
|
1070
|
+
log$2(chalk.red(`❌ Error generating sample YAML file: ${error.message}`));
|
|
1014
1071
|
}
|
|
1015
1072
|
} else {
|
|
1016
1073
|
log$2(
|
|
1017
1074
|
chalk.red(
|
|
1018
|
-
"❌ An unexpected error occurred while generating the sample
|
|
1075
|
+
"❌ An unexpected error occurred while generating the sample YAML file"
|
|
1019
1076
|
)
|
|
1020
1077
|
);
|
|
1021
1078
|
}
|
|
1022
1079
|
}
|
|
1023
1080
|
};
|
|
1081
|
+
const detectFileFormat = (filePath) => {
|
|
1082
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
1083
|
+
switch (extension) {
|
|
1084
|
+
case ".json":
|
|
1085
|
+
return "json";
|
|
1086
|
+
case ".yaml":
|
|
1087
|
+
case ".yml":
|
|
1088
|
+
return "yaml";
|
|
1089
|
+
default:
|
|
1090
|
+
return null;
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
const parseJsonContent = (content) => {
|
|
1094
|
+
return JSON.parse(content);
|
|
1095
|
+
};
|
|
1096
|
+
const parseYamlContent = (content) => {
|
|
1097
|
+
try {
|
|
1098
|
+
return yaml.load(content);
|
|
1099
|
+
} catch (error) {
|
|
1100
|
+
if (error instanceof yaml.YAMLException) {
|
|
1101
|
+
throw new Error(`YAMLException: ${error.message}`);
|
|
1102
|
+
}
|
|
1103
|
+
throw error;
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1106
|
+
const getSupportedExtensions = () => {
|
|
1107
|
+
return [".json", ".yaml", ".yml"];
|
|
1108
|
+
};
|
|
1109
|
+
const formatSupportedExtensions = () => {
|
|
1110
|
+
return getSupportedExtensions().join(", ");
|
|
1111
|
+
};
|
|
1024
1112
|
const log$1 = console.log;
|
|
1025
|
-
const
|
|
1113
|
+
const importLabelsFromFile = async (configs2, filePath) => {
|
|
1026
1114
|
try {
|
|
1027
1115
|
if (!fs.existsSync(filePath)) {
|
|
1028
1116
|
log$1(chalk.red(`Error: File not found at path: ${filePath}`));
|
|
1029
1117
|
return;
|
|
1030
1118
|
}
|
|
1119
|
+
const format = detectFileFormat(filePath);
|
|
1120
|
+
if (!format) {
|
|
1121
|
+
log$1(
|
|
1122
|
+
chalk.red(
|
|
1123
|
+
`Error: Unsupported file format. Supported formats: ${formatSupportedExtensions()}`
|
|
1124
|
+
)
|
|
1125
|
+
);
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1031
1128
|
const fileContent = fs.readFileSync(filePath, "utf8");
|
|
1032
|
-
let
|
|
1129
|
+
let parsedData;
|
|
1033
1130
|
try {
|
|
1034
|
-
|
|
1131
|
+
if (format === "json") {
|
|
1132
|
+
parsedData = parseJsonContent(fileContent);
|
|
1133
|
+
} else if (format === "yaml") {
|
|
1134
|
+
parsedData = parseYamlContent(fileContent);
|
|
1135
|
+
}
|
|
1035
1136
|
} catch (parseError) {
|
|
1036
|
-
|
|
1137
|
+
const formatName = format.toUpperCase();
|
|
1138
|
+
log$1(chalk.red(`Error: Invalid ${formatName} syntax in file: ${filePath}`));
|
|
1037
1139
|
log$1(
|
|
1038
1140
|
chalk.red(
|
|
1039
1141
|
`Parse error: ${parseError instanceof Error ? parseError.message : "Unknown error"}`
|
|
@@ -1041,13 +1143,13 @@ const importLabelsFromJson = async (configs2, filePath) => {
|
|
|
1041
1143
|
);
|
|
1042
1144
|
return;
|
|
1043
1145
|
}
|
|
1044
|
-
if (!Array.isArray(
|
|
1045
|
-
log$1(chalk.red("Error:
|
|
1146
|
+
if (!Array.isArray(parsedData)) {
|
|
1147
|
+
log$1(chalk.red("Error: File must contain an array of label objects"));
|
|
1046
1148
|
return;
|
|
1047
1149
|
}
|
|
1048
1150
|
const validLabels = [];
|
|
1049
|
-
for (let i = 0; i <
|
|
1050
|
-
const item =
|
|
1151
|
+
for (let i = 0; i < parsedData.length; i++) {
|
|
1152
|
+
const item = parsedData[i];
|
|
1051
1153
|
if (typeof item !== "object" || item === null) {
|
|
1052
1154
|
log$1(chalk.red(`Error: Item at index ${i} is not a valid object`));
|
|
1053
1155
|
continue;
|
|
@@ -1128,7 +1230,7 @@ const importLabelsFromJson = async (configs2, filePath) => {
|
|
|
1128
1230
|
validLabels.push(validLabel);
|
|
1129
1231
|
}
|
|
1130
1232
|
if (validLabels.length === 0) {
|
|
1131
|
-
log$1(chalk.red("Error: No valid labels found in
|
|
1233
|
+
log$1(chalk.red("Error: No valid labels found in file"));
|
|
1132
1234
|
return;
|
|
1133
1235
|
}
|
|
1134
1236
|
log$1(chalk.blue(`Starting import of ${validLabels.length} labels...`));
|
|
@@ -1485,8 +1587,8 @@ const getGitHubConfigs = async () => {
|
|
|
1485
1587
|
detectionMethod: "manual"
|
|
1486
1588
|
};
|
|
1487
1589
|
};
|
|
1488
|
-
const
|
|
1489
|
-
const response = await prompts(
|
|
1590
|
+
const getLabelFilePath = async () => {
|
|
1591
|
+
const response = await prompts(labelFilePath);
|
|
1490
1592
|
return response.filePath;
|
|
1491
1593
|
};
|
|
1492
1594
|
const getNewLabel = async () => {
|
|
@@ -1665,16 +1767,16 @@ const main = async () => {
|
|
|
1665
1767
|
}
|
|
1666
1768
|
case 4: {
|
|
1667
1769
|
try {
|
|
1668
|
-
const filePath = await
|
|
1770
|
+
const filePath = await getLabelFilePath();
|
|
1669
1771
|
if (filePath) {
|
|
1670
|
-
await
|
|
1772
|
+
await importLabelsFromFile(configs, filePath);
|
|
1671
1773
|
} else {
|
|
1672
1774
|
log(chalk.yellow("No file path provided. Returning to main menu."));
|
|
1673
1775
|
}
|
|
1674
1776
|
} catch (error) {
|
|
1675
1777
|
log(
|
|
1676
1778
|
chalk.red(
|
|
1677
|
-
`Error during
|
|
1779
|
+
`Error during label import: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1678
1780
|
)
|
|
1679
1781
|
);
|
|
1680
1782
|
}
|
|
@@ -1695,13 +1797,27 @@ const main = async () => {
|
|
|
1695
1797
|
break;
|
|
1696
1798
|
}
|
|
1697
1799
|
case 6: {
|
|
1698
|
-
|
|
1800
|
+
try {
|
|
1801
|
+
await generateSampleYaml();
|
|
1802
|
+
} catch (error) {
|
|
1803
|
+
log(
|
|
1804
|
+
chalk.red(
|
|
1805
|
+
`Error generating sample YAML: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1806
|
+
)
|
|
1807
|
+
);
|
|
1808
|
+
}
|
|
1699
1809
|
firstStart = firstStart && false;
|
|
1700
1810
|
break;
|
|
1701
1811
|
}
|
|
1702
1812
|
case 7: {
|
|
1813
|
+
await displaySettings();
|
|
1814
|
+
firstStart = firstStart && false;
|
|
1815
|
+
break;
|
|
1816
|
+
}
|
|
1817
|
+
case 8: {
|
|
1703
1818
|
console.log("exit");
|
|
1704
1819
|
process.exit(0);
|
|
1820
|
+
return;
|
|
1705
1821
|
}
|
|
1706
1822
|
// eslint-disable-next-line no-fallthrough
|
|
1707
1823
|
default: {
|
package/package.json
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hyouji",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
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
|
+
"author": "koji <baxin1919@gmail.com>",
|
|
7
|
+
"homepage": "https://github.com/koji/Hyouji",
|
|
6
8
|
"bin": {
|
|
7
9
|
"hyouji": "dist/index.js"
|
|
8
10
|
},
|
|
@@ -20,21 +22,21 @@
|
|
|
20
22
|
"hyouji",
|
|
21
23
|
"cli",
|
|
22
24
|
"表示",
|
|
23
|
-
"Hyouji(表示)"
|
|
25
|
+
"Hyouji(表示)",
|
|
26
|
+
"JSON",
|
|
27
|
+
"yaml",
|
|
28
|
+
"yml"
|
|
24
29
|
],
|
|
25
30
|
"scripts": {
|
|
26
31
|
"start": "node dist/index.js",
|
|
27
32
|
"build": "vite build",
|
|
28
33
|
"dev": "vite build --watch",
|
|
29
|
-
"
|
|
30
|
-
"fix
|
|
31
|
-
"fix:lint": "eslint src --ext .ts --fix",
|
|
34
|
+
"lint": "biome lint .",
|
|
35
|
+
"lint:fix": "biome lint --write .",
|
|
32
36
|
"test": "vitest run",
|
|
33
37
|
"test:watch": "vitest",
|
|
34
38
|
"test:ui": "vitest --ui",
|
|
35
39
|
"test:coverage": "vitest run --coverage",
|
|
36
|
-
"test:lint": "eslint src --ext .ts",
|
|
37
|
-
"test:prettier": "prettier \"src/**/*.ts\" --list-different",
|
|
38
40
|
"test:error-handling": "node tests/scripts/error-handling/run-all.cjs",
|
|
39
41
|
"test:config": "node tests/scripts/config/run-all.cjs",
|
|
40
42
|
"test:integration": "node tests/scripts/run-integration.cjs",
|
|
@@ -56,25 +58,19 @@
|
|
|
56
58
|
"dependencies": {
|
|
57
59
|
"@octokit/core": "^7.0.3",
|
|
58
60
|
"chalk": "^5.4.1",
|
|
61
|
+
"js-yaml": "^4.1.0",
|
|
59
62
|
"oh-my-logo": "^0.3.0",
|
|
60
63
|
"prompts": "^2.4.2"
|
|
61
64
|
},
|
|
62
65
|
"devDependencies": {
|
|
66
|
+
"@biomejs/biome": "2.2.6",
|
|
67
|
+
"@types/js-yaml": "^4.0.9",
|
|
63
68
|
"@types/node": "^24.0.13",
|
|
64
69
|
"@vitest/coverage-v8": "^3.2.4",
|
|
65
70
|
"@vitest/ui": "^3.2.4",
|
|
66
|
-
"eslint": "^9.30.1",
|
|
67
|
-
"eslint-config-prettier": "^10.1.5",
|
|
68
|
-
"eslint-import-resolver-typescript": "^4.4.4",
|
|
69
|
-
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
70
|
-
"eslint-plugin-import": "^2.32.0",
|
|
71
|
-
"globals": "^16.3.0",
|
|
72
|
-
"npm-run-all": "^4.1.5",
|
|
73
|
-
"prettier": "^3.6.2",
|
|
74
71
|
"standard-version": "^9.5.0",
|
|
75
72
|
"ts-node": "^10.9.2",
|
|
76
73
|
"typescript": "^5.8.3",
|
|
77
|
-
"typescript-eslint": "^8.36.0",
|
|
78
74
|
"vite": "^7.1.2",
|
|
79
75
|
"vitest": "^3.2.4"
|
|
80
76
|
},
|
|
@@ -83,8 +79,5 @@
|
|
|
83
79
|
"README.md",
|
|
84
80
|
"LICENSE"
|
|
85
81
|
],
|
|
86
|
-
"prettier": {
|
|
87
|
-
"singleQuote": true
|
|
88
|
-
},
|
|
89
82
|
"type": "module"
|
|
90
83
|
}
|