@magicappdev/cli 0.0.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/bin/cli.js +8 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +38 -0
- package/dist/commands/auth.d.ts +6 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +65 -0
- package/dist/commands/chat.d.ts +6 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +95 -0
- package/dist/commands/doctor.d.ts +6 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +174 -0
- package/dist/commands/generate/index.d.ts +6 -0
- package/dist/commands/generate/index.d.ts.map +1 -0
- package/dist/commands/generate/index.js +110 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +105 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/lib/api.d.ts +5 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/api.js +13 -0
- package/dist/lib/config.d.ts +10 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +40 -0
- package/dist/lib/index.d.ts +7 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +6 -0
- package/dist/lib/prompts.d.ts +29 -0
- package/dist/lib/prompts.d.ts.map +1 -0
- package/dist/lib/prompts.js +116 -0
- package/dist/lib/spinner.d.ts +14 -0
- package/dist/lib/spinner.d.ts.map +1 -0
- package/dist/lib/spinner.js +25 -0
- package/dist/lib/ui.d.ts +35 -0
- package/dist/lib/ui.d.ts.map +1 -0
- package/dist/lib/ui.js +78 -0
- package/package.json +47 -0
package/bin/cli.js
ADDED
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MagicAppDev CLI
|
|
3
|
+
*/
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
/** Create the CLI program */
|
|
6
|
+
export declare function createProgram(): Command;
|
|
7
|
+
/** Run the CLI */
|
|
8
|
+
export declare function run(argv?: string[]): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,6BAA6B;AAC7B,wBAAgB,aAAa,IAAI,OAAO,CAgBvC;AAED,kBAAkB;AAClB,wBAAsB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGxD"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MagicAppDev CLI
|
|
3
|
+
*/
|
|
4
|
+
import { generateCommand } from "./commands/generate/index.js";
|
|
5
|
+
import { doctorCommand } from "./commands/doctor.js";
|
|
6
|
+
import { initCommand } from "./commands/init.js";
|
|
7
|
+
import { authCommand } from "./commands/auth.js";
|
|
8
|
+
import { chatCommand } from "./commands/chat.js";
|
|
9
|
+
import { createRequire } from "module";
|
|
10
|
+
import { Command } from "commander";
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const pkg = require("../package.json");
|
|
13
|
+
/** Package version */
|
|
14
|
+
const VERSION = pkg.version;
|
|
15
|
+
/** Create the CLI program */
|
|
16
|
+
export function createProgram() {
|
|
17
|
+
const program = new Command();
|
|
18
|
+
program
|
|
19
|
+
.name("magicappdev")
|
|
20
|
+
.description("CLI for creating and managing MagicAppDev apps")
|
|
21
|
+
.version(VERSION, "-v, --version", "Display version number");
|
|
22
|
+
// Add commands
|
|
23
|
+
program.addCommand(initCommand);
|
|
24
|
+
program.addCommand(authCommand);
|
|
25
|
+
program.addCommand(chatCommand);
|
|
26
|
+
program.addCommand(generateCommand);
|
|
27
|
+
program.addCommand(doctorCommand);
|
|
28
|
+
return program;
|
|
29
|
+
}
|
|
30
|
+
/** Run the CLI */
|
|
31
|
+
export async function run(argv) {
|
|
32
|
+
const program = createProgram();
|
|
33
|
+
await program.parseAsync(argv || process.argv);
|
|
34
|
+
}
|
|
35
|
+
// Run if executed directly
|
|
36
|
+
if (import.meta.url.endsWith(process.argv[1].replace(/\\/g, "/"))) {
|
|
37
|
+
run().catch(console.error);
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,eAAO,MAAM,WAAW,SAEvB,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth commands - Login and logout
|
|
3
|
+
*/
|
|
4
|
+
import { header, logo, success, error, info, command } from "../lib/ui.js";
|
|
5
|
+
import { saveConfig, loadConfig } from "../lib/config.js";
|
|
6
|
+
import { api } from "../lib/api.js";
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import open from "open";
|
|
9
|
+
import http from "http";
|
|
10
|
+
export const authCommand = new Command("auth").description("Authentication commands");
|
|
11
|
+
authCommand
|
|
12
|
+
.command("login")
|
|
13
|
+
.description("Login to MagicAppDev using GitHub")
|
|
14
|
+
.action(async () => {
|
|
15
|
+
logo();
|
|
16
|
+
header("Authentication");
|
|
17
|
+
info("Opening GitHub login in your browser...");
|
|
18
|
+
// Setup local callback server
|
|
19
|
+
const server = http.createServer(async (req, res) => {
|
|
20
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
21
|
+
const accessToken = url.searchParams.get("accessToken");
|
|
22
|
+
const refreshToken = url.searchParams.get("refreshToken");
|
|
23
|
+
if (accessToken && refreshToken) {
|
|
24
|
+
// Store tokens
|
|
25
|
+
await saveConfig({ accessToken, refreshToken });
|
|
26
|
+
api.setToken(accessToken);
|
|
27
|
+
info(`Access Token received and saved`);
|
|
28
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
29
|
+
res.end("<h1>Login Successful!</h1><p>You can close this window now.</p>");
|
|
30
|
+
success("Successfully logged in!");
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
res.writeHead(400);
|
|
35
|
+
res.end("Login failed: Missing tokens");
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
server.listen(0, async () => {
|
|
39
|
+
const address = server.address();
|
|
40
|
+
const port = address.port;
|
|
41
|
+
const redirectUri = `http://localhost:${port}`;
|
|
42
|
+
const loginUrl = api.getGitHubLoginUrl("mobile") +
|
|
43
|
+
`&redirect_uri=${encodeURIComponent(redirectUri)}`;
|
|
44
|
+
await open(loginUrl);
|
|
45
|
+
info(`If the browser didn't open, visit: ${loginUrl}`);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
authCommand
|
|
49
|
+
.command("whoami")
|
|
50
|
+
.description("Show current user")
|
|
51
|
+
.action(async () => {
|
|
52
|
+
try {
|
|
53
|
+
const config = await loadConfig();
|
|
54
|
+
if (!config.accessToken) {
|
|
55
|
+
throw new Error("No token found");
|
|
56
|
+
}
|
|
57
|
+
api.setToken(config.accessToken);
|
|
58
|
+
const user = await api.getCurrentUser();
|
|
59
|
+
success(`Logged in as: ${user.name} (${user.email})`);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
error("Not logged in or session expired.");
|
|
63
|
+
command("magicappdev auth login");
|
|
64
|
+
}
|
|
65
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,eAAO,MAAM,WAAW,SA8BpB,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat command - Interactive AI App Builder
|
|
3
|
+
*/
|
|
4
|
+
import { header, logo, info } from "../lib/ui.js";
|
|
5
|
+
import { AgentClient } from "agents/client";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { AGENT_HOST } from "../lib/api.js";
|
|
8
|
+
import prompts from "prompts";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import ora from "ora";
|
|
11
|
+
export const chatCommand = new Command("chat")
|
|
12
|
+
.description("Chat with the Magic AI App Builder")
|
|
13
|
+
.action(async () => {
|
|
14
|
+
logo();
|
|
15
|
+
header("Magic AI Assistant");
|
|
16
|
+
info("Connecting to agent...");
|
|
17
|
+
const spinner = ora("Initializing connection").start();
|
|
18
|
+
// Initialize Agent Client
|
|
19
|
+
const client = new AgentClient({
|
|
20
|
+
host: AGENT_HOST,
|
|
21
|
+
agent: "magic-agent",
|
|
22
|
+
name: "default",
|
|
23
|
+
});
|
|
24
|
+
client.addEventListener("open", () => {
|
|
25
|
+
spinner.succeed("Connected to Magic AI Assistant");
|
|
26
|
+
startChatLoop(client);
|
|
27
|
+
});
|
|
28
|
+
client.addEventListener("close", () => {
|
|
29
|
+
spinner.fail("Disconnected from agent");
|
|
30
|
+
process.exit(0);
|
|
31
|
+
});
|
|
32
|
+
client.addEventListener("error", (event) => {
|
|
33
|
+
spinner.fail(`Connection error: ${event.message || "Unknown error"}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
async function startChatLoop(client) {
|
|
38
|
+
console.log(chalk.dim("\nType your message below (type 'exit' to quit)"));
|
|
39
|
+
while (true) {
|
|
40
|
+
const response = await prompts({
|
|
41
|
+
type: "text",
|
|
42
|
+
name: "message",
|
|
43
|
+
message: chalk.cyan("You:"),
|
|
44
|
+
});
|
|
45
|
+
if (!response.message || response.message.toLowerCase() === "exit") {
|
|
46
|
+
client.close();
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
const spinner = ora("Magic AI is thinking...").start();
|
|
50
|
+
let currentResponse = "";
|
|
51
|
+
const messageHandler = (event) => {
|
|
52
|
+
try {
|
|
53
|
+
const data = JSON.parse(event.data);
|
|
54
|
+
if (data.type === "chat_chunk") {
|
|
55
|
+
currentResponse += data.content;
|
|
56
|
+
spinner.text = chalk.gray(currentResponse.split("\n").pop() || "...");
|
|
57
|
+
}
|
|
58
|
+
else if (data.type === "chat_done") {
|
|
59
|
+
spinner.stop();
|
|
60
|
+
console.log(chalk.green("\nMagic AI:"), currentResponse);
|
|
61
|
+
if (data.suggestedTemplate) {
|
|
62
|
+
console.log(chalk.yellow("\n💡 Suggested Template:"), chalk.bold(data.suggestedTemplate));
|
|
63
|
+
console.log(chalk.dim(`Run 'magicappdev init --template ${data.suggestedTemplate}' to use it.`));
|
|
64
|
+
}
|
|
65
|
+
console.log(""); // Spacing
|
|
66
|
+
client.removeEventListener("message", messageHandler);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Ignore parse errors for chunks
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
client.addEventListener("message", messageHandler);
|
|
74
|
+
client.send(JSON.stringify({
|
|
75
|
+
type: "chat",
|
|
76
|
+
content: response.message,
|
|
77
|
+
}));
|
|
78
|
+
// Wait for the response to finish before next prompt
|
|
79
|
+
await new Promise(resolve => {
|
|
80
|
+
const doneHandler = (event) => {
|
|
81
|
+
try {
|
|
82
|
+
const data = JSON.parse(event.data);
|
|
83
|
+
if (data.type === "chat_done" || data.type === "error") {
|
|
84
|
+
client.removeEventListener("message", doneHandler);
|
|
85
|
+
resolve();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Ignore
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
client.addEventListener("message", doneHandler);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,eAAO,MAAM,aAAa,SA0KtB,CAAC"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor command - Diagnose project issues
|
|
3
|
+
*/
|
|
4
|
+
import { header, success, error, warn, info, newline, divider, } from "../lib/ui.js";
|
|
5
|
+
import { createSpinner } from "../lib/spinner.js";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
import * as fs from "node:fs";
|
|
9
|
+
import { execa } from "execa";
|
|
10
|
+
export const doctorCommand = new Command("doctor")
|
|
11
|
+
.description("Diagnose project issues and check environment")
|
|
12
|
+
.option("--fix", "Attempt to fix issues automatically")
|
|
13
|
+
.action(async () => {
|
|
14
|
+
header("Project Diagnostics");
|
|
15
|
+
const checks = [];
|
|
16
|
+
// Check Node.js version
|
|
17
|
+
const spinner = createSpinner("Checking environment...").start();
|
|
18
|
+
try {
|
|
19
|
+
const nodeVersion = process.version;
|
|
20
|
+
const majorVersion = parseInt(nodeVersion.slice(1).split(".")[0]);
|
|
21
|
+
if (majorVersion >= 18) {
|
|
22
|
+
checks.push({
|
|
23
|
+
name: "Node.js",
|
|
24
|
+
status: "pass",
|
|
25
|
+
message: `Version ${nodeVersion}`,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
checks.push({
|
|
30
|
+
name: "Node.js",
|
|
31
|
+
status: "warn",
|
|
32
|
+
message: `Version ${nodeVersion} (recommend 18+)`,
|
|
33
|
+
fix: "nvm install 20",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
checks.push({
|
|
39
|
+
name: "Node.js",
|
|
40
|
+
status: "fail",
|
|
41
|
+
message: "Could not determine version",
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// Check npm/pnpm
|
|
45
|
+
try {
|
|
46
|
+
const { stdout: npmVersion } = await execa("npm", ["--version"]);
|
|
47
|
+
checks.push({
|
|
48
|
+
name: "npm",
|
|
49
|
+
status: "pass",
|
|
50
|
+
message: `Version ${npmVersion.trim()}`,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
checks.push({
|
|
55
|
+
name: "npm",
|
|
56
|
+
status: "fail",
|
|
57
|
+
message: "Not found",
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
// Check pnpm
|
|
61
|
+
try {
|
|
62
|
+
const { stdout: pnpmVersion } = await execa("pnpm", ["--version"]);
|
|
63
|
+
checks.push({
|
|
64
|
+
name: "pnpm",
|
|
65
|
+
status: "pass",
|
|
66
|
+
message: `Version ${pnpmVersion.trim()}`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
checks.push({
|
|
71
|
+
name: "pnpm",
|
|
72
|
+
status: "warn",
|
|
73
|
+
message: "Not found (optional)",
|
|
74
|
+
fix: "npm install -g pnpm",
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// Check git
|
|
78
|
+
try {
|
|
79
|
+
const { stdout: gitVersion } = await execa("git", ["--version"]);
|
|
80
|
+
checks.push({
|
|
81
|
+
name: "Git",
|
|
82
|
+
status: "pass",
|
|
83
|
+
message: gitVersion.trim(),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
checks.push({
|
|
88
|
+
name: "Git",
|
|
89
|
+
status: "fail",
|
|
90
|
+
message: "Not found",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// Check for package.json in current directory
|
|
94
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
95
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
96
|
+
try {
|
|
97
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
98
|
+
checks.push({
|
|
99
|
+
name: "package.json",
|
|
100
|
+
status: "pass",
|
|
101
|
+
message: `Found (${packageJson.name || "unnamed"})`,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
checks.push({
|
|
106
|
+
name: "package.json",
|
|
107
|
+
status: "warn",
|
|
108
|
+
message: "Found but could not parse",
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
checks.push({
|
|
114
|
+
name: "package.json",
|
|
115
|
+
status: "warn",
|
|
116
|
+
message: "Not found in current directory",
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
// Check for node_modules
|
|
120
|
+
const nodeModulesPath = path.join(process.cwd(), "node_modules");
|
|
121
|
+
if (fs.existsSync(nodeModulesPath)) {
|
|
122
|
+
checks.push({
|
|
123
|
+
name: "node_modules",
|
|
124
|
+
status: "pass",
|
|
125
|
+
message: "Dependencies installed",
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
checks.push({
|
|
130
|
+
name: "node_modules",
|
|
131
|
+
status: "warn",
|
|
132
|
+
message: "Not found - run npm install",
|
|
133
|
+
fix: "npm install",
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
spinner.stop();
|
|
137
|
+
// Display results
|
|
138
|
+
newline();
|
|
139
|
+
divider();
|
|
140
|
+
let hasWarnings = false;
|
|
141
|
+
let hasErrors = false;
|
|
142
|
+
for (const check of checks) {
|
|
143
|
+
if (check.status === "pass") {
|
|
144
|
+
success(`${check.name}: ${check.message}`);
|
|
145
|
+
}
|
|
146
|
+
else if (check.status === "warn") {
|
|
147
|
+
warn(`${check.name}: ${check.message}`);
|
|
148
|
+
hasWarnings = true;
|
|
149
|
+
if (check.fix) {
|
|
150
|
+
info(` Fix: ${check.fix}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
error(`${check.name}: ${check.message}`);
|
|
155
|
+
hasErrors = true;
|
|
156
|
+
if (check.fix) {
|
|
157
|
+
info(` Fix: ${check.fix}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
divider();
|
|
162
|
+
newline();
|
|
163
|
+
if (hasErrors) {
|
|
164
|
+
error("Some checks failed. Please fix the issues above.");
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
else if (hasWarnings) {
|
|
168
|
+
warn("Some checks have warnings. Consider addressing them.");
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
success("All checks passed! Your environment is ready.");
|
|
172
|
+
}
|
|
173
|
+
newline();
|
|
174
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/generate/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAkBH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,eAAO,MAAM,eAAe,SAoIzB,CAAC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate command - Generate components, screens, etc.
|
|
3
|
+
*/
|
|
4
|
+
import { generateComponent, buttonComponentTemplate, generateScreen, screenTemplate, } from "@magicappdev/templates";
|
|
5
|
+
import { header, success, error, info, keyValue, newline, } from "../../lib/ui.js";
|
|
6
|
+
import { withSpinner } from "../../lib/spinner.js";
|
|
7
|
+
import { promptText } from "../../lib/prompts.js";
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
export const generateCommand = new Command("generate")
|
|
10
|
+
.alias("g")
|
|
11
|
+
.description("Generate code from templates")
|
|
12
|
+
.addCommand(new Command("component")
|
|
13
|
+
.alias("c")
|
|
14
|
+
.description("Generate a new component")
|
|
15
|
+
.argument("[name]", "Component name")
|
|
16
|
+
.option("-p, --path <path>", "Output path", "./src/components")
|
|
17
|
+
.option("--typescript", "Use TypeScript", true)
|
|
18
|
+
.action(async (name, options) => {
|
|
19
|
+
header("Generate Component");
|
|
20
|
+
try {
|
|
21
|
+
// Get component name
|
|
22
|
+
let componentName = name;
|
|
23
|
+
if (!componentName) {
|
|
24
|
+
componentName = await promptText("What is the component name?", {
|
|
25
|
+
validate: value => {
|
|
26
|
+
if (!value || value.length < 1) {
|
|
27
|
+
return "Component name is required";
|
|
28
|
+
}
|
|
29
|
+
if (!/^[A-Z][a-zA-Z0-9]*$/.test(value)) {
|
|
30
|
+
return "Component name must be PascalCase";
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (!componentName) {
|
|
37
|
+
error("Component name is required");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
const outputDir = options.path || "./src/components";
|
|
41
|
+
newline();
|
|
42
|
+
info("Generating component:");
|
|
43
|
+
keyValue("Name", componentName);
|
|
44
|
+
keyValue("Path", outputDir);
|
|
45
|
+
newline();
|
|
46
|
+
const result = await withSpinner(`Creating ${componentName}...`, async () => {
|
|
47
|
+
return generateComponent(componentName, buttonComponentTemplate, outputDir, {
|
|
48
|
+
typescript: options.typescript ?? true,
|
|
49
|
+
withVariants: true,
|
|
50
|
+
});
|
|
51
|
+
}, { successText: `Created ${componentName}` });
|
|
52
|
+
newline();
|
|
53
|
+
success(`Component "${componentName}" created successfully!`);
|
|
54
|
+
info(`Files created: ${result.files.join(", ")}`);
|
|
55
|
+
newline();
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
error(err instanceof Error ? err.message : "Failed to generate component");
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
}))
|
|
62
|
+
.addCommand(new Command("screen")
|
|
63
|
+
.alias("s")
|
|
64
|
+
.description("Generate a new screen")
|
|
65
|
+
.argument("[name]", "Screen name")
|
|
66
|
+
.option("-p, --path <path>", "Output path", "./src/screens")
|
|
67
|
+
.option("--typescript", "Use TypeScript", true)
|
|
68
|
+
.action(async (name, options) => {
|
|
69
|
+
header("Generate Screen");
|
|
70
|
+
try {
|
|
71
|
+
// Get screen name
|
|
72
|
+
let screenName = name;
|
|
73
|
+
if (!screenName) {
|
|
74
|
+
screenName = await promptText("What is the screen name?", {
|
|
75
|
+
validate: value => {
|
|
76
|
+
if (!value || value.length < 1) {
|
|
77
|
+
return "Screen name is required";
|
|
78
|
+
}
|
|
79
|
+
if (!/^[A-Z][a-zA-Z0-9]*$/.test(value)) {
|
|
80
|
+
return "Screen name must be PascalCase";
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (!screenName) {
|
|
87
|
+
error("Screen name is required");
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
const outputDir = options.path || "./src/screens";
|
|
91
|
+
newline();
|
|
92
|
+
info("Generating screen:");
|
|
93
|
+
keyValue("Name", screenName);
|
|
94
|
+
keyValue("Path", outputDir);
|
|
95
|
+
newline();
|
|
96
|
+
const result = await withSpinner(`Creating ${screenName}...`, async () => {
|
|
97
|
+
return generateScreen(screenName, screenTemplate, outputDir, {
|
|
98
|
+
typescript: options.typescript ?? true,
|
|
99
|
+
});
|
|
100
|
+
}, { successText: `Created ${screenName}` });
|
|
101
|
+
newline();
|
|
102
|
+
success(`Screen "${screenName}" created successfully!`);
|
|
103
|
+
info(`Files created: ${result.files.join(", ")}`);
|
|
104
|
+
newline();
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
error(err instanceof Error ? err.message : "Failed to generate screen");
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;GAEG;AA0BH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,eAAO,MAAM,WAAW,SA6GpB,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Init command - Initialize a new MagicAppDev project
|
|
3
|
+
*/
|
|
4
|
+
import { promptProjectName, promptFramework, promptStyling, promptTypeScript, promptSelect, } from "../lib/prompts.js";
|
|
5
|
+
import { header, logo, success, error, info, keyValue, command, newline, divider, } from "../lib/ui.js";
|
|
6
|
+
import { generateApp, blankAppTemplate, tabsAppTemplate, } from "@magicappdev/templates";
|
|
7
|
+
import { withSpinner } from "../lib/spinner.js";
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
export const initCommand = new Command("init")
|
|
10
|
+
.description("Initialize a new MagicAppDev project")
|
|
11
|
+
.argument("[name]", "Project name")
|
|
12
|
+
.option("-t, --template <template>", "Template to use (blank, tabs)")
|
|
13
|
+
.option("-f, --framework <framework>", "Framework (expo, react-native, next)")
|
|
14
|
+
.option("--typescript", "Use TypeScript", true)
|
|
15
|
+
.option("-y, --yes", "Skip prompts and use defaults")
|
|
16
|
+
.action(async (name, options) => {
|
|
17
|
+
logo();
|
|
18
|
+
header("Create a new project");
|
|
19
|
+
try {
|
|
20
|
+
// Get project name
|
|
21
|
+
let projectName = name;
|
|
22
|
+
if (!projectName && !options.yes) {
|
|
23
|
+
projectName = await promptProjectName();
|
|
24
|
+
}
|
|
25
|
+
if (!projectName) {
|
|
26
|
+
projectName = "my-app";
|
|
27
|
+
}
|
|
28
|
+
// Get template
|
|
29
|
+
let templateSlug = options.template;
|
|
30
|
+
if (!templateSlug && !options.yes) {
|
|
31
|
+
templateSlug = await promptSelect("Choose a template:", [
|
|
32
|
+
{
|
|
33
|
+
title: "Blank",
|
|
34
|
+
value: "blank",
|
|
35
|
+
description: "Minimal starter template",
|
|
36
|
+
},
|
|
37
|
+
{ title: "Tabs", value: "tabs", description: "Tab-based navigation" },
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
if (!templateSlug) {
|
|
41
|
+
templateSlug = "blank";
|
|
42
|
+
}
|
|
43
|
+
// Get framework
|
|
44
|
+
let framework = options.framework;
|
|
45
|
+
if (!framework && !options.yes) {
|
|
46
|
+
framework = await promptFramework();
|
|
47
|
+
}
|
|
48
|
+
if (!framework) {
|
|
49
|
+
framework = "expo";
|
|
50
|
+
}
|
|
51
|
+
// Get TypeScript preference
|
|
52
|
+
let typescript = options.typescript;
|
|
53
|
+
if (typescript === undefined && !options.yes) {
|
|
54
|
+
typescript = await promptTypeScript();
|
|
55
|
+
}
|
|
56
|
+
if (typescript === undefined) {
|
|
57
|
+
typescript = true;
|
|
58
|
+
}
|
|
59
|
+
// Get styling
|
|
60
|
+
let styling;
|
|
61
|
+
if (!options.yes) {
|
|
62
|
+
styling = await promptStyling(framework);
|
|
63
|
+
}
|
|
64
|
+
if (!styling) {
|
|
65
|
+
styling =
|
|
66
|
+
framework === "expo" || framework === "react-native"
|
|
67
|
+
? "nativewind"
|
|
68
|
+
: "tailwind";
|
|
69
|
+
}
|
|
70
|
+
newline();
|
|
71
|
+
divider();
|
|
72
|
+
info("Creating project with:");
|
|
73
|
+
keyValue("Name", projectName);
|
|
74
|
+
keyValue("Template", templateSlug);
|
|
75
|
+
keyValue("Framework", framework);
|
|
76
|
+
keyValue("TypeScript", typescript ? "Yes" : "No");
|
|
77
|
+
keyValue("Styling", styling);
|
|
78
|
+
divider();
|
|
79
|
+
newline();
|
|
80
|
+
// Find template
|
|
81
|
+
const template = templateSlug === "tabs" ? tabsAppTemplate : blankAppTemplate;
|
|
82
|
+
// Generate project
|
|
83
|
+
const outputDir = process.cwd();
|
|
84
|
+
const result = await withSpinner(`Creating ${projectName}...`, async () => {
|
|
85
|
+
return generateApp(projectName, template, outputDir, {
|
|
86
|
+
typescript,
|
|
87
|
+
styling,
|
|
88
|
+
framework,
|
|
89
|
+
});
|
|
90
|
+
}, { successText: `Created ${projectName}` });
|
|
91
|
+
newline();
|
|
92
|
+
success(`Project created successfully!`);
|
|
93
|
+
info(`Files created: ${result.files.length}`);
|
|
94
|
+
newline();
|
|
95
|
+
info("Next steps:");
|
|
96
|
+
command(`cd ${projectName}`);
|
|
97
|
+
command("npm install");
|
|
98
|
+
command("npm start");
|
|
99
|
+
newline();
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
error(err instanceof Error ? err.message : "Failed to create project");
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC9C,cAAc,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAUpD,eAAO,MAAM,UAAU,QAGsB,CAAC;AAE9C,eAAO,MAAM,GAAG,WAAyB,CAAC;AAM1C,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/lib/api.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ApiClient } from "@magicappdev/shared/api";
|
|
2
|
+
import { loadConfig } from "./config.js";
|
|
3
|
+
const config = await loadConfig();
|
|
4
|
+
const API_URL = process.env.MAGICAPPDEV_API_URL ||
|
|
5
|
+
config.apiUrl ||
|
|
6
|
+
"https://magicappdev-api.magicappdev.workers.dev";
|
|
7
|
+
export const AGENT_HOST = process.env.MAGICAPPDEV_AGENT_HOST ||
|
|
8
|
+
config.agentHost ||
|
|
9
|
+
"magicappdev-agent.magicappdev.workers.dev";
|
|
10
|
+
export const api = new ApiClient(API_URL);
|
|
11
|
+
if (config.accessToken) {
|
|
12
|
+
api.setToken(config.accessToken);
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface Config {
|
|
2
|
+
accessToken?: string;
|
|
3
|
+
refreshToken?: string;
|
|
4
|
+
apiUrl?: string;
|
|
5
|
+
agentHost?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function saveConfig(config: Partial<Config>): Promise<void>;
|
|
8
|
+
export declare function loadConfig(): Promise<Config>;
|
|
9
|
+
export declare function clearConfig(): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,MAAM;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD,wBAAsB,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBvE;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAOlD;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAMjD"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
const CONFIG_DIR = join(homedir(), ".magicappdev");
|
|
5
|
+
const CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
6
|
+
export async function saveConfig(config) {
|
|
7
|
+
try {
|
|
8
|
+
await mkdir(CONFIG_DIR, { recursive: true });
|
|
9
|
+
let existingConfig = {};
|
|
10
|
+
try {
|
|
11
|
+
const data = await readFile(CONFIG_FILE, "utf-8");
|
|
12
|
+
existingConfig = JSON.parse(data);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Ignore if file doesn't exist
|
|
16
|
+
}
|
|
17
|
+
const updatedConfig = { ...existingConfig, ...config };
|
|
18
|
+
await writeFile(CONFIG_FILE, JSON.stringify(updatedConfig, null, 2));
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.error("Failed to save config:", error);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function loadConfig() {
|
|
25
|
+
try {
|
|
26
|
+
const data = await readFile(CONFIG_FILE, "utf-8");
|
|
27
|
+
return JSON.parse(data);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export async function clearConfig() {
|
|
34
|
+
try {
|
|
35
|
+
await writeFile(CONFIG_FILE, JSON.stringify({}));
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error("Failed to clear config:", error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive prompts for user input
|
|
3
|
+
*/
|
|
4
|
+
/** Prompt for text input */
|
|
5
|
+
export declare function promptText(message: string, options?: {
|
|
6
|
+
initial?: string;
|
|
7
|
+
validate?: (value: string) => boolean | string;
|
|
8
|
+
}): Promise<string | undefined>;
|
|
9
|
+
/** Prompt for selection from a list */
|
|
10
|
+
export declare function promptSelect<T>(message: string, choices: Array<{
|
|
11
|
+
title: string;
|
|
12
|
+
value: T;
|
|
13
|
+
description?: string;
|
|
14
|
+
}>, options?: {
|
|
15
|
+
initial?: number;
|
|
16
|
+
}): Promise<T | undefined>;
|
|
17
|
+
/** Prompt for confirmation (yes/no) */
|
|
18
|
+
export declare function promptConfirm(message: string, options?: {
|
|
19
|
+
initial?: boolean;
|
|
20
|
+
}): Promise<boolean>;
|
|
21
|
+
/** Prompt for project name with validation */
|
|
22
|
+
export declare function promptProjectName(initial?: string): Promise<string | undefined>;
|
|
23
|
+
/** Prompt for framework selection */
|
|
24
|
+
export declare function promptFramework(): Promise<string | undefined>;
|
|
25
|
+
/** Prompt for styling selection */
|
|
26
|
+
export declare function promptStyling(framework: string): Promise<string | undefined>;
|
|
27
|
+
/** Prompt for TypeScript */
|
|
28
|
+
export declare function promptTypeScript(): Promise<boolean>;
|
|
29
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/lib/prompts.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,4BAA4B;AAC5B,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,MAAM,CAAC;CAC3C,GACL,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAU7B;AAED,uCAAuC;AACvC,wBAAsB,YAAY,CAAC,CAAC,EAClC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EACjE,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,MAAM,CAAC;CACb,GACL,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAUxB;AAED,uCAAuC;AACvC,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IACP,OAAO,CAAC,EAAE,OAAO,CAAC;CACd,GACL,OAAO,CAAC,OAAO,CAAC,CASlB;AAED,8CAA8C;AAC9C,wBAAsB,iBAAiB,CACrC,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAa7B;AAED,qCAAqC;AACrC,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmBnE;AAED,mCAAmC;AACnC,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA0C7B;AAED,4BAA4B;AAC5B,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAEzD"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive prompts for user input
|
|
3
|
+
*/
|
|
4
|
+
import prompts from "prompts";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
/** Prompt for text input */
|
|
7
|
+
export async function promptText(message, options = {}) {
|
|
8
|
+
const response = await prompts({
|
|
9
|
+
type: "text",
|
|
10
|
+
name: "value",
|
|
11
|
+
message,
|
|
12
|
+
initial: options.initial,
|
|
13
|
+
validate: options.validate,
|
|
14
|
+
});
|
|
15
|
+
return response.value;
|
|
16
|
+
}
|
|
17
|
+
/** Prompt for selection from a list */
|
|
18
|
+
export async function promptSelect(message, choices, options = {}) {
|
|
19
|
+
const response = await prompts({
|
|
20
|
+
type: "select",
|
|
21
|
+
name: "value",
|
|
22
|
+
message,
|
|
23
|
+
choices,
|
|
24
|
+
initial: options.initial || 0,
|
|
25
|
+
});
|
|
26
|
+
return response.value;
|
|
27
|
+
}
|
|
28
|
+
/** Prompt for confirmation (yes/no) */
|
|
29
|
+
export async function promptConfirm(message, options = {}) {
|
|
30
|
+
const response = await prompts({
|
|
31
|
+
type: "confirm",
|
|
32
|
+
name: "value",
|
|
33
|
+
message,
|
|
34
|
+
initial: options.initial ?? true,
|
|
35
|
+
});
|
|
36
|
+
return response.value;
|
|
37
|
+
}
|
|
38
|
+
/** Prompt for project name with validation */
|
|
39
|
+
export async function promptProjectName(initial) {
|
|
40
|
+
return promptText("What is your project name?", {
|
|
41
|
+
initial,
|
|
42
|
+
validate: value => {
|
|
43
|
+
if (!value || value.length < 2) {
|
|
44
|
+
return "Project name must be at least 2 characters";
|
|
45
|
+
}
|
|
46
|
+
if (!/^[a-zA-Z][a-zA-Z0-9-_ ]*$/.test(value)) {
|
|
47
|
+
return "Project name must start with a letter and contain only letters, numbers, hyphens, underscores, and spaces";
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/** Prompt for framework selection */
|
|
54
|
+
export async function promptFramework() {
|
|
55
|
+
return promptSelect("Which framework would you like to use?", [
|
|
56
|
+
{
|
|
57
|
+
title: chalk.cyan("Expo") + " (Recommended)",
|
|
58
|
+
value: "expo",
|
|
59
|
+
description: "React Native with Expo",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
title: "React Native (bare)",
|
|
63
|
+
value: "react-native",
|
|
64
|
+
description: "React Native without Expo",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
title: "Next.js",
|
|
68
|
+
value: "next",
|
|
69
|
+
description: "React framework for the web",
|
|
70
|
+
},
|
|
71
|
+
{ title: "Remix", value: "remix", description: "Full stack web framework" },
|
|
72
|
+
]);
|
|
73
|
+
}
|
|
74
|
+
/** Prompt for styling selection */
|
|
75
|
+
export async function promptStyling(framework) {
|
|
76
|
+
const choices = framework === "expo" || framework === "react-native"
|
|
77
|
+
? [
|
|
78
|
+
{
|
|
79
|
+
title: chalk.cyan("NativeWind") + " (Recommended)",
|
|
80
|
+
value: "nativewind",
|
|
81
|
+
description: "Tailwind CSS for React Native",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
title: "StyleSheet",
|
|
85
|
+
value: "stylesheet",
|
|
86
|
+
description: "React Native default styling",
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
title: "Styled Components",
|
|
90
|
+
value: "styled-components",
|
|
91
|
+
description: "CSS-in-JS library",
|
|
92
|
+
},
|
|
93
|
+
]
|
|
94
|
+
: [
|
|
95
|
+
{
|
|
96
|
+
title: chalk.cyan("Tailwind CSS") + " (Recommended)",
|
|
97
|
+
value: "tailwind",
|
|
98
|
+
description: "Utility-first CSS",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
title: "CSS Modules",
|
|
102
|
+
value: "css-modules",
|
|
103
|
+
description: "Scoped CSS",
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
title: "Styled Components",
|
|
107
|
+
value: "styled-components",
|
|
108
|
+
description: "CSS-in-JS library",
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
return promptSelect("Which styling approach would you like to use?", choices);
|
|
112
|
+
}
|
|
113
|
+
/** Prompt for TypeScript */
|
|
114
|
+
export async function promptTypeScript() {
|
|
115
|
+
return promptConfirm("Would you like to use TypeScript?", { initial: true });
|
|
116
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner utility for long-running operations
|
|
3
|
+
*/
|
|
4
|
+
import ora from "ora";
|
|
5
|
+
/** Spinner instance type */
|
|
6
|
+
export type Spinner = ReturnType<typeof ora>;
|
|
7
|
+
/** Create a spinner with default styling */
|
|
8
|
+
export declare function createSpinner(text?: string): Spinner;
|
|
9
|
+
/** Run an async task with a spinner */
|
|
10
|
+
export declare function withSpinner<T>(text: string, task: () => Promise<T>, options?: {
|
|
11
|
+
successText?: string;
|
|
12
|
+
failText?: string;
|
|
13
|
+
}): Promise<T>;
|
|
14
|
+
//# sourceMappingURL=spinner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../../src/lib/spinner.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,GAAG,MAAM,KAAK,CAAC;AAEtB,4BAA4B;AAC5B,MAAM,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,CAAC;AAE7C,4CAA4C;AAC5C,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAMpD;AAED,uCAAuC;AACvC,wBAAsB,WAAW,CAAC,CAAC,EACjC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACtB,OAAO,GAAE;IACP,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACd,GACL,OAAO,CAAC,CAAC,CAAC,CAWZ"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner utility for long-running operations
|
|
3
|
+
*/
|
|
4
|
+
import ora from "ora";
|
|
5
|
+
/** Create a spinner with default styling */
|
|
6
|
+
export function createSpinner(text) {
|
|
7
|
+
return ora({
|
|
8
|
+
text,
|
|
9
|
+
color: "cyan",
|
|
10
|
+
spinner: "dots",
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
/** Run an async task with a spinner */
|
|
14
|
+
export async function withSpinner(text, task, options = {}) {
|
|
15
|
+
const spinner = createSpinner(text).start();
|
|
16
|
+
try {
|
|
17
|
+
const result = await task();
|
|
18
|
+
spinner.succeed(options.successText || text);
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
spinner.fail(options.failText || `Failed: ${text}`);
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
}
|
package/dist/lib/ui.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI UI utilities using Chalk
|
|
3
|
+
*/
|
|
4
|
+
/** Print a header */
|
|
5
|
+
export declare function header(text: string): void;
|
|
6
|
+
/** Print the app logo */
|
|
7
|
+
export declare function logo(): void;
|
|
8
|
+
/** Print a success message */
|
|
9
|
+
export declare function success(text: string): void;
|
|
10
|
+
/** Print an error message */
|
|
11
|
+
export declare function error(text: string): void;
|
|
12
|
+
/** Print a warning message */
|
|
13
|
+
export declare function warn(text: string): void;
|
|
14
|
+
/** Print an info message */
|
|
15
|
+
export declare function info(text: string): void;
|
|
16
|
+
/** Print a step message */
|
|
17
|
+
export declare function step(number: number, text: string): void;
|
|
18
|
+
/** Print a key-value pair */
|
|
19
|
+
export declare function keyValue(key: string, value: string): void;
|
|
20
|
+
/** Print a list item */
|
|
21
|
+
export declare function listItem(text: string, indent?: number): void;
|
|
22
|
+
/** Print a code block */
|
|
23
|
+
export declare function code(text: string): void;
|
|
24
|
+
/** Print a command suggestion */
|
|
25
|
+
export declare function command(text: string): void;
|
|
26
|
+
/** Print a divider */
|
|
27
|
+
export declare function divider(): void;
|
|
28
|
+
/** Print a newline */
|
|
29
|
+
export declare function newline(): void;
|
|
30
|
+
/** Color function type */
|
|
31
|
+
type ColorFn = (text: string | number) => string;
|
|
32
|
+
/** Colors for exports */
|
|
33
|
+
export declare const colors: Record<string, ColorFn>;
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=ui.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/lib/ui.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,qBAAqB;AACrB,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAUzC;AAED,yBAAyB;AACzB,wBAAgB,IAAI,IAAI,IAAI,CAQ3B;AAED,8BAA8B;AAC9B,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,6BAA6B;AAC7B,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAExC;AAED,8BAA8B;AAC9B,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEvC;AAED,4BAA4B;AAC5B,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEvC;AAED,2BAA2B;AAC3B,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED,6BAA6B;AAC7B,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEzD;AAED,wBAAwB;AACxB,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,IAAI,CAEvD;AAED,yBAAyB;AACzB,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEvC;AAED,iCAAiC;AACjC,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,sBAAsB;AACtB,wBAAgB,OAAO,IAAI,IAAI,CAE9B;AAED,sBAAsB;AACtB,wBAAgB,OAAO,IAAI,IAAI,CAE9B;AAED,0BAA0B;AAC1B,KAAK,OAAO,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,KAAK,MAAM,CAAC;AAEjD,yBAAyB;AACzB,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAU1C,CAAC"}
|
package/dist/lib/ui.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI UI utilities using Chalk
|
|
3
|
+
*/
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
/** Print a header */
|
|
6
|
+
export function header(text) {
|
|
7
|
+
console.log();
|
|
8
|
+
console.log(chalk.bold.cyan("╭─────────────────────────────────────────╮"));
|
|
9
|
+
console.log(chalk.bold.cyan("│") +
|
|
10
|
+
chalk.bold.white(` ${text.padEnd(39)} `) +
|
|
11
|
+
chalk.bold.cyan("│"));
|
|
12
|
+
console.log(chalk.bold.cyan("╰─────────────────────────────────────────╯"));
|
|
13
|
+
console.log();
|
|
14
|
+
}
|
|
15
|
+
/** Print the app logo */
|
|
16
|
+
export function logo() {
|
|
17
|
+
console.log(chalk.bold.magenta(`
|
|
18
|
+
╔╦╗╔═╗╔═╗╦╔═╗ ╔═╗╔═╗╔═╗╔╦╗╔═╗╦ ╦
|
|
19
|
+
║║║╠═╣║ ╦║║ ╠═╣╠═╝╠═╝ ║║║╣ ╚╗╔╝
|
|
20
|
+
╩ ╩╩ ╩╚═╝╩╚═╝ ╩ ╩╩ ╩ ═╩╝╚═╝ ╚╝
|
|
21
|
+
`));
|
|
22
|
+
}
|
|
23
|
+
/** Print a success message */
|
|
24
|
+
export function success(text) {
|
|
25
|
+
console.log(chalk.green("✓") + " " + text);
|
|
26
|
+
}
|
|
27
|
+
/** Print an error message */
|
|
28
|
+
export function error(text) {
|
|
29
|
+
console.log(chalk.red("✗") + " " + text);
|
|
30
|
+
}
|
|
31
|
+
/** Print a warning message */
|
|
32
|
+
export function warn(text) {
|
|
33
|
+
console.log(chalk.yellow("⚠") + " " + text);
|
|
34
|
+
}
|
|
35
|
+
/** Print an info message */
|
|
36
|
+
export function info(text) {
|
|
37
|
+
console.log(chalk.blue("ℹ") + " " + text);
|
|
38
|
+
}
|
|
39
|
+
/** Print a step message */
|
|
40
|
+
export function step(number, text) {
|
|
41
|
+
console.log(chalk.dim(`[${number}]`) + " " + text);
|
|
42
|
+
}
|
|
43
|
+
/** Print a key-value pair */
|
|
44
|
+
export function keyValue(key, value) {
|
|
45
|
+
console.log(chalk.dim(key + ":") + " " + chalk.white(value));
|
|
46
|
+
}
|
|
47
|
+
/** Print a list item */
|
|
48
|
+
export function listItem(text, indent = 2) {
|
|
49
|
+
console.log(" ".repeat(indent) + chalk.dim("•") + " " + text);
|
|
50
|
+
}
|
|
51
|
+
/** Print a code block */
|
|
52
|
+
export function code(text) {
|
|
53
|
+
console.log(chalk.gray.inverse(` ${text} `));
|
|
54
|
+
}
|
|
55
|
+
/** Print a command suggestion */
|
|
56
|
+
export function command(text) {
|
|
57
|
+
console.log(chalk.cyan("$") + " " + chalk.bold(text));
|
|
58
|
+
}
|
|
59
|
+
/** Print a divider */
|
|
60
|
+
export function divider() {
|
|
61
|
+
console.log(chalk.dim("─".repeat(45)));
|
|
62
|
+
}
|
|
63
|
+
/** Print a newline */
|
|
64
|
+
export function newline() {
|
|
65
|
+
console.log();
|
|
66
|
+
}
|
|
67
|
+
/** Colors for exports */
|
|
68
|
+
export const colors = {
|
|
69
|
+
primary: chalk.cyan,
|
|
70
|
+
secondary: chalk.magenta,
|
|
71
|
+
success: chalk.green,
|
|
72
|
+
error: chalk.red,
|
|
73
|
+
warning: chalk.yellow,
|
|
74
|
+
info: chalk.blue,
|
|
75
|
+
muted: chalk.dim,
|
|
76
|
+
bold: chalk.bold,
|
|
77
|
+
white: chalk.white,
|
|
78
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@magicappdev/cli",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "CLI tool for creating and managing MagicAppDev apps",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"createmagicapp": "./bin/cli.js",
|
|
10
|
+
"mad": "./bin/cli.js",
|
|
11
|
+
"magicappdev": "./bin/cli.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"bin",
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc -p tsconfig.lib.json",
|
|
19
|
+
"dev": "tsx src/cli.ts",
|
|
20
|
+
"lint": "eslint .",
|
|
21
|
+
"lint:fix": "eslint . --fix",
|
|
22
|
+
"typecheck": "tsc -b"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@magicappdev/shared": "workspace:*",
|
|
26
|
+
"@magicappdev/templates": "workspace:*",
|
|
27
|
+
"agents": "^0.3.6",
|
|
28
|
+
"chalk": "^5.4.0",
|
|
29
|
+
"commander": "^13.0.0",
|
|
30
|
+
"execa": "^9.5.0",
|
|
31
|
+
"open": "^11.0.0",
|
|
32
|
+
"ora": "^8.1.0",
|
|
33
|
+
"prompts": "^2.4.0",
|
|
34
|
+
"tslib": "^2.3.0",
|
|
35
|
+
"ws": "^8.18.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^25.0.0",
|
|
39
|
+
"@types/prompts": "^2.4.0",
|
|
40
|
+
"@types/ws": "^8.5.10",
|
|
41
|
+
"eslint": "^9.39.2",
|
|
42
|
+
"eslint-config-prettier": "^10.1.8",
|
|
43
|
+
"tsx": "^4.19.0",
|
|
44
|
+
"typescript": "~5.8.2",
|
|
45
|
+
"typescript-eslint": "^8.53.1"
|
|
46
|
+
}
|
|
47
|
+
}
|