@stackweld/cli 0.2.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/dist/__tests__/commands.test.d.ts +2 -0
- package/dist/__tests__/commands.test.js +275 -0
- package/dist/commands/ai.d.ts +8 -0
- package/dist/commands/ai.js +167 -0
- package/dist/commands/analyze.d.ts +6 -0
- package/dist/commands/analyze.js +90 -0
- package/dist/commands/benchmark.d.ts +6 -0
- package/dist/commands/benchmark.js +86 -0
- package/dist/commands/browse.d.ts +6 -0
- package/dist/commands/browse.js +101 -0
- package/dist/commands/clone.d.ts +3 -0
- package/dist/commands/clone.js +37 -0
- package/dist/commands/compare.d.ts +6 -0
- package/dist/commands/compare.js +93 -0
- package/dist/commands/completion.d.ts +6 -0
- package/dist/commands/completion.js +86 -0
- package/dist/commands/config.d.ts +6 -0
- package/dist/commands/config.js +56 -0
- package/dist/commands/cost.d.ts +6 -0
- package/dist/commands/cost.js +101 -0
- package/dist/commands/create.d.ts +7 -0
- package/dist/commands/create.js +111 -0
- package/dist/commands/delete.d.ts +6 -0
- package/dist/commands/delete.js +33 -0
- package/dist/commands/deploy.d.ts +6 -0
- package/dist/commands/deploy.js +90 -0
- package/dist/commands/doctor.d.ts +6 -0
- package/dist/commands/doctor.js +144 -0
- package/dist/commands/down.d.ts +6 -0
- package/dist/commands/down.js +37 -0
- package/dist/commands/env.d.ts +6 -0
- package/dist/commands/env.js +129 -0
- package/dist/commands/export-stack.d.ts +6 -0
- package/dist/commands/export-stack.js +51 -0
- package/dist/commands/generate.d.ts +9 -0
- package/dist/commands/generate.js +542 -0
- package/dist/commands/health.d.ts +6 -0
- package/dist/commands/health.js +68 -0
- package/dist/commands/import-stack.d.ts +6 -0
- package/dist/commands/import-stack.js +68 -0
- package/dist/commands/info.d.ts +6 -0
- package/dist/commands/info.js +56 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.js +186 -0
- package/dist/commands/learn.d.ts +6 -0
- package/dist/commands/learn.js +91 -0
- package/dist/commands/lint.d.ts +6 -0
- package/dist/commands/lint.js +193 -0
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.js +27 -0
- package/dist/commands/logs.d.ts +6 -0
- package/dist/commands/logs.js +37 -0
- package/dist/commands/migrate.d.ts +6 -0
- package/dist/commands/migrate.js +57 -0
- package/dist/commands/plugin.d.ts +8 -0
- package/dist/commands/plugin.js +131 -0
- package/dist/commands/preview.d.ts +7 -0
- package/dist/commands/preview.js +100 -0
- package/dist/commands/save.d.ts +6 -0
- package/dist/commands/save.js +32 -0
- package/dist/commands/scaffold.d.ts +7 -0
- package/dist/commands/scaffold.js +100 -0
- package/dist/commands/score.d.ts +9 -0
- package/dist/commands/score.js +111 -0
- package/dist/commands/share.d.ts +10 -0
- package/dist/commands/share.js +93 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.js +39 -0
- package/dist/commands/template.d.ts +3 -0
- package/dist/commands/template.js +162 -0
- package/dist/commands/up.d.ts +6 -0
- package/dist/commands/up.js +54 -0
- package/dist/commands/version-cmd.d.ts +6 -0
- package/dist/commands/version-cmd.js +100 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +160 -0
- package/dist/ui/context.d.ts +10 -0
- package/dist/ui/context.js +90 -0
- package/dist/ui/format.d.ts +59 -0
- package/dist/ui/format.js +295 -0
- package/package.json +52 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared CLI context — initializes engine instances once.
|
|
3
|
+
* All initialization is lazy with proper error handling.
|
|
4
|
+
*/
|
|
5
|
+
import { RulesEngine, RuntimeManager, ScaffoldOrchestrator, StackEngine } from "@stackweld/core";
|
|
6
|
+
import { loadAllTechnologies } from "@stackweld/registry";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
let _techs = null;
|
|
9
|
+
let _rulesEngine = null;
|
|
10
|
+
let _stackEngine = null;
|
|
11
|
+
let _scaffoldOrchestrator = null;
|
|
12
|
+
let _runtimeManager = null;
|
|
13
|
+
function getTechs() {
|
|
14
|
+
if (!_techs) {
|
|
15
|
+
try {
|
|
16
|
+
_techs = loadAllTechnologies();
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
console.error(chalk.red("\u2716 Failed to load technology registry."));
|
|
20
|
+
if (err instanceof Error) {
|
|
21
|
+
console.error(chalk.dim(` ${err.message}`));
|
|
22
|
+
}
|
|
23
|
+
console.error(chalk.dim(" Make sure @stackweld/registry is built: pnpm build"));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return _techs;
|
|
28
|
+
}
|
|
29
|
+
export function getRulesEngine() {
|
|
30
|
+
if (!_rulesEngine) {
|
|
31
|
+
try {
|
|
32
|
+
_rulesEngine = new RulesEngine(getTechs());
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
console.error(chalk.red("\u2716 Failed to initialize rules engine."));
|
|
36
|
+
if (err instanceof Error)
|
|
37
|
+
console.error(chalk.dim(` ${err.message}`));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return _rulesEngine;
|
|
42
|
+
}
|
|
43
|
+
export function getStackEngine() {
|
|
44
|
+
if (!_stackEngine) {
|
|
45
|
+
try {
|
|
46
|
+
_stackEngine = new StackEngine(getRulesEngine());
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
console.error(chalk.red("\u2716 Failed to initialize stack engine."));
|
|
50
|
+
if (err instanceof Error) {
|
|
51
|
+
console.error(chalk.dim(` ${err.message}`));
|
|
52
|
+
}
|
|
53
|
+
if (String(err).includes("SQLITE") || String(err).includes("database")) {
|
|
54
|
+
console.error(chalk.dim(" The local database could not be created or opened."));
|
|
55
|
+
console.error(chalk.dim(" Check write permissions in your home directory (~/.stackweld/)."));
|
|
56
|
+
}
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return _stackEngine;
|
|
61
|
+
}
|
|
62
|
+
export function getScaffoldOrchestrator() {
|
|
63
|
+
if (!_scaffoldOrchestrator) {
|
|
64
|
+
try {
|
|
65
|
+
_scaffoldOrchestrator = new ScaffoldOrchestrator(getTechs());
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
console.error(chalk.red("\u2716 Failed to initialize scaffold orchestrator."));
|
|
69
|
+
if (err instanceof Error)
|
|
70
|
+
console.error(chalk.dim(` ${err.message}`));
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return _scaffoldOrchestrator;
|
|
75
|
+
}
|
|
76
|
+
export function getRuntimeManager() {
|
|
77
|
+
if (!_runtimeManager) {
|
|
78
|
+
try {
|
|
79
|
+
_runtimeManager = new RuntimeManager(getTechs());
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
console.error(chalk.red("\u2716 Failed to initialize runtime manager."));
|
|
83
|
+
if (err instanceof Error)
|
|
84
|
+
console.error(chalk.dim(` ${err.message}`));
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return _runtimeManager;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared formatting utilities for CLI output.
|
|
3
|
+
* Premium visual design for Stackweld CLI.
|
|
4
|
+
*/
|
|
5
|
+
import type { StackDefinition, Technology, Template, ValidationResult } from "@stackweld/core";
|
|
6
|
+
declare const ICONS: {
|
|
7
|
+
readonly success: string;
|
|
8
|
+
readonly error: string;
|
|
9
|
+
readonly warning: string;
|
|
10
|
+
readonly info: string;
|
|
11
|
+
readonly bullet: string;
|
|
12
|
+
readonly arrow: string;
|
|
13
|
+
readonly dot: {
|
|
14
|
+
readonly green: string;
|
|
15
|
+
readonly red: string;
|
|
16
|
+
readonly yellow: string;
|
|
17
|
+
readonly blue: string;
|
|
18
|
+
readonly dim: string;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
declare const BRAND_COLOR: import("chalk").ChalkInstance;
|
|
22
|
+
export declare function success(message: string): string;
|
|
23
|
+
export declare function error(message: string): string;
|
|
24
|
+
export declare function warning(message: string): string;
|
|
25
|
+
export declare function info(message: string): string;
|
|
26
|
+
export declare function dim(message: string): string;
|
|
27
|
+
export declare function label(key: string, value: string): string;
|
|
28
|
+
export declare function banner(version: string): string;
|
|
29
|
+
export declare function gradientHeader(text: string): string;
|
|
30
|
+
export declare function sectionHeader(title: string): string;
|
|
31
|
+
export declare function box(content: string, title?: string): string;
|
|
32
|
+
interface TableColumn {
|
|
33
|
+
header: string;
|
|
34
|
+
key: string;
|
|
35
|
+
align?: "left" | "right";
|
|
36
|
+
color?: (v: string) => string;
|
|
37
|
+
}
|
|
38
|
+
export declare function table(data: Record<string, string>[], columns: TableColumn[]): string;
|
|
39
|
+
export declare function progressBar(current: number, total: number, width?: number): string;
|
|
40
|
+
export declare function stepIndicator(current: number, total: number, label: string): string;
|
|
41
|
+
export declare function formatStackRow(stack: StackDefinition): string;
|
|
42
|
+
export declare function formatStackTable(stacks: StackDefinition[]): string;
|
|
43
|
+
export declare function formatStackSummary(stack: StackDefinition): string;
|
|
44
|
+
export declare function formatTechnology(tech: Technology): string;
|
|
45
|
+
export declare function formatTechTable(techs: Technology[]): string;
|
|
46
|
+
export declare function formatTemplate(template: Template): string;
|
|
47
|
+
export declare function formatValidation(result: ValidationResult): string;
|
|
48
|
+
export declare function formatServiceStatus(services: Array<{
|
|
49
|
+
name: string;
|
|
50
|
+
status: string;
|
|
51
|
+
port?: number;
|
|
52
|
+
healthCheck?: string;
|
|
53
|
+
}>): string;
|
|
54
|
+
export declare function formatToolCheck(name: string, found: boolean, version?: string, suggestion?: string): string;
|
|
55
|
+
export declare function formatJson(data: unknown): string;
|
|
56
|
+
export declare function emptyState(message: string, hint?: string): string;
|
|
57
|
+
export declare function nextSteps(steps: string[]): string;
|
|
58
|
+
export { BRAND_COLOR, ICONS };
|
|
59
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared formatting utilities for CLI output.
|
|
3
|
+
* Premium visual design for Stackweld CLI.
|
|
4
|
+
*/
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
// ─── Constants ─────────────────────────────────────────
|
|
7
|
+
const ICONS = {
|
|
8
|
+
success: chalk.green("\u2714"),
|
|
9
|
+
error: chalk.red("\u2716"),
|
|
10
|
+
warning: chalk.yellow("\u26A0"),
|
|
11
|
+
info: chalk.blue("\u2139"),
|
|
12
|
+
bullet: chalk.dim("\u2022"),
|
|
13
|
+
arrow: chalk.dim("\u2192"),
|
|
14
|
+
dot: {
|
|
15
|
+
green: chalk.green("\u25CF"),
|
|
16
|
+
red: chalk.red("\u25CF"),
|
|
17
|
+
yellow: chalk.yellow("\u25CF"),
|
|
18
|
+
blue: chalk.blue("\u25CF"),
|
|
19
|
+
dim: chalk.dim("\u25CB"),
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
const BOX = {
|
|
23
|
+
tl: "\u256D",
|
|
24
|
+
tr: "\u256E",
|
|
25
|
+
bl: "\u2570",
|
|
26
|
+
br: "\u256F",
|
|
27
|
+
h: "\u2500",
|
|
28
|
+
v: "\u2502",
|
|
29
|
+
};
|
|
30
|
+
const BRAND_COLOR = chalk.hex("#7C5CFC");
|
|
31
|
+
// ─── Message Functions ─────────────────────────────────
|
|
32
|
+
export function success(message) {
|
|
33
|
+
return `${ICONS.success} ${chalk.green(message)}`;
|
|
34
|
+
}
|
|
35
|
+
export function error(message) {
|
|
36
|
+
return `${ICONS.error} ${chalk.red(message)}`;
|
|
37
|
+
}
|
|
38
|
+
export function warning(message) {
|
|
39
|
+
return `${ICONS.warning} ${chalk.yellow(message)}`;
|
|
40
|
+
}
|
|
41
|
+
export function info(message) {
|
|
42
|
+
return `${ICONS.info} ${chalk.blue(message)}`;
|
|
43
|
+
}
|
|
44
|
+
export function dim(message) {
|
|
45
|
+
return chalk.dim(message);
|
|
46
|
+
}
|
|
47
|
+
export function label(key, value) {
|
|
48
|
+
return ` ${chalk.dim(`${key}:`)} ${value}`;
|
|
49
|
+
}
|
|
50
|
+
// ─── Banner & Headers ──────────────────────────────────
|
|
51
|
+
export function banner(version) {
|
|
52
|
+
const lines = [
|
|
53
|
+
"",
|
|
54
|
+
BRAND_COLOR.bold(" Stackweld") + chalk.dim(` v${version}`),
|
|
55
|
+
chalk.dim(" The operating system for your dev stacks"),
|
|
56
|
+
"",
|
|
57
|
+
];
|
|
58
|
+
return lines.join("\n");
|
|
59
|
+
}
|
|
60
|
+
export function gradientHeader(text) {
|
|
61
|
+
const colors = ["#7C5CFC", "#6B8AFF", "#5CB8FF", "#4DE4FC"];
|
|
62
|
+
let result = "";
|
|
63
|
+
for (let i = 0; i < text.length; i++) {
|
|
64
|
+
const color = colors[Math.floor((i / text.length) * colors.length)];
|
|
65
|
+
result += chalk.hex(color).bold(text[i]);
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
export function sectionHeader(title) {
|
|
70
|
+
return `\n${chalk.bold(title)}`;
|
|
71
|
+
}
|
|
72
|
+
// ─── Box Drawing ───────────────────────────────────────
|
|
73
|
+
export function box(content, title) {
|
|
74
|
+
const lines = content.split("\n");
|
|
75
|
+
const maxLen = Math.max(...lines.map((l) => stripAnsi(l).length), title ? stripAnsi(title).length + 2 : 0);
|
|
76
|
+
const width = Math.min(Math.max(maxLen + 4, 40), 80);
|
|
77
|
+
const innerWidth = width - 2;
|
|
78
|
+
const hLine = BOX.h.repeat(innerWidth);
|
|
79
|
+
const topLine = title
|
|
80
|
+
? `${BOX.tl}${BOX.h} ${chalk.bold(title)} ${BOX.h.repeat(Math.max(0, innerWidth - stripAnsi(title).length - 3))}${BOX.tr}`
|
|
81
|
+
: `${BOX.tl}${hLine}${BOX.tr}`;
|
|
82
|
+
const paddedLines = lines.map((line) => {
|
|
83
|
+
const visLen = stripAnsi(line).length;
|
|
84
|
+
const padding = Math.max(0, innerWidth - visLen - 2);
|
|
85
|
+
return `${BOX.v} ${line}${" ".repeat(padding)} ${BOX.v}`;
|
|
86
|
+
});
|
|
87
|
+
return [topLine, ...paddedLines, `${BOX.bl}${hLine}${BOX.br}`].join("\n");
|
|
88
|
+
}
|
|
89
|
+
function stripAnsi(str) {
|
|
90
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape sequences require matching the ESC control character
|
|
91
|
+
return str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
92
|
+
}
|
|
93
|
+
export function table(data, columns) {
|
|
94
|
+
if (data.length === 0)
|
|
95
|
+
return "";
|
|
96
|
+
// Compute column widths
|
|
97
|
+
const widths = columns.map((col) => {
|
|
98
|
+
const headerLen = col.header.length;
|
|
99
|
+
const maxDataLen = Math.max(0, ...data.map((row) => (row[col.key] || "").length));
|
|
100
|
+
return Math.max(headerLen, maxDataLen);
|
|
101
|
+
});
|
|
102
|
+
// Header
|
|
103
|
+
const headerLine = columns
|
|
104
|
+
.map((col, i) => chalk.bold(pad(col.header, widths[i], col.align)))
|
|
105
|
+
.join(" ");
|
|
106
|
+
const separator = columns.map((_, i) => chalk.dim("\u2500".repeat(widths[i]))).join(" ");
|
|
107
|
+
// Rows
|
|
108
|
+
const rows = data.map((row) => columns
|
|
109
|
+
.map((col, i) => {
|
|
110
|
+
const value = row[col.key] || "";
|
|
111
|
+
const padded = pad(value, widths[i], col.align);
|
|
112
|
+
return col.color ? col.color(padded) : padded;
|
|
113
|
+
})
|
|
114
|
+
.join(" "));
|
|
115
|
+
return [headerLine, separator, ...rows].join("\n");
|
|
116
|
+
}
|
|
117
|
+
function pad(str, len, align = "left") {
|
|
118
|
+
if (align === "right")
|
|
119
|
+
return str.padStart(len);
|
|
120
|
+
return str.padEnd(len);
|
|
121
|
+
}
|
|
122
|
+
// ─── Progress Indicator ────────────────────────────────
|
|
123
|
+
export function progressBar(current, total, width = 30) {
|
|
124
|
+
const ratio = Math.min(current / total, 1);
|
|
125
|
+
const filled = Math.round(width * ratio);
|
|
126
|
+
const empty = width - filled;
|
|
127
|
+
const bar = chalk.green("\u2588".repeat(filled)) + chalk.dim("\u2591".repeat(empty));
|
|
128
|
+
const pct = Math.round(ratio * 100);
|
|
129
|
+
return `${bar} ${chalk.dim(`${pct}%`)}`;
|
|
130
|
+
}
|
|
131
|
+
export function stepIndicator(current, total, label) {
|
|
132
|
+
return `${chalk.dim(`[${current}/${total}]`)} ${label}`;
|
|
133
|
+
}
|
|
134
|
+
// ─── Stack Formatting ──────────────────────────────────
|
|
135
|
+
export function formatStackRow(stack) {
|
|
136
|
+
const techs = stack.technologies.map((t) => t.technologyId).join(", ");
|
|
137
|
+
return `${chalk.cyan.bold(stack.name)} ${chalk.dim(`(${stack.profile})`)} ${chalk.dim(`v${stack.version}`)}
|
|
138
|
+
${chalk.dim("ID:")} ${stack.id}
|
|
139
|
+
${chalk.dim("Technologies:")} ${techs}
|
|
140
|
+
${chalk.dim("Updated:")} ${stack.updatedAt}`;
|
|
141
|
+
}
|
|
142
|
+
export function formatStackTable(stacks) {
|
|
143
|
+
const data = stacks.map((s) => ({
|
|
144
|
+
name: s.name,
|
|
145
|
+
profile: s.profile,
|
|
146
|
+
techs: s.technologies.map((t) => t.technologyId).join(", "),
|
|
147
|
+
version: `v${s.version}`,
|
|
148
|
+
updated: typeof s.updatedAt === "string" ? s.updatedAt.split("T")[0] : String(s.updatedAt),
|
|
149
|
+
}));
|
|
150
|
+
return table(data, [
|
|
151
|
+
{ header: "Name", key: "name", color: (v) => chalk.cyan(v) },
|
|
152
|
+
{ header: "Profile", key: "profile" },
|
|
153
|
+
{ header: "Technologies", key: "techs", color: (v) => chalk.dim(v) },
|
|
154
|
+
{ header: "Ver", key: "version" },
|
|
155
|
+
{ header: "Updated", key: "updated", color: (v) => chalk.dim(v) },
|
|
156
|
+
]);
|
|
157
|
+
}
|
|
158
|
+
export function formatStackSummary(stack) {
|
|
159
|
+
const techs = stack.technologies.map((t) => t.technologyId).join(", ");
|
|
160
|
+
const content = [
|
|
161
|
+
`${chalk.dim("Name:")} ${chalk.cyan.bold(stack.name)}`,
|
|
162
|
+
`${chalk.dim("Profile:")} ${stack.profile}`,
|
|
163
|
+
`${chalk.dim("Version:")} v${stack.version}`,
|
|
164
|
+
`${chalk.dim("Techs:")} ${techs}`,
|
|
165
|
+
`${chalk.dim("ID:")} ${chalk.dim(stack.id)}`,
|
|
166
|
+
].join("\n");
|
|
167
|
+
return box(content, "Stack Summary");
|
|
168
|
+
}
|
|
169
|
+
export function formatTechnology(tech) {
|
|
170
|
+
const lines = [
|
|
171
|
+
`${chalk.bold.cyan(tech.name)} ${chalk.dim(`(${tech.id})`)}`,
|
|
172
|
+
` ${chalk.dim("Category:")} ${tech.category}`,
|
|
173
|
+
` ${chalk.dim("Description:")} ${tech.description}`,
|
|
174
|
+
` ${chalk.dim("Default version:")} ${tech.defaultVersion}`,
|
|
175
|
+
];
|
|
176
|
+
if (tech.defaultPort) {
|
|
177
|
+
lines.push(` ${chalk.dim("Default port:")} ${tech.defaultPort}`);
|
|
178
|
+
}
|
|
179
|
+
if (tech.requires.length > 0) {
|
|
180
|
+
lines.push(` ${chalk.dim("Requires:")} ${tech.requires.join(", ")}`);
|
|
181
|
+
}
|
|
182
|
+
if (tech.incompatibleWith.length > 0) {
|
|
183
|
+
lines.push(` ${chalk.dim("Incompatible with:")} ${tech.incompatibleWith.join(", ")}`);
|
|
184
|
+
}
|
|
185
|
+
if (tech.suggestedWith.length > 0) {
|
|
186
|
+
lines.push(` ${chalk.dim("Suggested with:")} ${tech.suggestedWith.join(", ")}`);
|
|
187
|
+
}
|
|
188
|
+
if (tech.officialScaffold) {
|
|
189
|
+
lines.push(` ${chalk.dim("Scaffold:")} ${tech.officialScaffold}`);
|
|
190
|
+
}
|
|
191
|
+
if (tech.dockerImage) {
|
|
192
|
+
lines.push(` ${chalk.dim("Docker:")} ${tech.dockerImage}`);
|
|
193
|
+
}
|
|
194
|
+
return lines.join("\n");
|
|
195
|
+
}
|
|
196
|
+
export function formatTechTable(techs) {
|
|
197
|
+
const data = techs.map((t) => ({
|
|
198
|
+
name: t.name,
|
|
199
|
+
id: t.id,
|
|
200
|
+
category: t.category,
|
|
201
|
+
version: t.defaultVersion,
|
|
202
|
+
port: t.defaultPort ? String(t.defaultPort) : "-",
|
|
203
|
+
}));
|
|
204
|
+
return table(data, [
|
|
205
|
+
{ header: "Name", key: "name", color: (v) => chalk.cyan(v) },
|
|
206
|
+
{ header: "ID", key: "id", color: (v) => chalk.dim(v) },
|
|
207
|
+
{ header: "Category", key: "category" },
|
|
208
|
+
{ header: "Version", key: "version" },
|
|
209
|
+
{ header: "Port", key: "port", align: "right" },
|
|
210
|
+
]);
|
|
211
|
+
}
|
|
212
|
+
export function formatTemplate(template) {
|
|
213
|
+
return `${chalk.bold.magenta(template.name)} ${chalk.dim(`(${template.id})`)}
|
|
214
|
+
${chalk.dim("Profile:")} ${template.profile}
|
|
215
|
+
${chalk.dim("Description:")} ${template.description}
|
|
216
|
+
${chalk.dim("Technologies:")} ${template.technologyIds.join(", ")}
|
|
217
|
+
${chalk.dim("Scaffold steps:")} ${template.scaffoldSteps.length}
|
|
218
|
+
${chalk.dim("Hooks:")} ${template.hooks.length}`;
|
|
219
|
+
}
|
|
220
|
+
export function formatValidation(result) {
|
|
221
|
+
const lines = [];
|
|
222
|
+
if (result.valid) {
|
|
223
|
+
lines.push(success("Stack is valid"));
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
lines.push(error("Stack has errors"));
|
|
227
|
+
}
|
|
228
|
+
for (const issue of result.issues) {
|
|
229
|
+
const icon = issue.severity === "error"
|
|
230
|
+
? ICONS.error
|
|
231
|
+
: issue.severity === "warning"
|
|
232
|
+
? ICONS.warning
|
|
233
|
+
: ICONS.info;
|
|
234
|
+
lines.push(` ${icon} ${issue.message}`);
|
|
235
|
+
if (issue.suggestedFix) {
|
|
236
|
+
lines.push(` ${chalk.dim("Fix:")} ${issue.suggestedFix}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (result.resolvedDependencies.length > 0) {
|
|
240
|
+
lines.push(` ${chalk.dim("Auto-resolved:")} ${result.resolvedDependencies.join(", ")}`);
|
|
241
|
+
}
|
|
242
|
+
return lines.join("\n");
|
|
243
|
+
}
|
|
244
|
+
// ─── Service Status ────────────────────────────────────
|
|
245
|
+
export function formatServiceStatus(services) {
|
|
246
|
+
if (services.length === 0)
|
|
247
|
+
return chalk.dim("No services running.");
|
|
248
|
+
const lines = services.map((svc) => {
|
|
249
|
+
const icon = svc.status === "healthy" || svc.status === "running"
|
|
250
|
+
? ICONS.dot.green
|
|
251
|
+
: svc.status === "exited"
|
|
252
|
+
? ICONS.dot.red
|
|
253
|
+
: ICONS.dot.yellow;
|
|
254
|
+
const port = svc.port ? chalk.dim(`:${svc.port}`) : "";
|
|
255
|
+
const health = svc.healthCheck === "passing"
|
|
256
|
+
? chalk.green(" healthy")
|
|
257
|
+
: svc.healthCheck === "failing"
|
|
258
|
+
? chalk.red(" unhealthy")
|
|
259
|
+
: "";
|
|
260
|
+
return ` ${icon} ${chalk.cyan(svc.name)}${port} ${chalk.dim(svc.status)}${health}`;
|
|
261
|
+
});
|
|
262
|
+
return lines.join("\n");
|
|
263
|
+
}
|
|
264
|
+
// ─── Tool Check Status ─────────────────────────────────
|
|
265
|
+
export function formatToolCheck(name, found, version, suggestion) {
|
|
266
|
+
const icon = found ? ICONS.success : ICONS.error;
|
|
267
|
+
const detail = found ? chalk.dim(version || "") : chalk.dim("not found");
|
|
268
|
+
let line = ` ${icon} ${name} ${detail}`;
|
|
269
|
+
if (!found && suggestion) {
|
|
270
|
+
line += `\n ${ICONS.arrow} ${chalk.dim(suggestion)}`;
|
|
271
|
+
}
|
|
272
|
+
return line;
|
|
273
|
+
}
|
|
274
|
+
// ─── Utility ───────────────────────────────────────────
|
|
275
|
+
export function formatJson(data) {
|
|
276
|
+
return JSON.stringify(data, null, 2);
|
|
277
|
+
}
|
|
278
|
+
export function emptyState(message, hint) {
|
|
279
|
+
const lines = [`\n ${chalk.dim(message)}`];
|
|
280
|
+
if (hint) {
|
|
281
|
+
lines.push(` ${ICONS.arrow} ${chalk.dim(hint)}`);
|
|
282
|
+
}
|
|
283
|
+
lines.push("");
|
|
284
|
+
return lines.join("\n");
|
|
285
|
+
}
|
|
286
|
+
export function nextSteps(steps) {
|
|
287
|
+
const lines = [sectionHeader("Next steps:")];
|
|
288
|
+
for (const step of steps) {
|
|
289
|
+
lines.push(` ${ICONS.arrow} ${chalk.dim(step)}`);
|
|
290
|
+
}
|
|
291
|
+
lines.push("");
|
|
292
|
+
return lines.join("\n");
|
|
293
|
+
}
|
|
294
|
+
export { BRAND_COLOR, ICONS };
|
|
295
|
+
//# sourceMappingURL=format.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stackweld/cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "The operating system for your dev stacks — define, validate, scaffold, and launch development environments from the terminal.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Orlando Fernandez <hello@xplustechnologies.com>",
|
|
7
|
+
"homepage": "https://github.com/mundowise/Stackweld",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/mundowise/Stackweld.git",
|
|
11
|
+
"directory": "packages/cli"
|
|
12
|
+
},
|
|
13
|
+
"keywords": ["cli", "devtools", "scaffold", "stack", "docker", "developer-tools", "project-generator"],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"bin": {
|
|
17
|
+
"stackweld": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist/**/*.js",
|
|
21
|
+
"dist/**/*.d.ts",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=22.0.0"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -b",
|
|
30
|
+
"dev": "tsc -b --watch",
|
|
31
|
+
"test": "vitest run --passWithNoTests",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"lint": "biome check src/",
|
|
34
|
+
"clean": "rm -rf dist .turbo"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@anthropic-ai/sdk": "^0.80.0",
|
|
38
|
+
"@inquirer/prompts": "^7.0.0",
|
|
39
|
+
"@stackweld/core": "workspace:*",
|
|
40
|
+
"@stackweld/registry": "workspace:*",
|
|
41
|
+
"@stackweld/templates": "workspace:*",
|
|
42
|
+
"chalk": "^5.4.0",
|
|
43
|
+
"commander": "^13.0.0",
|
|
44
|
+
"ora": "^8.0.0",
|
|
45
|
+
"yaml": "^2.8.3"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@biomejs/biome": "^2.0.0",
|
|
49
|
+
"typescript": "^5.9.0",
|
|
50
|
+
"vitest": "^4.1.2"
|
|
51
|
+
}
|
|
52
|
+
}
|