@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.
Files changed (2) hide show
  1. package/dist/index.js +224 -72
  2. 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
- // Read version from package.json at build time; fallback for development
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
- // Show banner when run without arguments
89
- if (process.argv.length <= 2) {
88
+ // ── Interactive Menu ───────────────────────────────────────────────
89
+ async function interactiveMenu() {
90
+ console.clear();
90
91
  console.log(banner(VERSION));
91
- console.log(chalk.bold(" Commands:"));
92
- console.log("");
93
- console.log(chalk.cyan(" Setup"));
94
- console.log(`${chalk.dim(" init ")}Create a new stack interactively`);
95
- console.log(`${chalk.dim(" generate ")}Scaffold a full project (one-shot)`);
96
- console.log(`${chalk.dim(" create ")}Scaffold from a stack or template`);
97
- console.log(`${chalk.dim(" doctor ")}Check system requirements`);
98
- console.log("");
99
- console.log(chalk.cyan(" Stacks"));
100
- console.log(`${chalk.dim(" list ")}List all saved stacks`);
101
- console.log(`${chalk.dim(" info <id> ")}Show stack or technology details`);
102
- console.log(`${chalk.dim(" browse ")}Browse technology catalog`);
103
- console.log(`${chalk.dim(" save ")}Save a version snapshot`);
104
- console.log(`${chalk.dim(" delete ")}Delete a saved stack`);
105
- console.log(`${chalk.dim(" clone ")}Duplicate a stack`);
106
- console.log(`${chalk.dim(" export/import ")}Export or import stack definitions`);
107
- console.log(`${chalk.dim(" share <id> ")}Generate a shareable URL for a stack`);
108
- console.log(`${chalk.dim(" import-url <url> ")}Import a stack from a share URL`);
109
- console.log(`${chalk.dim(" preview <id> ")}Preview docker-compose.yml for a stack`);
110
- console.log("");
111
- console.log(chalk.cyan(" Runtime"));
112
- console.log(`${chalk.dim(" up ")}Start Docker services`);
113
- console.log(`${chalk.dim(" down ")}Stop Docker services`);
114
- console.log(`${chalk.dim(" status ")}Show service status`);
115
- console.log(`${chalk.dim(" logs ")}Show service logs`);
116
- console.log("");
117
- console.log(chalk.cyan(" Analysis"));
118
- console.log(`${chalk.dim(" analyze [path] ")}Detect project stack automatically`);
119
- console.log(`${chalk.dim(" health [path] ")}Check project health and best practices`);
120
- console.log(`${chalk.dim(" compare <a> <b> ")}Compare two saved stacks`);
121
- console.log(`${chalk.dim(" env [sync|check]")} Sync .env files and check for issues`);
122
- console.log(`${chalk.dim(" score <a> [b] ")}Compatibility score between technologies`);
123
- console.log("");
124
- console.log(chalk.cyan(" Deploy & Standards"));
125
- console.log(`${chalk.dim(" deploy <id> ")}Generate infrastructure files (VPS/AWS/GCP)`);
126
- console.log(`${chalk.dim(" lint ")}Validate stack against team standards`);
127
- console.log(`${chalk.dim(" benchmark <id> ")}Show performance profile for a stack`);
128
- console.log(`${chalk.dim(" cost <id> ")}Estimate monthly hosting costs`);
129
- console.log("");
130
- console.log(chalk.cyan(" Plugins"));
131
- console.log(`${chalk.dim(" plugin list ")}List installed plugins`);
132
- console.log(`${chalk.dim(" plugin install ")}Install a plugin from local directory`);
133
- console.log(`${chalk.dim(" plugin remove ")}Remove an installed plugin`);
134
- console.log(`${chalk.dim(" plugin info ")}Show plugin details`);
135
- console.log("");
136
- console.log(chalk.cyan(" Migration & Learning"));
137
- console.log(`${chalk.dim(" migrate ")}Generate a migration plan between techs`);
138
- console.log(`${chalk.dim(" learn <tech> ")}Show learning resources for a technology`);
139
- console.log("");
140
- console.log(chalk.cyan(" Extras"));
141
- console.log(`${chalk.dim(" ai suggest ")}AI-powered stack suggestions`);
142
- console.log(`${chalk.dim(" template ")}Manage templates`);
143
- console.log(`${chalk.dim(" config ")}Manage preferences`);
144
- console.log(`${chalk.dim(" completion ")}Generate shell completions`);
145
- console.log("");
146
- console.log(chalk.dim(` Run ${chalk.white("stackweld <command> --help")} for detailed usage.`));
147
- console.log("");
148
- process.exit(0);
149
- }
150
- program.parseAsync(process.argv).catch((err) => {
151
- if (err && err.code === "commander.unknownCommand") {
152
- const cmd = process.argv[2];
153
- console.error(chalk.red(`\u2716 Unknown command: "${cmd}"`));
154
- console.error(chalk.dim(` Run ${chalk.white("stackweld --help")} to see available commands.`));
155
- process.exit(1);
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
- console.error(chalk.red(`\u2716 ${err instanceof Error ? err.message : String(err)}`));
158
- process.exit(1);
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.0",
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": "workspace:*",
40
- "@stackweld/registry": "workspace:*",
41
- "@stackweld/templates": "workspace:*",
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",