lee-spec-kit 0.1.0 → 0.1.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/dist/index.js +109 -1
- package/package.json +9 -11
package/dist/index.js
CHANGED
|
@@ -38,6 +38,101 @@ function getTemplatesDir() {
|
|
|
38
38
|
return path4.join(rootDir, "templates");
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// src/utils/validation.ts
|
|
42
|
+
var VALID_PROJECT_TYPES = ["single", "fullstack"];
|
|
43
|
+
var VALID_LANGUAGES = ["ko", "en"];
|
|
44
|
+
var VALID_REPO_TYPES = ["be", "fe"];
|
|
45
|
+
function validateSafeName(name) {
|
|
46
|
+
if (!name || name.trim().length === 0) {
|
|
47
|
+
return { valid: false, error: "\uC774\uB984\uC740 \uBE44\uC5B4\uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." };
|
|
48
|
+
}
|
|
49
|
+
if (name.length > 100) {
|
|
50
|
+
return { valid: false, error: "\uC774\uB984\uC740 100\uC790\uB97C \uCD08\uACFC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." };
|
|
51
|
+
}
|
|
52
|
+
if (name.includes("..") || name.includes("/") || name.includes("\\")) {
|
|
53
|
+
return {
|
|
54
|
+
valid: false,
|
|
55
|
+
error: "\uC774\uB984\uC5D0 '..' \uB610\uB294 \uACBD\uB85C \uAD6C\uBD84\uC790\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (name.includes("\0")) {
|
|
59
|
+
return { valid: false, error: "\uC774\uB984\uC5D0 null \uBB38\uC790\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." };
|
|
60
|
+
}
|
|
61
|
+
const safePattern = /^[\w가-힣\-]+$/;
|
|
62
|
+
if (!safePattern.test(name)) {
|
|
63
|
+
return {
|
|
64
|
+
valid: false,
|
|
65
|
+
error: "\uC774\uB984\uC5D0\uB294 \uC601\uBB38, \uC22B\uC790, \uD558\uC774\uD508, \uC5B8\uB354\uC2A4\uCF54\uC5B4, \uD55C\uAE00\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4."
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const reservedNames = [
|
|
69
|
+
".",
|
|
70
|
+
"..",
|
|
71
|
+
"con",
|
|
72
|
+
"prn",
|
|
73
|
+
"aux",
|
|
74
|
+
"nul",
|
|
75
|
+
"com1",
|
|
76
|
+
"com2",
|
|
77
|
+
"com3",
|
|
78
|
+
"com4",
|
|
79
|
+
"lpt1",
|
|
80
|
+
"lpt2",
|
|
81
|
+
"lpt3",
|
|
82
|
+
"lpt4"
|
|
83
|
+
];
|
|
84
|
+
if (reservedNames.includes(name.toLowerCase())) {
|
|
85
|
+
return { valid: false, error: "\uC608\uC57D\uB41C \uC774\uB984\uC740 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." };
|
|
86
|
+
}
|
|
87
|
+
return { valid: true };
|
|
88
|
+
}
|
|
89
|
+
function validateProjectType(type) {
|
|
90
|
+
if (!VALID_PROJECT_TYPES.includes(type)) {
|
|
91
|
+
return {
|
|
92
|
+
valid: false,
|
|
93
|
+
error: `\uD504\uB85C\uC81D\uD2B8 \uD0C0\uC785\uC740 ${VALID_PROJECT_TYPES.join(", ")} \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4.`
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return { valid: true };
|
|
97
|
+
}
|
|
98
|
+
function validateLanguage(lang) {
|
|
99
|
+
if (!VALID_LANGUAGES.includes(lang)) {
|
|
100
|
+
return {
|
|
101
|
+
valid: false,
|
|
102
|
+
error: `\uC5B8\uC5B4\uB294 ${VALID_LANGUAGES.join(", ")} \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4.`
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return { valid: true };
|
|
106
|
+
}
|
|
107
|
+
function validateRepoType(repo) {
|
|
108
|
+
if (!VALID_REPO_TYPES.includes(repo)) {
|
|
109
|
+
return {
|
|
110
|
+
valid: false,
|
|
111
|
+
error: `\uB808\uD3EC\uC9C0\uD1A0\uB9AC \uD0C0\uC785\uC740 ${VALID_REPO_TYPES.join(", ")} \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4.`
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return { valid: true };
|
|
115
|
+
}
|
|
116
|
+
function validateFeatureId(id) {
|
|
117
|
+
if (!id || id.trim().length === 0) {
|
|
118
|
+
return { valid: false, error: "Feature ID\uB294 \uBE44\uC5B4\uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." };
|
|
119
|
+
}
|
|
120
|
+
const featureIdPattern = /^F\d{3,}$/;
|
|
121
|
+
if (!featureIdPattern.test(id)) {
|
|
122
|
+
return {
|
|
123
|
+
valid: false,
|
|
124
|
+
error: "Feature ID\uB294 'F' + \uC22B\uC790 \uD615\uC2DD\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4 (\uC608: F001)."
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return { valid: true };
|
|
128
|
+
}
|
|
129
|
+
function assertValid(result, context) {
|
|
130
|
+
if (!result.valid) {
|
|
131
|
+
const message = context ? `${context}: ${result.error}` : result.error ?? "\uAC80\uC99D \uC2E4\uD328";
|
|
132
|
+
throw new Error(message);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
41
136
|
// src/commands/init.ts
|
|
42
137
|
function initCommand(program2) {
|
|
43
138
|
program2.command("init").description("Initialize project documentation structure").option("-n, --name <name>", "Project name (default: current folder name)").option("-t, --type <type>", "Project type: single | fullstack").option("-l, --lang <lang>", "Language: ko | en (default: ko)").option("-d, --dir <dir>", "Target directory (default: ./docs)", "./docs").option("-y, --yes", "Skip prompts and use defaults").action(async (options) => {
|
|
@@ -111,6 +206,9 @@ async function runInit(options) {
|
|
|
111
206
|
if (!projectType) {
|
|
112
207
|
projectType = "single";
|
|
113
208
|
}
|
|
209
|
+
assertValid(validateSafeName(projectName), "\uD504\uB85C\uC81D\uD2B8 \uC774\uB984");
|
|
210
|
+
assertValid(validateProjectType(projectType), "\uD504\uB85C\uC81D\uD2B8 \uD0C0\uC785");
|
|
211
|
+
assertValid(validateLanguage(lang), "\uC5B8\uC5B4");
|
|
114
212
|
if (await fs5.pathExists(targetDir)) {
|
|
115
213
|
const files = await fs5.readdir(targetDir);
|
|
116
214
|
if (files.length > 0) {
|
|
@@ -203,6 +301,7 @@ async function runFeature(name, options) {
|
|
|
203
301
|
process.exit(1);
|
|
204
302
|
}
|
|
205
303
|
const { docsDir, projectType, lang } = config;
|
|
304
|
+
assertValid(validateSafeName(name), "\uAE30\uB2A5 \uC774\uB984");
|
|
206
305
|
let repo = options.repo;
|
|
207
306
|
if (projectType === "fullstack" && !repo) {
|
|
208
307
|
const response = await prompts(
|
|
@@ -223,7 +322,16 @@ async function runFeature(name, options) {
|
|
|
223
322
|
);
|
|
224
323
|
repo = response.repo;
|
|
225
324
|
}
|
|
226
|
-
|
|
325
|
+
if (repo) {
|
|
326
|
+
assertValid(validateRepoType(repo), "\uB808\uD3EC\uC9C0\uD1A0\uB9AC \uD0C0\uC785");
|
|
327
|
+
}
|
|
328
|
+
let featureId;
|
|
329
|
+
if (options.id) {
|
|
330
|
+
assertValid(validateFeatureId(options.id), "Feature ID");
|
|
331
|
+
featureId = options.id;
|
|
332
|
+
} else {
|
|
333
|
+
featureId = await getNextFeatureId(docsDir, projectType);
|
|
334
|
+
}
|
|
227
335
|
let featuresDir;
|
|
228
336
|
if (projectType === "fullstack" && repo) {
|
|
229
337
|
featuresDir = path4.join(docsDir, "features", repo);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lee-spec-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Project documentation structure generator for AI-assisted development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,13 +12,6 @@
|
|
|
12
12
|
"dist",
|
|
13
13
|
"templates"
|
|
14
14
|
],
|
|
15
|
-
"scripts": {
|
|
16
|
-
"build": "tsup",
|
|
17
|
-
"dev": "tsup --watch",
|
|
18
|
-
"lint": "eslint src",
|
|
19
|
-
"format": "prettier --write .",
|
|
20
|
-
"prepublishOnly": "pnpm build"
|
|
21
|
-
},
|
|
22
15
|
"keywords": [
|
|
23
16
|
"docs",
|
|
24
17
|
"template",
|
|
@@ -35,7 +28,7 @@
|
|
|
35
28
|
},
|
|
36
29
|
"repository": {
|
|
37
30
|
"type": "git",
|
|
38
|
-
"url": "https://github.com/
|
|
31
|
+
"url": "https://github.com/leey00nsu/lee-spec-kit"
|
|
39
32
|
},
|
|
40
33
|
"dependencies": {
|
|
41
34
|
"chalk": "^5.6.2",
|
|
@@ -56,5 +49,10 @@
|
|
|
56
49
|
"tsup": "^8.5.1",
|
|
57
50
|
"typescript": "^5.9.3"
|
|
58
51
|
},
|
|
59
|
-
"
|
|
60
|
-
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"dev": "tsup --watch",
|
|
55
|
+
"lint": "eslint src",
|
|
56
|
+
"format": "prettier --write ."
|
|
57
|
+
}
|
|
58
|
+
}
|