create-sitekick 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/README.md +1 -1
- package/dist/index.js +101 -88
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,7 +110,7 @@ The CLI handles authentication and resource creation for each service:
|
|
|
110
110
|
| **Neon** (`neonctl`) | Provisions a Postgres database, returns the connection string |
|
|
111
111
|
| **Vercel** (`vercel`) | Creates a project, links to GitHub, pushes env vars |
|
|
112
112
|
| **Sanity** (`sanity`) | Creates a Sanity project and production dataset |
|
|
113
|
-
| **OpenAI** | Stores your API key for AI-powered
|
|
113
|
+
| **OpenAI** | Stores your API key for AI-powered features |
|
|
114
114
|
|
|
115
115
|
Each step checks if the CLI tool is installed (and offers to install it), verifies authentication (and prompts login if needed), and asks whether to create a new resource or link an existing one.
|
|
116
116
|
|
package/dist/index.js
CHANGED
|
@@ -1269,57 +1269,100 @@ function runInteractive(cmd, opts) {
|
|
|
1269
1269
|
function isInstalled(cmd) {
|
|
1270
1270
|
return run(`which ${cmd}`) !== null;
|
|
1271
1271
|
}
|
|
1272
|
+
var scaffoldedDir = null;
|
|
1273
|
+
function abort(message = "Cancelled.") {
|
|
1274
|
+
if (scaffoldedDir && fs.existsSync(scaffoldedDir)) {
|
|
1275
|
+
fs.rmSync(scaffoldedDir, { recursive: true, force: true });
|
|
1276
|
+
}
|
|
1277
|
+
Ne(message);
|
|
1278
|
+
process.exit(0);
|
|
1279
|
+
}
|
|
1272
1280
|
async function cancelOrContinue(message) {
|
|
1273
1281
|
const result = await Re({ message, initialValue: true });
|
|
1274
|
-
if (Ct(result))
|
|
1275
|
-
|
|
1276
|
-
process.exit(0);
|
|
1277
|
-
}
|
|
1282
|
+
if (Ct(result))
|
|
1283
|
+
abort();
|
|
1278
1284
|
return result;
|
|
1279
1285
|
}
|
|
1280
1286
|
var TOOLS = {
|
|
1281
1287
|
gh: {
|
|
1282
1288
|
name: "GitHub CLI",
|
|
1283
1289
|
cmd: "gh",
|
|
1284
|
-
|
|
1290
|
+
brewPkg: "gh"
|
|
1285
1291
|
},
|
|
1286
1292
|
vercel: {
|
|
1287
1293
|
name: "Vercel CLI",
|
|
1288
1294
|
cmd: "vercel",
|
|
1289
|
-
|
|
1295
|
+
bunPkg: "vercel"
|
|
1290
1296
|
},
|
|
1291
1297
|
neonctl: {
|
|
1292
1298
|
name: "Neon CLI",
|
|
1293
1299
|
cmd: "neonctl",
|
|
1294
|
-
|
|
1300
|
+
bunPkg: "neonctl"
|
|
1295
1301
|
},
|
|
1296
1302
|
sanity: {
|
|
1297
1303
|
name: "Sanity CLI",
|
|
1298
1304
|
cmd: "sanity",
|
|
1299
|
-
|
|
1305
|
+
bunPkg: "sanity"
|
|
1300
1306
|
}
|
|
1301
1307
|
};
|
|
1308
|
+
async function ensureBrew(s) {
|
|
1309
|
+
if (isInstalled("brew"))
|
|
1310
|
+
return true;
|
|
1311
|
+
const shouldInstall = await cancelOrContinue("Homebrew is needed to install some tools. Install it now?");
|
|
1312
|
+
if (!shouldInstall)
|
|
1313
|
+
return false;
|
|
1314
|
+
s.start("Installing Homebrew...");
|
|
1315
|
+
try {
|
|
1316
|
+
execSync('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"', { stdio: "inherit" });
|
|
1317
|
+
} catch {
|
|
1318
|
+
s.stop("Failed to install Homebrew");
|
|
1319
|
+
return false;
|
|
1320
|
+
}
|
|
1321
|
+
const brewPaths = ["/opt/homebrew/bin/brew", "/usr/local/bin/brew"];
|
|
1322
|
+
for (const brewPath of brewPaths) {
|
|
1323
|
+
try {
|
|
1324
|
+
const shellEnv = execSync(`${brewPath} shellenv`, { encoding: "utf-8" });
|
|
1325
|
+
for (const line of shellEnv.split(`
|
|
1326
|
+
`)) {
|
|
1327
|
+
const match = line.match(/export\s+PATH="([^"]+)"/);
|
|
1328
|
+
if (match)
|
|
1329
|
+
process.env.PATH = `${match[1]}:${process.env.PATH}`;
|
|
1330
|
+
}
|
|
1331
|
+
break;
|
|
1332
|
+
} catch {}
|
|
1333
|
+
}
|
|
1334
|
+
if (!isInstalled("brew")) {
|
|
1335
|
+
s.stop("Homebrew installed but not found on PATH. Restart your terminal and try again.");
|
|
1336
|
+
return false;
|
|
1337
|
+
}
|
|
1338
|
+
s.stop("Homebrew installed");
|
|
1339
|
+
return true;
|
|
1340
|
+
}
|
|
1302
1341
|
async function ensureTool(key, s) {
|
|
1303
1342
|
const tool = TOOLS[key];
|
|
1304
1343
|
if (isInstalled(tool.cmd))
|
|
1305
1344
|
return true;
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1345
|
+
const shouldInstall = await cancelOrContinue(`${tool.name} (${tool.cmd}) is not installed. Install it now?`);
|
|
1346
|
+
if (!shouldInstall)
|
|
1347
|
+
return false;
|
|
1348
|
+
let installCmd;
|
|
1349
|
+
if (tool.bunPkg) {
|
|
1350
|
+
installCmd = `bun add -g ${tool.bunPkg}`;
|
|
1351
|
+
} else if (tool.brewPkg) {
|
|
1352
|
+
if (!await ensureBrew(s))
|
|
1314
1353
|
return false;
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
return
|
|
1354
|
+
installCmd = `brew install ${tool.brewPkg}`;
|
|
1355
|
+
} else {
|
|
1356
|
+
return false;
|
|
1357
|
+
}
|
|
1358
|
+
s.start(`Installing ${tool.name}...`);
|
|
1359
|
+
const result = run(installCmd);
|
|
1360
|
+
if (result === null) {
|
|
1361
|
+
s.stop(`Failed to install ${tool.name}`);
|
|
1362
|
+
return false;
|
|
1318
1363
|
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
const ready = await cancelOrContinue(`Have you installed ${tool.cmd}? Continue?`);
|
|
1322
|
-
return ready && isInstalled(tool.cmd);
|
|
1364
|
+
s.stop(`${tool.name} installed`);
|
|
1365
|
+
return true;
|
|
1323
1366
|
}
|
|
1324
1367
|
async function ensureAuth(toolName, checkCmd, authCmd, s) {
|
|
1325
1368
|
s.start(`Checking ${toolName} authentication...`);
|
|
@@ -1360,10 +1403,8 @@ async function selectGitHubOrg() {
|
|
|
1360
1403
|
...orgs.map((org) => ({ value: org, label: org }))
|
|
1361
1404
|
]
|
|
1362
1405
|
});
|
|
1363
|
-
if (Ct(selected))
|
|
1364
|
-
|
|
1365
|
-
process.exit(0);
|
|
1366
|
-
}
|
|
1406
|
+
if (Ct(selected))
|
|
1407
|
+
abort();
|
|
1367
1408
|
return selected === "__personal__" ? null : selected;
|
|
1368
1409
|
}
|
|
1369
1410
|
async function selectVercelTeam() {
|
|
@@ -1402,10 +1443,8 @@ async function selectVercelTeam() {
|
|
|
1402
1443
|
}))
|
|
1403
1444
|
]
|
|
1404
1445
|
});
|
|
1405
|
-
if (Ct(selected))
|
|
1406
|
-
|
|
1407
|
-
process.exit(0);
|
|
1408
|
-
}
|
|
1446
|
+
if (Ct(selected))
|
|
1447
|
+
abort();
|
|
1409
1448
|
return selected === "__personal__" ? null : selected;
|
|
1410
1449
|
}
|
|
1411
1450
|
async function selectNeonOrg() {
|
|
@@ -1427,10 +1466,8 @@ async function selectNeonOrg() {
|
|
|
1427
1466
|
}))
|
|
1428
1467
|
]
|
|
1429
1468
|
});
|
|
1430
|
-
if (Ct(selected))
|
|
1431
|
-
|
|
1432
|
-
process.exit(0);
|
|
1433
|
-
}
|
|
1469
|
+
if (Ct(selected))
|
|
1470
|
+
abort();
|
|
1434
1471
|
return selected === "__personal__" ? null : selected;
|
|
1435
1472
|
} catch {
|
|
1436
1473
|
return null;
|
|
@@ -1449,10 +1486,8 @@ async function setupGitHub(projectName, targetDir, s) {
|
|
|
1449
1486
|
{ value: "skip", label: "Skip GitHub setup" }
|
|
1450
1487
|
]
|
|
1451
1488
|
});
|
|
1452
|
-
if (Ct(existing))
|
|
1453
|
-
|
|
1454
|
-
process.exit(0);
|
|
1455
|
-
}
|
|
1489
|
+
if (Ct(existing))
|
|
1490
|
+
abort();
|
|
1456
1491
|
if (existing === "skip")
|
|
1457
1492
|
return null;
|
|
1458
1493
|
if (existing === "existing") {
|
|
@@ -1464,10 +1499,8 @@ async function setupGitHub(projectName, targetDir, s) {
|
|
|
1464
1499
|
return "Repo URL is required";
|
|
1465
1500
|
}
|
|
1466
1501
|
});
|
|
1467
|
-
if (Ct(repoUrl2))
|
|
1468
|
-
|
|
1469
|
-
process.exit(0);
|
|
1470
|
-
}
|
|
1502
|
+
if (Ct(repoUrl2))
|
|
1503
|
+
abort();
|
|
1471
1504
|
s.start("Linking to existing repo...");
|
|
1472
1505
|
run(`git remote add origin ${repoUrl2}`, { cwd: targetDir });
|
|
1473
1506
|
s.stop("Linked to existing repo");
|
|
@@ -1481,10 +1514,8 @@ async function setupGitHub(projectName, targetDir, s) {
|
|
|
1481
1514
|
{ value: "public", label: "Public" }
|
|
1482
1515
|
]
|
|
1483
1516
|
});
|
|
1484
|
-
if (Ct(visibility))
|
|
1485
|
-
|
|
1486
|
-
process.exit(0);
|
|
1487
|
-
}
|
|
1517
|
+
if (Ct(visibility))
|
|
1518
|
+
abort();
|
|
1488
1519
|
const repoFullName = ghOrg ? `${ghOrg}/${projectName}` : projectName;
|
|
1489
1520
|
s.start(`Creating GitHub repo ${import_picocolors3.default.cyan(repoFullName)}...`);
|
|
1490
1521
|
const result = run(`gh repo create ${repoFullName} --${visibility} --source . --remote origin`, { cwd: targetDir });
|
|
@@ -1509,10 +1540,8 @@ async function setupNeon(projectName, s) {
|
|
|
1509
1540
|
{ value: "skip", label: "Skip database setup" }
|
|
1510
1541
|
]
|
|
1511
1542
|
});
|
|
1512
|
-
if (Ct(existing))
|
|
1513
|
-
|
|
1514
|
-
process.exit(0);
|
|
1515
|
-
}
|
|
1543
|
+
if (Ct(existing))
|
|
1544
|
+
abort();
|
|
1516
1545
|
if (existing === "skip")
|
|
1517
1546
|
return null;
|
|
1518
1547
|
if (existing === "existing") {
|
|
@@ -1526,10 +1555,8 @@ async function setupNeon(projectName, s) {
|
|
|
1526
1555
|
return "Must be a PostgreSQL connection string";
|
|
1527
1556
|
}
|
|
1528
1557
|
});
|
|
1529
|
-
if (Ct(connStr))
|
|
1530
|
-
|
|
1531
|
-
process.exit(0);
|
|
1532
|
-
}
|
|
1558
|
+
if (Ct(connStr))
|
|
1559
|
+
abort();
|
|
1533
1560
|
return connStr;
|
|
1534
1561
|
}
|
|
1535
1562
|
const neonOrgId = await selectNeonOrg();
|
|
@@ -1583,10 +1610,8 @@ async function setupSanityProject(projectName, s) {
|
|
|
1583
1610
|
{ value: "skip", label: "Skip Sanity setup" }
|
|
1584
1611
|
]
|
|
1585
1612
|
});
|
|
1586
|
-
if (Ct(existing))
|
|
1587
|
-
|
|
1588
|
-
process.exit(0);
|
|
1589
|
-
}
|
|
1613
|
+
if (Ct(existing))
|
|
1614
|
+
abort();
|
|
1590
1615
|
if (existing === "skip")
|
|
1591
1616
|
return null;
|
|
1592
1617
|
if (existing === "existing") {
|
|
@@ -1598,19 +1623,15 @@ async function setupSanityProject(projectName, s) {
|
|
|
1598
1623
|
return "Project ID is required";
|
|
1599
1624
|
}
|
|
1600
1625
|
});
|
|
1601
|
-
if (Ct(projectId))
|
|
1602
|
-
|
|
1603
|
-
process.exit(0);
|
|
1604
|
-
}
|
|
1626
|
+
if (Ct(projectId))
|
|
1627
|
+
abort();
|
|
1605
1628
|
const dataset = await Ze({
|
|
1606
1629
|
message: "Dataset name:",
|
|
1607
1630
|
placeholder: "production",
|
|
1608
1631
|
initialValue: "production"
|
|
1609
1632
|
});
|
|
1610
|
-
if (Ct(dataset))
|
|
1611
|
-
|
|
1612
|
-
process.exit(0);
|
|
1613
|
-
}
|
|
1633
|
+
if (Ct(dataset))
|
|
1634
|
+
abort();
|
|
1614
1635
|
return { projectId, dataset };
|
|
1615
1636
|
}
|
|
1616
1637
|
s.start(`Creating Sanity project ${import_picocolors3.default.cyan(projectName)}...`);
|
|
@@ -1647,10 +1668,8 @@ async function setupVercel(projectName, targetDir, repoUrl, s) {
|
|
|
1647
1668
|
{ value: "skip", label: "Skip Vercel setup" }
|
|
1648
1669
|
]
|
|
1649
1670
|
});
|
|
1650
|
-
if (Ct(existing))
|
|
1651
|
-
|
|
1652
|
-
process.exit(0);
|
|
1653
|
-
}
|
|
1671
|
+
if (Ct(existing))
|
|
1672
|
+
abort();
|
|
1654
1673
|
if (existing === "skip")
|
|
1655
1674
|
return { url: null, scope: null };
|
|
1656
1675
|
const vercelTeam = await selectVercelTeam();
|
|
@@ -1705,16 +1724,14 @@ async function pushEnvToVercel(envVars, targetDir, scope, s) {
|
|
|
1705
1724
|
}
|
|
1706
1725
|
async function askOpenAIKey() {
|
|
1707
1726
|
const setup = await Je({
|
|
1708
|
-
message: "OpenAI API key
|
|
1727
|
+
message: "OpenAI API key:",
|
|
1709
1728
|
options: [
|
|
1710
1729
|
{ value: "enter", label: "Enter API key now" },
|
|
1711
1730
|
{ value: "skip", label: "Skip — set up later" }
|
|
1712
1731
|
]
|
|
1713
1732
|
});
|
|
1714
|
-
if (Ct(setup))
|
|
1715
|
-
|
|
1716
|
-
process.exit(0);
|
|
1717
|
-
}
|
|
1733
|
+
if (Ct(setup))
|
|
1734
|
+
abort();
|
|
1718
1735
|
if (setup === "skip")
|
|
1719
1736
|
return null;
|
|
1720
1737
|
const key = await Ze({
|
|
@@ -1727,10 +1744,8 @@ async function askOpenAIKey() {
|
|
|
1727
1744
|
return "OpenAI keys start with sk-";
|
|
1728
1745
|
}
|
|
1729
1746
|
});
|
|
1730
|
-
if (Ct(key))
|
|
1731
|
-
|
|
1732
|
-
process.exit(0);
|
|
1733
|
-
}
|
|
1747
|
+
if (Ct(key))
|
|
1748
|
+
abort();
|
|
1734
1749
|
return key;
|
|
1735
1750
|
}
|
|
1736
1751
|
function configureTemplate(targetDir, cms, projectName) {
|
|
@@ -1862,10 +1877,8 @@ async function main() {
|
|
|
1862
1877
|
return "Use lowercase letters, numbers, and hyphens only";
|
|
1863
1878
|
}
|
|
1864
1879
|
});
|
|
1865
|
-
if (Ct(projectName))
|
|
1866
|
-
|
|
1867
|
-
process.exit(0);
|
|
1868
|
-
}
|
|
1880
|
+
if (Ct(projectName))
|
|
1881
|
+
abort();
|
|
1869
1882
|
const cms = await Je({
|
|
1870
1883
|
message: "Which CMS do you want to use?",
|
|
1871
1884
|
options: [
|
|
@@ -1881,19 +1894,19 @@ async function main() {
|
|
|
1881
1894
|
}
|
|
1882
1895
|
]
|
|
1883
1896
|
});
|
|
1884
|
-
if (Ct(cms))
|
|
1885
|
-
|
|
1886
|
-
process.exit(0);
|
|
1887
|
-
}
|
|
1897
|
+
if (Ct(cms))
|
|
1898
|
+
abort();
|
|
1888
1899
|
const targetDir = path.resolve(process.cwd(), projectName);
|
|
1889
1900
|
if (fs.existsSync(targetDir)) {
|
|
1890
1901
|
Ne(`Directory ${import_picocolors3.default.cyan(projectName)} already exists.`);
|
|
1891
1902
|
process.exit(1);
|
|
1892
1903
|
}
|
|
1893
1904
|
const s = bt2();
|
|
1905
|
+
await ensureBrew(s);
|
|
1894
1906
|
s.start("Cloning sitekick-starter...");
|
|
1895
1907
|
execSync(`git clone --depth 1 https://github.com/sitekickcodes/sitekick-starter.git ${targetDir}`, { stdio: "pipe" });
|
|
1896
1908
|
fs.rmSync(path.join(targetDir, ".git"), { recursive: true, force: true });
|
|
1909
|
+
scaffoldedDir = targetDir;
|
|
1897
1910
|
s.stop("Cloned template");
|
|
1898
1911
|
s.start(`Configuring for ${cms === "payload" ? "Payload CMS" : "Sanity"}...`);
|
|
1899
1912
|
configureTemplate(targetDir, cms, projectName);
|