@kurokeita/add-skill 1.7.0 → 1.9.0
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 +2 -2
- package/dist/bin/cli.js +1020 -27
- package/dist/skills/skill-creator/scripts/init_skill.cjs +31 -31
- package/dist/skills/skill-creator/scripts/package_skill.cjs +29 -29
- package/dist/skills/skill-creator/scripts/validate_skill.cjs +41 -41
- package/package.json +11 -4
- package/dist/src/commands/add.js +0 -253
- package/dist/src/commands/import.js +0 -116
- package/dist/src/commands/list.js +0 -92
- package/dist/src/commands/remove.js +0 -118
- package/dist/src/utils/github.js +0 -65
- package/dist/src/utils/paths.js +0 -45
- package/dist/src/utils/toml.js +0 -60
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* node init_skill.cjs my-new-skill --path skills/public
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
const fs = require("node:fs")
|
|
16
|
-
const path = require("node:path")
|
|
15
|
+
const fs = require("node:fs")
|
|
16
|
+
const path = require("node:path")
|
|
17
17
|
|
|
18
18
|
const SKILL_TEMPLATE = `---
|
|
19
19
|
name: {skill_name}
|
|
@@ -100,7 +100,7 @@ Files not intended to be loaded into context, but rather used within the output
|
|
|
100
100
|
---
|
|
101
101
|
|
|
102
102
|
**Any unneeded directories can be deleted.** Not every skill requires all three types of resources.
|
|
103
|
-
|
|
103
|
+
`
|
|
104
104
|
|
|
105
105
|
const EXAMPLE_SCRIPT = `#!/usr/bin/env node
|
|
106
106
|
|
|
@@ -135,7 +135,7 @@ async function main() {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
main();
|
|
138
|
-
|
|
138
|
+
`
|
|
139
139
|
|
|
140
140
|
const EXAMPLE_REFERENCE = `# Reference Documentation for {skill_title}
|
|
141
141
|
|
|
@@ -154,24 +154,24 @@ Replace with actual reference content or delete if not needed.
|
|
|
154
154
|
- Prerequisites
|
|
155
155
|
- Step-by-step instructions
|
|
156
156
|
- Best practices
|
|
157
|
-
|
|
157
|
+
`
|
|
158
158
|
|
|
159
159
|
function titleCase(name) {
|
|
160
160
|
return name
|
|
161
161
|
.split("-")
|
|
162
162
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
163
|
-
.join(" ")
|
|
163
|
+
.join(" ")
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
async function main() {
|
|
167
|
-
const args = process.argv.slice(2)
|
|
167
|
+
const args = process.argv.slice(2)
|
|
168
168
|
if (args.length < 3 || args[1] !== "--path") {
|
|
169
|
-
console.log("Usage: node init_skill.cjs <skill-name> --path <path>")
|
|
170
|
-
process.exit(1)
|
|
169
|
+
console.log("Usage: node init_skill.cjs <skill-name> --path <path>")
|
|
170
|
+
process.exit(1)
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
const skillName = args[0]
|
|
174
|
-
const basePath = path.resolve(args[2])
|
|
173
|
+
const skillName = args[0]
|
|
174
|
+
const basePath = path.resolve(args[2])
|
|
175
175
|
|
|
176
176
|
// Prevent path traversal
|
|
177
177
|
if (
|
|
@@ -179,30 +179,30 @@ async function main() {
|
|
|
179
179
|
skillName.includes("/") ||
|
|
180
180
|
skillName.includes("\\")
|
|
181
181
|
) {
|
|
182
|
-
console.error("❌ Error: Skill name cannot contain path separators.")
|
|
183
|
-
process.exit(1)
|
|
182
|
+
console.error("❌ Error: Skill name cannot contain path separators.")
|
|
183
|
+
process.exit(1)
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
const skillDir = path.join(basePath, skillName)
|
|
186
|
+
const skillDir = path.join(basePath, skillName)
|
|
187
187
|
|
|
188
188
|
// Additional check to ensure the resolved skillDir is actually inside basePath
|
|
189
189
|
if (!skillDir.startsWith(basePath)) {
|
|
190
|
-
console.error("❌ Error: Invalid skill name or path.")
|
|
191
|
-
process.exit(1)
|
|
190
|
+
console.error("❌ Error: Invalid skill name or path.")
|
|
191
|
+
process.exit(1)
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
if (fs.existsSync(skillDir)) {
|
|
195
|
-
console.error(`❌ Error: Skill directory already exists: ${skillDir}`)
|
|
196
|
-
process.exit(1)
|
|
195
|
+
console.error(`❌ Error: Skill directory already exists: ${skillDir}`)
|
|
196
|
+
process.exit(1)
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
const skillTitle = titleCase(skillName)
|
|
199
|
+
const skillTitle = titleCase(skillName)
|
|
200
200
|
|
|
201
201
|
try {
|
|
202
|
-
fs.mkdirSync(skillDir, { recursive: true })
|
|
203
|
-
fs.mkdirSync(path.join(skillDir, "scripts"))
|
|
204
|
-
fs.mkdirSync(path.join(skillDir, "references"))
|
|
205
|
-
fs.mkdirSync(path.join(skillDir, "assets"))
|
|
202
|
+
fs.mkdirSync(skillDir, { recursive: true })
|
|
203
|
+
fs.mkdirSync(path.join(skillDir, "scripts"))
|
|
204
|
+
fs.mkdirSync(path.join(skillDir, "references"))
|
|
205
|
+
fs.mkdirSync(path.join(skillDir, "assets"))
|
|
206
206
|
|
|
207
207
|
fs.writeFileSync(
|
|
208
208
|
path.join(skillDir, "SKILL.md"),
|
|
@@ -210,26 +210,26 @@ async function main() {
|
|
|
210
210
|
/{skill_title}/g,
|
|
211
211
|
skillTitle,
|
|
212
212
|
),
|
|
213
|
-
)
|
|
213
|
+
)
|
|
214
214
|
fs.writeFileSync(
|
|
215
215
|
path.join(skillDir, "scripts/example_script.cjs"),
|
|
216
216
|
EXAMPLE_SCRIPT.replace(/{skill_name}/g, skillName),
|
|
217
217
|
{ mode: 0o755 },
|
|
218
|
-
)
|
|
218
|
+
)
|
|
219
219
|
fs.writeFileSync(
|
|
220
220
|
path.join(skillDir, "references/example_reference.md"),
|
|
221
221
|
EXAMPLE_REFERENCE.replace(/{skill_title}/g, skillTitle),
|
|
222
|
-
)
|
|
222
|
+
)
|
|
223
223
|
fs.writeFileSync(
|
|
224
224
|
path.join(skillDir, "assets/example_asset.txt"),
|
|
225
225
|
"Placeholder for assets.",
|
|
226
|
-
)
|
|
226
|
+
)
|
|
227
227
|
|
|
228
|
-
console.log(`✅ Skill '${skillName}' initialized at ${skillDir}`)
|
|
228
|
+
console.log(`✅ Skill '${skillName}' initialized at ${skillDir}`)
|
|
229
229
|
} catch (err) {
|
|
230
|
-
console.error(`❌ Error: ${err.message}`)
|
|
231
|
-
process.exit(1)
|
|
230
|
+
console.error(`❌ Error: ${err.message}`)
|
|
231
|
+
process.exit(1)
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
main()
|
|
235
|
+
main()
|
|
@@ -9,48 +9,48 @@
|
|
|
9
9
|
* node package_skill.js <path/to/skill-folder> [output-directory]
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
const path = require("node:path")
|
|
13
|
-
const { spawnSync } = require("node:child_process")
|
|
14
|
-
const { validateSkill } = require("./validate_skill.cjs")
|
|
12
|
+
const path = require("node:path")
|
|
13
|
+
const { spawnSync } = require("node:child_process")
|
|
14
|
+
const { validateSkill } = require("./validate_skill.cjs")
|
|
15
15
|
|
|
16
16
|
async function main() {
|
|
17
|
-
const args = process.argv.slice(2)
|
|
17
|
+
const args = process.argv.slice(2)
|
|
18
18
|
if (args.length < 1) {
|
|
19
19
|
console.log(
|
|
20
20
|
"Usage: node package_skill.js <path/to/skill-folder> [output-directory]",
|
|
21
|
-
)
|
|
22
|
-
process.exit(1)
|
|
21
|
+
)
|
|
22
|
+
process.exit(1)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const skillPathArg = args[0]
|
|
26
|
-
const outputDirArg = args[1]
|
|
25
|
+
const skillPathArg = args[0]
|
|
26
|
+
const outputDirArg = args[1]
|
|
27
27
|
|
|
28
28
|
if (skillPathArg.includes("..") || outputDirArg?.includes("..")) {
|
|
29
|
-
console.error("❌ Error: Path traversal detected in arguments.")
|
|
30
|
-
process.exit(1)
|
|
29
|
+
console.error("❌ Error: Path traversal detected in arguments.")
|
|
30
|
+
process.exit(1)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const skillPath = path.resolve(skillPathArg)
|
|
34
|
-
const outputDir = outputDirArg ? path.resolve(outputDirArg) : process.cwd()
|
|
35
|
-
const skillName = path.basename(skillPath)
|
|
33
|
+
const skillPath = path.resolve(skillPathArg)
|
|
34
|
+
const outputDir = outputDirArg ? path.resolve(outputDirArg) : process.cwd()
|
|
35
|
+
const skillName = path.basename(skillPath)
|
|
36
36
|
|
|
37
37
|
// 1. Validate first
|
|
38
|
-
console.log("🔍 Validating skill...")
|
|
39
|
-
const result = validateSkill(skillPath)
|
|
38
|
+
console.log("🔍 Validating skill...")
|
|
39
|
+
const result = validateSkill(skillPath)
|
|
40
40
|
if (!result.valid) {
|
|
41
|
-
console.error(`❌ Validation failed: ${result.message}`)
|
|
42
|
-
process.exit(1)
|
|
41
|
+
console.error(`❌ Validation failed: ${result.message}`)
|
|
42
|
+
process.exit(1)
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
if (result.warning) {
|
|
46
|
-
console.warn(`⚠️ ${result.warning}`)
|
|
47
|
-
console.log("Please resolve all TODOs before packaging.")
|
|
48
|
-
process.exit(1)
|
|
46
|
+
console.warn(`⚠️ ${result.warning}`)
|
|
47
|
+
console.log("Please resolve all TODOs before packaging.")
|
|
48
|
+
process.exit(1)
|
|
49
49
|
}
|
|
50
|
-
console.log("✅ Skill is valid!")
|
|
50
|
+
console.log("✅ Skill is valid!")
|
|
51
51
|
|
|
52
52
|
// 2. Package
|
|
53
|
-
const outputFilename = path.join(outputDir, `${skillName}.skill`)
|
|
53
|
+
const outputFilename = path.join(outputDir, `${skillName}.skill`)
|
|
54
54
|
|
|
55
55
|
try {
|
|
56
56
|
// Zip everything except junk, keeping the folder structure
|
|
@@ -64,21 +64,21 @@ async function main() {
|
|
|
64
64
|
const zipProcess = spawnSync("zip", ["-r", outputFilename, "."], {
|
|
65
65
|
cwd: skillPath,
|
|
66
66
|
stdio: "inherit",
|
|
67
|
-
})
|
|
67
|
+
})
|
|
68
68
|
|
|
69
69
|
if (zipProcess.error) {
|
|
70
|
-
throw zipProcess.error
|
|
70
|
+
throw zipProcess.error
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
if (zipProcess.status !== 0) {
|
|
74
|
-
throw new Error(`zip command failed with exit code ${zipProcess.status}`)
|
|
74
|
+
throw new Error(`zip command failed with exit code ${zipProcess.status}`)
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
console.log(`✅ Successfully packaged skill to: ${outputFilename}`)
|
|
77
|
+
console.log(`✅ Successfully packaged skill to: ${outputFilename}`)
|
|
78
78
|
} catch (err) {
|
|
79
|
-
console.error(`❌ Error packaging: ${err.message}`)
|
|
80
|
-
process.exit(1)
|
|
79
|
+
console.error(`❌ Error packaging: ${err.message}`)
|
|
80
|
+
process.exit(1)
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
main()
|
|
84
|
+
main()
|
|
@@ -5,123 +5,123 @@
|
|
|
5
5
|
* Leveraging existing dependencies when possible or providing a zero-dep fallback.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const fs = require("node:fs")
|
|
9
|
-
const path = require("node:path")
|
|
8
|
+
const fs = require("node:fs")
|
|
9
|
+
const path = require("node:path")
|
|
10
10
|
|
|
11
11
|
function validateSkill(skillPath) {
|
|
12
12
|
if (!fs.existsSync(skillPath) || !fs.statSync(skillPath).isDirectory()) {
|
|
13
|
-
return { valid: false, message: `Path is not a directory: ${skillPath}` }
|
|
13
|
+
return { valid: false, message: `Path is not a directory: ${skillPath}` }
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const skillMdPath = path.join(skillPath, "SKILL.md")
|
|
16
|
+
const skillMdPath = path.join(skillPath, "SKILL.md")
|
|
17
17
|
if (!fs.existsSync(skillMdPath)) {
|
|
18
|
-
return { valid: false, message: "SKILL.md not found" }
|
|
18
|
+
return { valid: false, message: "SKILL.md not found" }
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const content = fs.readFileSync(skillMdPath, "utf8")
|
|
21
|
+
const content = fs.readFileSync(skillMdPath, "utf8")
|
|
22
22
|
if (!content.startsWith("---")) {
|
|
23
|
-
return { valid: false, message: "No YAML frontmatter found" }
|
|
23
|
+
return { valid: false, message: "No YAML frontmatter found" }
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const parts = content.split("---")
|
|
26
|
+
const parts = content.split("---")
|
|
27
27
|
if (parts.length < 3) {
|
|
28
|
-
return { valid: false, message: "Invalid frontmatter format" }
|
|
28
|
+
return { valid: false, message: "Invalid frontmatter format" }
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const frontmatterText = parts[1]
|
|
31
|
+
const frontmatterText = parts[1]
|
|
32
32
|
|
|
33
|
-
const nameMatch = frontmatterText.match(/^name:\s*(.+)$/m)
|
|
33
|
+
const nameMatch = frontmatterText.match(/^name:\s*(.+)$/m)
|
|
34
34
|
// Match description: "text" or description: 'text' or description: text
|
|
35
35
|
const descMatch = frontmatterText.match(
|
|
36
36
|
/^description:\s*(?:'([^']*)'|"([^"]*)"|(.+))$/m,
|
|
37
|
-
)
|
|
37
|
+
)
|
|
38
38
|
|
|
39
39
|
if (!nameMatch)
|
|
40
|
-
return { valid: false, message: 'Missing "name" in frontmatter' }
|
|
40
|
+
return { valid: false, message: 'Missing "name" in frontmatter' }
|
|
41
41
|
if (!descMatch)
|
|
42
42
|
return {
|
|
43
43
|
valid: false,
|
|
44
44
|
message: "Description must be a single-line string: description: ...",
|
|
45
|
-
}
|
|
45
|
+
}
|
|
46
46
|
|
|
47
|
-
const name = nameMatch[1].trim()
|
|
47
|
+
const name = nameMatch[1].trim()
|
|
48
48
|
const description = (
|
|
49
49
|
descMatch[1] !== undefined
|
|
50
50
|
? descMatch[1]
|
|
51
51
|
: descMatch[2] !== undefined
|
|
52
52
|
? descMatch[2]
|
|
53
53
|
: descMatch[3] || ""
|
|
54
|
-
).trim()
|
|
54
|
+
).trim()
|
|
55
55
|
|
|
56
56
|
if (description.includes("\n")) {
|
|
57
57
|
return {
|
|
58
58
|
valid: false,
|
|
59
59
|
message: "Description must be a single line (no newlines)",
|
|
60
|
-
}
|
|
60
|
+
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
64
|
-
return { valid: false, message: `Name "${name}" should be hyphen-case` }
|
|
64
|
+
return { valid: false, message: `Name "${name}" should be hyphen-case` }
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
if (description.length > 1024) {
|
|
68
|
-
return { valid: false, message: "Description is too long (max 1024)" }
|
|
68
|
+
return { valid: false, message: "Description is too long (max 1024)" }
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// Check for TODOs
|
|
72
|
-
const files = getAllFiles(skillPath)
|
|
72
|
+
const files = getAllFiles(skillPath)
|
|
73
73
|
for (const file of files) {
|
|
74
|
-
const fileContent = fs.readFileSync(file, "utf8")
|
|
74
|
+
const fileContent = fs.readFileSync(file, "utf8")
|
|
75
75
|
if (fileContent.includes("TODO:")) {
|
|
76
76
|
return {
|
|
77
77
|
valid: true,
|
|
78
78
|
message: "Skill has unresolved TODOs",
|
|
79
79
|
warning: `Found unresolved TODO in ${path.relative(skillPath, file)}`,
|
|
80
|
-
}
|
|
80
|
+
}
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
return { valid: true, message: "Skill is valid!" }
|
|
84
|
+
return { valid: true, message: "Skill is valid!" }
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function getAllFiles(dir, fileList = []) {
|
|
88
|
-
const files = fs.readdirSync(dir)
|
|
88
|
+
const files = fs.readdirSync(dir)
|
|
89
89
|
files.forEach((file) => {
|
|
90
|
-
const name = path.join(dir, file)
|
|
90
|
+
const name = path.join(dir, file)
|
|
91
91
|
if (fs.statSync(name).isDirectory()) {
|
|
92
92
|
if (!["node_modules", ".git", "__pycache__"].includes(file)) {
|
|
93
|
-
getAllFiles(name, fileList)
|
|
93
|
+
getAllFiles(name, fileList)
|
|
94
94
|
}
|
|
95
95
|
} else {
|
|
96
|
-
fileList.push(name)
|
|
96
|
+
fileList.push(name)
|
|
97
97
|
}
|
|
98
|
-
})
|
|
99
|
-
return fileList
|
|
98
|
+
})
|
|
99
|
+
return fileList
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
if (require.main === module) {
|
|
103
|
-
const args = process.argv.slice(2)
|
|
103
|
+
const args = process.argv.slice(2)
|
|
104
104
|
if (args.length !== 1) {
|
|
105
|
-
console.log("Usage: node validate_skill.js <skill_directory>")
|
|
106
|
-
process.exit(1)
|
|
105
|
+
console.log("Usage: node validate_skill.js <skill_directory>")
|
|
106
|
+
process.exit(1)
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
const skillDirArg = args[0]
|
|
109
|
+
const skillDirArg = args[0]
|
|
110
110
|
if (skillDirArg.includes("..")) {
|
|
111
|
-
console.error("❌ Error: Path traversal detected in skill directory path.")
|
|
112
|
-
process.exit(1)
|
|
111
|
+
console.error("❌ Error: Path traversal detected in skill directory path.")
|
|
112
|
+
process.exit(1)
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
const result = validateSkill(path.resolve(skillDirArg))
|
|
115
|
+
const result = validateSkill(path.resolve(skillDirArg))
|
|
116
116
|
if (result.warning) {
|
|
117
|
-
console.warn(`⚠️ ${result.warning}`)
|
|
117
|
+
console.warn(`⚠️ ${result.warning}`)
|
|
118
118
|
}
|
|
119
119
|
if (result.valid) {
|
|
120
|
-
console.log(`✅ ${result.message}`)
|
|
120
|
+
console.log(`✅ ${result.message}`)
|
|
121
121
|
} else {
|
|
122
|
-
console.error(`❌ ${result.message}`)
|
|
123
|
-
process.exit(1)
|
|
122
|
+
console.error(`❌ ${result.message}`)
|
|
123
|
+
process.exit(1)
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
module.exports = { validateSkill }
|
|
127
|
+
module.exports = { validateSkill }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kurokeita/add-skill",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "CLI to install AI agent skills to various platforms",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,10 +13,12 @@
|
|
|
13
13
|
"scripts": {
|
|
14
14
|
"dev": "tsx bin/cli.ts",
|
|
15
15
|
"update:readme": "tsx scripts/update-readme.ts",
|
|
16
|
-
"build": "tsc && tsx scripts/copy-assets.ts",
|
|
16
|
+
"build": "tsc --noEmit && esbuild bin/cli.ts --bundle --platform=node --format=esm --outfile=dist/bin/cli.js --packages=external && tsx scripts/copy-assets.ts",
|
|
17
17
|
"prepublishOnly": "pnpm run build",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:coverage": "vitest run --coverage",
|
|
18
20
|
"lint": "pnpm run lint:ts && pnpm run lint:md '**/*.md'",
|
|
19
|
-
"lint:ts": "biome check .",
|
|
21
|
+
"lint:ts": "biome check --error-on-warnings .",
|
|
20
22
|
"lint:md": "markdownlint --ignore node_modules '**/*.md'",
|
|
21
23
|
"format": "biome check --write . && pnpm run lint:md --fix",
|
|
22
24
|
"release": "semantic-release"
|
|
@@ -38,17 +40,22 @@
|
|
|
38
40
|
"@clack/prompts": "^0.11.0",
|
|
39
41
|
"commander": "^14.0.2",
|
|
40
42
|
"fs-extra": "^11.3.3",
|
|
43
|
+
"js-yaml": "^4.1.1",
|
|
41
44
|
"picocolors": "^1.1.1"
|
|
42
45
|
},
|
|
43
46
|
"devDependencies": {
|
|
44
47
|
"@biomejs/biome": "^2.3.11",
|
|
45
48
|
"@semantic-release/git": "^10.0.1",
|
|
46
49
|
"@types/fs-extra": "^11.0.4",
|
|
50
|
+
"@types/js-yaml": "^4.0.9",
|
|
47
51
|
"@types/node": "^25.0.9",
|
|
52
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
53
|
+
"esbuild": "^0.25.0",
|
|
48
54
|
"markdownlint-cli": "^0.47.0",
|
|
49
55
|
"semantic-release": "^25.0.2",
|
|
50
56
|
"tsx": "^4.21.0",
|
|
51
|
-
"typescript": "^5.9.3"
|
|
57
|
+
"typescript": "^5.9.3",
|
|
58
|
+
"vitest": "^4.0.18"
|
|
52
59
|
},
|
|
53
60
|
"publishConfig": {
|
|
54
61
|
"access": "public"
|