@trieungoctam/vibekit 1.0.2 → 1.0.5
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/bin/vibekit.js +6 -0
- package/package.json +1 -1
- package/skills/utils/vk:write-skill/render-graphs.js +37 -32
- package/src/commands/init.js +72 -9
- package/src/commands/update.js +124 -0
package/bin/vibekit.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { program } from 'commander';
|
|
3
3
|
import { initCommand } from '../src/commands/init.js';
|
|
4
|
+
import { updateCommand } from '../src/commands/update.js';
|
|
4
5
|
|
|
5
6
|
program
|
|
6
7
|
.name('vibekit')
|
|
@@ -15,4 +16,9 @@ program
|
|
|
15
16
|
.option('--force', 'Overwrite existing files')
|
|
16
17
|
.action(initCommand);
|
|
17
18
|
|
|
19
|
+
program
|
|
20
|
+
.command('update')
|
|
21
|
+
.description('Update VibeKit to latest version')
|
|
22
|
+
.action(updateCommand);
|
|
23
|
+
|
|
18
24
|
program.parse();
|
package/package.json
CHANGED
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
* Requires: graphviz (dot) installed on system
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
const fs = require(
|
|
17
|
-
const path = require(
|
|
18
|
-
const { execSync } = require(
|
|
16
|
+
const fs = require("fs");
|
|
17
|
+
const path = require("path");
|
|
18
|
+
const { execSync } = require("child_process");
|
|
19
19
|
|
|
20
20
|
function extractDotBlocks(markdown) {
|
|
21
21
|
const blocks = [];
|
|
@@ -38,12 +38,12 @@ function extractDotBlocks(markdown) {
|
|
|
38
38
|
function extractGraphBody(dotContent) {
|
|
39
39
|
// Extract just the body (nodes and edges) from a digraph
|
|
40
40
|
const match = dotContent.match(/digraph\s+\w+\s*\{([\s\S]*)\}/);
|
|
41
|
-
if (!match) return
|
|
41
|
+
if (!match) return "";
|
|
42
42
|
|
|
43
43
|
let body = match[1];
|
|
44
44
|
|
|
45
45
|
// Remove rankdir (we'll set it once at the top level)
|
|
46
|
-
body = body.replace(/^\s*rankdir\s*=\s*\w+\s*;?\s*$/gm,
|
|
46
|
+
body = body.replace(/^\s*rankdir\s*=\s*\w+\s*;?\s*$/gm, "");
|
|
47
47
|
|
|
48
48
|
return body.trim();
|
|
49
49
|
}
|
|
@@ -54,7 +54,10 @@ function combineGraphs(blocks, skillName) {
|
|
|
54
54
|
// Wrap each subgraph in a cluster for visual grouping
|
|
55
55
|
return ` subgraph cluster_${i} {
|
|
56
56
|
label="${block.name}";
|
|
57
|
-
${body
|
|
57
|
+
${body
|
|
58
|
+
.split("\n")
|
|
59
|
+
.map((line) => " " + line)
|
|
60
|
+
.join("\n")}
|
|
58
61
|
}`;
|
|
59
62
|
});
|
|
60
63
|
|
|
@@ -63,19 +66,19 @@ function combineGraphs(blocks, skillName) {
|
|
|
63
66
|
compound=true;
|
|
64
67
|
newrank=true;
|
|
65
68
|
|
|
66
|
-
${bodies.join(
|
|
69
|
+
${bodies.join("\n\n")}
|
|
67
70
|
}`;
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
function renderToSvg(dotContent) {
|
|
71
74
|
try {
|
|
72
|
-
return execSync(
|
|
75
|
+
return execSync("dot -Tsvg", {
|
|
73
76
|
input: dotContent,
|
|
74
|
-
encoding:
|
|
75
|
-
maxBuffer: 10 * 1024 * 1024
|
|
77
|
+
encoding: "utf-8",
|
|
78
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
76
79
|
});
|
|
77
80
|
} catch (err) {
|
|
78
|
-
console.error(
|
|
81
|
+
console.error("Error running dot:", err.message);
|
|
79
82
|
if (err.stderr) console.error(err.stderr.toString());
|
|
80
83
|
return null;
|
|
81
84
|
}
|
|
@@ -83,24 +86,24 @@ function renderToSvg(dotContent) {
|
|
|
83
86
|
|
|
84
87
|
function main() {
|
|
85
88
|
const args = process.argv.slice(2);
|
|
86
|
-
const combine = args.includes(
|
|
87
|
-
const skillDirArg = args.find(a => !a.startsWith(
|
|
89
|
+
const combine = args.includes("--combine");
|
|
90
|
+
const skillDirArg = args.find((a) => !a.startsWith("--"));
|
|
88
91
|
|
|
89
92
|
if (!skillDirArg) {
|
|
90
|
-
console.error(
|
|
91
|
-
console.error(
|
|
92
|
-
console.error(
|
|
93
|
-
console.error(
|
|
94
|
-
console.error(
|
|
95
|
-
console.error(
|
|
96
|
-
console.error(
|
|
97
|
-
console.error(
|
|
93
|
+
console.error("Usage: render-graphs.js <skill-directory> [--combine]");
|
|
94
|
+
console.error("");
|
|
95
|
+
console.error("Options:");
|
|
96
|
+
console.error(" --combine Combine all diagrams into one SVG");
|
|
97
|
+
console.error("");
|
|
98
|
+
console.error("Example:");
|
|
99
|
+
console.error(" ./render-graphs.js ../subagent-driven-development");
|
|
100
|
+
console.error(" ./render-graphs.js ../subagent-driven-development --combine");
|
|
98
101
|
process.exit(1);
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
const skillDir = path.resolve(skillDirArg);
|
|
102
|
-
const skillFile = path.join(skillDir,
|
|
103
|
-
const skillName = path.basename(skillDir).replace(/-/g,
|
|
105
|
+
const skillFile = path.join(skillDir, "SKILL.md");
|
|
106
|
+
const skillName = path.basename(skillDir).replace(/-/g, "_");
|
|
104
107
|
|
|
105
108
|
if (!fs.existsSync(skillFile)) {
|
|
106
109
|
console.error(`Error: ${skillFile} not found`);
|
|
@@ -109,25 +112,27 @@ function main() {
|
|
|
109
112
|
|
|
110
113
|
// Check if dot is available
|
|
111
114
|
try {
|
|
112
|
-
execSync(
|
|
115
|
+
execSync("which dot", { encoding: "utf-8" });
|
|
113
116
|
} catch {
|
|
114
|
-
console.error(
|
|
115
|
-
console.error(
|
|
116
|
-
console.error(
|
|
117
|
+
console.error("Error: graphviz (dot) not found. Install with:");
|
|
118
|
+
console.error(" brew install graphviz # macOS");
|
|
119
|
+
console.error(" apt install graphviz # Linux");
|
|
117
120
|
process.exit(1);
|
|
118
121
|
}
|
|
119
122
|
|
|
120
|
-
const markdown = fs.readFileSync(skillFile,
|
|
123
|
+
const markdown = fs.readFileSync(skillFile, "utf-8");
|
|
121
124
|
const blocks = extractDotBlocks(markdown);
|
|
122
125
|
|
|
123
126
|
if (blocks.length === 0) {
|
|
124
|
-
console.log(
|
|
127
|
+
console.log("No ```dot blocks found in", skillFile);
|
|
125
128
|
process.exit(0);
|
|
126
129
|
}
|
|
127
130
|
|
|
128
|
-
console.log(
|
|
131
|
+
console.log(
|
|
132
|
+
`Found ${blocks.length} diagram(s) in ${path.basename(skillDir)}/SKILL.md`,
|
|
133
|
+
);
|
|
129
134
|
|
|
130
|
-
const outputDir = path.join(skillDir,
|
|
135
|
+
const outputDir = path.join(skillDir, "diagrams");
|
|
131
136
|
if (!fs.existsSync(outputDir)) {
|
|
132
137
|
fs.mkdirSync(outputDir);
|
|
133
138
|
}
|
|
@@ -146,7 +151,7 @@ function main() {
|
|
|
146
151
|
fs.writeFileSync(dotPath, combined);
|
|
147
152
|
console.log(` Source: ${skillName}_combined.dot`);
|
|
148
153
|
} else {
|
|
149
|
-
console.error(
|
|
154
|
+
console.error(" Failed to render combined diagram");
|
|
150
155
|
}
|
|
151
156
|
} else {
|
|
152
157
|
// Render each separately
|
package/src/commands/init.js
CHANGED
|
@@ -145,6 +145,22 @@ async function setupClaudeCode(targetDir, force) {
|
|
|
145
145
|
const claudeDir = path.join(targetDir, '.claude');
|
|
146
146
|
fs.ensureDirSync(claudeDir);
|
|
147
147
|
|
|
148
|
+
// Copy skills directly to .claude/skills (for slash commands)
|
|
149
|
+
const skillsDir = path.join(claudeDir, 'skills');
|
|
150
|
+
const srcSkills = path.join(VIBEKIT_ROOT, 'skills');
|
|
151
|
+
if (fs.existsSync(srcSkills)) {
|
|
152
|
+
fs.copySync(srcSkills, skillsDir, { overwrite: force });
|
|
153
|
+
console.log(chalk.gray(' ✓ .claude/skills/'));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Copy agents to .claude/agents
|
|
157
|
+
const agentsDir = path.join(claudeDir, 'agents');
|
|
158
|
+
const srcAgents = path.join(VIBEKIT_ROOT, 'agents');
|
|
159
|
+
if (fs.existsSync(srcAgents)) {
|
|
160
|
+
fs.copySync(srcAgents, agentsDir, { overwrite: force });
|
|
161
|
+
console.log(chalk.gray(' ✓ .claude/agents/'));
|
|
162
|
+
}
|
|
163
|
+
|
|
148
164
|
// CLAUDE.md
|
|
149
165
|
const claudeMd = path.join(targetDir, 'CLAUDE.md');
|
|
150
166
|
if (!fs.existsSync(claudeMd) || force) {
|
|
@@ -156,7 +172,7 @@ This project uses VibeKit for AI assistance.
|
|
|
156
172
|
See \`.vibekit/rules/\` for development rules.
|
|
157
173
|
|
|
158
174
|
## Skills
|
|
159
|
-
See \`.
|
|
175
|
+
See \`.claude/skills/\` for available skills (all with \`vk:\` prefix).
|
|
160
176
|
|
|
161
177
|
## Workflows
|
|
162
178
|
- \`/vk:brainstorm\` - Design & brainstorm
|
|
@@ -214,13 +230,20 @@ async function setupCursor(targetDir, force) {
|
|
|
214
230
|
const cursorDir = path.join(targetDir, '.cursor');
|
|
215
231
|
fs.ensureDirSync(cursorDir);
|
|
216
232
|
|
|
217
|
-
// rules
|
|
233
|
+
// Copy rules directly into .cursor/rules
|
|
218
234
|
const rulesDir = path.join(cursorDir, 'rules');
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
235
|
+
const srcRules = path.join(VIBEKIT_ROOT, 'rules');
|
|
236
|
+
if (fs.existsSync(srcRules)) {
|
|
237
|
+
fs.copySync(srcRules, rulesDir, { overwrite: force });
|
|
238
|
+
console.log(chalk.gray(' ✓ .cursor/rules/'));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Copy skills directly into .cursor/skills
|
|
242
|
+
const skillsDir = path.join(cursorDir, 'skills');
|
|
243
|
+
const srcSkills = path.join(VIBEKIT_ROOT, 'skills');
|
|
244
|
+
if (fs.existsSync(srcSkills)) {
|
|
245
|
+
fs.copySync(srcSkills, skillsDir, { overwrite: force });
|
|
246
|
+
console.log(chalk.gray(' ✓ .cursor/skills/'));
|
|
224
247
|
}
|
|
225
248
|
|
|
226
249
|
await addToGitignore(targetDir, ['.cursor/']);
|
|
@@ -232,6 +255,22 @@ async function setupCodex(targetDir, force) {
|
|
|
232
255
|
const codexDir = path.join(targetDir, '.codex');
|
|
233
256
|
fs.ensureDirSync(codexDir);
|
|
234
257
|
|
|
258
|
+
// Copy agents directly into .codex/agents
|
|
259
|
+
const agentsDir = path.join(codexDir, 'agents');
|
|
260
|
+
const srcAgents = path.join(VIBEKIT_ROOT, 'agents');
|
|
261
|
+
if (fs.existsSync(srcAgents)) {
|
|
262
|
+
fs.copySync(srcAgents, agentsDir, { overwrite: force });
|
|
263
|
+
console.log(chalk.gray(' ✓ .codex/agents/'));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Copy skills directly into .codex/skills
|
|
267
|
+
const skillsDir = path.join(codexDir, 'skills');
|
|
268
|
+
const srcSkills = path.join(VIBEKIT_ROOT, 'skills');
|
|
269
|
+
if (fs.existsSync(srcSkills)) {
|
|
270
|
+
fs.copySync(srcSkills, skillsDir, { overwrite: force });
|
|
271
|
+
console.log(chalk.gray(' ✓ .codex/skills/'));
|
|
272
|
+
}
|
|
273
|
+
|
|
235
274
|
// AGENTS.md
|
|
236
275
|
const agentsMd = path.join(targetDir, 'AGENTS.md');
|
|
237
276
|
if (!fs.existsSync(agentsMd) || force) {
|
|
@@ -240,7 +279,10 @@ async function setupCodex(targetDir, force) {
|
|
|
240
279
|
This project uses VibeKit for AI assistance.
|
|
241
280
|
|
|
242
281
|
## Available Agents
|
|
243
|
-
See \`.
|
|
282
|
+
See \`.codex/agents/\` for agent definitions.
|
|
283
|
+
|
|
284
|
+
## Skills
|
|
285
|
+
See \`.codex/skills/\` for available skills (all with \`vk:\` prefix).
|
|
244
286
|
|
|
245
287
|
`);
|
|
246
288
|
console.log(chalk.gray(' ✓ AGENTS.md'));
|
|
@@ -252,14 +294,35 @@ See \`.vibekit/agents/\` for agent definitions.
|
|
|
252
294
|
async function setupOpenCode(targetDir, force) {
|
|
253
295
|
console.log(chalk.blue('\n🔶 Setting up OpenCode'));
|
|
254
296
|
|
|
297
|
+
const opencodeDir = path.join(targetDir, '.opencode');
|
|
298
|
+
fs.ensureDirSync(opencodeDir);
|
|
299
|
+
|
|
300
|
+
// Copy rules directly into .opencode/rules
|
|
301
|
+
const rulesDir = path.join(opencodeDir, 'rules');
|
|
302
|
+
const srcRules = path.join(VIBEKIT_ROOT, 'rules');
|
|
303
|
+
if (fs.existsSync(srcRules)) {
|
|
304
|
+
fs.copySync(srcRules, rulesDir, { overwrite: force });
|
|
305
|
+
console.log(chalk.gray(' ✓ .opencode/rules/'));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Copy skills directly into .opencode/skills
|
|
309
|
+
const skillsDir = path.join(opencodeDir, 'skills');
|
|
310
|
+
const srcSkills = path.join(VIBEKIT_ROOT, 'skills');
|
|
311
|
+
if (fs.existsSync(srcSkills)) {
|
|
312
|
+
fs.copySync(srcSkills, skillsDir, { overwrite: force });
|
|
313
|
+
console.log(chalk.gray(' ✓ .opencode/skills/'));
|
|
314
|
+
}
|
|
315
|
+
|
|
255
316
|
// opencode.json
|
|
256
317
|
const opencodeJson = path.join(targetDir, 'opencode.json');
|
|
257
318
|
if (!fs.existsSync(opencodeJson) || force) {
|
|
258
319
|
fs.writeJsonSync(opencodeJson, {
|
|
259
|
-
instructions: ['.
|
|
320
|
+
instructions: ['.opencode/rules/*.md', 'CLAUDE.md']
|
|
260
321
|
}, { spaces: 2 });
|
|
261
322
|
console.log(chalk.gray(' ✓ opencode.json'));
|
|
262
323
|
}
|
|
324
|
+
|
|
325
|
+
await addToGitignore(targetDir, ['.opencode/']);
|
|
263
326
|
}
|
|
264
327
|
|
|
265
328
|
async function addToGitignore(targetDir, entries) {
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const VIBEKIT_ROOT = path.resolve(__dirname, '../../');
|
|
10
|
+
|
|
11
|
+
export async function updateCommand(options) {
|
|
12
|
+
const targetDir = process.cwd();
|
|
13
|
+
|
|
14
|
+
console.log(chalk.blue('\n🔄 VibeKit Update\n'));
|
|
15
|
+
|
|
16
|
+
// Check if .vibekit exists
|
|
17
|
+
const vibekitDir = path.join(targetDir, '.vibekit');
|
|
18
|
+
if (!fs.existsSync(vibekitDir)) {
|
|
19
|
+
console.log(chalk.red('VibeKit not initialized. Run `vibekit init` first.'));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log(chalk.gray('Checking for updates...'));
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// Get current version
|
|
27
|
+
const currentPkg = fs.readJsonSync(path.join(VIBEKIT_ROOT, 'package.json'));
|
|
28
|
+
console.log(chalk.gray(`Current version: ${currentPkg.version}`));
|
|
29
|
+
|
|
30
|
+
// Check latest version on npm
|
|
31
|
+
let latestVersion;
|
|
32
|
+
try {
|
|
33
|
+
latestVersion = execSync('npm view @trieungoctam/vibekit version', { encoding: 'utf-8' }).trim();
|
|
34
|
+
console.log(chalk.gray(`Latest version: ${latestVersion}`));
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.log(chalk.yellow('Could not check npm registry, updating from local...'));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Update folders
|
|
40
|
+
console.log(chalk.blue('\n📁 Updating files...'));
|
|
41
|
+
|
|
42
|
+
const folders = ['skills', 'agents', 'hooks', 'rules'];
|
|
43
|
+
for (const folder of folders) {
|
|
44
|
+
const src = path.join(VIBEKIT_ROOT, folder);
|
|
45
|
+
const dst = path.join(vibekitDir, folder);
|
|
46
|
+
if (fs.existsSync(src)) {
|
|
47
|
+
fs.removeSync(dst);
|
|
48
|
+
fs.copySync(src, dst);
|
|
49
|
+
console.log(chalk.gray(` ✓ ${folder}/`));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Update .claude/skills if exists
|
|
54
|
+
const claudeDir = path.join(targetDir, '.claude');
|
|
55
|
+
if (fs.existsSync(claudeDir)) {
|
|
56
|
+
const claudeSkillsDir = path.join(claudeDir, 'skills');
|
|
57
|
+
const claudeAgentsDir = path.join(claudeDir, 'agents');
|
|
58
|
+
|
|
59
|
+
fs.removeSync(claudeSkillsDir);
|
|
60
|
+
fs.copySync(path.join(VIBEKIT_ROOT, 'skills'), claudeSkillsDir);
|
|
61
|
+
console.log(chalk.gray(' ✓ .claude/skills/'));
|
|
62
|
+
|
|
63
|
+
fs.removeSync(claudeAgentsDir);
|
|
64
|
+
fs.copySync(path.join(VIBEKIT_ROOT, 'agents'), claudeAgentsDir);
|
|
65
|
+
console.log(chalk.gray(' ✓ .claude/agents/'));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Update .cursor if exists
|
|
69
|
+
const cursorDir = path.join(targetDir, '.cursor');
|
|
70
|
+
if (fs.existsSync(cursorDir)) {
|
|
71
|
+
const cursorRulesDir = path.join(cursorDir, 'rules');
|
|
72
|
+
const cursorSkillsDir = path.join(cursorDir, 'skills');
|
|
73
|
+
|
|
74
|
+
fs.removeSync(cursorRulesDir);
|
|
75
|
+
fs.copySync(path.join(VIBEKIT_ROOT, 'rules'), cursorRulesDir);
|
|
76
|
+
console.log(chalk.gray(' ✓ .cursor/rules/'));
|
|
77
|
+
|
|
78
|
+
fs.removeSync(cursorSkillsDir);
|
|
79
|
+
fs.copySync(path.join(VIBEKIT_ROOT, 'skills'), cursorSkillsDir);
|
|
80
|
+
console.log(chalk.gray(' ✓ .cursor/skills/'));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Update .codex if exists
|
|
84
|
+
const codexDir = path.join(targetDir, '.codex');
|
|
85
|
+
if (fs.existsSync(codexDir)) {
|
|
86
|
+
const codexAgentsDir = path.join(codexDir, 'agents');
|
|
87
|
+
const codexSkillsDir = path.join(codexDir, 'skills');
|
|
88
|
+
|
|
89
|
+
fs.removeSync(codexAgentsDir);
|
|
90
|
+
fs.copySync(path.join(VIBEKIT_ROOT, 'agents'), codexAgentsDir);
|
|
91
|
+
console.log(chalk.gray(' ✓ .codex/agents/'));
|
|
92
|
+
|
|
93
|
+
fs.removeSync(codexSkillsDir);
|
|
94
|
+
fs.copySync(path.join(VIBEKIT_ROOT, 'skills'), codexSkillsDir);
|
|
95
|
+
console.log(chalk.gray(' ✓ .codex/skills/'));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Update .opencode if exists
|
|
99
|
+
const opencodeDir = path.join(targetDir, '.opencode');
|
|
100
|
+
if (fs.existsSync(opencodeDir)) {
|
|
101
|
+
const opencodeRulesDir = path.join(opencodeDir, 'rules');
|
|
102
|
+
const opencodeSkillsDir = path.join(opencodeDir, 'skills');
|
|
103
|
+
|
|
104
|
+
fs.removeSync(opencodeRulesDir);
|
|
105
|
+
fs.copySync(path.join(VIBEKIT_ROOT, 'rules'), opencodeRulesDir);
|
|
106
|
+
console.log(chalk.gray(' ✓ .opencode/rules/'));
|
|
107
|
+
|
|
108
|
+
fs.removeSync(opencodeSkillsDir);
|
|
109
|
+
fs.copySync(path.join(VIBEKIT_ROOT, 'skills'), opencodeSkillsDir);
|
|
110
|
+
console.log(chalk.gray(' ✓ .opencode/skills/'));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
console.log(chalk.green('\n✅ VibeKit updated!\n'));
|
|
114
|
+
|
|
115
|
+
if (latestVersion && latestVersion !== currentPkg.version) {
|
|
116
|
+
console.log(chalk.yellow(`New version available: ${latestVersion}`));
|
|
117
|
+
console.log(chalk.gray('Run: npm install -g @trieungoctam/vibekit@latest'));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.log(chalk.red(`Update failed: ${error.message}`));
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
}
|