@stackweld/cli 0.2.0 → 0.2.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 +224 -72
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import { Command } from "commander";
|
|
7
|
+
import { select, input } from "@inquirer/prompts";
|
|
7
8
|
import { aiCommand } from "./commands/ai.js";
|
|
8
9
|
import { analyzeCommand } from "./commands/analyze.js";
|
|
9
10
|
import { benchmarkCommand } from "./commands/benchmark.js";
|
|
@@ -41,8 +42,7 @@ import { templateCommand } from "./commands/template.js";
|
|
|
41
42
|
import { upCommand } from "./commands/up.js";
|
|
42
43
|
import { versionCommand } from "./commands/version-cmd.js";
|
|
43
44
|
import { banner } from "./ui/format.js";
|
|
44
|
-
|
|
45
|
-
const VERSION = "0.2.0";
|
|
45
|
+
const VERSION = "0.2.1";
|
|
46
46
|
const program = new Command();
|
|
47
47
|
program
|
|
48
48
|
.name("stackweld")
|
|
@@ -85,76 +85,228 @@ program
|
|
|
85
85
|
.addCommand(costCommand)
|
|
86
86
|
.addCommand(pluginCommand)
|
|
87
87
|
.addCommand(versionCommand);
|
|
88
|
-
//
|
|
89
|
-
|
|
88
|
+
// ── Interactive Menu ───────────────────────────────────────────────
|
|
89
|
+
async function interactiveMenu() {
|
|
90
|
+
console.clear();
|
|
90
91
|
console.log(banner(VERSION));
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
92
|
+
while (true) {
|
|
93
|
+
try {
|
|
94
|
+
const action = await select({
|
|
95
|
+
message: chalk.bold("What would you like to do?"),
|
|
96
|
+
choices: [
|
|
97
|
+
{ name: `${chalk.green("🚀")} Create a new project`, value: "create" },
|
|
98
|
+
{ name: `${chalk.cyan("📋")} Browse technologies (83)`, value: "browse" },
|
|
99
|
+
{ name: `${chalk.yellow("📊")} Score compatibility`, value: "score" },
|
|
100
|
+
{ name: `${chalk.blue("🔍")} Analyze an existing project`, value: "analyze" },
|
|
101
|
+
{ name: `${chalk.magenta("🏥")} System health check (doctor)`, value: "doctor" },
|
|
102
|
+
{ name: `${chalk.cyan("📚")} Learn about a technology`, value: "learn" },
|
|
103
|
+
{ name: `${chalk.yellow("🔄")} Migrate between technologies`, value: "migrate" },
|
|
104
|
+
{ name: `${chalk.green("💰")} Estimate hosting costs`, value: "cost-ask" },
|
|
105
|
+
{ name: `${chalk.blue("⚡")} Performance profile`, value: "bench-ask" },
|
|
106
|
+
{ name: `${chalk.cyan("🩺")} Check project health`, value: "health" },
|
|
107
|
+
{ name: `${chalk.yellow("🌐")} Environment sync (.env)`, value: "env" },
|
|
108
|
+
new (await import("@inquirer/prompts")).Separator(),
|
|
109
|
+
{ name: `${chalk.dim("📦")} Manage saved stacks`, value: "stacks" },
|
|
110
|
+
{ name: `${chalk.dim("🐳")} Docker runtime`, value: "docker" },
|
|
111
|
+
{ name: `${chalk.dim("⚙️")} Settings & tools`, value: "settings" },
|
|
112
|
+
new (await import("@inquirer/prompts")).Separator(),
|
|
113
|
+
{ name: `${chalk.red("❌")} Exit`, value: "exit" },
|
|
114
|
+
],
|
|
115
|
+
});
|
|
116
|
+
if (action === "exit") {
|
|
117
|
+
console.log(chalk.dim("\n Goodbye! 👋\n"));
|
|
118
|
+
process.exit(0);
|
|
119
|
+
}
|
|
120
|
+
console.log("");
|
|
121
|
+
switch (action) {
|
|
122
|
+
case "create":
|
|
123
|
+
await program.parseAsync(["node", "stackweld", "init"]);
|
|
124
|
+
break;
|
|
125
|
+
case "browse":
|
|
126
|
+
await program.parseAsync(["node", "stackweld", "browse"]);
|
|
127
|
+
break;
|
|
128
|
+
case "score": {
|
|
129
|
+
const techA = await input({ message: "First technology ID:" });
|
|
130
|
+
const techB = await input({ message: "Second technology ID (or Enter to see best/worst):" });
|
|
131
|
+
const args = ["node", "stackweld", "score", techA];
|
|
132
|
+
if (techB)
|
|
133
|
+
args.push(techB);
|
|
134
|
+
await program.parseAsync(args);
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
case "analyze": {
|
|
138
|
+
const path = await input({ message: "Project path:", default: "." });
|
|
139
|
+
await program.parseAsync(["node", "stackweld", "analyze", path]);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case "doctor":
|
|
143
|
+
await program.parseAsync(["node", "stackweld", "doctor"]);
|
|
144
|
+
break;
|
|
145
|
+
case "learn": {
|
|
146
|
+
const tech = await input({ message: "Technology to learn about:" });
|
|
147
|
+
await program.parseAsync(["node", "stackweld", "learn", tech]);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
case "migrate": {
|
|
151
|
+
const from = await input({ message: "Migrate FROM (technology ID):" });
|
|
152
|
+
const to = await input({ message: "Migrate TO (technology ID):" });
|
|
153
|
+
await program.parseAsync(["node", "stackweld", "migrate", "--from", from, "--to", to]);
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case "cost-ask": {
|
|
157
|
+
const stackId = await input({ message: "Stack ID:" });
|
|
158
|
+
await program.parseAsync(["node", "stackweld", "cost", stackId]);
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
case "bench-ask": {
|
|
162
|
+
const stackId = await input({ message: "Stack ID:" });
|
|
163
|
+
await program.parseAsync(["node", "stackweld", "benchmark", stackId]);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
case "health": {
|
|
167
|
+
const path = await input({ message: "Project path:", default: "." });
|
|
168
|
+
await program.parseAsync(["node", "stackweld", "health", path]);
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
case "env": {
|
|
172
|
+
const subAction = await select({
|
|
173
|
+
message: "Environment action:",
|
|
174
|
+
choices: [
|
|
175
|
+
{ name: "Sync .env.example with .env", value: "sync" },
|
|
176
|
+
{ name: "Check for dangerous values", value: "check" },
|
|
177
|
+
],
|
|
178
|
+
});
|
|
179
|
+
const path = await input({ message: "Project path:", default: "." });
|
|
180
|
+
await program.parseAsync(["node", "stackweld", "env", subAction, "--path", path]);
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
case "stacks": {
|
|
184
|
+
const subAction = await select({
|
|
185
|
+
message: "Stack management:",
|
|
186
|
+
choices: [
|
|
187
|
+
{ name: "📋 List all stacks", value: "list" },
|
|
188
|
+
{ name: "🔍 Stack info", value: "info" },
|
|
189
|
+
{ name: "🗑️ Delete a stack", value: "delete" },
|
|
190
|
+
{ name: "📤 Export a stack", value: "export" },
|
|
191
|
+
{ name: "📥 Import a stack", value: "import" },
|
|
192
|
+
{ name: "🔗 Share via URL", value: "share" },
|
|
193
|
+
{ name: "📑 Compare two stacks", value: "compare" },
|
|
194
|
+
{ name: "📋 Clone a stack", value: "clone" },
|
|
195
|
+
{ name: "⬅️ Back", value: "back" },
|
|
196
|
+
],
|
|
197
|
+
});
|
|
198
|
+
if (subAction === "back")
|
|
199
|
+
break;
|
|
200
|
+
if (subAction === "list") {
|
|
201
|
+
await program.parseAsync(["node", "stackweld", "list"]);
|
|
202
|
+
}
|
|
203
|
+
else if (subAction === "info") {
|
|
204
|
+
const id = await input({ message: "Stack ID:" });
|
|
205
|
+
await program.parseAsync(["node", "stackweld", "info", id]);
|
|
206
|
+
}
|
|
207
|
+
else if (subAction === "delete") {
|
|
208
|
+
const id = await input({ message: "Stack ID to delete:" });
|
|
209
|
+
await program.parseAsync(["node", "stackweld", "delete", id]);
|
|
210
|
+
}
|
|
211
|
+
else if (subAction === "export") {
|
|
212
|
+
const id = await input({ message: "Stack ID:" });
|
|
213
|
+
await program.parseAsync(["node", "stackweld", "export", id]);
|
|
214
|
+
}
|
|
215
|
+
else if (subAction === "import") {
|
|
216
|
+
const file = await input({ message: "File path:" });
|
|
217
|
+
await program.parseAsync(["node", "stackweld", "import", file]);
|
|
218
|
+
}
|
|
219
|
+
else if (subAction === "share") {
|
|
220
|
+
const id = await input({ message: "Stack ID:" });
|
|
221
|
+
await program.parseAsync(["node", "stackweld", "share", id]);
|
|
222
|
+
}
|
|
223
|
+
else if (subAction === "compare") {
|
|
224
|
+
const a = await input({ message: "First stack ID:" });
|
|
225
|
+
const b = await input({ message: "Second stack ID:" });
|
|
226
|
+
await program.parseAsync(["node", "stackweld", "compare", a, b]);
|
|
227
|
+
}
|
|
228
|
+
else if (subAction === "clone") {
|
|
229
|
+
const id = await input({ message: "Stack ID to clone:" });
|
|
230
|
+
await program.parseAsync(["node", "stackweld", "clone", id]);
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
case "docker": {
|
|
235
|
+
const subAction = await select({
|
|
236
|
+
message: "Docker runtime:",
|
|
237
|
+
choices: [
|
|
238
|
+
{ name: "▶️ Start services (up)", value: "up" },
|
|
239
|
+
{ name: "⏹️ Stop services (down)", value: "down" },
|
|
240
|
+
{ name: "📊 Service status", value: "status" },
|
|
241
|
+
{ name: "📜 View logs", value: "logs" },
|
|
242
|
+
{ name: "⬅️ Back", value: "back" },
|
|
243
|
+
],
|
|
244
|
+
});
|
|
245
|
+
if (subAction === "back")
|
|
246
|
+
break;
|
|
247
|
+
await program.parseAsync(["node", "stackweld", subAction]);
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
case "settings": {
|
|
251
|
+
const subAction = await select({
|
|
252
|
+
message: "Settings & tools:",
|
|
253
|
+
choices: [
|
|
254
|
+
{ name: "⚙️ Configuration", value: "config" },
|
|
255
|
+
{ name: "🔌 Plugins", value: "plugin" },
|
|
256
|
+
{ name: "🏗️ Deploy (generate IaC)", value: "deploy-ask" },
|
|
257
|
+
{ name: "✅ Lint (team standards)", value: "lint" },
|
|
258
|
+
{ name: "📝 Templates", value: "template" },
|
|
259
|
+
{ name: "🤖 AI assistant", value: "ai" },
|
|
260
|
+
{ name: "⬅️ Back", value: "back" },
|
|
261
|
+
],
|
|
262
|
+
});
|
|
263
|
+
if (subAction === "back")
|
|
264
|
+
break;
|
|
265
|
+
if (subAction === "deploy-ask") {
|
|
266
|
+
const id = await input({ message: "Stack ID:" });
|
|
267
|
+
const target = await select({
|
|
268
|
+
message: "Deploy target:",
|
|
269
|
+
choices: [
|
|
270
|
+
{ name: "VPS (Docker + nginx)", value: "vps" },
|
|
271
|
+
{ name: "AWS (ECS Fargate)", value: "aws" },
|
|
272
|
+
{ name: "GCP (Cloud Run)", value: "gcp" },
|
|
273
|
+
],
|
|
274
|
+
});
|
|
275
|
+
await program.parseAsync(["node", "stackweld", "deploy", id, "--target", target]);
|
|
276
|
+
}
|
|
277
|
+
else if (subAction === "config") {
|
|
278
|
+
await program.parseAsync(["node", "stackweld", "config", "list"]);
|
|
279
|
+
}
|
|
280
|
+
else if (subAction === "plugin") {
|
|
281
|
+
await program.parseAsync(["node", "stackweld", "plugin", "list"]);
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
await program.parseAsync(["node", "stackweld", subAction]);
|
|
285
|
+
}
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Pause before showing menu again
|
|
290
|
+
console.log("");
|
|
291
|
+
await input({ message: chalk.dim("Press Enter to continue...") });
|
|
292
|
+
console.clear();
|
|
293
|
+
console.log(banner(VERSION));
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
// Handle Ctrl+C gracefully
|
|
297
|
+
if (err && typeof err === "object" && "name" in err && err.name === "ExitPromptError") {
|
|
298
|
+
console.log(chalk.dim("\n Goodbye! 👋\n"));
|
|
299
|
+
process.exit(0);
|
|
300
|
+
}
|
|
301
|
+
throw err;
|
|
302
|
+
}
|
|
156
303
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
304
|
+
}
|
|
305
|
+
// ── Entry Point ───────────────────────────────────────────────────
|
|
306
|
+
if (process.argv.length <= 2) {
|
|
307
|
+
interactiveMenu().catch(() => process.exit(0));
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
program.parse();
|
|
311
|
+
}
|
|
160
312
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackweld/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "The operating system for your dev stacks — define, validate, scaffold, and launch development environments from the terminal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Orlando Fernandez <hello@xplustechnologies.com>",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@anthropic-ai/sdk": "^0.80.0",
|
|
38
38
|
"@inquirer/prompts": "^7.0.0",
|
|
39
|
-
"@stackweld/core": "
|
|
40
|
-
"@stackweld/registry": "
|
|
41
|
-
"@stackweld/templates": "
|
|
39
|
+
"@stackweld/core": "0.2.1",
|
|
40
|
+
"@stackweld/registry": "0.2.1",
|
|
41
|
+
"@stackweld/templates": "0.2.1",
|
|
42
42
|
"chalk": "^5.4.0",
|
|
43
43
|
"commander": "^13.0.0",
|
|
44
44
|
"ora": "^8.0.0",
|